Module: FactPulse::Helpers::AmountHelpers

Defined in:
lib/factpulse/helpers/client.rb

Overview

Helpers for creating simplified total amounts.

Class Method Summary collapse

Class Method Details

.amount(value) ⇒ Object



38
39
40
41
42
# File 'lib/factpulse/helpers/client.rb', line 38

def self.amount(value)
  return '0.00' if value.nil?
  return format('%.2f', value) if value.is_a?(Numeric)
  value.is_a?(String) ? value : '0.00'
end

.beneficiary(name, **options) ⇒ Hash

Creates a beneficiary (factor) for factoring.

The beneficiary (BG-10 / PayeeTradeParty) is used when payment must be made to a third party different from the supplier, typically a factor (factoring company).

For factored invoices, you also need to:

  • Use a factored document type (393, 396, 501, 502, 472, 473)

  • Add an ACC note with the subrogation mention

  • The beneficiary’s IBAN will be used for payment

Examples:

factor = beneficiary('FACTOR SAS',
  siret: '30000000700033',
  iban: 'FR76 3000 4000 0500 0012 3456 789'
)

Parameters:

  • name (String)

    Factor’s business name (BT-59)

  • options (Hash)

    Options: :siret (BT-60), :siren (BT-61), :iban, :bic

Returns:

  • (Hash)

    Dict ready to be used in a factored invoice



151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/factpulse/helpers/client.rb', line 151

def self.beneficiary(name, **options)
  # Auto-compute SIREN from SIRET
  siret = options[:siret]
  siren = options[:siren] || (siret && siret.length == 14 ? siret[0, 9] : nil)

  result = { 'name' => name }
  result['siret'] = siret if siret
  result['siren'] = siren if siren
  result['iban'] = options[:iban] if options[:iban]
  result['bic'] = options[:bic] if options[:bic]
  result
end

.compute_vat_intra(siren) ⇒ Object

Computes the French intra-community VAT number from a SIREN.



95
96
97
98
99
# File 'lib/factpulse/helpers/client.rb', line 95

def self.compute_vat_intra(siren)
  return nil if siren.nil? || siren.length != 9 || !siren.match?(/^\d+$/)
  cle = (12 + 3 * (siren.to_i % 97)) % 97
  format('FR%02d%s', cle, siren)
end

.electronic_address(identifier, scheme_id: '0009') ⇒ Object

Creates an electronic address. scheme_id: “0009”=SIREN, “0225”=SIRET



90
91
92
# File 'lib/factpulse/helpers/client.rb', line 90

def self.electronic_address(identifier, scheme_id: '0009')
  { 'identifier' => identifier, 'schemeId' => scheme_id }
end

.invoice_line(number, description, quantity, unit_price_excl_tax, line_total_excl_tax, vat_rate: '20.00', vat_category: 'S', unit: 'LUMP_SUM', **options) ⇒ Object

Creates an invoice line (aligned with InvoiceLine in models.py).



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/factpulse/helpers/client.rb', line 56

def self.invoice_line(number, description, quantity, unit_price_excl_tax, line_total_excl_tax,
                      vat_rate: '20.00', vat_category: 'S', unit: 'LUMP_SUM', **options)
  result = {
    'number' => number, 'description' => description,
    'quantity' => amount(quantity), 'unitPriceExclTax' => amount(unit_price_excl_tax),
    'lineTotalExclTax' => amount(line_total_excl_tax), 'vatRateManual' => amount(vat_rate),
    'vatCategory' => vat_category, 'unit' => unit
  }
  result['reference'] = options[:reference] if options[:reference]
  result['discountExclTax'] = amount(options[:discount_excl_tax]) if options[:discount_excl_tax]
  result['discountReasonCode'] = options[:discount_reason_code] if options[:discount_reason_code]
  result['discountReason'] = options[:discount_reason] if options[:discount_reason]
  result['periodStartDate'] = options[:period_start_date] if options[:period_start_date]
  result['periodEndDate'] = options[:period_end_date] if options[:period_end_date]
  result
