ubl 0.1.0 → 0.1.2
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/README.md +15 -1
- data/lib/ubl/builder.rb +32 -5
- data/lib/ubl/constants.rb +6 -0
- data/lib/ubl/validate.rb +78 -0
- data/lib/ubl/version.rb +1 -1
- data/lib/ubl.rb +32 -17
- data/xsd/common/CCTS_CCT_SchemaModule-2.1.xsd +731 -0
- data/xsd/common/UBL-CommonAggregateComponents-2.1.xsd +39799 -0
- data/xsd/common/UBL-CommonBasicComponents-2.1.xsd +5389 -0
- data/xsd/common/UBL-CommonExtensionComponents-2.1.xsd +223 -0
- data/xsd/common/UBL-CommonSignatureComponents-2.1.xsd +101 -0
- data/xsd/common/UBL-CoreComponentParameters-2.1.xsd +63 -0
- data/xsd/common/UBL-ExtensionContentDataType-2.1.xsd +89 -0
- data/xsd/common/UBL-QualifiedDataTypes-2.1.xsd +69 -0
- data/xsd/common/UBL-SignatureAggregateComponents-2.1.xsd +138 -0
- data/xsd/common/UBL-SignatureBasicComponents-2.1.xsd +78 -0
- data/xsd/common/UBL-UnqualifiedDataTypes-2.1.xsd +553 -0
- data/xsd/common/UBL-XAdESv132-2.1.xsd +476 -0
- data/xsd/common/UBL-XAdESv141-2.1.xsd +25 -0
- data/xsd/common/UBL-xmldsig-core-schema-2.1.xsd +330 -0
- data/xsd/maindoc/UBL-ApplicationResponse-2.1.xsd +362 -0
- data/xsd/maindoc/UBL-AttachedDocument-2.1.xsd +416 -0
- data/xsd/maindoc/UBL-AwardedNotification-2.1.xsd +418 -0
- data/xsd/maindoc/UBL-BillOfLading-2.1.xsd +540 -0
- data/xsd/maindoc/UBL-CallForTenders-2.1.xsd +513 -0
- data/xsd/maindoc/UBL-Catalogue-2.1.xsd +565 -0
- data/xsd/maindoc/UBL-CatalogueDeletion-2.1.xsd +465 -0
- data/xsd/maindoc/UBL-CatalogueItemSpecificationUpdate-2.1.xsd +533 -0
- data/xsd/maindoc/UBL-CataloguePricingUpdate-2.1.xsd +532 -0
- data/xsd/maindoc/UBL-CatalogueRequest-2.1.xsd +571 -0
- data/xsd/maindoc/UBL-CertificateOfOrigin-2.1.xsd +417 -0
- data/xsd/maindoc/UBL-ContractAwardNotice-2.1.xsd +493 -0
- data/xsd/maindoc/UBL-ContractNotice-2.1.xsd +459 -0
- data/xsd/maindoc/UBL-CreditNote-2.1.xsd +951 -0
- data/xsd/maindoc/UBL-DebitNote-2.1.xsd +916 -0
- data/xsd/maindoc/UBL-DespatchAdvice-2.1.xsd +482 -0
- data/xsd/maindoc/UBL-DocumentStatus-2.1.xsd +350 -0
- data/xsd/maindoc/UBL-DocumentStatusRequest-2.1.xsd +348 -0
- data/xsd/maindoc/UBL-ExceptionCriteria-2.1.xsd +417 -0
- data/xsd/maindoc/UBL-ExceptionNotification-2.1.xsd +403 -0
- data/xsd/maindoc/UBL-Forecast-2.1.xsd +454 -0
- data/xsd/maindoc/UBL-ForecastRevision-2.1.xsd +453 -0
- data/xsd/maindoc/UBL-ForwardingInstructions-2.1.xsd +517 -0
- data/xsd/maindoc/UBL-FreightInvoice-2.1.xsd +867 -0
- data/xsd/maindoc/UBL-FulfilmentCancellation-2.1.xsd +471 -0
- data/xsd/maindoc/UBL-GoodsItemItinerary-2.1.xsd +431 -0
- data/xsd/maindoc/UBL-GuaranteeCertificate-2.1.xsd +481 -0
- data/xsd/maindoc/UBL-InstructionForReturns-2.1.xsd +383 -0
- data/xsd/maindoc/UBL-InventoryReport-2.1.xsd +401 -0
- data/xsd/maindoc/UBL-Invoice-2.1.xsd +1002 -0
- data/xsd/maindoc/UBL-ItemInformationRequest-2.1.xsd +402 -0
- data/xsd/maindoc/UBL-Order-2.1.xsd +891 -0
- data/xsd/maindoc/UBL-OrderCancellation-2.1.xsd +416 -0
- data/xsd/maindoc/UBL-OrderChange-2.1.xsd +869 -0
- data/xsd/maindoc/UBL-OrderResponse-2.1.xsd +956 -0
- data/xsd/maindoc/UBL-OrderResponseSimple-2.1.xsd +486 -0
- data/xsd/maindoc/UBL-PackingList-2.1.xsd +432 -0
- data/xsd/maindoc/UBL-PriorInformationNotice-2.1.xsd +441 -0
- data/xsd/maindoc/UBL-ProductActivity-2.1.xsd +387 -0
- data/xsd/maindoc/UBL-Quotation-2.1.xsd +583 -0
- data/xsd/maindoc/UBL-ReceiptAdvice-2.1.xsd +485 -0
- data/xsd/maindoc/UBL-Reminder-2.1.xsd +759 -0
- data/xsd/maindoc/UBL-RemittanceAdvice-2.1.xsd +567 -0
- data/xsd/maindoc/UBL-RequestForQuotation-2.1.xsd +519 -0
- data/xsd/maindoc/UBL-RetailEvent-2.1.xsd +515 -0
- data/xsd/maindoc/UBL-SelfBilledCreditNote-2.1.xsd +918 -0
- data/xsd/maindoc/UBL-SelfBilledInvoice-2.1.xsd +933 -0
- data/xsd/maindoc/UBL-Statement-2.1.xsd +600 -0
- data/xsd/maindoc/UBL-StockAvailabilityReport-2.1.xsd +403 -0
- data/xsd/maindoc/UBL-Tender-2.1.xsd +464 -0
- data/xsd/maindoc/UBL-TenderReceipt-2.1.xsd +398 -0
- data/xsd/maindoc/UBL-TendererQualification-2.1.xsd +401 -0
- data/xsd/maindoc/UBL-TendererQualificationResponse-2.1.xsd +399 -0
- data/xsd/maindoc/UBL-TradeItemLocationProfile-2.1.xsd +415 -0
- data/xsd/maindoc/UBL-TransportExecutionPlan-2.1.xsd +769 -0
- data/xsd/maindoc/UBL-TransportExecutionPlanRequest-2.1.xsd +696 -0
- data/xsd/maindoc/UBL-TransportProgressStatus-2.1.xsd +396 -0
- data/xsd/maindoc/UBL-TransportProgressStatusRequest-2.1.xsd +345 -0
- data/xsd/maindoc/UBL-TransportServiceDescription-2.1.xsd +430 -0
- data/xsd/maindoc/UBL-TransportServiceDescriptionRequest-2.1.xsd +363 -0
- data/xsd/maindoc/UBL-TransportationStatus-2.1.xsd +566 -0
- data/xsd/maindoc/UBL-TransportationStatusRequest-2.1.xsd +484 -0
- data/xsd/maindoc/UBL-UnawardedNotification-2.1.xsd +415 -0
- data/xsd/maindoc/UBL-UtilityStatement-2.1.xsd +490 -0
- data/xsd/maindoc/UBL-Waybill-2.1.xsd +501 -0
- metadata +99 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07e7d21d74c69ab965fe5a9968fd7404629079233b2fd6554e7fac2f51aff5e6
|
4
|
+
data.tar.gz: e13d2459ed88883a5206449565fb665bee8a59a097bbccf489c975be4ace1489
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02d979487d2aafb9c96ab9ce923cb19f1821fd8850f8766a584ff0b5f06f86d5993747c0378c1eff62ef701518fe40876915d2234477f52bcb8e4c41660ef6f1
|
7
|
+
data.tar.gz: 73a22110aa073edce5165461c9883ec1796d92e5ded7b320de8c0de220d82f6755f2407e74fe9bd43dd95f545ca19390e78e1e3d17d518fdb8db284a1ae9f2c8
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# Ubl
|
4
4
|
|
5
|
-
Generate UBL (Universal Business Language) documents, such as invoices and credit notes, compliant with the Peppol network.
|
5
|
+
Generate and validate UBL (Universal Business Language) documents, such as invoices and credit notes, compliant with the Peppol network.
|
6
6
|
|
7
7
|
## installation
|
8
8
|
|
@@ -51,6 +51,20 @@ invoice.add_line(name: "Software License", quantity: 1, unit_price: 500.0, tax_r
|
|
51
51
|
invoice.build
|
52
52
|
```
|
53
53
|
|
54
|
+
If you need the UBL.BE version:
|
55
|
+
```ruby
|
56
|
+
invoice = Ubl::Invoice.new("UBL_BE")
|
57
|
+
```
|
58
|
+
|
59
|
+
You can also validate the result.
|
60
|
+
You need Docker for the schematron validation. (https://github.com/roel4d/peppol_schematron)
|
61
|
+
With `schematron: false` you can disable this and only the xsd validation will run.
|
62
|
+
```ruby
|
63
|
+
Tempfile.create("invoice.xml") do |invoice_file|
|
64
|
+
File.write(invoice_file, content)
|
65
|
+
p Ubl.validate_invoice(invoice_file.path, extension: "UBL_BE", schematron: true)
|
66
|
+
end
|
67
|
+
```
|
54
68
|
|
55
69
|
## development
|
56
70
|
|
data/lib/ubl/builder.rb
CHANGED
@@ -3,18 +3,15 @@
|
|
3
3
|
require "nokogiri"
|
4
4
|
require "date"
|
5
5
|
require "base64"
|
6
|
+
require_relative "constants"
|
6
7
|
|
7
8
|
module Ubl
|
8
9
|
class UblBuilder
|
9
10
|
attr_accessor :invoice_nr, :issue_date, :due_date, :currency, :supplier,
|
10
11
|
:customer, :invoice_lines, :tax_total, :legal_monetary_total, :pdffile
|
11
12
|
|
12
|
-
CUSTOMIZATION_ID = "urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0"
|
13
|
-
CUSTOMIZATION_UBL_BE = "urn:cen.eu:en16931:2017#conformant#urn:UBL.BE:1.0.0.20180214"
|
14
|
-
PROFILE_ID = "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0"
|
15
|
-
|
16
13
|
def initialize(extension = nil)
|
17
|
-
@ubl_be = extension ==
|
14
|
+
@ubl_be = extension == UBL_BE
|
18
15
|
@issue_date = Date.today
|
19
16
|
@due_date = @issue_date + 30
|
20
17
|
@currency = "EUR"
|
@@ -54,6 +51,13 @@ module Ubl
|
|
54
51
|
}
|
55
52
|
end
|
56
53
|
|
54
|
+
def add_payment_means(iban:, bic:)
|
55
|
+
@payment_means = {
|
56
|
+
iban: iban,
|
57
|
+
bic: bic
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
57
61
|
def add_line(name:, quantity:, unit_price:, tax_rate: 21.0, unit: "ZZ")
|
58
62
|
line_extension_amount = (quantity * unit_price).round(2)
|
59
63
|
tax_amount = (line_extension_amount * (tax_rate / 100.0)).round(2)
|
@@ -114,6 +118,15 @@ module Ubl
|
|
114
118
|
end
|
115
119
|
end
|
116
120
|
|
121
|
+
def build_content(xml)
|
122
|
+
build_party(xml, @supplier, "AccountingSupplierParty")
|
123
|
+
build_party(xml, @customer, "AccountingCustomerParty")
|
124
|
+
build_payment_means(xml)
|
125
|
+
build_tax_total(xml)
|
126
|
+
build_monetary_total(xml)
|
127
|
+
build_invoice_lines(xml)
|
128
|
+
end
|
129
|
+
|
117
130
|
def add_attachment(id, filename)
|
118
131
|
@attachments << {id: id, content:}
|
119
132
|
end
|
@@ -255,5 +268,19 @@ module Ubl
|
|
255
268
|
xml["cbc"].PayableAmount(currencyID: @currency) { xml.text sprintf("%.2f", @legal_monetary_total) }
|
256
269
|
end
|
257
270
|
end
|
271
|
+
|
272
|
+
def build_payment_means(xml)
|
273
|
+
return unless @payment_means
|
274
|
+
|
275
|
+
xml["cac"].PaymentMeans do
|
276
|
+
xml["cbc"].PaymentMeansCode "1"
|
277
|
+
xml["cac"].PayeeFinancialAccount do
|
278
|
+
xml["cbc"].ID @payment_means[:iban]
|
279
|
+
xml["cac"].FinancialInstitutionBranch do
|
280
|
+
xml["cbc"].ID @payment_means[:bic]
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
258
285
|
end
|
259
286
|
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
module Ubl
|
2
|
+
CUSTOMIZATION_ID = "urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0"
|
3
|
+
CUSTOMIZATION_UBL_BE = "urn:cen.eu:en16931:2017#conformant#urn:UBL.BE:1.0.0.20180214"
|
4
|
+
PROFILE_ID = "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0"
|
5
|
+
UBL_BE = "UBL_BE"
|
6
|
+
end
|
data/lib/ubl/validate.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
require "date"
|
3
|
+
require "fileutils"
|
4
|
+
require "colorize"
|
5
|
+
require_relative "constants"
|
6
|
+
|
7
|
+
module Ubl
|
8
|
+
class Validator
|
9
|
+
def initialize(extension: nil, schematron: true)
|
10
|
+
@ubl_be = extension == UBL_BE
|
11
|
+
@schematron = schematron
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate_invoice(path)
|
15
|
+
xsd = File.join(__dir__, "../../xsd/maindoc/UBL-Invoice-2.1.xsd")
|
16
|
+
validate(path, xsd)
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate_credit_note(path)
|
20
|
+
xsd = File.join(__dir__, "../../xsd/maindoc/UBL-CreditNote-2.1.xsd")
|
21
|
+
validate(path, xsd)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def validate(path, xsd)
|
27
|
+
ubl_content = File.read(path)
|
28
|
+
errors = validate_xsd(ubl_content, xsd)
|
29
|
+
return errors if errors.any?
|
30
|
+
|
31
|
+
errors = validate_schematron(path) if @schematron
|
32
|
+
|
33
|
+
errors
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate_xsd(xml_content, xsd)
|
37
|
+
return ["XSD not found: #{xsd}"] unless File.exist?(xsd)
|
38
|
+
|
39
|
+
begin
|
40
|
+
xml_doc = Nokogiri::XML(xml_content)
|
41
|
+
xsd_doc = Nokogiri::XML::Schema(File.open(xsd))
|
42
|
+
errors = xsd_doc.validate(xml_doc)
|
43
|
+
errors.map(&:message)
|
44
|
+
rescue => e
|
45
|
+
["XSD validation error: #{e.message}"]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_schematron(invoice_file)
|
50
|
+
env = @ubl_be ? "-e UBL_BE=true" : ""
|
51
|
+
cmd = "docker run --rm #{env} -v #{invoice_file}:/app/invoice.xml:ro ghcr.io/roel4d/peppol_schematron:latest 2>/dev/null"
|
52
|
+
# puts cmd
|
53
|
+
svrl_content = `#{cmd}`
|
54
|
+
parse_svrl_errors(svrl_content)
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_svrl_errors(svrl_doc, flag, color)
|
58
|
+
errors = []
|
59
|
+
svrl_doc.xpath("//failed-assert[@flag=\"#{flag}\"]").each do |node|
|
60
|
+
test = node["test"].squeeze(" ")
|
61
|
+
# location = node['location']
|
62
|
+
text = node.xpath("text").map(&:content).join(" ").tr("\n", " ").squeeze(" ").strip
|
63
|
+
errors << flag.colorize(color) + ": #{text}\n #{test.colorize(:grey)}"
|
64
|
+
end
|
65
|
+
errors
|
66
|
+
end
|
67
|
+
|
68
|
+
# Parse SVRL (Schematron Validation Report Language) output for errors
|
69
|
+
def parse_svrl_errors(svrl_content)
|
70
|
+
svrl_doc = Nokogiri::XML(svrl_content)
|
71
|
+
svrl_doc.remove_namespaces!
|
72
|
+
errors = []
|
73
|
+
errors << get_svrl_errors(svrl_doc, "fatal", :red)
|
74
|
+
errors << get_svrl_errors(svrl_doc, "warning", :light_red)
|
75
|
+
errors.flatten
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/ubl/version.rb
CHANGED
data/lib/ubl.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require_relative "ubl/builder"
|
2
|
+
require_relative "ubl/validate"
|
2
3
|
|
3
4
|
##
|
4
|
-
# Generate UBL (Universal Business Language) documents,
|
5
|
+
# Generate and validate UBL (Universal Business Language) documents,
|
5
6
|
# such as invoices and credit notes, compliant with the Peppol network.
|
6
7
|
module Ubl
|
7
8
|
class Invoice < UblBuilder
|
@@ -22,15 +23,8 @@ module Ubl
|
|
22
23
|
build_header(xml) do |xml|
|
23
24
|
xml["cbc"].InvoiceTypeCode "380"
|
24
25
|
end
|
25
|
-
|
26
26
|
build_document_reference(xml, "CommercialInvoice")
|
27
|
-
|
28
|
-
build_party(xml, @supplier, "AccountingSupplierParty")
|
29
|
-
build_party(xml, @customer, "AccountingCustomerParty")
|
30
|
-
|
31
|
-
build_tax_total(xml)
|
32
|
-
build_monetary_total(xml)
|
33
|
-
build_invoice_lines(xml)
|
27
|
+
build_content(xml)
|
34
28
|
end
|
35
29
|
end
|
36
30
|
builder.to_xml
|
@@ -55,18 +49,39 @@ module Ubl
|
|
55
49
|
build_header(xml) do |xml|
|
56
50
|
xml["cbc"].CreditNoteTypeCode "381"
|
57
51
|
end
|
58
|
-
|
59
52
|
build_document_reference(xml, "CreditNote")
|
60
|
-
|
61
|
-
build_party(xml, @supplier, "AccountingSupplierParty")
|
62
|
-
build_party(xml, @customer, "AccountingCustomerParty")
|
63
|
-
|
64
|
-
build_tax_total(xml)
|
65
|
-
build_monetary_total(xml)
|
66
|
-
build_invoice_lines(xml)
|
53
|
+
build_content(xml)
|
67
54
|
end
|
68
55
|
end
|
69
56
|
builder.to_xml
|
70
57
|
end
|
71
58
|
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Validate an invoice
|
62
|
+
#
|
63
|
+
# == Parameters
|
64
|
+
# * +path+ - The path to the XML invoice that needs validation.
|
65
|
+
# * +extension+ - Set to +"UBL_BE"+ to generate UBL.BE compliant documents.
|
66
|
+
# Defaults to +nil+ for standard PEPPOL format.
|
67
|
+
# * +schematron+ - If +true+, run a Schematron validation using Docker.
|
68
|
+
# Requires Docker to be installed and running. Defaults to +true+.
|
69
|
+
def self.validate_invoice(path, extension: nil, schematron: true)
|
70
|
+
validator = Validator.new(extension:, schematron:)
|
71
|
+
validator.validate_invoice(path)
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Validate a credit note
|
76
|
+
#
|
77
|
+
# == Parameters
|
78
|
+
# * +path+ - The path to the XML credit note that needs validation.
|
79
|
+
# * +extension+ - Set to +"UBL_BE"+ to generate UBL.BE compliant documents.
|
80
|
+
# Defaults to +nil+ for standard PEPPOL format.
|
81
|
+
# * +schematron+ - If +true+, run a Schematron validation using Docker.
|
82
|
+
# Requires Docker to be installed and running. Defaults to +true+.
|
83
|
+
def self.validate_credit_note(path, extension: nil, schematron: true)
|
84
|
+
validator = Validator.new(extension:, schematron:)
|
85
|
+
validator.validate_credit_note(path)
|
86
|
+
end
|
72
87
|
end
|