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 +4 -4
- data/lib/zugpferd/cii/mapping.rb +11 -1
- data/lib/zugpferd/cii/reader.rb +17 -3
- data/lib/zugpferd/cii/writer.rb +29 -3
- data/lib/zugpferd/model/billing_document.rb +3 -1
- 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/model/trade_party.rb +3 -1
- data/lib/zugpferd/ubl/mapping.rb +14 -1
- data/lib/zugpferd/ubl/reader.rb +17 -4
- data/lib/zugpferd/ubl/writer.rb +33 -6
- data/lib/zugpferd.rb +1 -0
- 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: 97af66296e92817aef2f4f2fbea2e0ebb7170fb3856732a552ac32210a8e085f
|
|
4
|
+
data.tar.gz: cc23dea33a361b6fcfc93a52c0928a70bea18757cb2e022dbf123b142480383a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 79479ce622f08767f6ac5d3d63b5826ca3a3eef621d768005c417600f16b308856280e270a7188f49a03b3ff25f03ec0b169861570cde588fc72005d64a96b92
|
|
7
|
+
data.tar.gz: 31d343e25f7d8c5e9d2eb459f941b5a0f67d38169e8f264f66100382e8d11ace7e5f2bfb79497d5d0dbbf6f2097965078f7b9fd2ffb0903ae1a56c3c622bdc53
|
data/lib/zugpferd/cii/mapping.rb
CHANGED
|
@@ -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:
|
|
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
|
data/lib/zugpferd/cii/reader.rb
CHANGED
|
@@ -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
|
-
|
|
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(
|
data/lib/zugpferd/cii/writer.rb
CHANGED
|
@@ -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
|
-
|
|
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-
|
|
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
|
|
@@ -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
|
|
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"
|
|
@@ -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:
|
|
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
|
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,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,
|
|
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
|
-
|
|
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
|
|
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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|