Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/secretariat/invoice.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ def taxes
line_items.each do |line_item|
if line_item.tax_percent.nil?
taxes['0'] = Tax.new(tax_percent: BigDecimal(0), tax_category: line_item.tax_category, tax_amount: BigDecimal(0)) if taxes['0'].nil?
taxes['0'].base_amount += BigDecimal(line_item.net_amount) * line_item.quantity
taxes['0'].base_amount += BigDecimal(line_item.net_amount) * line_item.billed_quantity
else
taxes[line_item.tax_percent] = Tax.new(tax_percent: BigDecimal(line_item.tax_percent), tax_category: line_item.tax_category) if taxes[line_item.tax_percent].nil?
taxes[line_item.tax_percent].tax_amount += BigDecimal(line_item.tax_amount)
taxes[line_item.tax_percent].base_amount += BigDecimal(line_item.net_amount) * line_item.quantity
taxes[line_item.tax_percent].base_amount += BigDecimal(line_item.net_amount) * line_item.billed_quantity
end
end

Expand Down Expand Up @@ -140,7 +140,7 @@ def valid?
return false
end
line_item_sum = line_items.inject(BigDecimal(0)) do |m, item|
m + BigDecimal(item.quantity.negative? ? -item.charge_amount : item.charge_amount)
m + BigDecimal(item.billed_quantity.negative? ? -item.charge_amount : item.charge_amount)
end
if line_item_sum != basis
@errors << "Line items do not add up to basis amount #{line_item_sum} / #{basis}"
Expand Down
48 changes: 40 additions & 8 deletions lib/secretariat/line_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module Secretariat

LineItem = Struct.new('LineItem',
:name,
:quantity,
:billed_quantity,
:unit,
:gross_amount,
:net_amount,
Expand All @@ -32,11 +32,36 @@ module Secretariat
:charge_amount,
:origin_country_code,
:currency_code,
:basis_quantity,
keyword_init: true
) do

include Versioner

def initialize(**kwargs)
if kwargs.key?(:quantity) && !kwargs.key?(:billed_quantity)
kwargs[:billed_quantity] = kwargs.delete(:quantity)
end
super(**kwargs)
end

def quantity
warn_once_quantity
billed_quantity
end

def quantity=(val)
warn_once_quantity
self.billed_quantity = val
end

def warn_once_quantity
unless @__warned_quantity
Kernel.warn("[secretariat] LineItem#quantity is deprecated; use #billed_quantity")
@__warned_quantity = true
end
end

def errors
@errors
end
Expand All @@ -47,7 +72,7 @@ def valid?
gross_price = BigDecimal(gross_amount)
charge_price = BigDecimal(charge_amount)
tax = BigDecimal(tax_amount)
unit_price = net_price * BigDecimal(quantity.abs)
unit_price = net_price * BigDecimal(billed_quantity.abs)

if charge_price != unit_price
@errors << "charge price and gross price times quantity deviate: #{charge_price} / #{unit_price}"
Expand All @@ -65,7 +90,7 @@ def valid?
self.tax_percent ||= BigDecimal(0)
calculated_tax = charge_price * BigDecimal(tax_percent) / BigDecimal(100)
calculated_tax = calculated_tax.round(2)
calculated_tax = -calculated_tax if quantity.negative?
calculated_tax = -calculated_tax if billed_quantity.negative?
if calculated_tax != tax
@errors << "Tax and calculated tax deviate: #{tax} / #{calculated_tax}"
return false
Expand All @@ -78,6 +103,13 @@ def unit_code
UNIT_CODES[unit] || 'C62'
end

# If not provided, BasisQuantity should be 1.0 (price per 1 unit)
def effective_basis_quantity
q = basis_quantity.nil? ? BigDecimal("1.0") : BigDecimal(basis_quantity.to_s)
raise ArgumentError, "basis_quantity must be > 0" if q <= 0
q
end

def tax_category_code(version: 2)
if version == 1
return TAX_CATEGORY_CODES_1[tax_category] || 'S'
Expand All @@ -103,7 +135,7 @@ def to_xml(xml, line_item_index, version: 2, validate: true)
if net_price&.negative?
# Zugferd doesn't allow negative amounts at the item level.
# Instead, a negative quantity is used.
self.quantity = -quantity
self.billed_quantity = -billed_quantity
self.gross_amount = gross_price&.abs
self.net_amount = net_price&.abs
self.charge_amount = charge_price&.abs
Expand Down Expand Up @@ -132,7 +164,7 @@ def to_xml(xml, line_item_index, version: 2, validate: true)
Helpers.currency_element(xml, 'ram', 'ChargeAmount', gross_amount, currency_code, add_currency: version == 1, digits: 4)
if version == 2 && discount_amount
xml['ram'].BasisQuantity(unitCode: unit_code) do
xml.text(Helpers.format(quantity, digits: 4))
xml.text(Helpers.format(effective_basis_quantity, digits: 4))
end
xml['ram'].AppliedTradeAllowanceCharge do
xml['ram'].ChargeIndicator do
Expand All @@ -156,7 +188,7 @@ def to_xml(xml, line_item_index, version: 2, validate: true)
Helpers.currency_element(xml, 'ram', 'ChargeAmount', net_amount, currency_code, add_currency: version == 1, digits: 4)
if version == 2
xml['ram'].BasisQuantity(unitCode: unit_code) do
xml.text(Helpers.format(quantity, digits: 4))
xml.text(Helpers.format(effective_basis_quantity, digits: 4))
end
end
end
Expand All @@ -166,7 +198,7 @@ def to_xml(xml, line_item_index, version: 2, validate: true)

xml['ram'].send(delivery) do
xml['ram'].BilledQuantity(unitCode: unit_code) do
xml.text(Helpers.format(quantity, digits: 4))
xml.text(Helpers.format(billed_quantity, digits: 4))
end
end

Expand All @@ -183,7 +215,7 @@ def to_xml(xml, line_item_index, version: 2, validate: true)
end
monetary_summation = by_version(version, 'SpecifiedTradeSettlementMonetarySummation', 'SpecifiedTradeSettlementLineMonetarySummation')
xml['ram'].send(monetary_summation) do
Helpers.currency_element(xml, 'ram', 'LineTotalAmount', (quantity.negative? ? -charge_amount : charge_amount), currency_code, add_currency: version == 1)
Helpers.currency_element(xml, 'ram', 'LineTotalAmount', (billed_quantity.negative? ? -charge_amount : charge_amount), currency_code, add_currency: version == 1)
end
end

Expand Down