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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -1
  3. data/lib/ubl/builder.rb +32 -5
  4. data/lib/ubl/constants.rb +6 -0
  5. data/lib/ubl/validate.rb +78 -0
  6. data/lib/ubl/version.rb +1 -1
  7. data/lib/ubl.rb +32 -17
  8. data/xsd/common/CCTS_CCT_SchemaModule-2.1.xsd +731 -0
  9. data/xsd/common/UBL-CommonAggregateComponents-2.1.xsd +39799 -0
  10. data/xsd/common/UBL-CommonBasicComponents-2.1.xsd +5389 -0
  11. data/xsd/common/UBL-CommonExtensionComponents-2.1.xsd +223 -0
  12. data/xsd/common/UBL-CommonSignatureComponents-2.1.xsd +101 -0
  13. data/xsd/common/UBL-CoreComponentParameters-2.1.xsd +63 -0
  14. data/xsd/common/UBL-ExtensionContentDataType-2.1.xsd +89 -0
  15. data/xsd/common/UBL-QualifiedDataTypes-2.1.xsd +69 -0
  16. data/xsd/common/UBL-SignatureAggregateComponents-2.1.xsd +138 -0
  17. data/xsd/common/UBL-SignatureBasicComponents-2.1.xsd +78 -0
  18. data/xsd/common/UBL-UnqualifiedDataTypes-2.1.xsd +553 -0
  19. data/xsd/common/UBL-XAdESv132-2.1.xsd +476 -0
  20. data/xsd/common/UBL-XAdESv141-2.1.xsd +25 -0
  21. data/xsd/common/UBL-xmldsig-core-schema-2.1.xsd +330 -0
  22. data/xsd/maindoc/UBL-ApplicationResponse-2.1.xsd +362 -0
  23. data/xsd/maindoc/UBL-AttachedDocument-2.1.xsd +416 -0
  24. data/xsd/maindoc/UBL-AwardedNotification-2.1.xsd +418 -0
  25. data/xsd/maindoc/UBL-BillOfLading-2.1.xsd +540 -0
  26. data/xsd/maindoc/UBL-CallForTenders-2.1.xsd +513 -0
  27. data/xsd/maindoc/UBL-Catalogue-2.1.xsd +565 -0
  28. data/xsd/maindoc/UBL-CatalogueDeletion-2.1.xsd +465 -0
  29. data/xsd/maindoc/UBL-CatalogueItemSpecificationUpdate-2.1.xsd +533 -0
  30. data/xsd/maindoc/UBL-CataloguePricingUpdate-2.1.xsd +532 -0
  31. data/xsd/maindoc/UBL-CatalogueRequest-2.1.xsd +571 -0
  32. data/xsd/maindoc/UBL-CertificateOfOrigin-2.1.xsd +417 -0
  33. data/xsd/maindoc/UBL-ContractAwardNotice-2.1.xsd +493 -0
  34. data/xsd/maindoc/UBL-ContractNotice-2.1.xsd +459 -0
  35. data/xsd/maindoc/UBL-CreditNote-2.1.xsd +951 -0
  36. data/xsd/maindoc/UBL-DebitNote-2.1.xsd +916 -0
  37. data/xsd/maindoc/UBL-DespatchAdvice-2.1.xsd +482 -0
  38. data/xsd/maindoc/UBL-DocumentStatus-2.1.xsd +350 -0
  39. data/xsd/maindoc/UBL-DocumentStatusRequest-2.1.xsd +348 -0
  40. data/xsd/maindoc/UBL-ExceptionCriteria-2.1.xsd +417 -0
  41. data/xsd/maindoc/UBL-ExceptionNotification-2.1.xsd +403 -0
  42. data/xsd/maindoc/UBL-Forecast-2.1.xsd +454 -0
  43. data/xsd/maindoc/UBL-ForecastRevision-2.1.xsd +453 -0
  44. data/xsd/maindoc/UBL-ForwardingInstructions-2.1.xsd +517 -0
  45. data/xsd/maindoc/UBL-FreightInvoice-2.1.xsd +867 -0
  46. data/xsd/maindoc/UBL-FulfilmentCancellation-2.1.xsd +471 -0
  47. data/xsd/maindoc/UBL-GoodsItemItinerary-2.1.xsd +431 -0
  48. data/xsd/maindoc/UBL-GuaranteeCertificate-2.1.xsd +481 -0
  49. data/xsd/maindoc/UBL-InstructionForReturns-2.1.xsd +383 -0
  50. data/xsd/maindoc/UBL-InventoryReport-2.1.xsd +401 -0
  51. data/xsd/maindoc/UBL-Invoice-2.1.xsd +1002 -0
  52. data/xsd/maindoc/UBL-ItemInformationRequest-2.1.xsd +402 -0
  53. data/xsd/maindoc/UBL-Order-2.1.xsd +891 -0
  54. data/xsd/maindoc/UBL-OrderCancellation-2.1.xsd +416 -0
  55. data/xsd/maindoc/UBL-OrderChange-2.1.xsd +869 -0
  56. data/xsd/maindoc/UBL-OrderResponse-2.1.xsd +956 -0
  57. data/xsd/maindoc/UBL-OrderResponseSimple-2.1.xsd +486 -0
  58. data/xsd/maindoc/UBL-PackingList-2.1.xsd +432 -0
  59. data/xsd/maindoc/UBL-PriorInformationNotice-2.1.xsd +441 -0
  60. data/xsd/maindoc/UBL-ProductActivity-2.1.xsd +387 -0
  61. data/xsd/maindoc/UBL-Quotation-2.1.xsd +583 -0
  62. data/xsd/maindoc/UBL-ReceiptAdvice-2.1.xsd +485 -0
  63. data/xsd/maindoc/UBL-Reminder-2.1.xsd +759 -0
  64. data/xsd/maindoc/UBL-RemittanceAdvice-2.1.xsd +567 -0
  65. data/xsd/maindoc/UBL-RequestForQuotation-2.1.xsd +519 -0
  66. data/xsd/maindoc/UBL-RetailEvent-2.1.xsd +515 -0
  67. data/xsd/maindoc/UBL-SelfBilledCreditNote-2.1.xsd +918 -0
  68. data/xsd/maindoc/UBL-SelfBilledInvoice-2.1.xsd +933 -0
  69. data/xsd/maindoc/UBL-Statement-2.1.xsd +600 -0
  70. data/xsd/maindoc/UBL-StockAvailabilityReport-2.1.xsd +403 -0
  71. data/xsd/maindoc/UBL-Tender-2.1.xsd +464 -0
  72. data/xsd/maindoc/UBL-TenderReceipt-2.1.xsd +398 -0
  73. data/xsd/maindoc/UBL-TendererQualification-2.1.xsd +401 -0
  74. data/xsd/maindoc/UBL-TendererQualificationResponse-2.1.xsd +399 -0
  75. data/xsd/maindoc/UBL-TradeItemLocationProfile-2.1.xsd +415 -0
  76. data/xsd/maindoc/UBL-TransportExecutionPlan-2.1.xsd +769 -0
  77. data/xsd/maindoc/UBL-TransportExecutionPlanRequest-2.1.xsd +696 -0
  78. data/xsd/maindoc/UBL-TransportProgressStatus-2.1.xsd +396 -0
  79. data/xsd/maindoc/UBL-TransportProgressStatusRequest-2.1.xsd +345 -0
  80. data/xsd/maindoc/UBL-TransportServiceDescription-2.1.xsd +430 -0
  81. data/xsd/maindoc/UBL-TransportServiceDescriptionRequest-2.1.xsd +363 -0
  82. data/xsd/maindoc/UBL-TransportationStatus-2.1.xsd +566 -0
  83. data/xsd/maindoc/UBL-TransportationStatusRequest-2.1.xsd +484 -0
  84. data/xsd/maindoc/UBL-UnawardedNotification-2.1.xsd +415 -0
  85. data/xsd/maindoc/UBL-UtilityStatement-2.1.xsd +490 -0
  86. data/xsd/maindoc/UBL-Waybill-2.1.xsd +501 -0
  87. metadata +99 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7910f61d2fb9d3441defbd3a471c8dee214fcc2328430ddc74564e03839e02f
4
- data.tar.gz: 94702d68716de735e38896e7c927fadb216917f422b1ed9cc58f8aac697541aa
3
+ metadata.gz: 07e7d21d74c69ab965fe5a9968fd7404629079233b2fd6554e7fac2f51aff5e6
4
+ data.tar.gz: e13d2459ed88883a5206449565fb665bee8a59a097bbccf489c975be4ace1489
5
5
  SHA512:
6
- metadata.gz: 274419fa7cbf69522f8ba6ade35415aead6750e63efce27f623e136accb2705dd29fc339dfcdfea63f9a9a16fcf0bd3587ef07e46aa3d073048af2724ed6d19b
7
- data.tar.gz: 52e29b3ecaa743ea11d525f743ec0de05207a4c0d2980509ee9ec53fd2ccffb8a6901ab4758ec9fcdc99713ca16411d483cd73fa63e05605dba5ac736a957496
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 == "UBL_BE"
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
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ubl
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
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