zugpferd 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 837410c3386f0fb24e0c903378bad0e4d797e24867b2a3b15b7fdc6d23f1edb3
4
- data.tar.gz: e5a4808ba918693dcd393212064c3e6590038aa61406fcd0fccf37e3a91794d0
3
+ metadata.gz: eb023a7256c9cf26f6688283b6204bbccc0f46f2a6c8f0bb8082c2b1db277151
4
+ data.tar.gz: 00d5dc92b2d38a3744af4388017629d83b74b28241014448c75b684fb8de7c5c
5
5
  SHA512:
6
- metadata.gz: 1f0fcf101222d3f841c8d1ea2601159509deafcc1c8344a04a70d4b63049d32dd78c180b4e9061f768f65b0675adfdc1393dbfbde9f40ef2efafdd37447e5478
7
- data.tar.gz: 9bcf99a1530b14c1540551eab5534be30ffe6c6f1c1bf666f7875f2e7f442f0b929ebc08ff6351b477685520fe724dd7419292f2fce5f5a508a29b3b22cb162d
6
+ metadata.gz: 0b6313dfce9be63204e9c7317be9abfbd38c0a53c97e5a7960c4b4fab98421f7f5ec892af59457c4babcdc0ec811de4159f926ae8f443df5f1d417055eb3847f
7
+ data.tar.gz: 449b38ee768cea1a0a5167f498c5b80a6bc3604bfe92967cabed4e51d230fe56b307dd88c5313886cb2b67d6b053932bf5f8e575b18385b781fd323f74d10fdf
@@ -38,6 +38,13 @@ module Zugpferd
38
38
  end_date: "ram:EndDateTime/udt:DateTimeString",
39
39
  }.freeze
40
40
 
