zugpferd 0.5.1 → 0.7.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: 97af66296e92817aef2f4f2fbea2e0ebb7170fb3856732a552ac32210a8e085f
4
+ data.tar.gz: cc23dea33a361b6fcfc93a52c0928a70bea18757cb2e022dbf123b142480383a
5
5
  SHA512:
6
- metadata.gz: 1f0fcf101222d3f841c8d1ea2601159509deafcc1c8344a04a70d4b63049d32dd78c180b4e9061f768f65b0675adfdc1393dbfbde9f40ef2efafdd37447e5478
7
- data.tar.gz: 9bcf99a1530b14c1540551eab5534be30ffe6c6f1c1bf666f7875f2e7f442f0b929ebc08ff6351b477685520fe724dd7419292f2fce5f5a508a29b3b22cb162d
6
+ metadata.gz: 79479ce622f08767f6ac5d3d63b5826ca3a3eef621d768005c417600f16b308856280e270a7188f49a03b3ff25f03ec0b169861570cde588fc72005d64a96b92
7
+ data.tar.gz: 31d343e25f7d8c5e9d2eb459f941b5a0f67d38169e8f264f66100382e8d11ace7e5f2bfb79497d5d0dbbf6f2097965078f7b9fd2ffb0903ae1a56c3c622bdc53
@@ -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",
@@ -57,6 +64,7 @@ module Zugpferd
57
64
  legal_registration_id: "ram:SpecifiedLegalOrganization/ram:ID",
58
65
  legal_form: "ram:Description",
59
66
  vat_identifier: "ram:SpecifiedTaxRegistration/ram:ID[@schemeID='VA']",
67
+ tax_registration_identifier: "ram:SpecifiedTaxRegistration/ram:ID[@schemeID='FC']",
60
68
  electronic_address: "ram:URIUniversalCommunication/ram:URIID",
61
69
  }.freeze
62
70
 
@@ -166,7 +174,9 @@ module Zugpferd
166
174
  # Price (BG-29)
167
175
  PRICE = "ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice"
168
176
  PRICE_FIELDS = {
169
- amount: "ram:ChargeAmount",
177
+ amount: "ram:ChargeAmount",
178
+ base_quantity: "ram:BasisQuantity",
179
+ base_quantity_unit_code: "ram:BasisQuantity/@unitCode",
170
180
  }.freeze
171
181
  end
172
182
  end
@@ -58,17 +58,19 @@ module Zugpferd
58
58
  customization_id: text(root, INVOICE[:customization_id]),
59
59
  profile_id: text(root, INVOICE[:profile_id]),
60
60
  note: text(root, INVOICE[:note]),
61
- seller: build_party(root.at_xpath(SELLER, NS)),
61
+ seller: build_party(root.at_xpath(SELLER, NS), seller: true),
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),
68
69
  )
69
70
  end
70
71
 
71
- def build_party(node)
72
+ # Seller-only terms are read only for the seller
73
+ def build_party(node, seller: false)
72
74
  return nil unless node
73
75
 
74
76
  party = Model::TradeParty.new(
@@ -76,8 +78,9 @@ module Zugpferd
76
78
  trading_name: text(node, PARTY[:trading_name]),
77
79
  identifier: text(node, PARTY[:identifier]),
78
80
  legal_registration_id: text(node, PARTY[:legal_registration_id]),
79
- legal_form: text(node, PARTY[:legal_form]),
81
+ legal_form: (text(node, PARTY[:legal_form]) if seller),
80
82
  vat_identifier: text(node, PARTY[:vat_identifier]),
83
+ tax_registration_identifier: (text(node, PARTY[:tax_registration_identifier]) if seller),
81
84
  electronic_address: text(node, PARTY[:electronic_address]),
82
85
  )
83
86
 
@@ -218,9 +221,20 @@ module Zugpferd
218
221
 
219
222
  Model::Price.new(
220
223
  amount: text(node, PRICE_FIELDS[:amount]),
224
+ base_quantity: parse_decimal(text(node, PRICE_FIELDS[:base_quantity])),
225
+ base_quantity_unit_code: node.at_xpath(PRICE_FIELDS[:base_quantity_unit_code], NS)&.text,
221
226
  )
