Class: Ksef::FA3::Builder

Inherits:
Object
  • Object
show all
Includes:
Advances, Corrections, Subjects
Defined in:
lib/ksef/fa3/builder.rb,
lib/ksef/fa3/builder/advances.rb,
lib/ksef/fa3/builder/subjects.rb,
lib/ksef/fa3/builder/corrections.rb

Overview

The object yielded by build. Collects fields, then hands them to Invoice — which keeps every computation and every schema default in one place rather than splitting them between the model and the DSL.

Two deliberate choices about strictness. An unknown or misspelled key raises, listing what is permitted, because the alternative is a silently incomplete invoice — the same reasoning as Serializer's treatment of unknown element names. But a single-value field set twice simply takes the later value, which is what anyone writing a builder expects, and what f.number called twice obviously ought to do.

Defined Under Namespace

Modules: Advances, Corrections, Subjects

Constant Summary collapse

SUBJECT_KEYS =
%i[nip name address local_government_unit vat_group_member buyer_id].freeze
ADDRESS_KEYS =
%i[line1 line2 country street city postal_code].freeze
LINE_KEYS =
%i[name quantity unit net_unit_price vat_rate net_amount row_number state_before].freeze
CORRECTION_KEYS =
%i[reason effect period corrected_number previous_seller previous_buyers
paid_before exchange_rate_before].freeze
CORRECTED_KEYS =
%i[number issue_date ksef_number].freeze
ORDER_KEYS =
%i[total].freeze
ORDER_LINE_KEYS =
%i[name quantity unit net_unit_price net_amount vat_amount vat_rate
row_number state_before].freeze
ADVANCE_KEYS =
%i[ksef_number number].freeze
LINE_ALIASES =

English shorthand from DESIGN.md §8's example. The canonical names work too, so qty: and quantity: are interchangeable — but passing both is an error rather than a silent last-one-wins.

{ qty: :quantity, vat: :vat_rate }.freeze
REQUIRED =

Reported together, so one round trip tells the caller everything that is missing.

%i[seller buyer number issue_date].freeze

Instance Method Summary collapse

Methods included from Advances

#order, #order_line, #settles

Methods included from Corrections

#correction, #corrects

Constructor Details

#initialize ⇒ Builder

Returns a new instance of Builder.



39
40
41
42
43
44
45
46
47
# File 'lib/ksef/fa3/builder.rb', line 39

def initialize
  @fields = {}
  @lines = []
  @corrected = []
  @correction = nil
  @order = nil
  @order_lines = []
  @advances = []
end

Instance Method Details

#attachment(value) ⇒ Object

The invoice attachment (Zalacznik), a sibling of Fa carrying no amounts.

Takes an Attachment, or the blocks to make one from — the one-block case is the common one and does not deserve two levels of ceremony:

f.attachment Ksef::FA3::DataBlock.new(metadata: { "Kod PPE" => "999" })

Parameters:



85
# File 'lib/ksef/fa3/builder.rb', line 85

def attachment(value) = @fields[:attachment] = Attachment.wrap(value)

#buyer(**attributes) ⇒ Object

See Also:



54
# File 'lib/ksef/fa3/builder.rb', line 54

def buyer(**attributes) = @fields[:buyer] = subject(attributes, role: :buyer)

#currency(value) ⇒ Object

Parameters:

  • value (String) —

    ISO-4217 code; defaults to "PLN"



63
# File 'lib/ksef/fa3/builder.rb', line 63

def currency(value) = @fields[:currency] = value

#invoice_type(value) ⇒ Object

Parameters:

  • value (String) —

    a TRodzajFaktury code; defaults to "VAT"



75
# File 'lib/ksef/fa3/builder.rb', line 75

def invoice_type(value) = @fields[:invoice_type] = value

#issue_date(value) ⇒ Object

Parameters:

  • value (Date, String) —

    date of issue (P_1)



60
# File 'lib/ksef/fa3/builder.rb', line 60

def issue_date(value) = @fields[:issue_date] = value

#issued_at(value) ⇒ Object

Parameters:

  • value (Time, DateTime, Date, String) —

    document generation timestamp; defaults to the moment of serialisation



67
# File 'lib/ksef/fa3/builder.rb', line 67

def issued_at(value) = @fields[:issued_at] = value

#line(**attributes) ⇒ Object

Appends a line. Call once per line; row numbers are assigned on serialisation unless a line states its own.

Parameters:

  • attributes (Hash) —

    :name, :quantity (or :qty), :unit, :net_unit_price, :vat_rate (or :vat), and optionally :net_amount, :row_number, :state_before



93
94
95
# File 'lib/ksef/fa3/builder.rb', line 93

def line(**attributes)
  @lines << Line.new(**normalise(attributes, LINE_KEYS, LINE_ALIASES, "line"))
end

#number(value) ⇒ Object

Parameters:

  • value (String) —

    the invoice number (P_2)



57
# File 'lib/ksef/fa3/builder.rb', line 57

def number(value) = @fields[:number] = value

#rounding(value) ⇒ Object

Polish VAT law permits both strategies and they can differ by a grosz, so this is explicit rather than inferred (DESIGN.md §7.3).

Parameters:

  • value (Symbol) —

    :per_line (default) or :per_summary



72
# File 'lib/ksef/fa3/builder.rb', line 72

def rounding(value) = @fields[:rounding] = value

#seller(**attributes) ⇒ Object

Parameters:

  • attributes (Hash) —

    :nip, :name, :address, and optionally :local_government_unit / :vat_group_member



51
# File 'lib/ksef/fa3/builder.rb', line 51

def seller(**attributes) = @fields[:seller] = subject(attributes, role: :seller)

#to_invoice ⇒ Invoice

Returns:

Raises:



119
120
121
122
123
124
125
126
127
128
129
# File 'lib/ksef/fa3/builder.rb', line 119

def to_invoice
  missing = REQUIRED.reject { |key| @fields.key?(key) }
  unless missing.empty?
    raise ValidationError,
          "Incomplete invoice, missing #{missing.join(", ")}. " \
          "Every invoice needs a seller, a buyer, a number and an issue date."
  end

  Invoice.new(**@fields, lines: @lines, correction: assembled_correction,
                         order: assembled_order, advances: @advances)
end

#totals(gross:, net: {}, vat: {}) ⇒ Object

States the tax summary instead of having it computed from the lines — which a correction must do, its buckets being deltas its rows need not determine (docs/REFERENCE.md §8.4).

Keyed by rate code, as #line is, and mapped to summary buckets here. The model stores the buckets, because that mapping is not invertible: "23" and "22" share one (§8.1a).

Parameters:

  • gross (BigDecimal, Integer, String) —

    P_15

  • net (Hash{String => Object}) (defaults to: {}) —

    rate code => net amount

  • vat (Hash{String => Object}) (defaults to: {}) —

    rate code => tax amount



108
109
110
111
112
113
114
115
# File 'lib/ksef/fa3/builder.rb', line 108

def totals(gross:, net: {}, vat: {})
  buckets = {}
  # `nil` reads as "no buckets of this kind", which is what a caller who has only one
  # side of the summary naturally passes. Left alone it raised a bare NoMethodError.
  (net || {}).each { |code, amount| accumulate(buckets, VatRate.bucket(code).first, amount) }
  (vat || {}).each { |code, amount| accumulate(buckets, tax_element(code), amount) }
  @fields[:totals] = Totals.new(buckets: buckets, gross: gross)
end