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.
- checksums.yaml +7 -0
- data/.editorconfig +13 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +53 -0
- data/Rakefile +10 -0
- data/lib/tradedoc/code/agency.rb +8 -0
- data/lib/tradedoc/code/document_type.rb +95 -0
- data/lib/tradedoc/code/national_clearing_system.rb +97 -0
- data/lib/tradedoc/code/payment_means_type.rb +107 -0
- data/lib/tradedoc/code/tax_category.rb +97 -0
- data/lib/tradedoc/code.rb +12 -0
- data/lib/tradedoc/format/apeh.rb +36 -0
- data/lib/tradedoc/format/cii/coder/address.rb +55 -0
- data/lib/tradedoc/format/cii/coder/big_decimal.rb +21 -0
- data/lib/tradedoc/format/cii/coder/contact.rb +43 -0
- data/lib/tradedoc/format/cii/coder/date.rb +87 -0
- data/lib/tradedoc/format/cii/coder/document_reference.rb +33 -0
- data/lib/tradedoc/format/cii/coder/exchange_rate.rb +33 -0
- data/lib/tradedoc/format/cii/coder/financial_account.rb +45 -0
- data/lib/tradedoc/format/cii/coder/financial_institution.rb +39 -0
- data/lib/tradedoc/format/cii/coder/invoice.rb +127 -0
- data/lib/tradedoc/format/cii/coder/invoice_line.rb +88 -0
- data/lib/tradedoc/format/cii/coder/monetary_total.rb +36 -0
- data/lib/tradedoc/format/cii/coder/money.rb +27 -0
- data/lib/tradedoc/format/cii/coder/payment_means.rb +49 -0
- data/lib/tradedoc/format/cii/coder/period.rb +27 -0
- data/lib/tradedoc/format/cii/coder/remittance_advice.rb +130 -0
- data/lib/tradedoc/format/cii/coder/string.rb +21 -0
- data/lib/tradedoc/format/cii/coder/tax_subtotal.rb +35 -0
- data/lib/tradedoc/format/cii/coder/trade_party.rb +29 -0
- data/lib/tradedoc/format/cii/coder.rb +27 -0
- data/lib/tradedoc/format/cii.rb +12 -0
- data/lib/tradedoc/format/facturae.rb +51 -0
- data/lib/tradedoc/format/fatturapa/coder/invoice.rb +31 -0
- data/lib/tradedoc/format/fatturapa/coder.rb +8 -0
- data/lib/tradedoc/format/fatturapa.rb +13 -0
- data/lib/tradedoc/format/isdoc.rb +40 -0
- data/lib/tradedoc/format/ksef/coder/invoice.rb +32 -0
- data/lib/tradedoc/format/ksef/coder.rb +10 -0
- data/lib/tradedoc/format/ksef.rb +17 -0
- data/lib/tradedoc/format/mydata.rb +42 -0
- data/lib/tradedoc/format/nav.rb +38 -0
- data/lib/tradedoc/format/ubl/coder/address.rb +48 -0
- data/lib/tradedoc/format/ubl/coder/big_decimal.rb +21 -0
- data/lib/tradedoc/format/ubl/coder/contact.rb +29 -0
- data/lib/tradedoc/format/ubl/coder/country.rb +27 -0
- data/lib/tradedoc/format/ubl/coder/date.rb +21 -0
- data/lib/tradedoc/format/ubl/coder/document_reference.rb +55 -0
- data/lib/tradedoc/format/ubl/coder/exchange_rate.rb +33 -0
- data/lib/tradedoc/format/ubl/coder/financial_account.rb +32 -0
- data/lib/tradedoc/format/ubl/coder/financial_institution.rb +42 -0
- data/lib/tradedoc/format/ubl/coder/invoice.rb +92 -0
- data/lib/tradedoc/format/ubl/coder/invoice_line.rb +53 -0
- data/lib/tradedoc/format/ubl/coder/monetary_total.rb +39 -0
- data/lib/tradedoc/format/ubl/coder/money.rb +22 -0
- data/lib/tradedoc/format/ubl/coder/payment_means.rb +39 -0
- data/lib/tradedoc/format/ubl/coder/period.rb +27 -0
- data/lib/tradedoc/format/ubl/coder/product.rb +37 -0
- data/lib/tradedoc/format/ubl/coder/remittance_advice.rb +91 -0
- data/lib/tradedoc/format/ubl/coder/string.rb +21 -0
- data/lib/tradedoc/format/ubl/coder/tax_breakdown.rb +27 -0
- data/lib/tradedoc/format/ubl/coder/tax_category.rb +37 -0
- data/lib/tradedoc/format/ubl/coder/tax_subtotal.rb +29 -0
- data/lib/tradedoc/format/ubl/coder/trade_party.rb +37 -0
- data/lib/tradedoc/format/ubl/coder.rb +29 -0
- data/lib/tradedoc/format/ubl.rb +13 -0
- data/lib/tradedoc/format/zugferdv1/coder/invoice.rb +33 -0
- data/lib/tradedoc/format/zugferdv1/coder.rb +8 -0
- data/lib/tradedoc/format/zugferdv1.rb +21 -0
- data/lib/tradedoc/format.rb +110 -0
- data/lib/tradedoc/model/address.rb +21 -0
- data/lib/tradedoc/model/base.rb +153 -0
- data/lib/tradedoc/model/contact.rb +9 -0
- data/lib/tradedoc/model/country.rb +8 -0
- data/lib/tradedoc/model/country_subdivision.rb +8 -0
- data/lib/tradedoc/model/document.rb +9 -0
- data/lib/tradedoc/model/document_reference.rb +11 -0
- data/lib/tradedoc/model/exchange_rate.rb +17 -0
- data/lib/tradedoc/model/financial_account.rb +19 -0
- data/lib/tradedoc/model/financial_institution.rb +11 -0
- data/lib/tradedoc/model/invoice.rb +57 -0
- data/lib/tradedoc/model/invoice_line.rb +46 -0
- data/lib/tradedoc/model/monetary_total.rb +34 -0
- data/lib/tradedoc/model/payment_means.rb +27 -0
- data/lib/tradedoc/model/period.rb +8 -0
- data/lib/tradedoc/model/price.rb +21 -0
- data/lib/tradedoc/model/product.rb +18 -0
- data/lib/tradedoc/model/remittance_advice.rb +20 -0
- data/lib/tradedoc/model/remittance_advice_line.rb +24 -0
- data/lib/tradedoc/model/tax_breakdown.rb +11 -0
- data/lib/tradedoc/model/tax_category.rb +26 -0
- data/lib/tradedoc/model/tax_subtotal.rb +13 -0
- data/lib/tradedoc/model/trade_party.rb +9 -0
- data/lib/tradedoc/model.rb +33 -0
- data/lib/tradedoc/version.rb +5 -0
- data/lib/tradedoc/xml/reader.rb +163 -0
- data/lib/tradedoc/xml/writer.rb +56 -0
- data/lib/tradedoc/xml.rb +8 -0
- data/lib/tradedoc.rb +98 -0
- data/sig/tradedoc.rbs +4 -0
- 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
|