tradedoc 0.1.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.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +13 -0
  3. data/CHANGELOG.md +5 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +53 -0
  6. data/Rakefile +10 -0
  7. data/lib/tradedoc/code/agency.rb +8 -0
  8. data/lib/tradedoc/code/document_type.rb +95 -0
  9. data/lib/tradedoc/code/national_clearing_system.rb +97 -0
  10. data/lib/tradedoc/code/payment_means_type.rb +107 -0
  11. data/lib/tradedoc/code/tax_category.rb +97 -0
  12. data/lib/tradedoc/code.rb +12 -0
  13. data/lib/tradedoc/format/apeh.rb +36 -0
  14. data/lib/tradedoc/format/cii/coder/address.rb +55 -0
  15. data/lib/tradedoc/format/cii/coder/big_decimal.rb +21 -0
  16. data/lib/tradedoc/format/cii/coder/contact.rb +43 -0
  17. data/lib/tradedoc/format/cii/coder/date.rb +87 -0
  18. data/lib/tradedoc/format/cii/coder/document_reference.rb +33 -0
  19. data/lib/tradedoc/format/cii/coder/exchange_rate.rb +33 -0
  20. data/lib/tradedoc/format/cii/coder/financial_account.rb +45 -0
  21. data/lib/tradedoc/format/cii/coder/financial_institution.rb +39 -0
  22. data/lib/tradedoc/format/cii/coder/invoice.rb +127 -0
  23. data/lib/tradedoc/format/cii/coder/invoice_line.rb +88 -0
  24. data/lib/tradedoc/format/cii/coder/monetary_total.rb +36 -0
  25. data/lib/tradedoc/format/cii/coder/money.rb +27 -0
  26. data/lib/tradedoc/format/cii/coder/payment_means.rb +49 -0
  27. data/lib/tradedoc/format/cii/coder/period.rb +27 -0
  28. data/lib/tradedoc/format/cii/coder/remittance_advice.rb +130 -0
  29. data/lib/tradedoc/format/cii/coder/string.rb +21 -0
  30. data/lib/tradedoc/format/cii/coder/tax_subtotal.rb +35 -0
  31. data/lib/tradedoc/format/cii/coder/trade_party.rb +29 -0
  32. data/lib/tradedoc/format/cii/coder.rb +27 -0
  33. data/lib/tradedoc/format/cii.rb +12 -0
  34. data/lib/tradedoc/format/facturae.rb +51 -0
  35. data/lib/tradedoc/format/fatturapa/coder/invoice.rb +31 -0
  36. data/lib/tradedoc/format/fatturapa/coder.rb +8 -0
  37. data/lib/tradedoc/format/fatturapa.rb +13 -0
  38. data/lib/tradedoc/format/isdoc.rb +40 -0
  39. data/lib/tradedoc/format/ksef/coder/invoice.rb +32 -0
  40. data/lib/tradedoc/format/ksef/coder.rb +10 -0
  41. data/lib/tradedoc/format/ksef.rb +17 -0
  42. data/lib/tradedoc/format/mydata.rb +42 -0
  43. data/lib/tradedoc/format/nav.rb +38 -0
  44. data/lib/tradedoc/format/ubl/coder/address.rb +48 -0
  45. data/lib/tradedoc/format/ubl/coder/big_decimal.rb +21 -0
  46. data/lib/tradedoc/format/ubl/coder/contact.rb +29 -0
  47. data/lib/tradedoc/format/ubl/coder/country.rb +27 -0
  48. data/lib/tradedoc/format/ubl/coder/date.rb +21 -0
  49. data/lib/tradedoc/format/ubl/coder/document_reference.rb +55 -0
  50. data/lib/tradedoc/format/ubl/coder/exchange_rate.rb +33 -0
  51. data/lib/tradedoc/format/ubl/coder/financial_account.rb +32 -0
  52. data/lib/tradedoc/format/ubl/coder/financial_institution.rb +42 -0
  53. data/lib/tradedoc/format/ubl/coder/invoice.rb +92 -0
  54. data/lib/tradedoc/format/ubl/coder/invoice_line.rb +53 -0
  55. data/lib/tradedoc/format/ubl/coder/monetary_total.rb +39 -0
  56. data/lib/tradedoc/format/ubl/coder/money.rb +22 -0
  57. data/lib/tradedoc/format/ubl/coder/payment_means.rb +39 -0
  58. data/lib/tradedoc/format/ubl/coder/period.rb +27 -0
  59. data/lib/tradedoc/format/ubl/coder/product.rb +37 -0
  60. data/lib/tradedoc/format/ubl/coder/remittance_advice.rb +91 -0
  61. data/lib/tradedoc/format/ubl/coder/string.rb +21 -0
  62. data/lib/tradedoc/format/ubl/coder/tax_breakdown.rb +27 -0
  63. data/lib/tradedoc/format/ubl/coder/tax_category.rb +37 -0
  64. data/lib/tradedoc/format/ubl/coder/tax_subtotal.rb +29 -0
  65. data/lib/tradedoc/format/ubl/coder/trade_party.rb +37 -0
  66. data/lib/tradedoc/format/ubl/coder.rb +29 -0
  67. data/lib/tradedoc/format/ubl.rb +13 -0
  68. data/lib/tradedoc/format/zugferdv1/coder/invoice.rb +33 -0
  69. data/lib/tradedoc/format/zugferdv1/coder.rb +8 -0
  70. data/lib/tradedoc/format/zugferdv1.rb +21 -0
  71. data/lib/tradedoc/format.rb +110 -0
  72. data/lib/tradedoc/model/address.rb +21 -0
  73. data/lib/tradedoc/model/base.rb +153 -0
  74. data/lib/tradedoc/model/contact.rb +9 -0
  75. data/lib/tradedoc/model/country.rb +8 -0
  76. data/lib/tradedoc/model/country_subdivision.rb +8 -0
  77. data/lib/tradedoc/model/document.rb +9 -0
  78. data/lib/tradedoc/model/document_reference.rb +11 -0
  79. data/lib/tradedoc/model/exchange_rate.rb +17 -0
  80. data/lib/tradedoc/model/financial_account.rb +19 -0
  81. data/lib/tradedoc/model/financial_institution.rb +11 -0
  82. data/lib/tradedoc/model/invoice.rb +57 -0
  83. data/lib/tradedoc/model/invoice_line.rb +46 -0
  84. data/lib/tradedoc/model/monetary_total.rb +34 -0
  85. data/lib/tradedoc/model/payment_means.rb +27 -0
  86. data/lib/tradedoc/model/period.rb +8 -0
  87. data/lib/tradedoc/model/price.rb +21 -0
  88. data/lib/tradedoc/model/product.rb +18 -0
  89. data/lib/tradedoc/model/remittance_advice.rb +20 -0
  90. data/lib/tradedoc/model/remittance_advice_line.rb +24 -0
  91. data/lib/tradedoc/model/tax_breakdown.rb +11 -0
  92. data/lib/tradedoc/model/tax_category.rb +26 -0
  93. data/lib/tradedoc/model/tax_subtotal.rb +13 -0
  94. data/lib/tradedoc/model/trade_party.rb +9 -0
  95. data/lib/tradedoc/model.rb +33 -0
  96. data/lib/tradedoc/version.rb +5 -0
  97. data/lib/tradedoc/xml/reader.rb +163 -0
  98. data/lib/tradedoc/xml/writer.rb +56 -0
  99. data/lib/tradedoc/xml.rb +8 -0
  100. data/lib/tradedoc.rb +98 -0
  101. data/sig/tradedoc.rbs +4 -0
  102. metadata +187 -0
