Class: Einvoicing::Invoice

Inherits:
Data
  • Object
show all
Defined in:
lib/einvoicing/invoice.rb

Overview

Core invoice model. All monetary values are in the invoice currency.

Examples:

seller = Einvoicing::Party.new(name: "Acme SAS", siren: "123456789", vat_number: "FR12123456789")
buyer  = Einvoicing::Party.new(name: "Client SA", siren: "987654321")
line   = Einvoicing::LineItem.new(description: "Consulting", quantity: 1, unit_price: 1000.00)

invoice = Einvoicing::Invoice.new(
  invoice_number: "INV-2024-001",
  issue_date: Date.today,
  seller: seller,
  buyer: buyer,
  lines: [line]
)

Constant Summary collapse

RECOMPUTES_TAX_BREAKDOWN =

Changing any of these invalidates the memoised breakdown, so the copy has to recompute it — unless the caller supplies one of their own.

%i[lines allowances charges].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(invoice_number:, issue_date:, seller:, buyer:, lines:, allowances: [], charges: [], due_date: nil, currency: "EUR", tax_currency: nil, tax_exchange_rate: nil, tax_breakdown: nil, payment_reference: nil, note: nil, payment_means_code: nil, iban: nil, bic: nil, document_type: :invoice, original_invoice_number: nil, original_invoice_date: nil, prepaid_amount: BigDecimal(0)) ⇒ Invoice

Returns a new instance of Invoice.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/einvoicing/invoice.rb', line 44

def initialize(invoice_number:, issue_date:, seller:, buyer:, lines:,
               allowances: [], charges: [],
               due_date: nil, currency: "EUR", tax_currency: nil,
               tax_exchange_rate: nil, tax_breakdown: nil,
               payment_reference: nil, note: nil,
               payment_means_code: nil, iban: nil, bic: nil,
               document_type: :invoice, original_invoice_number: nil, original_invoice_date: nil,
               prepaid_amount: BigDecimal(0))
  allowances = allowances || []
  charges = charges || []
  computed_breakdown = tax_breakdown || compute_tax_breakdown(lines, allowances, charges)
  super(
    invoice_number: invoice_number,
    issue_date: issue_date,
    due_date: due_date,
    currency: currency,
    tax_currency: tax_currency,
    tax_exchange_rate: tax_exchange_rate.nil? ? nil : BigDecimal(tax_exchange_rate.to_s),
    seller: seller,
    buyer: buyer,
    lines: lines,
    allowances: allowances,
    charges: charges,
    tax_breakdown: computed_breakdown,
    payment_reference: payment_reference,
    note: note,
    payment_means_code: payment_means_code,
    iban: iban,
    bic: bic,
    document_type: document_type,
    original_invoice_number: original_invoice_number,
    original_invoice_date: original_invoice_date,
    prepaid_amount: prepaid_amount.nil? ? BigDecimal(0) : BigDecimal(prepaid_amount.to_s)
  )
end

Instance Attribute Details

#allowancesObject (readonly)

Returns the value of attribute allowances

Returns:

  • (Object)

    the current value of allowances



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def allowances
  @allowances
end

#bicObject (readonly)

Returns the value of attribute bic

Returns:

  • (Object)

    the current value of bic



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def bic
  @bic
end

#buyerObject (readonly)

Returns the value of attribute buyer

Returns:

  • (Object)

    the current value of buyer



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def buyer
  @buyer
end

#chargesObject (readonly)

Returns the value of attribute charges

Returns:

  • (Object)

    the current value of charges



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def charges
  @charges
end

#currencyObject (readonly)

Returns the value of attribute currency

Returns:

  • (Object)

    the current value of currency



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def currency
  @currency
end

#document_typeObject (readonly)

Returns the value of attribute document_type

Returns:

  • (Object)

    the current value of document_type



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def document_type
  @document_type
end

#due_dateObject (readonly)

Returns the value of attribute due_date

Returns:

  • (Object)

    the current value of due_date



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def due_date
  @due_date
end

#ibanObject (readonly)

Returns the value of attribute iban

Returns:

  • (Object)

    the current value of iban



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def iban
  @iban
end

#invoice_numberObject (readonly)

Returns the value of attribute invoice_number

Returns:

  • (Object)

    the current value of invoice_number



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def invoice_number
  @invoice_number
end

#issue_dateObject (readonly)

Returns the value of attribute issue_date

Returns:

  • (Object)

    the current value of issue_date



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def issue_date
  @issue_date
end

#linesObject (readonly)

Returns the value of attribute lines

Returns:

  • (Object)

    the current value of lines



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def lines
  @lines
end

#noteObject (readonly)

Returns the value of attribute note

Returns:

  • (Object)

    the current value of note



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def note
  @note
end

#original_invoice_dateObject (readonly)

Returns the value of attribute original_invoice_date

Returns:

  • (Object)

    the current value of original_invoice_date



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def original_invoice_date
  @original_invoice_date
end

#original_invoice_numberObject (readonly)

Returns the value of attribute original_invoice_number