222
227
  end
223
228
 
229
+ def build_preceding_invoices(settlement_node)
230
+ settlement_node.xpath(PRECEDING_INVOICE, NS).map do |node|
231
+ Model::PrecedingInvoice.new(
232
+ number: text(node, PRECEDING_INVOICE_FIELDS[:number]),
233
+ issue_date: parse_cii_date(text(node, PRECEDING_INVOICE_FIELDS[:issue_date])),
234
+ )
235
+ end
236
+ end
237
+
224
238
  def build_allowance_charges(settlement_node)
225
239
  settlement_node.xpath(ALLOWANCE_CHARGE, NS).map do |node|
226
240
  Model::AllowanceCharge.new(
@@ -74,7 +74,7 @@ module Zugpferd
74
74
  def build_agreement(xml, doc)
75
75
  xml["ram"].ApplicableHeaderTradeAgreement do
76
76
  xml["ram"].BuyerReference doc.buyer_reference if doc.buyer_reference
77
- build_party(xml, "SellerTradeParty", doc.seller) if doc.seller
77
+ build_party(xml, "SellerTradeParty", doc.seller, seller: true) if doc.seller
78
78
  build_party(xml, "BuyerTradeParty", doc.buyer) if doc.buyer
79
79
  build_buyer_order_reference(xml, doc) if doc.purchase_order_reference
80
80
  end
@@ -86,7 +86,8 @@ module Zugpferd
86
86
  end
87
87
  end
88
88
 
89
- def build_party(xml, element_name, party)
89
+ # Seller-only terms are written only for the seller
90
+ def build_party(xml, element_name, party, seller: false)
90
91
  xml["ram"].send(element_name) do
91
92
  if party.identifier
92
93
  xml["ram"].ID party.identifier
@@ -94,7 +95,7 @@ module Zugpferd
94
95
 
95
96
  xml["ram"].Name party.name
96
97
 
97
- xml["ram"].Description party.legal_form if party.legal_form
98
+ xml["ram"].Description party.legal_form if seller && party.legal_form
98
99
 
99
100
  if party.legal_registration_id || party.trading_name
100
101
  xml["ram"].SpecifiedLegalOrganization do
@@ -120,6 +121,12 @@ module Zugpferd
120
121
  xml["ram"].ID(party.vat_identifier, schemeID: "VA")
121
122
  end
122
123
  end
124
+
125
+ if seller && party.tax_registration_identifier
126
+ xml["ram"].SpecifiedTaxRegistration do
127
+ xml["ram"].ID(party.tax_registration_identifier, schemeID: "FC")
128
+ end
129
+ end
123
130
  end
124
131
  end
125
132
 
@@ -197,6 +204,20 @@ module Zugpferd
197
204
  end
198
205
 
199
206
  build_monetary_total(xml, doc.monetary_totals, doc.tax_breakdown) if doc.monetary_totals
207
+
208
+ # CII allows a single InvoiceReferencedDocument, so only the first BG-3 is written
209
+ build_preceding_invoice(xml, doc.preceding_invoices.first) if doc.preceding_invoices.any?
210
+ end
211
+ end
212
+
213
+ def build_preceding_invoice(xml, preceding)
214
+ xml["ram"].InvoiceReferencedDocument do
215
+ xml["ram"].IssuerAssignedID preceding.number
216
+ if preceding.issue_date
217
+ xml["ram"].FormattedIssueDateTime do
218
+ xml["qdt"].DateTimeString(format_cii_date(preceding.issue_date), format: "102")
219
+ end
220
+ end
200
221
  end
201
222
  end
202
223
 
@@ -303,6 +324,11 @@ module Zugpferd
303
324
  if line.price
304
325
  xml["ram"].NetPriceProductTradePrice do
305
326
  xml["ram"].ChargeAmount format_decimal(line.price.amount)
327
+ if line.price.base_quantity
328
+ attrs = {}
329
+ attrs[:unitCode] = line.price.base_quantity_unit_code if line.price.base_quantity_unit_code
330
+ xml["ram"].BasisQuantity(format_decimal(line.price.base_quantity), attrs)
331
+ end
306
332
  end
307
333
  end
308
334
  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
@@ -10,14 +10,16 @@ module Zugpferd
10
10
  # @return [String, nil] BT-28/BT-45 Trading name
11
11
  # @return [String, nil] BT-29/BT-46 Party identifier
12
12
  # @return [String, nil] BT-30/BT-47 Legal registration identifier
13
- # @return [String, nil] BT-33 Company legal form
13
+ # @return [String, nil] BT-33 Company legal form (seller only)
14
14
  # @return [String, nil] BT-31/BT-48 VAT identifier
15
+ # @return [String, nil] BT-32 Seller tax registration identifier (seller only)
15
16
  # @return [String, nil] BT-34/BT-49 Electronic address
16
17
  # @return [String, nil] BT-34-1/BT-49-1 Electronic address scheme
17
18
  # @return [PostalAddress, nil] BG-5/BG-8 Postal address
18
19
  # @return [Contact, nil] BG-6/BG-9 Contact information
19
20
  attr_accessor :name, :trading_name, :identifier,
20
21
  :legal_registration_id, :legal_form, :vat_identifier,
22
+ :tax_registration_identifier,
21
23
  :electronic_address, :electronic_address_scheme,
22
24
  :postal_address, :contact
23
25
 
@@ -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)
@@ -53,6 +63,7 @@ module Zugpferd
53
63
  legal_registration_id: "cac:PartyLegalEntity/cbc:CompanyID",
