@@ -20,7 +20,7 @@ module Secretariat
2020
2121 LineItem = Struct . new ( 'LineItem' ,
2222 :name ,
23- :quantity ,
23+ :billed_quantity ,
2424 :unit ,
2525 :gross_amount ,
2626 :net_amount ,
@@ -34,11 +34,36 @@ module Secretariat
3434 :currency_code ,
3535 :service_period_start , # if start present start & end are required
3636 :service_period_end , # end has to be on or after start (secretariat does not validate this)
37+ :basis_quantity ,
3738 keyword_init : true
3839 ) do
3940
4041 include Versioner
4142
43+ def initialize ( **kwargs )
44+ if kwargs . key? ( :quantity ) && !kwargs . key? ( :billed_quantity )
45+ kwargs [ :billed_quantity ] = kwargs . delete ( :quantity )
46+ end
47+ super ( **kwargs )
48+ end
49+
50+ def quantity
51+ warn_once_quantity
52+ billed_quantity
53+ end
54+
55+ def quantity = ( val )
56+ warn_once_quantity
57+ self . billed_quantity = val
58+ end
59+
60+ def warn_once_quantity
61+ unless @__warned_quantity
62+ Kernel . warn ( "[secretariat] LineItem#quantity is deprecated; use #billed_quantity" )
63+ @__warned_quantity = true
64+ end
65+ end
66+
4267 def errors
4368 @errors
4469 end
@@ -49,7 +74,7 @@ def valid?
4974 gross_price = BigDecimal ( gross_amount )
5075 charge_price = BigDecimal ( charge_amount )
5176 tax = BigDecimal ( tax_amount )
52- unit_price = net_price * BigDecimal ( quantity . abs )
77+ unit_price = net_price * BigDecimal ( billed_quantity . abs )
5378
5479 if charge_price != unit_price
5580 @errors << "charge price and gross price times quantity deviate: #{ charge_price } / #{ unit_price } "
@@ -67,7 +92,7 @@ def valid?
6792 self . tax_percent ||= BigDecimal ( 0 )
6893 calculated_tax = charge_price * BigDecimal ( tax_percent ) / BigDecimal ( 100 )
6994 calculated_tax = calculated_tax . round ( 2 )
70- calculated_tax = -calculated_tax if quantity . negative?
95+ calculated_tax = -calculated_tax if billed_quantity . negative?
7196 if calculated_tax != tax
7297 @errors << "Tax and calculated tax deviate: #{ tax } / #{ calculated_tax } "
7398 return false
@@ -80,6 +105,13 @@ def unit_code
80105 UNIT_CODES [ unit ] || 'C62'
81106 end
82107
108+ # If not provided, BasisQuantity should be 1.0 (price per 1 unit)
109+ def effective_basis_quantity
110+ q = basis_quantity . nil? ? BigDecimal ( "1.0" ) : BigDecimal ( basis_quantity . to_s )
111+ raise ArgumentError , "basis_quantity must be > 0" if q <= 0
112+ q
113+ end
114+
83115 def tax_category_code ( version : 2 )
84116 if version == 1
85117 return TAX_CATEGORY_CODES_1 [ tax_category ] || 'S'
@@ -105,7 +137,7 @@ def to_xml(xml, line_item_index, version: 2, validate: true)
105137 if net_price &.negative?
106138 # Zugferd doesn't allow negative amounts at the item level.
107139 # Instead, a negative quantity is used.
108- self . quantity = -quantity
140+ self . billed_quantity = -billed_quantity
109141 self . gross_amount = gross_price &.abs
110142 self . net_amount = net_price &.abs
111143 self . charge_amount = charge_price &.abs
@@ -134,7 +166,7 @@ def to_xml(xml, line_item_index, version: 2, validate: true)
134166 Helpers . currency_element ( xml , 'ram' , 'ChargeAmount' , gross_amount , currency_code , add_currency : version == 1 , digits : 4 )
135167 if version == 2 && discount_amount
136168 xml [ 'ram' ] . BasisQuantity ( unitCode : unit_code ) do
137- xml . text ( Helpers . format ( quantity , digits : 4 ) )
169+ xml . text ( Helpers . format ( effective_basis_quantity , digits : 4 ) )
138170 end
139171 xml [ 'ram' ] . AppliedTradeAllowanceCharge do
140172 xml [ 'ram' ] . ChargeIndicator do
@@ -158,7 +190,7 @@ def to_xml(xml, line_item_index, version: 2, validate: true)
158190 Helpers . currency_element ( xml , 'ram' , 'ChargeAmount' , net_amount , currency_code , add_currency : version == 1 , digits : 4 )
159191 if version == 2
160192 xml [ 'ram' ] . BasisQuantity ( unitCode : unit_code ) do
161- xml . text ( Helpers . format ( quantity , digits : 4 ) )
193+ xml . text ( Helpers . format ( effective_basis_quantity , digits : 4 ) )
162194 end
163195 end
164196 end
@@ -168,7 +200,7 @@ def to_xml(xml, line_item_index, version: 2, validate: true)
168200
169201 xml [ 'ram' ] . send ( delivery ) do
170202 xml [ 'ram' ] . BilledQuantity ( unitCode : unit_code ) do
171- xml . text ( Helpers . format ( quantity , digits : 4 ) )
203+ xml . text ( Helpers . format ( billed_quantity , digits : 4 ) )
172204 end
173205 end
174206
@@ -201,7 +233,7 @@ def to_xml(xml, line_item_index, version: 2, validate: true)
201233
202234 monetary_summation = by_version ( version , 'SpecifiedTradeSettlementMonetarySummation' , 'SpecifiedTradeSettlementLineMonetarySummation' )
203235 xml [ 'ram' ] . send ( monetary_summation ) do
204- Helpers . currency_element ( xml , 'ram' , 'LineTotalAmount' , ( quantity . negative? ? -charge_amount : charge_amount ) , currency_code , add_currency : version == 1 )
236+ Helpers . currency_element ( xml , 'ram' , 'LineTotalAmount' , ( billed_quantity . negative? ? -charge_amount : charge_amount ) , currency_code , add_currency : version == 1 )
205237 end
206238 end
207239
0 commit comments