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,87 @@
1
+ module Tradedoc
2
+ module Format
3
+ module CII
4
+ module Coder
5
+ # Dates and times are a bit special.
6
+ # (This is written for DateTime but this applies to plain Date too)
7
+ #
8
+ # Sometimes they're used as `udt:DateTime` which on its own, is an element
9
+ # requiring a child that is ONE OF: `udt:DateTimeString` or `xsd:DateTime`.
10
+ # Other times, like `qdt:FormattedIssueDateTime`, it can *only* have a `udt:DateTimeString`.
11
+ # Since there have been no observed cases of a type not allowing `DateTimeString`,
12
+ # the default is to generate those. We'll parse either format though.
13
+ #
14
+ # This class is called `Date`, but it does get used with schema elements
15
+ # that are technically a DateTime, so it supports formats for time as well.
16
+ # The reason for this: most DateTime fields in CII are only Date fields
17
+ # in other formats and often, a time component doesn't make sense anyway.
18
+ # For example: invoice date. No need for time.
19
+ class Date
20
+ # https://service.unece.org/trade/untdid/d00b/tred/tred2379.htm
21
+ FORMATS = {
22
+ "2" => "%d%m%y",
23
+ "3" => "%m%d%y",
24
+ "4" => "%d%m%Y",
25
+ "101" => "%y%m%d",
26
+ "102" => "%Y%m%d",
27
+
28
+ # These are of course time formats, but it allows us to read and write
29
+ # Time fields as if they're Dates and we get all zeroes for the time components.
30
+ "204" => "%Y%m%d%H%M%S",
31
+ "205" => "%Y%m%d%H%M%z"
32
+ }
33
+ NS_U = "udt"
34
+ NS_Q = "qdt"
35
+ ISO_EL_NAME = "#{NS_U}:Date"
36
+ UN_EL_NAME = "DateTimeString"
37
+ DEFAULT_DATE_FORMAT = "102"
38
+ DEFAULT_TIME_FORMAT = "205"
39
+
40
+ private_constant :NS_U, :NS_Q, :ISO_EL_NAME, :UN_EL_NAME
41
+ private_constant :FORMATS, :DEFAULT_DATE_FORMAT, :DEFAULT_TIME_FORMAT
42
+
43
+ def self.ruby_type
44
+ ::Date
45
+ end
46
+
47
+ # @param obj [Date | Time]
48
+ # @param format_id [String]
49
+ # ID of a pre-defined UN/CEFACT format with a `udt:DateTimeString`
50
+ # Use "ISO", or :iso to render an `xsd:Date` with ISO formatting.
51
+ # @param qualified [Boolean]
52
+ # Some time fields use "qualified" times which have the same formats
53
+ # as non-qualified, but they use a different namespace.
54
+ def self.dump(w, obj, as:, format_id: nil, qualified: false)
55
+ if format_id.nil?
56
+ format_id = obj.is_a?(::Date) ? DEFAULT_DATE_FORMAT : DEFAULT_TIME_FORMAT
57
+ end
58
+
59
+ w.add(as) do
60
+ if format_id.to_s.casecmp?("iso")
61
+ w.add(ISO_EL_NAME, obj.iso8601)
62
+ else
63
+ ns = qualified ? NS_Q : NS_U
64
+ fmt = FORMATS.fetch(format_id)
65
+ w.add("#{ns}:#{UN_EL_NAME}", obj.strftime(fmt), format: format_id)
66
+ end
67
+ end
68
+ end
69
+
70
+ def self.parse(r)
71
+ # Only one of these nodes will match
72
+ r.with_node(ISO_EL_NAME) do
73
+ return ruby_type.parse(r.text)
74
+ end
75
+
76
+ [NS_Q, NS_U].each do |ns|
77
+ r.with_node("#{ns}:#{UN_EL_NAME}") do
78
+ format_id = r.attribute("format")
79
+ return ruby_type.strptime(r.text, FORMATS.fetch(format_id))
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,33 @@
1
+ module Tradedoc
2
+ module Format
3
+ module CII
4
+ module Coder
5
+ class DocumentReference
6
+ def self.ruby_type
7
+ Model::DocumentReference
8
+ end
9
+
10
+ def self.dump(w, obj, as: "ram:AssociatedReferencedDocument")
11
+ w.add(as) do
12
+ w.add("ram:IssuerAssignedID", obj.id)
13
+ w.add("ram:TypeCode", obj.type.cefact_id, listAgencyID: Code::Agency::CEFACT)
14
+ w.render(obj.uuid, as: "ram:GlobalID")
15
+ w.render(obj.issue_date, :Date, as: "ram:FormattedIssueDateTime", qualified: true)
16
+ w.render(obj.note, as: "ram:IncludedNote")
17
+ end
18
+ end
19
+
20
+ def self.parse(r)
21
+ ruby_type.new.tap do |dr|
22
+ r.with_node("ram:TypeCode") { dr.type = Code::DocumentType.get(r.text) }
23
+ r.parse("ram:IssuerAssignedID", :String) { dr.id = it }
24
+ r.parse("ram:GlobalID", :String) { dr.uuid = it }
25
+ r.parse("ram:IncludedNote", :String) { dr.note = it }
26
+ r.parse("ram:FormattedIssueDateTime", :Date) { dr.issue_date = it }
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module Tradedoc
2
+ module Format
3
+ module CII
4
+ module Coder
5
+ class ExchangeRate
6
+ def self.ruby_type
7
+ Model::ExchangeRate
8
+ end
9
+
10
+ def self.dump(w, obj, as:)
11
+ w.add(as) do
12
+ w.render(obj.source_currency_code, as: "SourceCurrencyCode")
13
+ w.render(obj.target_currency_code, as: "TargetCurrencyCode")
14
+ w.render(obj.market_id, as: "MarketID")
15
+ w.render(obj.rate, as: "ConversionRate")
16
+ w.render(obj.date, as: "ConversionRateDateTime")
17
+ end
18
+ end
19
+
20
+ def self.parse(r)
21
+ ruby_type.new.tap do |xr|
22
+ r.parse("ram:SourceCurrencyCode", :String) { xr.source_currency_code = it }
23
+ r.parse("ram:TargetCurrencyCode", :String) { xr.target_currency_code = it }
24
+ r.parse("ram:MarketID", :String) { xr.market_id = it }
25
+ r.parse("ram:ConversionRate", :BigDecimal) { xr.rate = it }
26
+ r.parse("ram:ConversionRateDateTime", :Date) { xr.date = it }
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,45 @@
1
+ module Tradedoc
2
+ module Format
3
+ module CII
4
+ module Coder
5
+ class FinancialAccount
6
+ # When an account is IBAN, it gets a node called IBANID with the account number as the text.
7
+ # For any other account type, the node is ProprietaryID.
8
+ IBAN_SCHEME = "IBAN"
9
+ EL_IBAN = "ram:IBANID"
10
+ EL_PROPRIETARY = "ram:ProprietaryID"
11
+
12
+ private_constant :IBAN_SCHEME, :EL_IBAN, :EL_PROPRIETARY
13
+
14
+ def self.ruby_type
15
+ Model::FinancialAccount
16
+ end
17
+
18
+ def self.dump(w, obj, as:)
19
+ w.add("ram:#{as}") do
20
+ case obj.scheme_name
21
+ in IBAN_SCHEME
22
+ w.add(EL_IBAN, obj.account_number)
23
+ in other
24
+ w.add(EL_PROPRIETARY, obj.account_number, schemeName: other)
25
+ end
26
+ end
27
+ end
28
+
29
+ def self.parse(r)
30
+ # only one of these nodes will be present
31
+
32
+ r.with_node(EL_IBAN) do
33
+ return ruby_type.new(scheme_name: IBAN_SCHEME, account_number: r.text)
34
+ end
35
+
36
+ r.with_node(EL_PROPRIETARY) do
37
+ scheme_name = r.attribute("schemeName")
38
+ return ruby_type.new(scheme_name:, account_number: r.text)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,39 @@
1
+ module Tradedoc
2
+ module Format
3
+ module CII
4
+ module Coder
5
+ class FinancialInstitution
6
+ def self.ruby_type
7
+ Model::FinancialInstitution
8
+ end
9
+
10
+ # This one is a little unusual.
11
+ def self.dump(w, obj, as:)
12
+ w.add(as) do
13
+ # BIC is *not* a national clearing system.
14
+ # If no clearing system is defined, then this is BIC.
15
+ if (ncs = obj.national_clearing_system)
16
+ w.add("ram:#{ncs.cefact_id}", obj.id)
17
+ else
18
+ w.add("ram:BICID", obj.id)
19
+ end
20
+ end
21
+ end
22
+
23
+ def self.parse(r)
24
+ r.with_node("ram:BICID") do
25
+ return ruby_type.new(id: r.text)
26
+ end
27
+
28
+ # Now we have to figure out the national clearing system by node name
29
+ Tradedoc::Code::NationalClearingSystem.all.each do |ncs|
30
+ r.with_node("ram:#{ncs.cefact_id}") do
31
+ return ruby_type.new(id: r.text, national_clearing_system: ncs)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,127 @@
1
+ module Tradedoc
2
+ module Format
3
+ module CII
4
+ module Coder
5
+ class Invoice
6
+ NS = {
7
+ "xmlns:qdt" => "urn:un:unece:uncefact:data:standard:QualifiedDataType:100",
8
+ "xmlns:ram" => "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100",
9
+ "xmlns:rsm" => "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100",
10
+ "xmlns:udt" => "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
11
+ }.freeze
12
+ private_constant :NS
13
+
14
+ def self.namespaces
15
+ NS
16
+ end
17
+
18
+ # @param xmldoc [Nokogiri::XML::Document]
19
+ def self.can_parse?(xmldoc)
20
+ xmldoc.root.namespace&.href == namespaces.fetch("xmlns:rsm")
21
+ end
22
+
23
+ def self.ruby_type
24
+ Model::Invoice
25
+ end
26
+
27
+ def self.dump(w, obj)
28
+ w.add("rsm:CrossIndustryInvoice", NS) do
29
+ if (spec_id = obj.specification_id)
30
+ w.add("rsm:ExchangedDocumentContext") do
31
+ w.add("ram:GuidelineSpecifiedDocumentContextParameter") do
32
+ w.render(spec_id, as: "ram:ID")
33
+ end
34
+ end
35
+ end
36
+
37
+ w.add("rsm:ExchangedDocument") do
38
+ w.render(obj.invoice_number, as: "ram:ID")
39
+ w.render(obj.invoice_type_code, as: "ram:TypeCode")
40
+ w.render(obj.issue_date, as: "ram:IssueDateTime")
41
+ if (note = obj.note)
42
+ w.add("ram:IncludedNote") do
43
+ w.add("ram:Content", note)
44
+ end
45
+ end
46
+ end
47
+
48
+ w.add("rsm:SupplyChainTradeTransaction") do
49
+ w.render_list(obj.lines)
50
+
51
+ w.add("ram:ApplicableHeaderTradeAgreement") do
52
+ w.render(obj.supplier, as: "ram:SellerTradeParty")
53
+ w.render(obj.buyer, as: "ram:BuyerTradeParty")
54
+ end
55
+
56
+ w.add("ram:ApplicableHeaderTradeDelivery")
57
+
58
+ w.add("ram:ApplicableHeaderTradeSettlement") do
59
+ w.render(obj.purchase_order_number, as: "ram:PaymentReference")
60
+ w.render(obj.currency_code, as: "ram:InvoiceCurrencyCode")
61
+
62
+ w.render_list(obj.monetary_total&.tax_breakdown&.subtotals)
63
+
64
+ w.add("ram:SpecifiedTradePaymentTerms") do
65
+ w.render(obj.due_date, as: "ram:DueDateDateTime")
66
+ end
67
+ w.render(obj.monetary_total, as: "ram:SpecifiedTradeSettlementHeaderMonetarySummation")
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def self.parse(r)
74
+ ruby_type.new.tap do |inv|
75
+ r.with_node("rsm:CrossIndustryInvoice") do
76
+ r.with_node("rsm:ExchangedDocumentContext") do
77
+ r.parse("ram:GuidelineSpecifiedDocumentContextParameter/ram:ID", :String) { inv.specification_id = it }
78
+ end
79
+ r.with_node("rsm:ExchangedDocument") do
80
+ r.parse("ram:ID", :String) { inv.invoice_number = it }
81
+ r.parse("ram:TypeCode", :String) { inv.invoice_type_code = it }
82
+ r.parse("ram:IssueDateTime", :Date) { inv.issue_date = it }
83
+ r.parse("ram:IncludedNote", :String) { inv.note = it }
84
+ end
85
+
86
+ # It seems common or normal in CII docs to omit the currency code for money attributes
87
+ # which is fair enough considering the document specifies a currency code, but
88
+ # we require a currency code for all `Money` types.
89
+ # This gets set when parsing `InvoiceCurrencyCode`
90
+ default_currency = nil
91
+
92
+ r.with_node("rsm:SupplyChainTradeTransaction") do
93
+ r.with_node("ram:ApplicableHeaderTradeSettlement") do
94
+ r.parse("ram:PaymentReference", :String) { inv.purchase_order_number = it }
95
+ r.parse("ram:InvoiceCurrencyCode", :String) do
96
+ inv.currency_code = it
97
+ default_currency = inv.currency_code
98
+ end
99
+ r.parse_list("ram:SpecifiedTradeSettlementPaymentMeans", :PaymentMeans) { inv.payment_means = it }
100
+ r.with_node("ram:SpecifiedTradePaymentTerms") do
101
+ r.parse("ram:DueDateDateTime", :Date) { inv.due_date = it }
102
+ end
103
+ r.parse("ram:SpecifiedTradeSettlementHeaderMonetarySummation", :MonetaryTotal, default_currency:) do
104
+ inv.monetary_total = it
105
+ end
106
+ r.parse_list("ram:ApplicableTradeTax", :TaxSubtotal, default_currency:) do
107
+ inv.monetary_total.tax_breakdown.subtotals = it
108
+ end
109
+ end
110
+
111
+ r.with_node("ram:ApplicableHeaderTradeAgreement") do
112
+ r.parse("ram:SellerTradeParty", :TradeParty) { inv.supplier = it }
113
+ r.parse("ram:BuyerTradeParty", :TradeParty) { inv.buyer = it }
114
+ end
115
+
116
+ r.parse_list("ram:IncludedSupplyChainTradeLineItem", :InvoiceLine, default_currency:) do
117
+ inv.lines = it
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,88 @@
1
+ module Tradedoc
2
+ module Format
3
+ module CII
4
+ module Coder
5
+ class InvoiceLine
6
+ def self.ruby_type
7
+ Model::InvoiceLine
8
+ end
9
+
10
+ def self.dump(w, obj)
11
+ w.add("ram:IncludedSupplyChainTradeLineItem") do
12
+ w.add("ram:AssociatedDocumentLineDocument") do
13
+ w.render(obj.id, as: "ram:LineID")
14
+ end
15
+
16
+ w.add("ram:SpecifiedTradeProduct") do
17
+ w.render(obj.product.seller_assigned_id, as: "ram:SellerAssignedID")
18
+ w.render(obj.product.name, as: "ram:Name")
19
+ w.render(obj.product.description, as: "ram:Description")
20
+ end
21
+
22
+ w.add("ram:SpecifiedLineTradeAgreement") do
23
+ if (gross = obj.price.gross)
24
+ w.add("ram:GrossPriceProductTradePrice") do
25
+ w.render(gross, as: "ram:ChargeAmount")
26
+ end
27
+ end
28
+
29
+ if (net = obj.price.net)
30
+ w.add("ram:NetPriceProductTradePrice") do
31
+ w.render(net, as: "ram:ChargeAmount")
32
+ end
33
+ end
34
+ end
35
+
36
+ w.add("ram:SpecifiedLineTradeDelivery") do
37
+ w.render(obj.invoiced_quantity, as: "ram:BilledQuantity")
38
+ end
39
+
40
+ w.add("ram:SpecifiedLineTradeSettlement") do
41
+ w.render(obj.tax_subtotal)
42
+ w.add("ram:SpecifiedTradeSettlementLineMonetarySummation") do
43
+ w.render(obj.total_excluding_tax, as: "ram:LineTotalAmount")
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def self.parse(r, default_currency: nil)
50
+ line = ruby_type.new
51
+ price = Model::Price.new
52
+
53
+ r.parse("ram:AssociatedDocumentLineDocument/ram:LineID", :String) { line.id = it }
54
+ r.with_node("ram:SpecifiedLineTradeAgreement") do
55
+ r.with_node("ram:GrossPriceProductTradePrice") do
56
+ r.parse("ram:ChargeAmount", :Money, default_currency:) { price.gross = it }
57
+ end
58
+ r.with_node("ram:NetPriceProductTradePrice") do
59
+ r.parse("ram:ChargeAmount", :Money, default_currency:) { price.net = it }
60
+ end
61
+ end
62
+ r.with_node("ram:SpecifiedTradeProduct") do
63
+ line.product = Model::Product.new.tap do |product|
64
+ r.parse("ram:SellerAssignedID", :String) { product.seller_assigned_id = it }
65
+ r.parse("ram:Name", :String) { product.name = it }
66
+ r.parse("ram:Description", :String) { product.description = it }
67
+ end
68
+ end
69
+ r.with_node("ram:SpecifiedLineTradeDelivery") do
70
+ r.parse("ram:BilledQuantity", :BigDecimal) { line.invoiced_quantity = it }
71
+ end
72
+
73
+ r.with_node("ram:SpecifiedLineTradeSettlement") do
74
+ r.parse("ram:ApplicableTradeTax", :TaxSubtotal) { line.tax_subtotal = it }
75
+ r.with_node("ram:SpecifiedTradeSettlementLineMonetarySummation") do
76
+ r.parse("ram:LineTotalAmount", :Money, default_currency:) { line.total_excluding_tax = it }
77
+ end
78
+ end
79
+
80
+ line.price = price
81
+
82
+ line
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,36 @@
1
+ module Tradedoc
2
+ module Format
3
+ module CII
4
+ module Coder
5
+ class MonetaryTotal
6
+ def self.ruby_type
7
+ Model::MonetaryTotal
8
+ end
9
+
10
+ def self.dump(w, obj, as:)
11
+ w.add(as) do
12
+ w.render(obj.line_items_tax_exclusive, as: "ram:LineTotalAmount")
13
+ w.render(obj.tax_exclusive, as: "ram:TaxBasisTotalAmount")
14
+ w.render(obj.tax_breakdown.total_tax, as: "ram:TaxTotalAmount")
15
+ w.render(obj.tax_inclusive, as: "ram:GrandTotalAmount")
16
+ w.render(obj.payable, as: "ram:DuePayableAmount")
17
+ end
18
+ end
19
+
20
+ def self.parse(r, **opts)
21
+ ruby_type.new.tap do |mt|
22
+ r.parse("ram:LineTotalAmount", :Money, **opts) { mt.line_items_tax_exclusive = it }
23
+ r.parse("ram:TaxBasisTotalAmount", :Money, **opts) { mt.tax_exclusive = it }
24
+ r.parse("ram:TaxTotalAmount", :Money, **opts) do
25
+ mt.tax_breakdown ||= Model::TaxBreakdown.new
26
+ mt.tax_breakdown.total_tax = it
27
+ end
28
+ r.parse("ram:GrandTotalAmount", :Money, **opts) { mt.tax_inclusive = it }
29
+ r.parse("ram:DuePayableAmount", :Money, **opts) { mt.payable = it }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ module Tradedoc
2
+ module Format
3
+ module CII
4
+ module Coder
5
+ class Money
6
+ def self.ruby_type
7
+ ::Money
8
+ end
9
+
10
+ def self.dump(w, obj, as:)
11
+ formatted = obj.to_d.to_s("F")
12
+ w.add(as, formatted, currencyID: obj.currency.iso_code)
13
+ end
14
+
15
+ # @param default_currency [String]
16
+ # Documents specify a top-level currency that indicate a default.
17
+ # Money amount fields may not include the currency code as attributes,
18
+ # so in those cases we'll fall-back to the document currency.
19
+ def self.parse(r, default_currency: nil)
20
+ iso_code = r.attribute("currencyID") || default_currency
21
+ ruby_type.from_amount(BigDecimal(r.text), iso_code)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ module Tradedoc
2
+ module Format
3
+ module CII
4
+ module Coder
5
+ class PaymentMeans
6
+ def self.ruby_type
7
+ Model::PaymentMeans
8
+ end
9
+
10
+ def self.dump(w, obj)
11
+ w.add("ram:SpecifiedTradeSettlementPaymentMeans") do
12
+ w.add("ram:TypeCode", obj.type_code.code, listAgencyID: Code::Agency::CEFACT)
13
+
14
+ obj.messages.each do |msg|
15
+ w.add("ram:Information", msg)
16
+ end
17
+
18
+ w.render(obj.sending_account, as: "PayerPartyDebtorFinancialAccount")
19
+ w.render(obj.receiving_account, as: "PayeePartyCreditorFinancialAccount")
20
+
21
+ w.render(obj.sending_account.financial_institution, as: "PayerSpecifiedDebtorFinancialInstitution")
22
+ w.render(obj.receiving_account.financial_institution, as: "PayeeSpecifiedCreditorFinancialInstitution")
23
+ end
24
+ end
25
+
26
+ def self.parse(r)
27
+ ruby_type.new.tap do |pm|
28
+ r.parse("ram:TypeCode", :String) { pm.type_code = it }
29
+
30
+ r.with_nodes("ram:Information") do
31
+ pm.messages.push(r.text)
32
+ end
33
+
34
+ r.parse("ram:PayerPartyDebtorFinancialAccount", :FinancialAccount) { pm.sending_account = it }
35
+ r.parse("ram:PayerSpecifiedDebtorFinancialInstitution", :FinancialInstitution) do
36
+ pm.sending_account.financial_institution = it
37
+ end
38
+
39
+ r.parse("ram:PayeePartyCreditorFinancialAccount", :FinancialAccount) { pm.receiving_account = it }
40
+ r.parse("ram:PayeeSpecifiedCreditorFinancialInstitution", :FinancialInstitution) do
41
+ pm.receiving_account.financial_institution = it
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,27 @@
1
+ module Tradedoc
2
+ module Format
3
+ module CII
4
+ module Coder
5
+ class Period
6
+ def self.ruby_type
7
+ Model::Period
8
+ end
9
+
10
+ def self.dump(w, obj, as:)
11
+ w.add(as) do
12
+ w.render(obj.start_date, as: "ram:StartDateTime")
13
+ w.render(obj.end_date, as: "ram:EndDateTime")
14
+ end
15
+ end
16
+
17
+ def self.parse(r)
18
+ ruby_type.new.tap do |obj|
19
+ r.parse("ram:StartDateTime", :Date) { obj.start_date = it }
20
+ r.parse("ram:EndDateTime", :Date) { obj.end_date = it }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end