54
64
  legal_form: "cac:PartyLegalEntity/cbc:CompanyLegalForm",
55
65
  vat_identifier: "cac:PartyTaxScheme[cac:TaxScheme/cbc:ID='VAT']/cbc:CompanyID",
66
+ tax_registration_identifier: "cac:PartyTaxScheme[cac:TaxScheme/cbc:ID!='VAT']/cbc:CompanyID",
56
67
  electronic_address: "cbc:EndpointID",
57
68
  }.freeze
58
69
 
@@ -155,7 +166,9 @@ module Zugpferd
155
166
  # Price (BG-29)
156
167
  PRICE = "cac:Price"
157
168
  PRICE_FIELDS = {
158
- amount: "cbc:PriceAmount",
169
+ amount: "cbc:PriceAmount",
170
+ base_quantity: "cbc:BaseQuantity",
171
+ base_quantity_unit_code: "cbc:BaseQuantity/@unitCode",
159
172
  }.freeze
160
173
  end
161
174
  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,
@@ -50,17 +51,19 @@ module Zugpferd
50
51
  customization_id: text(root, INVOICE[:customization_id]),
51
52
  profile_id: text(root, INVOICE[:profile_id]),
52
53
  note: text(root, INVOICE[:note]),
53
- seller: build_party(root.at_xpath(SELLER, @ns)),
54
+ seller: build_party(root.at_xpath(SELLER, @ns), seller: true),
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),
60
62
  )
61
63
  end
62
64
 
63
- def build_party(node)
65
+ # Seller-only terms are read only for the seller
66
+ def build_party(node, seller: false)
64
67
  return nil unless node
65
68
 
66
69
  party = Model::TradeParty.new(
@@ -68,8 +71,9 @@ module Zugpferd
68
71
  trading_name: text(node, PARTY[:trading_name]),
69
72
  identifier: text(node, PARTY[:identifier]),
70
73
  legal_registration_id: text(node, PARTY[:legal_registration_id]),
71
- legal_form: text(node, PARTY[:legal_form]),
74
+ legal_form: (text(node, PARTY[:legal_form]) if seller),
72
75
  vat_identifier: text(node, PARTY[:vat_identifier]),
76
+ tax_registration_identifier: (text(node, PARTY[:tax_registration_identifier]) if seller),
73
77
  electronic_address: text(node, PARTY[:electronic_address]),
74
78
  )
75
79
 
@@ -204,6 +208,15 @@ module Zugpferd
204
208
 