@@ -0,0 +1,34 @@
1
+ module Tradedoc
2
+ module Model
3
+ # [BG-22] Totals for a document
4
+ class MonetaryTotal < Base
5
+ # [BT-106] Sum of all line items' net amounts. No tax.
6
+ # (quantity * unit price) - line-level discounts
7
+ has :line_items_tax_exclusive, Money
8
+
9
+ # [BT-107] Document-level discounts (aka allowances) e.g. "10% off everything"
10
+ has :discounts, Money
11
+
12
+ # [BT-108] Document-level charges e.g. shipping, processing
13
+ has :charges, Money
14
+
15
+ # [BT-109] aka. net amount
16
+ has :tax_exclusive, Money
17
+
18
+ has :tax_breakdown, TaxBreakdown
19
+
20
+ # [BT-112]
21
+ has :tax_inclusive, Money
22
+
23
+ # [BT-113] Money already paid by the buyer *before* this. e.g. deposits
24
+ has :prepaid, Money
25
+
26
+ # [BT-114] Adjustment of a few cents to reconcile discrepancies caused by rounding.
27
+ has :rounding, Money
28
+
29
+ # [BT-115] The actual total amount remaining to be paid by the buyer
30
+ # tax_exclusive - prepaid + rounding
31
+ has :payable, Money
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,27 @@
1
+ module Tradedoc
2
+ module Model
3
+ class PaymentMeans < Base
4
+ # [BT-81]
5
+ has :type_code, Code::PaymentMeansType
6
+
7
+ # Multiple formats call these "Payer" and "Payee", but it's too easy to
8
+ # miss that single character difference which obviously makes a huge difference.
9
+ # Opt for the obviously distinctive naming.
10
+ has :sending_account, FinancialAccount
11
+
12
+ # [BG-18]
13
+ has :receiving_account, FinancialAccount
14
+
15
+ # [BT-83]
16
+ has :payment_id, String
17
+
18
+ has :instruction_id, String
19
+
20
+ # Zero or many lines of messages that were sent to the bank about the payment.
21
+ # This is the text that the receiving party will typically see in their account
22
+ # with a label like "message" or "details".
23
+ # Banking Circle for example allows 4 lines of "remittance information".
24
+ has_many :messages, String
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,8 @@
1
+ module Tradedoc
2
+ module Model
3
+ class Period < Base
4
+ has :start_date, Date
5
+ has :end_date, Date
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ module Tradedoc
2
+ module Model
3
+ # [BG-29]
4
+ # The price of the invoiced item, including the net price, base quantity,
5
+ # and any price discount from the gross price.
6
+ class Price < Base
7
+ # [BT-146] gross - discount
8
+ has :net, Money
9
+
10
+ # [BT-147] Discount applied to the gross to arrive at the net
11
+ has :discount, Money
12
+
13
+ # [BT-148] Price before any discounts
14
+ has :gross, Money
15
+
16
+ # [BT-149] The quantity to which the price applies
17
+ # (e.g. price per 100 units). Defaults to 1 if not specified.
18
+ has :base_quantity, BigDecimal
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ module Tradedoc
2
+ module Model
3
+ # [BG-31]
4
+ # Known in UBL as an `Item`, this is any product or item that can be traded.
5
+ # Includes things like the description, colour, product code, catalogue code, weights, etc.
6
+ class Product < Base
7
+ # [BT-153] Required
8
+ has :name, String
9
+
10
+ # [BT-154]
11
+ has :description, String
12
+
13
+ has :seller_assigned_id, String
14
+
15
+ has :tax_category, TaxCategory
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module Tradedoc
2
+ module Model
3
+ class RemittanceAdvice < Base
4
+ is_document
5
+
6
+ has :id, String
7
+ has :uuid, String
8
+ has :issue_date, Date
9
+ has :note, String
10
+ has :buyer, TradeParty
11
+ has :supplier, TradeParty
12
+ has :total_payment_amount, Money
13
+ has :invoice_period, Period
14
+ has :payment_means, PaymentMeans
15
+
16
+ has_many :lines, RemittanceAdviceLine
17
+ alias_method :line_items, :lines
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ module Tradedoc
2
+ module Model
3
+ class RemittanceAdviceLine < Base
4
+ has :id, String
5
+ has :uuid, String
6
+ has :note, String
7
+
8
+ has :document_reference, DocumentReference
9
+
10
+ # Original amount due.
11
+ # Not supported by CII
12
+ has :debit_amount, Money
13
+
14
+ # If there's any offset/credit to apply. Usually 0.
15
+ # Not supported by CII
16
+ has :credit_amount, Money
17
+
18
+ # Actual amount being paid.
19
+ has :balance_amount, Money
20
+
21
+ has :exchange_rate, ExchangeRate
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ module Tradedoc
2
+ module Model
3
+ # [BG-23]
4
+ class TaxBreakdown < Base
5
+ # [BT-110] Tax total for the whole document
6
+ has :total_tax, Money
7
+
8
+ has_many :subtotals, TaxSubtotal
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ module Tradedoc
2
+ module Model
3
+ # [BG-30]
4
+ class TaxCategory < Base
5
+ # [BT-118] VAT category code
6
+ # https://unece.org/fileadmin/DAM/trade/untdid/d16b/tred/tred5305.htm
7
+ has :code, Code::TaxCategory
8
+
9
+ # [BT-119]
10
+ # Stores as the percentage, not as a fraction of 1. e.g. 4.5% = 4.50
11
+ has :rate_percent, BigDecimal
12
+
13
+ # [BT-120]
14
+ has :exemption_reason, String
15
+
16
+ # [BT-121]
17
+ # https://docs.peppol.eu/poacc/billing/3.0/codelist/vatex/
18
+ # https://ec.europa.eu/digital-building-blocks/sites/spaces/DIGITAL/pages/467108957/Code+lists
19
+ # Could also be CWA 15577 codes
20
+ has :exemption_reason_code, String
21
+
22
+ # https://service.unece.org/trade/untdid/d00a/tred/tred5153.htm
23
+ has :tax_scheme, String
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ module Tradedoc
2
+ module Model
3
+ class TaxSubtotal < Base
4
+ # [BT-116] Amount subject to tax
5
+ has :taxable_amount, Money
6
+
7
+ # [BT-117] Amount taxed in this category
8
+ has :tax_amount, Money
9
+
10
+ has :tax_category, TaxCategory
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Tradedoc
2
+ module Model
3
+ class TradeParty < Base
4
+ has :name, String
5
+ has :address, Address
6
+ has :contact, Contact
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,33 @@
1
+ module Tradedoc
2
+ module Model
3
+ end
4
+ end
5
+
6
+ require_relative "model/base"
7
+
8
+ # no dependencies
9
+ require_relative "model/contact"
10
+ require_relative "model/country"
11
+ require_relative "model/country_subdivision"
12
+ require_relative "model/exchange_rate"
13
+ require_relative "model/financial_institution"
14
+ require_relative "model/period"
15
+ require_relative "model/price"
16
+ require_relative "model/tax_category"
17
+ require_relative "model/tax_subtotal"
18
+
19
+ # depends on others. order may matter
20
+ require_relative "model/address"
21
+ require_relative "model/financial_account"
22
+ require_relative "model/trade_party"
23
+ require_relative "model/document_reference"
24
+ require_relative "model/tax_breakdown"
25
+ require_relative "model/monetary_total"
26
+ require_relative "model/payment_means"
27
+ require_relative "model/product"
28
+
29
+ require_relative "model/remittance_advice_line"
30
+ require_relative "model/remittance_advice"
31
+
32
+ require_relative "model/invoice_line"
33
+ require_relative "model/invoice"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tradedoc
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,163 @@
1
+ module Tradedoc
2
+ module XML
3
+ # Provides an XML reading experience similar to the `Nokogiri::XML::Builder`
4
+ # experience where you can traverse a document by selecting nodes without
5
+ # having to constantly do `nil` checks and assign ephemeral variables.
6
+ class Reader
7
+ attr_reader :node, :namespaces, :format
8
+
9
+ # @param node [Nokogiri::XML::Node]
10
+ # XML::Document is also considered a node
11
+ # @param namespaces [Hash]
12
+ # Namespaces *with aliases* to use when reading the document.
13
+ # You must not rely on namespace aliases defined in the source document
14
+ # since they can easily be different to what you expect.
15
+ # Define a list of all namespaces you'll read from with aliases.
16
+ #
17
+ # { "xmlns:foo" => "urn:example:Foo", "xmlns:bar" => "urn:example:Bar" }
18
+ # @param format [Module]
19
+ # The format to use for parsing.
20
+ # Must repond to `.coder_for(type)` to get a coder for a destination type.
21
+ def initialize(node, namespaces = {}, format:)
22
+ @node = node
23
+ @namespaces = namespaces
24
+ @format = format
25
+ end
26
+
27
+ # If a node exists at the given xpath, yield a reader for that node.
28
+ # This avoids the pattern nil-checking every single read operation.
29
+ #
30
+ # This pattern of swapping-out the current node makes reading XML similar
31
+ # to building a document with Nokogiri.
32
+ #
33
+ # @example
34
+ # reader = Reader.new(Nokogiri::XML.parse(str))
35
+ # obj = Person.new
36
+ #
37
+ # reader.with_node("Person") do
38
+ # obj.id = reader.at_xpath("ID").text
39
+ # reader.with_node("Address") do
40
+ # obj.address.city = reader.at_xpath("City").text
41
+ # end
42
+ # end
43
+ #
44
+ # @param input [String | Nokogiri::XML::Node] XPath or Node
45
+ def with_node(input)
46
+ previous_node = node
47
+
48
+ next_node = case input
49
+ in ::String => xpath
50
+ at_xpath(xpath)
51
+ in Nokogiri::XML::Node => n
52
+ n
53
+ end
54
+
55
+ if next_node.nil?
56
+ return
57
+ end
58
+
59
+ @node = next_node
60
+ yield
61
+ ensure
62
+ @node = previous_node
63
+ end
64
+
65
+ # Given an xpath that matches 0 or more nodes, maps each element using
66
+ # the given block. The block will reference the current node.
67
+ #
68
+ # @example
69
+ # r.with_nodes("LineItem") do
70
+ # Model::LineItem.new(description: r.at_xpath("Description").text)
71
+ # end
72
+ # => [Model::LineItem, Model::LineItem...]
73
+ def with_nodes(xpath, &block)
74
+ previous_node = node
75
+
76
+ self.xpath(xpath).map do |current|
77
+ @node = current
78
+ yield
79
+ end
80
+ ensure
81
+ @node = previous_node
82
+ end
83
+
84
+ # Given an xpath and a type coder class, if that node exists, parse it into
85
+ # an instance of the "real" type we want out of this node.
86
+ # Yields the parsed value so that callers can do something with it if
87
+ # the given node is present
88
+ #
89
+ # @example
90
+ # r.parse("udt:IssueDate", :Date) { doc.issue_date = it }
91
+ #
92
+ # @param xpath [String]
93
+ # @param coder_ref [Class | Symbol]
94
+ # Any class that responds to `.parse(r)` where r is this `Reader`
95
+ # @param opts [Hash] Options to pass to the underlying parser
96
+ # @return [Object | nil]
97
+ def parse(xpath, coder_ref, **opts)
98
+ coder_class = self.format.coder_for(coder_ref)
99
+ node_exists = false
100
+ value = with_node(xpath) do
101
+ node_exists = true
102
+ coder_class.parse(self, **opts)
103
+ end
104
+
105
+ # Allows parse callers to do something with the parsed value e.g.
106
+ # parse("//node", Address) { some_obj.address = it }
107
+ if block_given? && node_exists
108
+ yield value
109
+ else
110
+ value
111
+ end
112
+ end
113
+
114
+ def parse_list(xpath, coder_ref, **opts)
115
+ coder_class = self.format.coder_for(coder_ref)
116
+
117
+ values = with_nodes(xpath) do
118
+ coder_class.parse(self, **opts)
119
+ end
120
+
121
+ if block_given?
122
+ yield values
123
+ end
124
+
125
+ values
126
+ end
127
+
128
+ def text(strip: true, nilify: true)
129
+ v = node.text
130
+
131
+ return if v.nil?
132
+
133
+ if strip
134
+ v = v.strip
135
+ end
136
+
137
+ if nilify && v.empty?
138
+ v = nil
139
+ end
140
+
141
+ v
142
+ end
143
+
144
+ def at_xpath(path)
145
+ node.at_xpath(path, namespaces)
146
+ end
147
+
148
+ def xpath(path)
149
+ node.xpath(path, namespaces)
150
+ end
151
+
152
+ def attribute(name)
153
+ attribute!(name)
154
+ rescue KeyError
155
+ nil
156
+ end
157
+
158
+ def attribute!(name)
159
+ node.attributes.fetch(name).text
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,56 @@
1
+ module Tradedoc
2
+ module XML
3
+ # Wrapper for a `Nokogiri::XML::Builder` that creates an interface for common
4
+ # build actions, namely rendering "components" easily.
5
+ class Writer
6
+ InvalidNodeNameError = Class.new(Error)
7
+
8
+ # @param xml [Nokogiri::XML::Builder]
9
+ # @param format [Module]
10
+ # Any module responding to `.coder_for` and returning a coder.
11
+ # In reality, should be one of the `Tradedoc.coders`
12
+ def initialize(xml, format:)
13
+ @xml = xml
14
+ @format = format
15
+ end
16
+
17
+ # Directly adds an element to the document without any processing.
18
+ # Normally with a Nokogiri builder you'd do `xml["ns"].ElemName(text)`
19
+ # but with this you'd do `writer.add("ns:ElemName", value)`
20
+ def add(name, ...)
21
+ if name.include?("/")
22
+ raise InvalidNodeNameError, "element name #{name} includes `/`"
23
+ end
24
+
25
+ if (c = name.scan(":").count) && c > 1
26
+ raise InvalidNodeNameError, "element name #{name} includes `:` multiple times"
27
+ end
28
+
29
+ xml.public_send(name, ...)
30
+ end
31
+
32
+ # Given an object, figure out how to dump/render it into the XML builder.
33
+ #
34
+ # If the given object/value is `nil`, nothing is rendered. No empty element.
35
+ # Otherwise, a coder is looked-up and used for the given type.
36
+ # Assumes that the de-namespaced class name has a matching local coder.
37
+ # e.g. `Tradedoc::Model::Country` rendered by `Tradedoc::Format::UBL::Coder::Country`
38
+ def render(obj, coder_ref = nil, **opts)
39
+ return if obj.nil?
40
+
41
+ coder_class = self.format.coder_for(coder_ref || obj.class)
42
+ coder_class.dump(self, obj, **opts)
43
+ end
44
+
45
+ def render_list(objects, coder_ref = nil, **opts)
46
+ return if objects.nil? || objects.none?
47
+
48
+ objects.each { render(it, coder_ref, **opts) }
49
+ end
50
+
51
+ private
52
+
53
+ attr_reader :xml, :format
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,8 @@
1
+ module Tradedoc
2
+ module XML
3
+ Error = Class.new(Tradedoc::Error)
4
+ end
5
+ end
6
+
7
+ require_relative "xml/reader"
8
+ require_relative "xml/writer"
data/lib/tradedoc.rb ADDED
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bigdecimal"
4
+ require "nokogiri"
5
+ require "money"
6
+
7
+ module Tradedoc
8
+ Error = Class.new(StandardError)
9
+ end
10
+
11
+ # no dependencies
12
+ require_relative "tradedoc/version"
13
+ require_relative "tradedoc/xml"
14
+ require_relative "tradedoc/code"
15
+
16
+ # may have dependencies and order may matter
17
+ require_relative "tradedoc/format"
18
+ require_relative "tradedoc/model"
19
+
20
+ module Tradedoc
21
+ # List of formats supports for reading and writing documents
22
+ #
23
+ # @return [Set<Module>]
24
+ def self.formats
25
+ Set[
26
+ Format::APEH,
27
+ Format::CII,
28
+ Format::FacturaE,
29
+ Format::FatturaPA,
30
+ Format::ISDOC,
31
+ Format::KSEF,
32
+ Format::MyData,
33
+ Format::NAV,
34
+ Format::UBL,
35
+ Format::ZugferdV1
36
+ ]
37
+ end
38
+
39
+ # List of all possible file extensions that may be parsable by a format in this library
40
+ #
41
+ # @return [Set<String>]
42
+ def self.file_extensions
43
+ formats.reduce(Set[]) { |memo, fmt| memo + fmt.file_extensions }
44
+ end
45
+
46
+ # Detect the format and document type of a given source
47
+ #
48
+ # @return [Array<Module, Class>]
49
+ def self.detect(source)
50
+ xml = xml_from(source)
51
+ formats.each do |fmt|
52
+ if (dt = fmt.detect_document_coder(xml))
53
+ return [fmt, dt]
54
+ end
55
+ end
56
+
57
+ nil
58
+ end
59
+
60
+ # Given an XML document or XML string, detect the format, document type, and
61
+ # parse it into a model.
62
+ #
63
+ # Returns `nil` when the format or document type isn't supported.
64
+ #
65
+ # @param source [Nokogiri::XML::Document | String]
66
+ # @return [Object | nil]
67
+ def self.parse(source)
68
+ xml = xml_from(source)
69
+ if detect(xml) in [fmt, coder]
70
+ fmt.parse(xml, coder:)
71
+ end
72
+ end
73
+
74
+ # Get a format by a flexible source selector
75
+ #
76
+ # @param source [Symbol | String | Module]
77
+ # @return [Module]
78
+ def self.format_from(source)
79
+ case source
80
+ in Module => m if formats.include?(m)
81
+ m
82
+ in String | Symbol => name
83
+ name = name.to_s
84
+ formats.detect { it.label.casecmp?(name) || it.name.split("::").last.casecmp?(name) }
85
+ end
86
+ end
87
+
88
+ # @param source [String | Nokogiri::XML::Document]
89
+ # @return [Nokogiri::XML::Document]
90
+ def self.xml_from(source)
91
+ case source
92
+ in Nokogiri::XML::Document => doc
93
+ doc
94
+ in String => str
95
+ Nokogiri::XML.parse(str)
96
+ end
97
+ end
98
+ end
data/sig/tradedoc.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Tradedoc
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end