Returns:

  • (Object)

    the current value of original_invoice_number



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def original_invoice_number
  @original_invoice_number
end

#payment_means_codeObject (readonly)

Returns the value of attribute payment_means_code

Returns:

  • (Object)

    the current value of payment_means_code



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def payment_means_code
  @payment_means_code
end

#payment_referenceObject (readonly)

Returns the value of attribute payment_reference

Returns:

  • (Object)

    the current value of payment_reference



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def payment_reference
  @payment_reference
end

#prepaid_amountObject (readonly)

Returns the value of attribute prepaid_amount

Returns:

  • (Object)

    the current value of prepaid_amount



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def prepaid_amount
  @prepaid_amount
end

#sellerObject (readonly)

Returns the value of attribute seller

Returns:

  • (Object)

    the current value of seller



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def seller
  @seller
end

#tax_breakdownObject (readonly)

Returns the value of attribute tax_breakdown

Returns:

  • (Object)

    the current value of tax_breakdown



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def tax_breakdown
  @tax_breakdown
end

#tax_currencyObject (readonly)

Returns the value of attribute tax_currency

Returns:

  • (Object)

    the current value of tax_currency



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def tax_currency
  @tax_currency
end

#tax_exchange_rateObject (readonly)

Returns the value of attribute tax_exchange_rate

Returns:

  • (Object)

    the current value of tax_exchange_rate



21
22
23
# File 'lib/einvoicing/invoice.rb', line 21

def tax_exchange_rate
  @tax_exchange_rate
end

Instance Method Details

#allowance_totalObject

Sum of document-level allowances (BT-107).



105
106
107
# File 'lib/einvoicing/invoice.rb', line 105

def allowance_total
  allowances.sum(BigDecimal("0"), &:amount).round(2, :half_up)
end

#charge_totalObject

Sum of document-level charges (BT-108).



110
111
112
# File 'lib/einvoicing/invoice.rb', line 110

def charge_total
  charges.sum(BigDecimal("0"), &:amount).round(2, :half_up)
end

#due_amountObject

Amount due after deducting any retained/prepaid amount (BT-113). VAT remains due on the full gross_total — only the payable balance is reduced (EN 16931 BR-CO-16: DuePayableAmount = GrandTotal − TotalPrepaidAmount).



135
136
137
# File 'lib/einvoicing/invoice.rb', line 135

def due_amount
  gross_total - prepaid_amount
end

#gross_totalObject

Grand total including VAT (BT-112) — BR-CO-15: net_total + tax_total. Both operands are already rounded to 2 decimals from per-line amounts, so this introduces no double-rounding.



128
129
130
# File 'lib/einvoicing/invoice.rb', line 128

def gross_total
  (net_total + tax_total).round(2, :half_up)
end

#line_totalObject

Sum of all line net amounts, before document-level adjustments (BT-106).



100
101
102
# File 'lib/einvoicing/invoice.rb', line 100

def line_total
  lines.sum(BigDecimal("0"), &:net_amount).round(2, :half_up)
end

#net_totalObject

Total amount without VAT (BT-109) — BR-CO-13: line_total − allowance_total + charge_total.



116
117
118
# File 'lib/einvoicing/invoice.rb', line 116

def net_total
  (line_total - allowance_total + charge_total).round(2, :half_up)
end

#tax_accounting_currency?Boolean

BT-6 is set and differs from the invoice currency (BT-5), so BR-53 requires the total VAT to also be stated in the VAT accounting currency (BT-111).

Returns:

  • (Boolean)


141
142
143
# File 'lib/einvoicing/invoice.rb', line 141

def tax_accounting_currency?
  !tax_currency.nil? && tax_currency != currency
end

#tax_totalObject

Total VAT across every category (BT-110).



121
122
123
# File 'lib/einvoicing/invoice.rb', line 121

def tax_total
  tax_breakdown.sum(BigDecimal("0"), &:tax_amount).round(2, :half_up)
end

#tax_total_in_tax_currencyObject

Total VAT in the VAT accounting currency (BT-111). Derived from tax_exchange_rate when given; nil when there is nothing to convert.



147
148
149
150
151
152
# File 'lib/einvoicing/invoice.rb', line 147

def tax_total_in_tax_currency
  return nil unless tax_accounting_currency?
  return nil if tax_exchange_rate.nil?

  (tax_total * tax_exchange_rate).round(2, :half_up)
end

#with(**kwargs) ⇒ Object

Rebuilt through the constructor rather than by delegating to Data#with: only Ruby >= 3.3 routes #with through #initialize. On 3.2 it copies the members straight across, so tax_breakdown: nil would be written as-is and every total computed from it would blow up on nil.



88
89
90
91
92
93
94
95
96
97
# File 'lib/einvoicing/invoice.rb', line 88

def with(**kwargs)
  return self if kwargs.empty?

  attributes = to_h.merge(kwargs)
  if !kwargs.key?(:tax_breakdown) && kwargs.keys.intersect?(RECOMPUTES_TAX_BREAKDOWN)
    attributes[:tax_breakdown] = nil
  end

  self.class.new(**attributes)
end