facturx 0.1.0 → 1.0.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 +4 -4
- data/CHANGELOG.md +29 -2
- data/NOTICE.md +2 -0
- data/README.md +62 -4
- data/lib/facturx/builders/association.rb +8 -0
- data/lib/facturx/builders/base.rb +129 -0
- data/lib/facturx/builders/schema.rb +112 -0
- data/lib/facturx/builders.rb +25 -0
- data/lib/facturx/coerce.rb +74 -0
- data/lib/facturx/conformance.rb +163 -0
- data/lib/facturx/diagnostic.rb +20 -0
- data/lib/facturx/document.rb +45 -0
- data/lib/facturx/error.rb +3 -0
- data/lib/facturx/format.rb +85 -0
- data/lib/facturx/generate.rb +21 -0
- data/lib/facturx/group.rb +5 -0
- data/lib/facturx/model/common.rb +8 -0
- data/lib/facturx/model/immutable.rb +75 -0
- data/lib/facturx/model/line.rb +14 -0
- data/lib/facturx/model/party.rb +10 -0
- data/lib/facturx/model/payment.rb +9 -0
- data/lib/facturx/model/reference.rb +7 -0
- data/lib/facturx/model/trade.rb +9 -0
- data/lib/facturx/model.rb +9 -0
- data/lib/facturx/pdf/extractor.rb +7 -4
- data/lib/facturx/profile_resolver.rb +40 -0
- data/lib/facturx/reader/semantic_mapper/delivery_mapping.rb +43 -0
- data/lib/facturx/reader/semantic_mapper/document_mapping.rb +109 -0
- data/lib/facturx/reader/semantic_mapper/helpers.rb +114 -0
- data/lib/facturx/reader/semantic_mapper/line_mapping.rb +120 -0
- data/lib/facturx/reader/semantic_mapper/line_references.rb +19 -0
- data/lib/facturx/reader/semantic_mapper/party_mapping.rb +90 -0
- data/lib/facturx/reader/semantic_mapper/payment_mapping.rb +108 -0
- data/lib/facturx/reader/semantic_mapper/trade_mapping.rb +83 -0
- data/lib/facturx/reader/semantic_mapper.rb +76 -0
- data/lib/facturx/reader/term_reader/coercion.rb +69 -0
- data/lib/facturx/reader/term_reader/unmapped.rb +37 -0
- data/lib/facturx/reader/term_reader.rb +132 -0
- data/lib/facturx/reader.rb +97 -0
- data/lib/facturx/reading.rb +23 -0
- data/lib/facturx/source_reader.rb +30 -0
- data/lib/facturx/term.rb +5 -0
- data/lib/facturx/terms/declaration.rb +20 -0
- data/lib/facturx/terms/declarations/adjustments.rb +230 -0
- data/lib/facturx/terms/declarations/delivery.rb +83 -0
- data/lib/facturx/terms/declarations/document.rb +223 -0
- data/lib/facturx/terms/declarations/groups.rb +356 -0
- data/lib/facturx/terms/declarations/lines.rb +166 -0
- data/lib/facturx/terms/declarations/parties.rb +609 -0
- data/lib/facturx/terms/declarations/payment.rb +106 -0
- data/lib/facturx/terms/declarations/products.rb +103 -0
- data/lib/facturx/terms/declarations/references.rb +72 -0
- data/lib/facturx/terms/declarations/taxes.rb +92 -0
- data/lib/facturx/terms/declarations/totals.rb +107 -0
- data/lib/facturx/terms/reference.rb +21 -0
- data/lib/facturx/terms.rb +109 -0
- data/lib/facturx/version.rb +1 -1
- data/lib/facturx/writer/context.rb +53 -0
- data/lib/facturx/writer/stage.rb +47 -0
- data/lib/facturx/writer/stage_support/contact_details.rb +57 -0
- data/lib/facturx/writer/stage_support/emission.rb +78 -0
- data/lib/facturx/writer/stage_support/identifiers.rb +129 -0
- data/lib/facturx/writer/stage_support/structure.rb +88 -0
- data/lib/facturx/writer/stages/document_context.rb +31 -0
- data/lib/facturx/writer/stages/exchanged_document.rb +32 -0
- data/lib/facturx/writer/stages/trade_agreement/parties.rb +105 -0
- data/lib/facturx/writer/stages/trade_agreement/references.rb +92 -0
- data/lib/facturx/writer/stages/trade_agreement.rb +45 -0
- data/lib/facturx/writer/stages/trade_delivery.rb +77 -0
- data/lib/facturx/writer/stages/trade_lines/agreement.rb +92 -0
- data/lib/facturx/writer/stages/trade_lines/product.rb +78 -0
- data/lib/facturx/writer/stages/trade_lines/quantity.rb +17 -0
- data/lib/facturx/writer/stages/trade_lines/settlement.rb +89 -0
- data/lib/facturx/writer/stages/trade_lines.rb +67 -0
- data/lib/facturx/writer/stages/trade_settlement/parties.rb +33 -0
- data/lib/facturx/writer/stages/trade_settlement/payment.rb +97 -0
- data/lib/facturx/writer/stages/trade_settlement/summary.rb +88 -0
- data/lib/facturx/writer/stages/trade_settlement/taxes.rb +121 -0
- data/lib/facturx/writer/stages/trade_settlement.rb +70 -0
- data/lib/facturx/writer.rb +68 -0
- data/lib/facturx/xml/namespaces.rb +19 -0
- data/lib/facturx/xml/profile_detector.rb +2 -4
- data/lib/facturx.rb +42 -0
- data/rbi/facturx.rbi +1090 -0
- metadata +79 -4
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'trade_agreement/parties'
|
|
4
|
+
require_relative 'trade_agreement/references'
|
|
5
|
+
|
|
6
|
+
module Facturx
|
|
7
|
+
class Writer
|
|
8
|
+
module Stages
|
|
9
|
+
class TradeAgreement < Stage
|
|
10
|
+
include TradeAgreementSupport::Parties
|
|
11
|
+
include TradeAgreementSupport::References
|
|
12
|
+
|
|
13
|
+
SELLER_ADDRESS = {
|
|
14
|
+
postcode: 'BT-38', line_one: 'BT-35', line_two: 'BT-36', line_three: 'BT-162', city: 'BT-37',
|
|
15
|
+
country_code: 'BT-40', country_subdivision: 'BT-39'
|
|
16
|
+
}.freeze
|
|
17
|
+
BUYER_ADDRESS = {
|
|
18
|
+
postcode: 'BT-53', line_one: 'BT-50', line_two: 'BT-51', line_three: 'BT-163', city: 'BT-52',
|
|
19
|
+
country_code: 'BT-55', country_subdivision: 'BT-54'
|
|
20
|
+
}.freeze
|
|
21
|
+
REPRESENTATIVE_ADDRESS = {
|
|
22
|
+
postcode: 'BT-67', line_one: 'BT-64', line_two: 'BT-65', line_three: 'BT-164', city: 'BT-66',
|
|
23
|
+
country_code: 'BT-69', country_subdivision: 'BT-68'
|
|
24
|
+
}.freeze
|
|
25
|
+
SELLER_CONTACT = { name: 'BT-41', telephone: 'BT-42', email: 'BT-43' }.freeze
|
|
26
|
+
BUYER_CONTACT = { name: 'BT-56', telephone: 'BT-57', email: 'BT-58' }.freeze
|
|
27
|
+
|
|
28
|
+
term_ids(*%w[
|
|
29
|
+
BT-10 BT-29 BT-29-1 BT-27 BT-33 BT-30 BT-30-1 BT-28 BT-41 BT-42 BT-43 BT-38 BT-35 BT-36
|
|
30
|
+
BT-162 BT-37 BT-40 BT-39 BT-34 BT-34-1 BT-31 BT-32 BT-46 BT-46-1 BT-44 BT-47 BT-47-1 BT-45
|
|
31
|
+
BT-56 BT-57 BT-58 BT-53 BT-50 BT-51 BT-163 BT-52 BT-55 BT-54 BT-49 BT-49-1 BT-48 BT-62 BT-67
|
|
32
|
+
BT-64 BT-65 BT-164 BT-66 BT-69 BT-68 BT-63 BT-14 BT-13 BT-12 BT-17 BT-18 BT-18-1 BT-122 BT-124
|
|
33
|
+
BT-123 BT-125 BT-125-1 BT-125-2 BT-11
|
|
34
|
+
])
|
|
35
|
+
|
|
36
|
+
def call
|
|
37
|
+
agreement = context.element(context.transaction, 'ram:ApplicableHeaderTradeAgreement')
|
|
38
|
+
emit('BT-10', document.buyer_reference, element: 'ram:BuyerReference', parent: agreement)
|
|
39
|
+
write_parties(agreement)
|
|
40
|
+
write_references(agreement)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Facturx
|
|
4
|
+
class Writer
|
|
5
|
+
module Stages
|
|
6
|
+
class TradeDelivery < Stage
|
|
7
|
+
ADDRESS_IDS = {
|
|
8
|
+
postcode: 'BT-78', line_one: 'BT-75', line_two: 'BT-76', line_three: 'BT-165', city: 'BT-77',
|
|
9
|
+
country_code: 'BT-80', country_subdivision: 'BT-79'
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
term_ids(*%w[BT-71 BT-71-1 BT-70 BT-78 BT-75 BT-76 BT-165 BT-77 BT-80 BT-79 BT-72 BT-16 BT-15])
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
value = document.delivery
|
|
16
|
+
node = context.element(context.transaction, 'ram:ApplicableHeaderTradeDelivery')
|
|
17
|
+
delivery(node, value) if delivery_accepted?(value)
|
|
18
|
+
references(node)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def delivery_accepted?(value)
|
|
24
|
+
group = Terms.group('BG-13-00')
|
|
25
|
+
tracker.observe_group?(group, count: value ? 1 : 0, path: group.xpath)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def references(parent)
|
|
29
|
+
emit_document_reference(parent, 'BT-16', document.despatch_advice_reference,
|
|
30
|
+
'ram:DespatchAdviceReferencedDocument')
|
|
31
|
+
emit_document_reference(parent, 'BT-15', document.receiving_advice_reference,
|
|
32
|
+
'ram:ReceivingAdviceReferencedDocument')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def delivery(parent, value)
|
|
36
|
+
ship_to(parent, value)
|
|
37
|
+
delivery_date(parent, value.date)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def delivery_date(parent, value)
|
|
41
|
+
unless value
|
|
42
|
+
observe?('BT-72', nil)
|
|
43
|
+
return
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
container(parent, 'ram:ActualDeliverySupplyChainEvent') do |event|
|
|
47
|
+
emit_date(event, 'BT-72', value, wrapper: 'ram:OccurrenceDateTime')
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def ship_to(parent, delivery)
|
|
52
|
+
party = delivery.party
|
|
53
|
+
location = delivery.location_identifier
|
|
54
|
+
present = party || location
|
|
55
|
+
within_group('BG-13', present, element: 'ram:ShipToTradeParty', parent:,
|
|
56
|
+
represented_attributes: %i[name address]) do |node,|
|
|
57
|
+
delivery_identifier(node, location)
|
|
58
|
+
emit('BT-70', party&.name, element: 'ram:Name', parent: node)
|
|
59
|
+
emit_address(node, party&.address, 'BG-15', ADDRESS_IDS)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def delivery_identifier(parent, value)
|
|
64
|
+
if value&.scheme_id
|
|
65
|
+
node = if observe?('BT-71', value.value)
|
|
66
|
+
technical(parent, 'ram:GlobalID', format_identifier_value('BT-71', value.value))
|
|
67
|
+
end
|
|
68
|
+
emit_attribute('BT-71-1', value.scheme_id, node:, attribute: 'schemeID', group_present: !node.nil?)
|
|
69
|
+
else
|
|
70
|
+
emit('BT-71', value&.value, element: 'ram:ID', parent:)
|
|
71
|
+
observe?('BT-71-1', nil, group_present: false)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Facturx
|
|
4
|
+
class Writer
|
|
5
|
+
module Stages
|
|
6
|
+
class TradeLines
|
|
7
|
+
class Agreement < Stage
|
|
8
|
+
PRICES = {
|
|
9
|
+
gross: { ids: %w[BT-148 BT-149-1 BT-150-1], element: 'ram:GrossPriceProductTradePrice' },
|
|
10
|
+
net: { ids: %w[BT-146 BT-149 BT-150], element: 'ram:NetPriceProductTradePrice' }
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
def initialize(context, quantity:)
|
|
14
|
+
super(context)
|
|
15
|
+
@quantity = quantity
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(parent:, line:)
|
|
19
|
+
within_group('BG-29', line, element: 'ram:SpecifiedLineTradeAgreement', parent:) do |node, item|
|
|
20
|
+
buyer_order_reference(node, item.buyer_order_reference)
|
|
21
|
+
price(node, item.gross_price, kind: :gross)
|
|
22
|
+
price(node, item.net_price, kind: :net)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def buyer_order_reference(parent, value)
|
|
29
|
+
return observe?('BT-132', nil) unless value
|
|
30
|
+
|
|
31
|
+
report_unrepresentable_reference_attributes('BT-132', value, represented_attributes: [:line_id])
|
|
32
|
+
|
|
33
|
+
container(parent, 'ram:BuyerOrderReferencedDocument') do |node|
|
|
34
|
+
emit('BT-132', value.line_id, element: 'ram:LineID', parent: node)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def price(parent, value, kind:)
|
|
39
|
+
config = PRICES.fetch(kind)
|
|
40
|
+
return observe_missing_price(config, kind:) unless value
|
|
41
|
+
return unrepresentable(config.fetch(:ids).first, 'Price requires an amount') unless value.amount
|
|
42
|
+
|
|
43
|
+
represented_attributes = %i[amount basis_quantity]
|
|
44
|
+
represented_attributes << :discount if kind == :gross
|
|
45
|
+
report_unrepresentable_attributes('BG-29', value, represented_attributes:)
|
|
46
|
+
|
|
47
|
+
container(parent, config.fetch(:element)) do |node|
|
|
48
|
+
emit_price(node, value, config.fetch(:ids))
|
|
49
|
+
gross_discount(node, value.discount) if kind == :gross
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def emit_price(parent, value, ids)
|
|
54
|
+
emit(ids[0], value.amount, element: 'ram:ChargeAmount', parent:)
|
|
55
|
+
@quantity.call(parent:, value: value.basis_quantity, amount_id: ids[1], unit_id: ids[2],
|
|
56
|
+
element: 'ram:BasisQuantity')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def observe_missing_price(config, kind:)
|
|
60
|
+
config.fetch(:ids).each { |id| observe?(id, nil) }
|
|
61
|
+
return unless kind == :gross
|
|
62
|
+
|
|
63
|
+
observe?('BT-147-01', nil)
|
|
64
|
+
observe?('BT-147', nil)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def gross_discount(parent, value)
|
|
68
|
+
return observe_missing_discount? unless value
|
|
69
|
+
return unless observe?('BT-147-01', false) && observe?('BT-147-02', false)
|
|
70
|
+
|
|
71
|
+
emit_discount(parent, value)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def observe_missing_discount?
|
|
75
|
+
observe?('BT-147-01', nil)
|
|
76
|
+
observe?('BT-147-02', nil, group_present: false)
|
|
77
|
+
observe?('BT-147', nil)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def emit_discount(parent, value)
|
|
81
|
+
container(parent, 'ram:AppliedTradeAllowanceCharge') do |node|
|
|
82
|
+
container(node, 'ram:ChargeIndicator') do |indicator|
|
|
83
|
+
technical(indicator, 'udt:Indicator', format_identifier_value('BT-147-02', false))
|
|
84
|
+
end
|
|
85
|
+
emit('BT-147', value, element: 'ram:ActualAmount', parent: node)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Facturx
|
|
4
|
+
class Writer
|
|
5
|
+
module Stages
|
|
6
|
+
class TradeLines
|
|
7
|
+
class Product < Stage
|
|
8
|
+
def call(parent:, value:)
|
|
9
|
+
within_group('BG-31', value, element: 'ram:SpecifiedTradeProduct', parent:,
|
|
10
|
+
represented_attributes: %i[attributes classifications]) do |node, item|
|
|
11
|
+
identifiers(node, item)
|
|
12
|
+
emit('BT-153', item.name, element: 'ram:Name', parent: node)
|
|
13
|
+
emit('BT-154', item.description, element: 'ram:Description', parent: node)
|
|
14
|
+
attributes(node, item.attributes)
|
|
15
|
+
classifications(node, item.classifications)
|
|
16
|
+
origin_country(node, item.origin_country_code)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def identifiers(parent, item)
|
|
23
|
+
emit_compound_identifier(parent, item.global_identifier, value_id: 'BT-157', scheme_id: 'BT-157-1',
|
|
24
|
+
element: 'ram:GlobalID')
|
|
25
|
+
emit_identifier_value('BT-155', item.seller_identifier, 'ram:SellerAssignedID', parent)
|
|
26
|
+
emit_identifier_value('BT-156', item.buyer_identifier, 'ram:BuyerAssignedID', parent)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def attributes(parent, values)
|
|
30
|
+
each_group('BG-32', values, element: 'ram:ApplicableProductCharacteristic', parent:) do |node, item|
|
|
31
|
+
emit('BT-160', item.name, element: 'ram:Description', parent: node)
|
|
32
|
+
emit('BT-161', item.value, element: 'ram:Value', parent: node)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def classifications(parent, values)
|
|
37
|
+
emit_unrepresentable_classifications(parent, values)
|
|
38
|
+
emit_representable_classifications(parent, values)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def emit_unrepresentable_classifications(parent, values)
|
|
42
|
+
Array(values).reject { |item| classification_code?(item) }.each do |item|
|
|
43
|
+
classification(parent, item)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def emit_representable_classifications(parent, values)
|
|
48
|
+
items = Array(values).select { |item| classification_code?(item) }
|
|
49
|
+
return unless observe?('BT-158', items.map(&:code))
|
|
50
|
+
|
|
51
|
+
items.each { |item| classification(parent, item) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def classification_code?(item)
|
|
55
|
+
!item.code.to_s.strip.empty?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def classification(parent, item)
|
|
59
|
+
container(parent, 'ram:DesignatedProductClassification') do |node|
|
|
60
|
+
code = technical(node, 'ram:ClassCode', format_identifier_value('BT-158', item.code))
|
|
61
|
+
emit_attribute('BT-158-1', item.list_id, node: code, attribute: 'listID', group_present: !code.nil?)
|
|
62
|
+
emit_attribute('BT-158-2', item.list_version_id, node: code, attribute: 'listVersionID',
|
|
63
|
+
group_present: !code.nil?)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def origin_country(parent, value)
|
|
68
|
+
return observe?('BT-159', nil) unless value
|
|
69
|
+
|
|
70
|
+
container(parent, 'ram:OriginTradeCountry') do |node|
|
|
71
|
+
emit('BT-159', value, element: 'ram:ID', parent: node)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Facturx
|
|
4
|
+
class Writer
|
|
5
|
+
module Stages
|
|
6
|
+
class TradeLines
|
|
7
|
+
class Quantity < Stage
|
|
8
|
+
def call(parent:, value:, amount_id:, unit_id:, element:)
|
|
9
|
+
node = emit(amount_id, value&.value, element:, parent:).first
|
|
10
|
+
emit_attribute(unit_id, value&.unit_code, node:, attribute: 'unitCode',
|
|
11
|
+
group_present: !node.nil?)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Facturx
|
|
4
|
+
class Writer
|
|
5
|
+
module Stages
|
|
6
|
+
class TradeLines
|
|
7
|
+
class Settlement < Stage
|
|
8
|
+
ADJUSTMENT_IDS = {
|
|
9
|
+
'BG-27' => %w[BT-138 BT-137 BT-136 BT-140 BT-139],
|
|
10
|
+
'BG-28' => %w[BT-143 BT-142 BT-141 BT-145 BT-144]
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
def call(parent:, line:)
|
|
14
|
+
node = context.element(parent, 'ram:SpecifiedLineTradeSettlement')
|
|
15
|
+
line_tax(node, line.tax)
|
|
16
|
+
period(node, line.period)
|
|
17
|
+
adjustment_groups(node, line.allowances, 'BG-27', false)
|
|
18
|
+
adjustment_groups(node, line.charges, 'BG-28', true)
|
|
19
|
+
monetary_sum(node, line.net_amount)
|
|
20
|
+
line_invoiced_object(node, line.invoiced_object_identifier)
|
|
21
|
+
accounting_reference(node, 'BT-133', line.buyer_accounting_reference)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def line_tax(parent, value)
|
|
27
|
+
within_group('BG-30', value, element: 'ram:ApplicableTradeTax', parent:,
|
|
28
|
+
represented_attributes: [:type_code]) do |node, item|
|
|
29
|
+
technical(node, 'ram:TypeCode', item.type_code, default: 'VAT')
|
|
30
|
+
emit('BT-151', item.category_code, element: 'ram:CategoryCode', parent: node)
|
|
31
|
+
emit('BT-152', item.rate, element: 'ram:RateApplicablePercent', parent: node)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def period(parent, value)
|
|
36
|
+
within_group('BG-26', value, element: 'ram:BillingSpecifiedPeriod', parent:) do |node, item|
|
|
37
|
+
emit_date(node, 'BT-134', item.start_date, wrapper: 'ram:StartDateTime')
|
|
38
|
+
emit_date(node, 'BT-135', item.end_date, wrapper: 'ram:EndDateTime')
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def adjustment_groups(parent, values, group_id, indicator)
|
|
43
|
+
ids = ADJUSTMENT_IDS.fetch(group_id)
|
|
44
|
+
each_group(group_id, values, element: 'ram:SpecifiedTradeAllowanceCharge', parent:,
|
|
45
|
+
represented_attributes: [:indicator]) do |node, item|
|
|
46
|
+
report_unrepresentable_attributes(group_id, item.tax) if item.tax
|
|
47
|
+
adjustment_indicator(node, group_id, indicator)
|
|
48
|
+
emit_adjustment_fields(node, item, ids)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def emit_adjustment_fields(parent, item, ids)
|
|
53
|
+
emit(ids[0], item.percentage, element: 'ram:CalculationPercent', parent:)
|
|
54
|
+
emit(ids[1], item.base_amount, element: 'ram:BasisAmount', parent:)
|
|
55
|
+
emit(ids[2], item.amount, element: 'ram:ActualAmount', parent:)
|
|
56
|
+
emit(ids[3], item.reason_code, element: 'ram:ReasonCode', parent:)
|
|
57
|
+
emit(ids[4], item.reason, element: 'ram:Reason', parent:)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def adjustment_indicator(parent, group_id, value)
|
|
61
|
+
within_group("#{group_id}-0", value, element: 'ram:ChargeIndicator', parent:) do |node, indicator|
|
|
62
|
+
within_group("#{group_id}-1", indicator, element: 'udt:Indicator', parent: node) do |leaf, raw|
|
|
63
|
+
leaf.content = raw ? 'true' : 'false'
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def monetary_sum(parent, value)
|
|
69
|
+
container(parent, 'ram:SpecifiedTradeSettlementLineMonetarySummation') do |node|
|
|
70
|
+
emit('BT-131', value, element: 'ram:LineTotalAmount', parent: node)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def line_invoiced_object(parent, value)
|
|
75
|
+
emit_invoiced_object(parent, value, 'BT-128', 'BT-128-1')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def accounting_reference(parent, id, value)
|
|
79
|
+
return observe?(id, nil) unless value
|
|
80
|
+
|
|
81
|
+
container(parent, 'ram:ReceivableSpecifiedTradeAccountingAccount') do |node|
|
|
82
|
+
emit(id, value, element: 'ram:ID', parent: node)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Facturx
|
|
4
|
+
class Writer
|
|
5
|
+
module Stages
|
|
6
|
+
class TradeLines < Stage
|
|
7
|
+
REPRESENTED_ATTRIBUTES = %i[
|
|
8
|
+
product gross_price net_price tax period allowances charges buyer_order_reference
|
|
9
|
+
].freeze
|
|
10
|
+
|
|
11
|
+
term_ids(*%w[
|
|
12
|
+
BT-126 BT-127 BT-157 BT-157-1 BT-155 BT-156 BT-153 BT-154 BT-160 BT-161 BT-158 BT-158-1
|
|
13
|
+
BT-158-2 BT-159 BT-132 BT-148 BT-149-1 BT-150-1 BT-147-01 BT-147-02 BT-147 BT-146 BT-149
|
|
14
|
+
BT-150 BT-129 BT-130 BT-151 BT-152 BT-134 BT-135 BT-138 BT-137 BT-136 BT-140 BT-139 BT-143
|
|
15
|
+
BT-142 BT-141 BT-145 BT-144 BT-131 BT-128 BT-128-1 BT-133
|
|
16
|
+
])
|
|
17
|
+
|
|
18
|
+
def initialize(context)
|
|
19
|
+
super
|
|
20
|
+
quantity = Quantity.new(context)
|
|
21
|
+
@product = Product.new(context)
|
|
22
|
+
@agreement = Agreement.new(context, quantity:)
|
|
23
|
+
@settlement = Settlement.new(context)
|
|
24
|
+
@quantity = quantity
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def call
|
|
28
|
+
each_group('BG-25', document.lines, element: 'ram:IncludedSupplyChainTradeLineItem',
|
|
29
|
+
parent: context.transaction,
|
|
30
|
+
represented_attributes: REPRESENTED_ATTRIBUTES) do |node, line|
|
|
31
|
+
line_document(node, line)
|
|
32
|
+
@product.call(parent: node, value: line.product)
|
|
33
|
+
@agreement.call(parent: node, line:)
|
|
34
|
+
delivery(node, line)
|
|
35
|
+
@settlement.call(parent: node, line:)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def line_document(parent, line)
|
|
42
|
+
container(parent, 'ram:AssociatedDocumentLineDocument') do |node|
|
|
43
|
+
emit('BT-126', line.id, element: 'ram:LineID', parent: node)
|
|
44
|
+
if line.note
|
|
45
|
+
container(node, 'ram:IncludedNote') do |note|
|
|
46
|
+
emit('BT-127', line.note, element: 'ram:Content', parent: note)
|
|
47
|
+
end
|
|
48
|
+
else
|
|
49
|
+
observe?('BT-127', nil)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def delivery(parent, line)
|
|
55
|
+
node = context.element(parent, 'ram:SpecifiedLineTradeDelivery')
|
|
56
|
+
@quantity.call(parent: node, value: line.quantity, amount_id: 'BT-129', unit_id: 'BT-130',
|
|
57
|
+
element: 'ram:BilledQuantity')
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
require_relative 'trade_lines/quantity'
|
|
65
|
+
require_relative 'trade_lines/product'
|
|
66
|
+
require_relative 'trade_lines/agreement'
|
|
67
|
+
require_relative 'trade_lines/settlement'
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Facturx
|
|
4
|
+
class Writer
|
|
5
|
+
module Stages
|
|
6
|
+
module TradeSettlementParties
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def payee(parent)
|
|
10
|
+
within_group('BG-10', document.payee, element: 'ram:PayeeTradeParty', parent:) do |node, party|
|
|
11
|
+
emit_party_identifiers(node, party.identifiers, 'BT-60', 'BT-60-1')
|
|
12
|
+
emit('BT-59', party.name, element: 'ram:Name', parent: node)
|
|
13
|
+
payee_legal_organization(node, party)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def payee_legal_organization(parent, party)
|
|
18
|
+
legal = party.legal_registration
|
|
19
|
+
return missing_payee_registration? unless legal
|
|
20
|
+
|
|
21
|
+
container(parent, 'ram:SpecifiedLegalOrganization') do |node|
|
|
22
|
+
emit_compound_identifier(node, legal, value_id: 'BT-61', scheme_id: 'BT-61-1', element: 'ram:ID')
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def missing_payee_registration?
|
|
27
|
+
observe?('BT-61', nil)
|
|
28
|
+
observe?('BT-61-1', nil, group_present: false)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Facturx
|
|
4
|
+
class Writer
|
|
5
|
+
module Stages
|
|
6
|
+
module TradeSettlementPayment
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def payment_means(parent, payment)
|
|
10
|
+
value = payment if payment_means_present?(payment)
|
|
11
|
+
within_group('BG-16', value, element: 'ram:SpecifiedTradeSettlementPaymentMeans', parent:,
|
|
12
|
+
represented_attributes: %i[
|
|
13
|
+
remittance_information payment_card direct_debit credit_transfers
|
|
14
|
+
]) do |node, item|
|
|
15
|
+
emit_payment_means(parent, node, item)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def emit_payment_means(parent, node, payment)
|
|
20
|
+
transfers = payment.credit_transfers
|
|
21
|
+
nodes = [node, *additional_payment_means(parent, transfers)]
|
|
22
|
+
means_code, means_text = payment_descriptions(node, payment)
|
|
23
|
+
duplicate_payment_terms(nodes.drop(1), means_code, means_text)
|
|
24
|
+
payment_card(node, payment.payment_card)
|
|
25
|
+
debtor_account(node, payment.direct_debit)
|
|
26
|
+
credit_transfers(nodes, transfers)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def payment_descriptions(parent, payment)
|
|
30
|
+
code = emit('BT-81', payment.means_code, element: 'ram:TypeCode', parent:).first&.text
|
|
31
|
+
text = emit('BT-82', payment.means_text, element: 'ram:Information', parent:).first&.text
|
|
32
|
+
[code, text]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def additional_payment_means(parent, transfers)
|
|
36
|
+
Array.new([Array(transfers).size - 1, 0].max) do
|
|
37
|
+
context.element(parent, 'ram:SpecifiedTradeSettlementPaymentMeans')
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def duplicate_payment_terms(nodes, means_code, means_text)
|
|
42
|
+
nodes.each do |node|
|
|
43
|
+
technical(node, 'ram:TypeCode', means_code)
|
|
44
|
+
technical(node, 'ram:Information', means_text)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def payment_means_present?(payment)
|
|
49
|
+
return false unless payment
|
|
50
|
+
|
|
51
|
+
[payment.means_code, payment.means_text, payment.payment_card,
|
|
52
|
+
payment.direct_debit&.debtor_account_identifier, *payment.credit_transfers].any?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def payment_card(parent, value)
|
|
56
|
+
within_group('BG-18', value, element: 'ram:ApplicableTradeSettlementFinancialCard', parent:) do |node, card|
|
|
57
|
+
emit('BT-87', card.primary_account_number, element: 'ram:ID', parent: node)
|
|
58
|
+
emit('BT-88', card.holder_name, element: 'ram:CardholderName', parent: node)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def debtor_account(parent, value)
|
|
63
|
+
identifier = value&.debtor_account_identifier
|
|
64
|
+
return observe?('BT-91', nil) unless identifier
|
|
65
|
+
|
|
66
|
+
container(parent, 'ram:PayerPartyDebtorFinancialAccount') do |node|
|
|
67
|
+
emit_identifier_value('BT-91', identifier, 'ram:IBANID', node)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def credit_transfers(parents, values)
|
|
72
|
+
transfers = Array(values)
|
|
73
|
+
group = Terms.group('BG-17')
|
|
74
|
+
return unless tracker.observe_group?(group, count: transfers.size, path: group.xpath)
|
|
75
|
+
|
|
76
|
+
transfers.zip(parents).each { |item, parent| emit_credit_transfer(parent, item) }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def emit_credit_transfer(parent, transfer)
|
|
80
|
+
container(parent, 'ram:PayeePartyCreditorFinancialAccount') do |node|
|
|
81
|
+
emit_identifier_value('BT-84', transfer.account_identifier, 'ram:IBANID', node)
|
|
82
|
+
emit('BT-85', transfer.account_name, element: 'ram:AccountName', parent: node)
|
|
83
|
+
end
|
|
84
|
+
emit_provider(parent, transfer.provider_identifier)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def emit_provider(parent, identifier)
|
|
88
|
+
return observe?('BT-86', nil) unless identifier
|
|
89
|
+
|
|
90
|
+
container(parent, 'ram:PayeeSpecifiedCreditorFinancialInstitution') do |node|
|
|
91
|
+
emit_identifier_value('BT-86', identifier, 'ram:BICID', node)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|