205
209
  Model::Price.new(
206
210
  amount: text(node, PRICE_FIELDS[:amount]),
211
+ base_quantity: parse_decimal(text(node, PRICE_FIELDS[:base_quantity])),
212
+ base_quantity_unit_code: node.at_xpath(PRICE_FIELDS[:base_quantity_unit_code], @ns)&.text,
213
+ )
214
+ end
215
+
216
+ def build_preceding_invoice(node)
217
+ Model::PrecedingInvoice.new(
218
+ number: text(node, PRECEDING_INVOICE_FIELDS[:number]),
219
+ issue_date: parse_date(text(node, PRECEDING_INVOICE_FIELDS[:issue_date])),
207
220
  )
208
221
  end
209
222
 
@@ -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
@@ -67,7 +68,7 @@ module Zugpferd
67
68
  def build_supplier(xml, party, payment_instructions = nil)
68
69
  xml["cac"].AccountingSupplierParty do
69
70
  build_party(xml, party,
70
- creditor_reference_id: payment_instructions&.creditor_reference_id)
71
+ creditor_reference_id: payment_instructions&.creditor_reference_id, seller: true)
71
72
  end
72
73
  end
73
74
 
@@ -77,7 +78,8 @@ module Zugpferd
77
78
  end
78
79
  end
79
80
 
80
- def build_party(xml, party, creditor_reference_id: nil)
81
+ # Seller-only terms are written only for the seller
82
+ def build_party(xml, party, creditor_reference_id: nil, seller: false)
81
83
  xml["cac"].Party do
82
84
  if party.electronic_address
83
85
  attrs = {}
@@ -114,10 +116,19 @@ module Zugpferd
114
116
  end
115
117
  end
116
118
 
119
+ if seller && party.tax_registration_identifier
120
+ xml["cac"].PartyTaxScheme do
121
+ xml["cbc"].CompanyID party.tax_registration_identifier
122
+ xml["cac"].TaxScheme do
123
+ xml["cbc"].ID "FC"
124
+ end
125
+ end
126
+ end
127
+
117
128
  xml["cac"].PartyLegalEntity do
118
129
  xml["cbc"].RegistrationName party.name
119
130
  xml["cbc"].CompanyID party.legal_registration_id if party.legal_registration_id
120
- xml["cbc"].CompanyLegalForm party.legal_form if party.legal_form
131
+ xml["cbc"].CompanyLegalForm party.legal_form if seller && party.legal_form
121
132
  end
122
133
 
123
134
  build_contact(xml, party.contact) if party.contact
@@ -156,15 +167,26 @@ module Zugpferd
156
167
  end
157
168
  end
158
169
 
170
+ def build_preceding_invoice(xml, preceding)
171
+ xml["cac"].BillingReference do
172
+ xml["cac"].InvoiceDocumentReference do
173
+ xml["cbc"].ID preceding.number
174
+ xml["cbc"].IssueDate preceding.issue_date.to_s if preceding.issue_date
175
+ end
176
+ end
177
+ end
178
+
159
179
  def build_delivery(xml, doc)
160
180
  xml["cac"].Delivery do
161
181
  xml["cbc"].ActualDeliveryDate doc.delivery_date.to_s
162
182
  end
163
183
  end
164
184
 
165
- def build_payment_means(xml, payment)
185
+ # A credit note has no cbc:DueDate, BT-9 is written as PaymentDueDate instead.
186
+ def build_payment_means(xml, payment, due_date = nil)
166
187
  xml["cac"].PaymentMeans do
167
188
  xml["cbc"].PaymentMeansCode payment.payment_means_code
189
+ xml["cbc"].PaymentDueDate due_date.to_s if @credit_note && due_date
168
190
  xml["cbc"].PaymentID payment.payment_id if payment.payment_id
169
191
  if payment.card_account_id
170
192
  xml["cac"].CardAccount do
@@ -318,6 +340,11 @@ module Zugpferd
318
340
  def build_price(xml, price, currency_code)
319
341
  xml["cac"].Price do
320
342
  xml["cbc"].PriceAmount(format_decimal(price.amount), currencyID: currency_code)
343
+ if price.base_quantity
344
+ attrs = {}
345
+ attrs[:unitCode] = price.base_quantity_unit_code if price.base_quantity_unit_code
346
+ xml["cbc"].BaseQuantity(format_decimal(price.base_quantity), attrs)
347
+ end
321
348
  end
322
349
  end
323
350
 
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.7.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