41
+ # Preceding invoice reference (BG-3), relative to the settlement node
42
+ PRECEDING_INVOICE = "ram:InvoiceReferencedDocument"
43
+ PRECEDING_INVOICE_FIELDS = {
44
+ number: "ram:IssuerAssignedID",
45
+ issue_date: "ram:FormattedIssueDateTime/qdt:DateTimeString",
46
+ }.freeze
47
+
41
48
  INVOICE_SETTLEMENT = {
42
49
  currency_code: "#{SETTLEMENT}/ram:InvoiceCurrencyCode",
43
50
  buyer_reference: "#{AGREEMENT}/ram:BuyerReference",
@@ -166,7 +173,9 @@ module Zugpferd
166
173
  # Price (BG-29)
167
174
  PRICE = "ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice"
168
175
  PRICE_FIELDS = {
169
- amount: "ram:ChargeAmount",
176
+ amount: "ram:ChargeAmount",
177
+ base_quantity: "ram:BasisQuantity",
178
+ base_quantity_unit_code: "ram:BasisQuantity/@unitCode",
170
179
  }.freeze
171
180
  end
172
181
  end
@@ -62,6 +62,7 @@ module Zugpferd
62
62
  buyer: build_party(root.at_xpath(BUYER, NS)),
63
63
  line_items: root.xpath(INVOICE_LINE, NS).map { |n| build_line_item(n) },
64
64
  allowance_charges: settlement ? build_allowance_charges(settlement) : [],
65
+ preceding_invoices: settlement ? build_preceding_invoices(settlement) : [],
65
66
  tax_breakdown: build_tax_breakdown(settlement),
66
67
  monetary_totals: build_monetary_totals(settlement&.at_xpath(MONETARY_TOTAL, NS)),
67
68
  payment_instructions: build_payment_instructions(settlement),
@@ -218,9 +219,20 @@ module Zugpferd
218
219
 
219
220
  Model::Price.new(
220
221
  amount: text(node, PRICE_FIELDS[:amount]),
222
+ base_quantity: parse_decimal(text(node, PRICE_FIELDS[:base_quantity])),
223
+ base_quantity_unit_code: node.at_xpath(PRICE_FIELDS[:base_quantity_unit_code], NS)&.text,
221
224
  )
222
225
  end
223
226
 
227
+ def build_preceding_invoices(settlement_node)
228
+ settlement_node.xpath(PRECEDING_INVOICE, NS).map do |node|
229
+ Model::PrecedingInvoice.new(
230
+ number: text(node, PRECEDING_INVOICE_FIELDS[:number]),
231
+ issue_date: parse_cii_date(text(node, PRECEDING_INVOICE_FIELDS[:issue_date])),
232
+ )
233
+ end
234
+ end
235
+
224
236
  def build_allowance_charges(settlement_node)
225
237
  settlement_node.xpath(ALLOWANCE_CHARGE, NS).map do |node|
226
238
  Model::AllowanceCharge.new(
@@ -197,6 +197,20 @@ module Zugpferd
197
197
  end
198
198
 
199
199
  build_monetary_total(xml, doc.monetary_totals, doc.tax_breakdown) if doc.monetary_totals
200
+
201
+ # CII allows a single InvoiceReferencedDocument, so only the first BG-3 is written
202
+ build_preceding_invoice(xml, doc.preceding_invoices.first) if doc.preceding_invoices.any?
203
+ end
204
+ end
205
+
206
+ def build_preceding_invoice(xml, preceding)
207
+ xml["ram"].InvoiceReferencedDocument do
208
+ xml["ram"].IssuerAssignedID preceding.number
209
+ if preceding.issue_date
210
+ xml["ram"].FormattedIssueDateTime do
211
+ xml["qdt"].DateTimeString(format_cii_date(preceding.issue_date), format: "102")
212
+ end
213
+ end
200
214
  end
201
215
  end
202
216
 
@@ -303,6 +317,11 @@ module Zugpferd
303
317
  if line.price
304
318
  xml["ram"].NetPriceProductTradePrice do
305
319
  xml["ram"].ChargeAmount format_decimal(line.price.amount)
320
+ if line.price.base_quantity
321
+ attrs = {}
322
+ attrs[:unitCode] = line.price.base_quantity_unit_code if line.price.base_quantity_unit_code
323
+ xml["ram"].BasisQuantity(format_decimal(line.price.base_quantity), attrs)
324
+ end
306
325
  end
307
326
  end
308
327
  end
@@ -19,6 +19,7 @@ module Zugpferd
19
19
  # @return [String] BT-3 Invoice type code
20
20
  # @return [String] BT-5 Document currency code (default: "EUR")
21
21
  # @return [Date, nil] BT-72 Actual delivery date
22
+ # @return [Array<PrecedingInvoice>] BG-3 Preceding invoice references
22
23
  # @return [Date, nil] BT-73 Invoicing period start date
23
24
  # @return [Date, nil] BT-74 Invoicing period end date
24
25
  # @return [String, nil] BT-10 Buyer reference
@@ -39,7 +40,7 @@ module Zugpferd
39
40
  :buyer_reference, :purchase_order_reference,
40
41
  :customization_id, :profile_id, :note, :seller, :buyer,
41
42
  :line_items, :tax_breakdown, :monetary_totals,
42
- :payment_instructions, :allowance_charges
43
+ :payment_instructions, :allowance_charges, :preceding_invoices
43
44
 
44
45
  # @param number [String] BT-1 Invoice number
45
46
  # @param issue_date [Date] BT-2 Issue date
@@ -54,6 +55,7 @@ module Zugpferd
54
55
  @currency_code = currency_code
55
56
  @line_items = []
56
57
  @allowance_charges = []
58
+ @preceding_invoices = []
57
59
  @tax_breakdown = nil
58
60
  rest.each { |k, v| public_send(:"#{k}=", v) }
59
61
  end
@@ -6,7 +6,7 @@ module Zugpferd
6
6
  # @return [String] BT-81 Payment means type code
7
7
  # @return [String, nil] BT-83 Remittance information
8
8
  # @return [String, nil] BT-84 Payment account identifier (IBAN)
9
- # @return [String, nil] BT-82 Payment terms note
9
+ # @return [String, nil] BT-20 Payment terms
10
10
  # @return [String, nil] BT-87 Payment card primary account number
11
11
  # @return [String, nil] BT-88 Payment card holder name
12
12
  # @return [String, nil] Card network ID (UBL-only, required in CardAccount)
@@ -0,0 +1,17 @@
1
+ module Zugpferd
2
+ module Model
3
+ # Reference to a preceding invoice (BG-3).
4
+ class PrecedingInvoice
5
+ # @return [String] BT-25 Preceding invoice number
6
+ # @return [Date, nil] BT-26 Preceding invoice issue date
7
+ attr_accessor :number, :issue_date
8
+
9
+ # @param number [String] BT-25 Preceding invoice number
10
+ # @param rest [Hash] additional attributes set via accessors
11
+ def initialize(number:, **rest)
12
+ @number = number
13
+ rest.each { |k, v| public_send(:"#{k}=", v) }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -6,7 +6,8 @@ module Zugpferd
6
6
  class Price
7
7
  # @return [BigDecimal] BT-146 Item net price
8
8
  # @return [BigDecimal, nil] BT-149 Item price base quantity
9
- attr_accessor :amount, :base_quantity
9
+ # @return [String, nil] BT-150 Item price base quantity unit of measure code
10
+ attr_accessor :amount, :base_quantity, :base_quantity_unit_code
10
11
 
11
12
  # @param amount [String, BigDecimal] BT-146 Item net price
12
13
  # @param rest [Hash] additional attributes set via accessors
@@ -26,6 +26,9 @@ module Zugpferd
26
26
  note: "cbc:Note",
27
27
  }.freeze
28
28
 
29
+ # BT-9 in a credit note (it has no cbc:DueDate)
30
+ CREDIT_NOTE_DUE_DATE = "cac:PaymentMeans/cbc:PaymentDueDate"
31
+
29
32
  # Delivery (BG-13)
30
33
  DELIVERY = "cac:Delivery"
31
34
  DELIVERY_DATE = "cbc:ActualDeliveryDate"
@@ -40,6 +43,13 @@ module Zugpferd
40
43
  # Purchase order reference (BT-13)
41
44
  ORDER_REFERENCE = "cac:OrderReference/cbc:ID"
42
45
 
46
+ # Preceding invoice reference (BG-3)
47
+ PRECEDING_INVOICE = "cac:BillingReference/cac:InvoiceDocumentReference"
48
+ PRECEDING_INVOICE_FIELDS = {
49
+ number: "cbc:ID",
50
+ issue_date: "cbc:IssueDate",
51
+ }.freeze
52
+
43
53
  # Seller (BG-4)
44
54
  SELLER = "cac:AccountingSupplierParty/cac:Party"
45
55
  # Buyer (BG-7)
@@ -155,7 +165,9 @@ module Zugpferd
155
165
  # Price (BG-29)
156
166
  PRICE = "cac:Price"
157
167
  PRICE_FIELDS = {
158
- amount: "cbc:PriceAmount",
168
+ amount: "cbc:PriceAmount",
169
+ base_quantity: "cbc:BaseQuantity",
170
+ base_quantity_unit_code: "cbc:BaseQuantity/@unitCode",
159
171
  }.freeze
160
172
  end
161
173
  end
@@ -30,6 +30,7 @@ module Zugpferd
30
30
  def build_invoice(root)
31
31
  type_code_element = @credit_note ? "cbc:CreditNoteTypeCode" : INVOICE[:type_code]
32
32
  line_element = @credit_note ? "cac:CreditNoteLine" : INVOICE_LINE
33
+ due_date_element = @credit_note ? CREDIT_NOTE_DUE_DATE : INVOICE[:due_date]
33
34
 
34
35
  model_class = @credit_note ? Model::CreditNote : Model::Invoice
35
36
 
@@ -39,7 +40,7 @@ module Zugpferd
39
40
  model_class.new(
40
41
  number: text(root, INVOICE[:number]),
41
42
  issue_date: parse_date(text(root, INVOICE[:issue_date])),
42
- due_date: parse_date(text(root, INVOICE[:due_date])),
43
+ due_date: parse_date(text(root, due_date_element)),
43
44
  delivery_date: delivery_node ? parse_date(text(delivery_node, DELIVERY_DATE)) : nil,
44
45
  invoicing_period_start_date: period_node ? parse_date(text(period_node, INVOICE_PERIOD_FIELDS[:start_date])) : nil,
45
46
  invoicing_period_end_date: period_node ? parse_date(text(period_node, INVOICE_PERIOD_FIELDS[:end_date])) : nil,
@@ -54,6 +55,7 @@ module Zugpferd
54
55
  buyer: build_party(root.at_xpath(BUYER, @ns)),
55
56
  line_items: root.xpath(line_element, @ns).map { |n| build_line_item(n) },
56
57
  allowance_charges: root.xpath(ALLOWANCE_CHARGE, @ns).map { |n| build_allowance_charge(n) },
58
+ preceding_invoices: root.xpath(PRECEDING_INVOICE, @ns).map { |n| build_preceding_invoice(n) },
57
59
  tax_breakdown: build_tax_breakdown(root.at_xpath(TAX_TOTAL, @ns)),
58
60
  monetary_totals: build_monetary_totals(root.at_xpath(MONETARY_TOTAL, @ns)),
59
61
  payment_instructions: build_payment_instructions(root),
@@ -204,6 +206,15 @@ module Zugpferd
204
206
 
205
207
  Model::Price.new(
206
208
  amount: text(node, PRICE_FIELDS[:amount]),
209
+ base_quantity: parse_decimal(text(node, PRICE_FIELDS[:base_quantity])),
210
+ base_quantity_unit_code: node.at_xpath(PRICE_FIELDS[:base_quantity_unit_code], @ns)&.text,
211
+ )
212
+ end
213
+
214
+ def build_preceding_invoice(node)
215
+ Model::PrecedingInvoice.new(
216
+ number: text(node, PRECEDING_INVOICE_FIELDS[:number]),
217
+ issue_date: parse_date(text(node, PRECEDING_INVOICE_FIELDS[:issue_date])),
207
218
  )
208
219
  end
209
220
 
@@ -41,7 +41,7 @@ module Zugpferd
41
41
  xml["cbc"].ProfileID doc.profile_id if doc.profile_id
42
42
  xml["cbc"].ID doc.number
43
43
  xml["cbc"].IssueDate doc.issue_date.to_s
44
- xml["cbc"].DueDate doc.due_date.to_s if doc.due_date
44
+ xml["cbc"].DueDate doc.due_date.to_s if doc.due_date && !@credit_note
45
45
  if @credit_note
46
46
  xml["cbc"].CreditNoteTypeCode doc.type_code
47
47
  else
@@ -52,11 +52,12 @@ module Zugpferd
52
52
  xml["cbc"].BuyerReference doc.buyer_reference if doc.buyer_reference
53
53
  build_invoice_period(xml, doc) if doc.invoicing_period_start_date || doc.invoicing_period_end_date
54
54
  build_order_reference(xml, doc) if doc.purchase_order_reference
55
+ doc.preceding_invoices.each { |pi| build_preceding_invoice(xml, pi) }
55
56
 
56
57
  build_supplier(xml, doc.seller, doc.payment_instructions) if doc.seller
57
58
  build_customer(xml, doc.buyer) if doc.buyer
58
59
  build_delivery(xml, doc) if doc.delivery_date
59
- build_payment_means(xml, doc.payment_instructions) if doc.payment_instructions
60
+ build_payment_means(xml, doc.payment_instructions, doc.due_date) if doc.payment_instructions
60
61
  build_payment_terms(xml, doc.payment_instructions) if doc.payment_instructions&.note
61
62
  doc.allowance_charges.each { |ac| build_allowance_charge(xml, ac, doc.currency_code) }
62
63
  build_tax_total(xml, doc.tax_breakdown) if doc.tax_breakdown
@@ -156,15 +157,26 @@ module Zugpferd
156
157
  end
157
158
  end
158
159
 
160
+ def build_preceding_invoice(xml, preceding)
161
+ xml["cac"].BillingReference do
162
+ xml["cac"].InvoiceDocumentReference do
163
+ xml["cbc"].ID preceding.number
164
+ xml["cbc"].IssueDate preceding.issue_date.to_s if preceding.issue_date
165
+ end
166
+ end
167
+ end
168
+
159
169
  def build_delivery(xml, doc)
160
170
  xml["cac"].Delivery do
161
171
  xml["cbc"].ActualDeliveryDate doc.delivery_date.to_s
162
172
  end
163
173
  end
164
174
 
165
- def build_payment_means(xml, payment)
175
+ # A credit note has no cbc:DueDate, BT-9 is written as PaymentDueDate instead.
176
+ def build_payment_means(xml, payment, due_date = nil)
166
177
  xml["cac"].PaymentMeans do
167
178
  xml["cbc"].PaymentMeansCode payment.payment_means_code
179
+ xml["cbc"].PaymentDueDate due_date.to_s if @credit_note && due_date
168
180
  xml["cbc"].PaymentID payment.payment_id if payment.payment_id
169
181
  if payment.card_account_id
170
182
  xml["cac"].CardAccount do
@@ -318,6 +330,11 @@ module Zugpferd
318
330
  def build_price(xml, price, currency_code)
319
331
  xml["cac"].Price do
320
332
  xml["cbc"].PriceAmount(format_decimal(price.amount), currencyID: currency_code)
333
+ if price.base_quantity
334
+ attrs = {}
335
+ attrs[:unitCode] = price.base_quantity_unit_code if price.base_quantity_unit_code
336
+ xml["cbc"].BaseQuantity(format_decimal(price.base_quantity), attrs)
337
+ end
321
338
  end
322
339
  end
323
340
 
data/lib/zugpferd.rb CHANGED
@@ -20,6 +20,7 @@ require_relative "zugpferd/model/tax_breakdown"
20
20
  require_relative "zugpferd/model/tax_subtotal"
21
21
  require_relative "zugpferd/model/payment_instructions"
22
22
  require_relative "zugpferd/model/allowance_charge"
23
+ require_relative "zugpferd/model/preceding_invoice"
23
24
  require_relative "zugpferd/ubl/reader"
24
25
  require_relative "zugpferd/ubl/writer"
25
26
  require_relative "zugpferd/cii/reader"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zugpferd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Zeitler
@@ -90,6 +90,7 @@ files:
90
90
  - lib/zugpferd/model/partial_invoice.rb
91
91
  - lib/zugpferd/model/payment_instructions.rb
92
92
  - lib/zugpferd/model/postal_address.rb
93
+ - lib/zugpferd/model/preceding_invoice.rb
93
94
  - lib/zugpferd/model/prepayment_invoice.rb
94
95
  - lib/zugpferd/model/price.rb
95
96
  - lib/zugpferd/model/self_billed_invoice.rb