zugpferd 0.5.0 → 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 +4 -4
- data/lib/zugpferd/cii/mapping.rb +12 -1
- data/lib/zugpferd/cii/reader.rb +35 -7
- data/lib/zugpferd/cii/writer.rb +26 -0
- data/lib/zugpferd/model/billing_document.rb +6 -2
- data/lib/zugpferd/model/payment_instructions.rb +1 -1
- data/lib/zugpferd/model/preceding_invoice.rb +17 -0
- data/lib/zugpferd/model/price.rb +2 -1
- data/lib/zugpferd/ubl/mapping.rb +16 -1
- data/lib/zugpferd/ubl/reader.rb +13 -1
- data/lib/zugpferd/ubl/writer.rb +27 -3
- data/lib/zugpferd.rb +5 -4
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eb023a7256c9cf26f6688283b6204bbccc0f46f2a6c8f0bb8082c2b1db277151
|
|
4
|
+
data.tar.gz: 00d5dc92b2d38a3744af4388017629d83b74b28241014448c75b684fb8de7c5c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0b6313dfce9be63204e9c7317be9abfbd38c0a53c97e5a7960c4b4fab98421f7f5ec892af59457c4babcdc0ec811de4159f926ae8f443df5f1d417055eb3847f
|
|
7
|
+
data.tar.gz: 449b38ee768cea1a0a5167f498c5b80a6bc3604bfe92967cabed4e51d230fe56b307dd88c5313886cb2b67d6b053932bf5f8e575b18385b781fd323f74d10fdf
|
data/lib/zugpferd/cii/mapping.rb
CHANGED
|
@@ -38,9 +38,17 @@ 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",
|
|
51
|
+
purchase_order_reference: "#{AGREEMENT}/ram:BuyerOrderReferencedDocument/ram:IssuerAssignedID",
|
|
44
52
|
}.freeze
|
|
45
53
|
|
|
46
54
|
# Seller (BG-4)
|
|
@@ -93,6 +101,7 @@ module Zugpferd
|
|
|
93
101
|
|
|
94
102
|
# TaxTotal (BG-23)
|
|
95
103
|
TAX_SUBTOTAL = "ram:ApplicableTradeTax"
|
|
104
|
+
TAX_FALLBACK_CURRENCY = "ram:InvoiceCurrencyCode"
|
|
96
105
|
TAX = {
|
|
97
106
|
taxable_amount: "ram:BasisAmount",
|
|
98
107
|
tax_amount: "ram:CalculatedAmount",
|
|
@@ -164,7 +173,9 @@ module Zugpferd
|
|
|
164
173
|
# Price (BG-29)
|
|
165
174
|
PRICE = "ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice"
|
|
166
175
|
PRICE_FIELDS = {
|
|
167
|
-
amount:
|
|
176
|
+
amount: "ram:ChargeAmount",
|
|
177
|
+
base_quantity: "ram:BasisQuantity",
|
|
178
|
+
base_quantity_unit_code: "ram:BasisQuantity/@unitCode",
|
|
168
179
|
}.freeze
|
|
169
180
|
end
|
|
170
181
|
end
|
data/lib/zugpferd/cii/reader.rb
CHANGED
|
@@ -12,6 +12,9 @@ module Zugpferd
|
|
|
12
12
|
class Reader
|
|
13
13
|
include Mapping
|
|
14
14
|
|
|
15
|
+
# Raised when BT-110 is missing and the VAT breakdown (BG-23) does not sum to 0.
|
|
16
|
+
class MissingTaxTotalError < Zugpferd::Error; end
|
|
17
|
+
|
|
15
18
|
TYPE_CODE_MAP = {
|
|
16
19
|
"381" => Model::CreditNote,
|
|
17
20
|
"384" => Model::CorrectedInvoice,
|
|
@@ -25,6 +28,7 @@ module Zugpferd
|
|
|
25
28
|
# @param xml_string [String] valid CII D16B XML
|
|
26
29
|
# @return [Model::BillingDocument]
|
|
27
30
|
# @raise [Nokogiri::XML::SyntaxError] if the XML is malformed
|
|
31
|
+
# @raise [MissingTaxTotalError] if BT-110 is missing and the VAT breakdown does not sum to 0
|
|
28
32
|
def read(xml_string)
|
|
29
33
|
doc = Nokogiri::XML(xml_string) { |config| config.strict }
|
|
30
34
|
root = doc.root
|
|
@@ -50,6 +54,7 @@ module Zugpferd
|
|
|
50
54
|
type_code: text(root, INVOICE[:type_code]),
|
|
51
55
|
currency_code: text(root, INVOICE_SETTLEMENT[:currency_code]),
|
|
52
56
|
buyer_reference: text(root, INVOICE_SETTLEMENT[:buyer_reference]),
|
|
57
|
+
purchase_order_reference: text(root, INVOICE_SETTLEMENT[:purchase_order_reference]),
|
|
53
58
|
customization_id: text(root, INVOICE[:customization_id]),
|
|
54
59
|
profile_id: text(root, INVOICE[:profile_id]),
|
|
55
60
|
note: text(root, INVOICE[:note]),
|
|
@@ -57,6 +62,7 @@ module Zugpferd
|
|
|
57
62
|
buyer: build_party(root.at_xpath(BUYER, NS)),
|
|
58
63
|
line_items: root.xpath(INVOICE_LINE, NS).map { |n| build_line_item(n) },
|
|
59
64
|
allowance_charges: settlement ? build_allowance_charges(settlement) : [],
|
|
65
|
+
preceding_invoices: settlement ? build_preceding_invoices(settlement) : [],
|
|
60
66
|
tax_breakdown: build_tax_breakdown(settlement),
|
|
61
67
|
monetary_totals: build_monetary_totals(settlement&.at_xpath(MONETARY_TOTAL, NS)),
|
|
62
68
|
payment_instructions: build_payment_instructions(settlement),
|
|
@@ -129,14 +135,9 @@ module Zugpferd
|
|
|
129
135
|
|
|
130
136
|
totals_node = settlement_node.at_xpath(MONETARY_TOTAL, NS)
|
|
131
137
|
tax_total_node = totals_node&.at_xpath(TOTALS[:tax_total_amount], NS)
|
|
132
|
-
currency = tax_total_node
|
|
133
|
-
|
|
134
|
-
breakdown = Model::TaxBreakdown.new(
|
|
135
|
-
tax_amount: tax_total_node&.text,
|
|
136
|
-
currency_code: currency,
|
|
137
|
-
)
|
|
138
|
+
currency = tax_total_node ? tax_total_node["currencyID"] : text(settlement_node, TAX_FALLBACK_CURRENCY)
|
|
138
139
|
|
|
139
|
-
|
|
140
|
+
subtotals = settlement_node.xpath(TAX_SUBTOTAL, NS).map do |sub|
|
|
140
141
|
Model::TaxSubtotal.new(
|
|
141
142
|
taxable_amount: text(sub, TAX[:taxable_amount]),
|
|
142
143
|
tax_amount: text(sub, TAX[:tax_amount]),
|
|
@@ -148,9 +149,25 @@ module Zugpferd
|
|
|
148
149
|
)
|
|
149
150
|
end
|
|
150
151
|
|
|
152
|
+
breakdown = Model::TaxBreakdown.new(
|
|
153
|
+
tax_amount: tax_total_node ? tax_total_node.text : zero_tax_total(subtotals),
|
|
154
|
+
currency_code: currency,
|
|
155
|
+
)
|
|
156
|
+
breakdown.subtotals = subtotals
|
|
151
157
|
breakdown
|
|
152
158
|
end
|
|
153
159
|
|
|
160
|
+
# BR-CO-15 lets CII omit BT-110 on invoices without VAT. A breakdown
|
|
161
|
+
# that shows VAT contradicts that, even where the Schematron passes it.
|
|
162
|
+
def zero_tax_total(subtotals)
|
|
163
|
+
sum = subtotals.sum(BigDecimal("0"), &:tax_amount)
|
|
164
|
+
return sum if subtotals.any? && sum.zero?
|
|
165
|
+
|
|
166
|
+
raise MissingTaxTotalError,
|
|
167
|
+
"Invoice total VAT amount (BT-110) is missing and the VAT breakdown (BG-23) " \
|
|
168
|
+
"#{subtotals.empty? ? "is empty" : "sums to #{sum.to_s("F")}"}"
|
|
169
|
+
end
|
|
170
|
+
|
|
154
171
|
def build_monetary_totals(node)
|
|
155
172
|
return nil unless node
|
|
156
173
|
|
|
@@ -202,9 +219,20 @@ module Zugpferd
|
|
|
202
219
|
|
|
203
220
|
Model::Price.new(
|
|
204
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,
|
|
205
224
|
)
|
|
206
225
|
end
|
|
207
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
|
+
|
|
208
236
|
def build_allowance_charges(settlement_node)
|
|
209
237
|
settlement_node.xpath(ALLOWANCE_CHARGE, NS).map do |node|
|
|
210
238
|
Model::AllowanceCharge.new(
|
data/lib/zugpferd/cii/writer.rb
CHANGED
|
@@ -76,6 +76,13 @@ module Zugpferd
|
|
|
76
76
|
xml["ram"].BuyerReference doc.buyer_reference if doc.buyer_reference
|
|
77
77
|
build_party(xml, "SellerTradeParty", doc.seller) if doc.seller
|
|
78
78
|
build_party(xml, "BuyerTradeParty", doc.buyer) if doc.buyer
|
|
79
|
+
build_buyer_order_reference(xml, doc) if doc.purchase_order_reference
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def build_buyer_order_reference(xml, doc)
|
|
84
|
+
xml["ram"].BuyerOrderReferencedDocument do
|
|
85
|
+
xml["ram"].IssuerAssignedID doc.purchase_order_reference
|
|
79
86
|
end
|
|
80
87
|
end
|
|
81
88
|
|
|
@@ -190,6 +197,20 @@ module Zugpferd
|
|
|
190
197
|
end
|
|
191
198
|
|
|
192
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
|
|
193
214
|
end
|
|
194
215
|
end
|
|
195
216
|
|
|
@@ -296,6 +317,11 @@ module Zugpferd
|
|
|
296
317
|
if line.price
|
|
297
318
|
xml["ram"].NetPriceProductTradePrice do
|
|
298
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
|
|
299
325
|
end
|
|
300
326
|
end
|
|
301
327
|
end
|
|
@@ -19,9 +19,11 @@ 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
|
|
26
|
+
# @return [String, nil] BT-13 Purchase order reference
|
|
25
27
|
# @return [String, nil] BT-24 Specification identifier
|
|
26
28
|
# @return [String, nil] BT-23 Business process type
|
|
27
29
|
# @return [String, nil] BT-22 Invoice note
|
|
@@ -35,9 +37,10 @@ module Zugpferd
|
|
|
35
37
|
attr_accessor :number, :issue_date, :due_date, :type_code,
|
|
36
38
|
:currency_code, :delivery_date,
|
|
37
39
|
:invoicing_period_start_date, :invoicing_period_end_date,
|
|
38
|
-
:buyer_reference, :
|
|
40
|
+
:buyer_reference, :purchase_order_reference,
|
|
41
|
+
:customization_id, :profile_id, :note, :seller, :buyer,
|
|
39
42
|
:line_items, :tax_breakdown, :monetary_totals,
|
|
40
|
-
:payment_instructions, :allowance_charges
|
|
43
|
+
:payment_instructions, :allowance_charges, :preceding_invoices
|
|
41
44
|
|
|
42
45
|
# @param number [String] BT-1 Invoice number
|
|
43
46
|
# @param issue_date [Date] BT-2 Issue date
|
|
@@ -52,6 +55,7 @@ module Zugpferd
|
|
|
52
55
|
@currency_code = currency_code
|
|
53
56
|
@line_items = []
|
|
54
57
|
@allowance_charges = []
|
|
58
|
+
@preceding_invoices = []
|
|
55
59
|
@tax_breakdown = nil
|
|
56
60
|
rest.each { |k, v| public_send(:"#{k}=", v) }
|
|
57
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-
|
|
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
|
data/lib/zugpferd/model/price.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
data/lib/zugpferd/ubl/mapping.rb
CHANGED
|
@@ -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"
|
|
@@ -37,6 +40,16 @@ module Zugpferd
|
|
|
37
40
|
end_date: "cbc:EndDate",
|
|
38
41
|
}.freeze
|
|
39
42
|
|
|
43
|
+
# Purchase order reference (BT-13)
|
|
44
|
+
ORDER_REFERENCE = "cac:OrderReference/cbc:ID"
|
|
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
|
+
|
|
40
53
|
# Seller (BG-4)
|
|
41
54
|
SELLER = "cac:AccountingSupplierParty/cac:Party"
|
|
42
55
|
# Buyer (BG-7)
|
|
@@ -152,7 +165,9 @@ module Zugpferd
|
|
|
152
165
|
# Price (BG-29)
|
|
153
166
|
PRICE = "cac:Price"
|
|
154
167
|
PRICE_FIELDS = {
|
|
155
|
-
amount:
|
|
168
|
+
amount: "cbc:PriceAmount",
|
|
169
|
+
base_quantity: "cbc:BaseQuantity",
|
|
170
|
+
base_quantity_unit_code: "cbc:BaseQuantity/@unitCode",
|
|
156
171
|
}.freeze
|
|
157
172
|
end
|
|
158
173
|
end
|
data/lib/zugpferd/ubl/reader.rb
CHANGED
|
@@ -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,13 +40,14 @@ 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,
|
|
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,
|
|
46
47
|
type_code: text(root, type_code_element),
|
|
47
48
|
currency_code: text(root, INVOICE[:currency_code]),
|
|
48
49
|
buyer_reference: text(root, INVOICE[:buyer_reference]),
|
|
50
|
+
purchase_order_reference: text(root, ORDER_REFERENCE),
|
|
49
51
|
customization_id: text(root, INVOICE[:customization_id]),
|
|
50
52
|
profile_id: text(root, INVOICE[:profile_id]),
|
|
51
53
|
note: text(root, INVOICE[:note]),
|
|
@@ -53,6 +55,7 @@ module Zugpferd
|
|
|
53
55
|
buyer: build_party(root.at_xpath(BUYER, @ns)),
|
|
54
56
|
line_items: root.xpath(line_element, @ns).map { |n| build_line_item(n) },
|
|
55
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) },
|
|
56
59
|
tax_breakdown: build_tax_breakdown(root.at_xpath(TAX_TOTAL, @ns)),
|
|
57
60
|
monetary_totals: build_monetary_totals(root.at_xpath(MONETARY_TOTAL, @ns)),
|
|
58
61
|
payment_instructions: build_payment_instructions(root),
|
|
@@ -203,6 +206,15 @@ module Zugpferd
|
|
|
203
206
|
|
|
204
207
|
Model::Price.new(
|
|
205
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])),
|
|
206
218
|
)
|
|
207
219
|
end
|
|
208
220
|
|
data/lib/zugpferd/ubl/writer.rb
CHANGED
|
@@ -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
|
|
@@ -51,11 +51,13 @@ module Zugpferd
|
|
|
51
51
|
xml["cbc"].DocumentCurrencyCode doc.currency_code
|
|
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
|
+
build_order_reference(xml, doc) if doc.purchase_order_reference
|
|
55
|
+
doc.preceding_invoices.each { |pi| build_preceding_invoice(xml, pi) }
|
|
54
56
|
|
|
55
57
|
build_supplier(xml, doc.seller, doc.payment_instructions) if doc.seller
|
|
56
58
|
build_customer(xml, doc.buyer) if doc.buyer
|
|
57
59
|
build_delivery(xml, doc) if doc.delivery_date
|
|
58
|
-
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
|
|
59
61
|
build_payment_terms(xml, doc.payment_instructions) if doc.payment_instructions&.note
|
|
60
62
|
doc.allowance_charges.each { |ac| build_allowance_charge(xml, ac, doc.currency_code) }
|
|
61
63
|
build_tax_total(xml, doc.tax_breakdown) if doc.tax_breakdown
|
|
@@ -149,15 +151,32 @@ module Zugpferd
|
|
|
149
151
|
end
|
|
150
152
|
end
|
|
151
153
|
|
|
154
|
+
def build_order_reference(xml, doc)
|
|
155
|
+
xml["cac"].OrderReference do
|
|
156
|
+
xml["cbc"].ID doc.purchase_order_reference
|
|
157
|
+
end
|
|
158
|
+
end
|
|
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
|
+
|
|
152
169
|
def build_delivery(xml, doc)
|
|
153
170
|
xml["cac"].Delivery do
|
|
154
171
|
xml["cbc"].ActualDeliveryDate doc.delivery_date.to_s
|
|
155
172
|
end
|
|
156
173
|
end
|
|
157
174
|
|
|
158
|
-
|
|
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)
|
|
159
177
|
xml["cac"].PaymentMeans do
|
|
160
178
|
xml["cbc"].PaymentMeansCode payment.payment_means_code
|
|
179
|
+
xml["cbc"].PaymentDueDate due_date.to_s if @credit_note && due_date
|
|
161
180
|
xml["cbc"].PaymentID payment.payment_id if payment.payment_id
|
|
162
181
|
if payment.card_account_id
|
|
163
182
|
xml["cac"].CardAccount do
|
|
@@ -311,6 +330,11 @@ module Zugpferd
|
|
|
311
330
|
def build_price(xml, price, currency_code)
|
|
312
331
|
xml["cac"].Price do
|
|
313
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
|
|
314
338
|
end
|
|
315
339
|
end
|
|
316
340
|
|
data/lib/zugpferd.rb
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
module Zugpferd
|
|
2
|
+
class Error < StandardError; end
|
|
3
|
+
end
|
|
4
|
+
|
|
1
5
|
require_relative "zugpferd/model/billing_document"
|
|
2
6
|
require_relative "zugpferd/model/invoice"
|
|
3
7
|
require_relative "zugpferd/model/credit_note"
|
|
@@ -16,11 +20,8 @@ require_relative "zugpferd/model/tax_breakdown"
|
|
|
16
20
|
require_relative "zugpferd/model/tax_subtotal"
|
|
17
21
|
require_relative "zugpferd/model/payment_instructions"
|
|
18
22
|
require_relative "zugpferd/model/allowance_charge"
|
|
23
|
+
require_relative "zugpferd/model/preceding_invoice"
|
|
19
24
|
require_relative "zugpferd/ubl/reader"
|
|
20
25
|
require_relative "zugpferd/ubl/writer"
|
|
21
26
|
require_relative "zugpferd/cii/reader"
|
|
22
27
|
require_relative "zugpferd/cii/writer"
|
|
23
|
-
|
|
24
|
-
module Zugpferd
|
|
25
|
-
class Error < StandardError; end
|
|
26
|
-
end
|
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.
|
|
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
|