end

.invoice_totals(excl_tax, vat, incl_tax, amount_due, discount_incl_tax: nil, discount_reason: nil, prepayment: nil) ⇒ Object



44
45
46
47
48
49
50
51
52
53
# File 'lib/factpulse/helpers/client.rb', line 44

def self.invoice_totals(excl_tax, vat, incl_tax, amount_due, discount_incl_tax: nil, discount_reason: nil, prepayment: nil)
  result = {
    'totalExclTax' => amount(excl_tax), 'vatAmount' => amount(vat),
    'totalInclTax' => amount(incl_tax), 'amountDue' => amount(amount_due)
  }
  result['globalDiscountInclTax'] = amount(discount_incl_tax) if discount_incl_tax
  result['globalDiscountReason'] = discount_reason if discount_reason
  result['prepayment'] = amount(prepayment) if prepayment
  result
end

.postal_address(line1, postal_code, city, country: 'FR', line2: nil, line3: nil) ⇒ Object

Creates a postal address for the FactPulse API.



82
83
84
85
86
87
# File 'lib/factpulse/helpers/client.rb', line 82

def self.postal_address(line1, postal_code, city, country: 'FR', line2: nil, line3: nil)
  result = { 'line1' => line1, 'postalCode' => postal_code, 'city' => city, 'countryCode' => country }
  result['line2'] = line2 if line2
  result['line3'] = line3 if line3
  result
end

.recipient(name, siret, address_line1, postal_code, city, **options) ⇒ Object

Creates a recipient (customer) with auto-computed SIREN and addresses.



119
120
121
122
123
124
125
126
127
128
129
# File 'lib/factpulse/helpers/client.rb', line 119

def self.recipient(name, siret, address_line1, postal_code, city, **options)
  siren = options[:siren] || (siret.length == 14 ? siret[0, 9] : nil)
  result = {
    'name' => name, 'siret' => siret,
    'electronicAddress' => electronic_address(siret, scheme_id: '0225'),
    'postalAddress' => postal_address(address_line1, postal_code, city, country: options[:country] || 'FR', line2: options[:address_line2])
  }
  result['siren'] = siren if siren
  result['executingServiceCode'] = options[:executing_service_code] if options[:executing_service_code]
  result
end

.supplier(name, siret, address_line1, postal_code, city, **options) ⇒ Object

Creates a supplier (issuer) with auto-computed SIREN, intra-EU VAT number and addresses.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/factpulse/helpers/client.rb', line 102

def self.supplier(name, siret, address_line1, postal_code, city, **options)
  siren = options[:siren] || (siret.length == 14 ? siret[0, 9] : nil)
  vat_intra = options[:vat_intra] || (siren ? compute_vat_intra(siren) : nil)
  result = {
    'name' => name, 'supplierId' => options[:supplier_id] || 0, 'siret' => siret,
    'electronicAddress' => electronic_address(siret, scheme_id: '0225'),
    'postalAddress' => postal_address(address_line1, postal_code, city, country: options[:country] || 'FR', line2: options[:address_line2])
  }
  result['siren'] = siren if siren
  result['vatIntra'] = vat_intra if vat_intra
  result['iban'] = options[:iban] if options[:iban]
  result['supplierServiceId'] = options[:service_code] if options[:service_code]
  result['supplierBankCoordinatesCode'] = options[:bank_coordinates_code] if options[:bank_coordinates_code]
  result
end

.vat_line(rate_manual, base_amount_excl_tax, vat_amount, category: 'S') ⇒ Object

Creates a VAT line (aligned with VatLine in models.py).



74
75
76
77
78
79
# File 'lib/factpulse/helpers/client.rb', line 74

def self.vat_line(rate_manual, base_amount_excl_tax, vat_amount, category: 'S')
  {
    'rateManual' => amount(rate_manual), 'baseAmountExclTax' => amount(base_amount_excl_tax),
    'vatAmount' => amount(vat_amount), 'category' => category
  }
end