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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -2
  3. data/NOTICE.md +2 -0
  4. data/README.md +62 -4
  5. data/lib/facturx/builders/association.rb +8 -0
  6. data/lib/facturx/builders/base.rb +129 -0
  7. data/lib/facturx/builders/schema.rb +112 -0
  8. data/lib/facturx/builders.rb +25 -0
  9. data/lib/facturx/coerce.rb +74 -0
  10. data/lib/facturx/conformance.rb +163 -0
  11. data/lib/facturx/diagnostic.rb +20 -0
  12. data/lib/facturx/document.rb +45 -0
  13. data/lib/facturx/error.rb +3 -0
  14. data/lib/facturx/format.rb +85 -0
  15. data/lib/facturx/generate.rb +21 -0
  16. data/lib/facturx/group.rb +5 -0
  17. data/lib/facturx/model/common.rb +8 -0
  18. data/lib/facturx/model/immutable.rb +75 -0
  19. data/lib/facturx/model/line.rb +14 -0
  20. data/lib/facturx/model/party.rb +10 -0
  21. data/lib/facturx/model/payment.rb +9 -0
  22. data/lib/facturx/model/reference.rb +7 -0
  23. data/lib/facturx/model/trade.rb +9 -0
  24. data/lib/facturx/model.rb +9 -0
  25. data/lib/facturx/pdf/extractor.rb +7 -4
  26. data/lib/facturx/profile_resolver.rb +40 -0
  27. data/lib/facturx/reader/semantic_mapper/delivery_mapping.rb +43 -0
  28. data/lib/facturx/reader/semantic_mapper/document_mapping.rb +109 -0
  29. data/lib/facturx/reader/semantic_mapper/helpers.rb +114 -0
  30. data/lib/facturx/reader/semantic_mapper/line_mapping.rb +120 -0
  31. data/lib/facturx/reader/semantic_mapper/line_references.rb +19 -0
  32. data/lib/facturx/reader/semantic_mapper/party_mapping.rb +90 -0
  33. data/lib/facturx/reader/semantic_mapper/payment_mapping.rb +108 -0
  34. data/lib/facturx/reader/semantic_mapper/trade_mapping.rb +83 -0
  35. data/lib/facturx/reader/semantic_mapper.rb +76 -0
  36. data/lib/facturx/reader/term_reader/coercion.rb +69 -0
  37. data/lib/facturx/reader/term_reader/unmapped.rb +37 -0
  38. data/lib/facturx/reader/term_reader.rb +132 -0
  39. data/lib/facturx/reader.rb +97 -0
  40. data/lib/facturx/reading.rb +23 -0
  41. data/lib/facturx/source_reader.rb +30 -0
  42. data/lib/facturx/term.rb +5 -0
  43. data/lib/facturx/terms/declaration.rb +20 -0
  44. data/lib/facturx/terms/declarations/adjustments.rb +230 -0
  45. data/lib/facturx/terms/declarations/delivery.rb +83 -0
  46. data/lib/facturx/terms/declarations/document.rb +223 -0
  47. data/lib/facturx/terms/declarations/groups.rb +356 -0
  48. data/lib/facturx/terms/declarations/lines.rb +166 -0
  49. data/lib/facturx/terms/declarations/parties.rb +609 -0
  50. data/lib/facturx/terms/declarations/payment.rb +106 -0
  51. data/lib/facturx/terms/declarations/products.rb +103 -0
  52. data/lib/facturx/terms/declarations/references.rb +72 -0
  53. data/lib/facturx/terms/declarations/taxes.rb +92 -0
  54. data/lib/facturx/terms/declarations/totals.rb +107 -0
  55. data/lib/facturx/terms/reference.rb +21 -0
  56. data/lib/facturx/terms.rb +109 -0
  57. data/lib/facturx/version.rb +1 -1
  58. data/lib/facturx/writer/context.rb +53 -0
  59. data/lib/facturx/writer/stage.rb +47 -0
  60. data/lib/facturx/writer/stage_support/contact_details.rb +57 -0
  61. data/lib/facturx/writer/stage_support/emission.rb +78 -0
  62. data/lib/facturx/writer/stage_support/identifiers.rb +129 -0
  63. data/lib/facturx/writer/stage_support/structure.rb +88 -0
  64. data/lib/facturx/writer/stages/document_context.rb +31 -0
  65. data/lib/facturx/writer/stages/exchanged_document.rb +32 -0
  66. data/lib/facturx/writer/stages/trade_agreement/parties.rb +105 -0
  67. data/lib/facturx/writer/stages/trade_agreement/references.rb +92 -0
  68. data/lib/facturx/writer/stages/trade_agreement.rb +45 -0
  69. data/lib/facturx/writer/stages/trade_delivery.rb +77 -0
  70. data/lib/facturx/writer/stages/trade_lines/agreement.rb +92 -0
  71. data/lib/facturx/writer/stages/trade_lines/product.rb +78 -0
  72. data/lib/facturx/writer/stages/trade_lines/quantity.rb +17 -0
  73. data/lib/facturx/writer/stages/trade_lines/settlement.rb +89 -0
  74. data/lib/facturx/writer/stages/trade_lines.rb +67 -0
  75. data/lib/facturx/writer/stages/trade_settlement/parties.rb +33 -0
  76. data/lib/facturx/writer/stages/trade_settlement/payment.rb +97 -0
  77. data/lib/facturx/writer/stages/trade_settlement/summary.rb +88 -0
  78. data/lib/facturx/writer/stages/trade_settlement/taxes.rb +121 -0
  79. data/lib/facturx/writer/stages/trade_settlement.rb +70 -0
  80. data/lib/facturx/writer.rb +68 -0
  81. data/lib/facturx/xml/namespaces.rb +19 -0
  82. data/lib/facturx/xml/profile_detector.rb +2 -4
  83. data/lib/facturx.rb +42 -0
  84. data/rbi/facturx.rbi +1090 -0
  85. metadata +79 -4
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Facturx
4
+ class Writer
5
+ module Stages
6
+ module TradeSettlementSummary
7
+ private
8
+
9
+ def payment_terms(parent, direct_debit)
10
+ values = [document.payment_terms, document.payment_due_date, direct_debit&.mandate_identifier]
11
+ return missing_payment_terms? unless values.any?
12
+
13
+ container(parent, 'ram:SpecifiedTradePaymentTerms') do |node|
14
+ emit('BT-20', document.payment_terms, element: 'ram:Description', parent: node)
15
+ emit_date(node, 'BT-9', document.payment_due_date, wrapper: 'ram:DueDateDateTime')
16
+ emit_identifier_value('BT-89', direct_debit&.mandate_identifier, 'ram:DirectDebitMandateID', node)
17
+ end
18
+ end
19
+
20
+ def missing_payment_terms?
21
+ observe?('BT-20', nil)
22
+ observe?('BT-9', nil)
23
+ observe?('BT-89', nil)
24
+ end
25
+
26
+ def totals(parent)
27
+ within_group('BG-22', document.totals, element: 'ram:SpecifiedTradeSettlementHeaderMonetarySummation',
28
+ parent:) do |node, item|
29
+ emit_subtotals(node, item)
30
+ emit_tax_totals(node, item)
31
+ emit_balance(node, item)
32
+ end
33
+ end
34
+
35
+ def emit_subtotals(parent, totals)
36
+ emit('BT-106', totals.line_total, element: 'ram:LineTotalAmount', parent:)
37
+ emit('BT-108', totals.charge_total, element: 'ram:ChargeTotalAmount', parent:)
38
+ emit('BT-107', totals.allowance_total, element: 'ram:AllowanceTotalAmount', parent:)
39
+ emit('BT-109', totals.tax_basis_total, element: 'ram:TaxBasisTotalAmount', parent:)
40
+ end
41
+
42
+ def emit_tax_totals(parent, totals)
43
+ emit('BT-110', totals.tax_total, element: 'ram:TaxTotalAmount', parent:,
44
+ attributes: { 'currencyID' => document.currency })
45
+ emit_tax_total_in_tax_currency(parent, totals.tax_total_in_tax_currency)
46
+ end
47
+
48
+ def emit_tax_total_in_tax_currency(parent, value)
49
+ return observe?('BT-111', nil) unless value
50
+ return unrepresentable('BT-111', 'Tax total requires a distinct tax currency') unless distinct_tax_currency?
51
+
52
+ emit('BT-111', value, element: 'ram:TaxTotalAmount', parent:,
53
+ attributes: { 'currencyID' => document.tax_currency })
54
+ end
55
+
56
+ def distinct_tax_currency?
57
+ tax_currency = document.tax_currency
58
+ !tax_currency.to_s.strip.empty? && tax_currency != document.currency
59
+ end
60
+
61
+ def emit_balance(parent, totals)
62
+ emit('BT-114', totals.rounding, element: 'ram:RoundingAmount', parent:)
63
+ emit('BT-112', totals.grand_total, element: 'ram:GrandTotalAmount', parent:)
64
+ emit('BT-113', totals.prepaid, element: 'ram:TotalPrepaidAmount', parent:)
65
+ emit('BT-115', totals.due_payable, element: 'ram:DuePayableAmount', parent:)
66
+ end
67
+
68
+ def preceding_invoices(parent)
69
+ each_group('BG-3', document.preceding_invoices,
70
+ element: 'ram:InvoiceReferencedDocument', parent:) do |node, item|
71
+ emit('BT-25', item.id, element: 'ram:IssuerAssignedID', parent: node)
72
+ emit_date(node, 'BT-26', item.issue_date, wrapper: 'ram:FormattedIssueDateTime',
73
+ value_element: 'qdt:DateTimeString')
74
+ end
75
+ end
76
+
77
+ def accounting_reference(parent)
78
+ value = document.buyer_accounting_reference
79
+ return observe?('BT-19', nil) unless value
80
+
81
+ container(parent, 'ram:ReceivableSpecifiedTradeAccountingAccount') do |node|
82
+ emit('BT-19', value, element: 'ram:ID', parent: node)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Facturx
4
+ class Writer
5
+ module Stages
6
+ module TradeSettlementTaxes
7
+ ADJUSTMENT_IDS = {
8
+ false => %w[BT-94 BT-93 BT-92 BT-98 BT-97 BT-95 BT-96],
9
+ true => %w[BT-101 BT-100 BT-99 BT-105 BT-104 BT-102 BT-103]
10
+ }.freeze
11
+ ADJUSTMENT_FIELDS = [
12
+ [:percentage, 'ram:CalculationPercent'],
13
+ [:base_amount, 'ram:BasisAmount'],
14
+ [:amount, 'ram:ActualAmount'],
15
+ [:reason_code, 'ram:ReasonCode'],
16
+ [:reason, 'ram:Reason']
17
+ ].freeze
18
+ private_constant :ADJUSTMENT_IDS, :ADJUSTMENT_FIELDS
19
+
20
+ private
21
+
22
+ def taxes(parent)
23
+ first = true
24
+ each_group('BG-23', document.tax_breakdowns, element: 'ram:ApplicableTradeTax', parent:,
25
+ represented_attributes: [:type_code]) do |node, item|
26
+ emit_tax(node, item, include_tax_point: first)
27
+ first = false
28
+ end
29
+ return unless first
30
+
31
+ report_vat_point_attributes
32
+ end
33
+
34
+ def emit_tax(parent, tax, include_tax_point:)
35
+ emit('BT-117', tax.tax_amount, element: 'ram:CalculatedAmount', parent:)
36
+ technical(parent, 'ram:TypeCode', tax.type_code, default: 'VAT')
37
+ emit('BT-120', tax.exemption_reason, element: 'ram:ExemptionReason', parent:)
38
+ emit('BT-116', tax.basis_amount, element: 'ram:BasisAmount', parent:)
39
+ emit('BT-118', tax.category_code, element: 'ram:CategoryCode', parent:)
40
+ emit('BT-121', tax.exemption_reason_code, element: 'ram:ExemptionReasonCode', parent:)
41
+ tax_point_date(parent) if include_tax_point
42
+ tax_due_date_type(parent, tax, check_conflict: include_tax_point)
43
+ emit('BT-119', tax.rate, element: 'ram:RateApplicablePercent', parent:)
44
+ end
45
+
46
+ def tax_due_date_type(parent, tax, check_conflict:)
47
+ document_code = document.vat_point_date_code
48
+ if check_conflict && tax.due_date_type_code && document_code && tax.due_date_type_code != document_code
49
+ report_unrepresentable_attribute('BG-23', model: :document, attribute: :vat_point_date_code)
50
+ end
51
+ emit('BT-8', tax.due_date_type_code || document_code, element: 'ram:DueDateTypeCode', parent:)
52
+ end
53
+
54
+ def tax_point_date(parent)
55
+ emit_date(
56
+ parent, 'BT-7', document.vat_point_date, wrapper: 'ram:TaxPointDate', value_element: 'udt:DateString'
57
+ )
58
+ end
59
+
60
+ def report_vat_point_attributes
61
+ %i[vat_point_date vat_point_date_code].each do |attribute|
62
+ value = document.public_send(attribute)
63
+ report_unrepresentable_attribute('BG-23', model: :document, attribute:) if value
64
+ end
65
+ end
66
+
67
+ def billing_period(parent)
68
+ within_group('BG-14', document.billing_period, element: 'ram:BillingSpecifiedPeriod', parent:) do |node, item|
69
+ emit_date(node, 'BT-73', item.start_date, wrapper: 'ram:StartDateTime')
70
+ emit_date(node, 'BT-74', item.end_date, wrapper: 'ram:EndDateTime')
71
+ end
72
+ end
73
+
74
+ def adjustments(parent, values, group_id, indicator)
75
+ ids = ADJUSTMENT_IDS.fetch(indicator)
76
+ each_group(group_id, values, element: 'ram:SpecifiedTradeAllowanceCharge', parent:,
77
+ represented_attributes: [:indicator]) do |node, item|
78
+ emit_adjustment(node, item, group_id, indicator, ids)
79
+ end
80
+ end
81
+
82
+ def emit_adjustment(parent, adjustment, group_id, indicator, ids)
83
+ adjustment_indicator(parent, group_id, indicator)
84
+ emit_adjustment_values(parent, adjustment, ids)
85
+ adjustment_tax(parent, adjustment.tax, group_id, ids[5], ids[6])
86
+ end
87
+
88
+ def emit_adjustment_values(parent, adjustment, ids)
89
+ ADJUSTMENT_FIELDS.each_with_index do |(attribute, element), index|
90
+ emit(ids[index], adjustment.public_send(attribute), element:, parent:)
91
+ end
92
+ end
93
+
94
+ def adjustment_indicator(parent, group_id, value)
95
+ within_group("#{group_id}-0", value, element: 'ram:ChargeIndicator', parent:) do |node, indicator|
96
+ within_group("#{group_id}-1", indicator, element: 'udt:Indicator', parent: node) do |leaf, raw|
97
+ leaf.content = raw ? 'true' : 'false'
98
+ end
99
+ end
100
+ end
101
+
102
+ def adjustment_tax(parent, tax, group_id, category_id, rate_id)
103
+ return missing_adjustment_tax?(category_id, rate_id) unless tax
104
+
105
+ report_unrepresentable_attributes(group_id, tax, represented_attributes: %i[type_code category_code rate])
106
+
107
+ container(parent, 'ram:CategoryTradeTax') do |node|
108
+ technical(node, 'ram:TypeCode', tax.type_code, default: 'VAT')
109
+ emit(category_id, tax.category_code, element: 'ram:CategoryCode', parent: node)
110
+ emit(rate_id, tax.rate, element: 'ram:RateApplicablePercent', parent: node)
111
+ end
112
+ end
113
+
114
+ def missing_adjustment_tax?(category_id, rate_id)
115
+ observe?(category_id, nil)
116
+ observe?(rate_id, nil)
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'trade_settlement/parties'
4
+ require_relative 'trade_settlement/payment'
5
+ require_relative 'trade_settlement/taxes'
6
+ require_relative 'trade_settlement/summary'
7
+
8
+ module Facturx
9
+ class Writer
10
+ module Stages
11
+ class TradeSettlement < Stage
12
+ include TradeSettlementParties
13
+ include TradeSettlementPayment
14
+ include TradeSettlementTaxes
15
+ include TradeSettlementSummary
16
+
17
+ term_ids(*%w[
18
+ BT-90 BT-83 BT-6 BT-5 BT-60 BT-60-1 BT-59 BT-61 BT-61-1 BT-81 BT-82 BT-87 BT-88 BT-91 BT-84
19
+ BT-85 BT-86 BT-117 BT-120 BT-116 BT-118 BT-121 BT-7 BT-8 BT-119 BT-73 BT-74 BT-94 BT-93 BT-92
20
+ BT-98 BT-97 BT-95 BT-96 BT-101 BT-100 BT-99 BT-105 BT-104 BT-102 BT-103 BT-20 BT-9 BT-89 BT-106
21
+ BT-108 BT-107 BT-109 BT-110 BT-111 BT-114 BT-112 BT-113 BT-115 BT-25 BT-26 BT-19
22
+ ])
23
+
24
+ def call
25
+ node = context.element(context.transaction, 'ram:ApplicableHeaderTradeSettlement')
26
+ payment = document.payment
27
+ emit_header(node, payment)
28
+ emit_content(node, payment)
29
+ end
30
+
31
+ private
32
+
33
+ def emit_header(parent, payment)
34
+ direct_debit = payment&.direct_debit
35
+ emit_identifier_value('BT-90', direct_debit&.creditor_identifier, 'ram:CreditorReferenceID', parent)
36
+ emit('BT-83', payment&.remittance_information, element: 'ram:PaymentReference', parent:)
37
+ emit('BT-6', tax_currency_term_value, element: 'ram:TaxCurrencyCode', parent:)
38
+ emit('BT-5', document.currency, element: 'ram:InvoiceCurrencyCode', parent:)
39
+ end
40
+
41
+ def tax_currency_term_value
42
+ return if profile.id == :minimum && document.totals&.tax_total_in_tax_currency
43
+
44
+ document.tax_currency
45
+ end
46
+
47
+ def emit_content(parent, payment)
48
+ payee(parent)
49
+ payment_means(parent, payment)
50
+ taxes(parent)
51
+ billing_period(parent)
52
+ document_adjustments(parent)
53
+ settlement_summary(parent, payment&.direct_debit)
54
+ end
55
+
56
+ def document_adjustments(parent)
57
+ adjustments(parent, document.allowances, 'BG-20', false)
58
+ adjustments(parent, document.charges, 'BG-21', true)
59
+ end
60
+
61
+ def settlement_summary(parent, direct_debit)
62
+ payment_terms(parent, direct_debit)
63
+ totals(parent)
64
+ preceding_invoices(parent)
65
+ accounting_reference(parent)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'conformance'
4
+ require_relative 'error'
5
+ require_relative 'terms'
6
+ require_relative 'writer/context'
7
+ require_relative 'writer/stage'
8
+ require_relative 'writer/stages/document_context'
9
+ require_relative 'writer/stages/exchanged_document'
10
+ require_relative 'writer/stages/trade_lines'
11
+ require_relative 'writer/stages/trade_agreement'
12
+ require_relative 'writer/stages/trade_delivery'
13
+ require_relative 'writer/stages/trade_settlement'
14
+ require_relative 'xml/schema_validator'
15
+
16
+ module Facturx
17
+ class Writer
18
+ STAGES = [
19
+ Stages::DocumentContext,
20
+ Stages::ExchangedDocument,
21
+ Stages::TradeLines,
22
+ Stages::TradeAgreement,
23
+ Stages::TradeDelivery,
24
+ Stages::TradeSettlement
25
+ ].freeze
26
+ MODELED_TERM_IDS = STAGES.flat_map(&:term_ids).freeze
27
+
28
+ unless MODELED_TERM_IDS.sort == Terms.all.map(&:id).sort
29
+ raise 'Factur-X writer stages must cover every modeled term exactly once'
30
+ end
31
+
32
+ def initialize(schema_validator: Xml::SchemaValidator.new, tracker: Conformance::Tracker, stages: STAGES)
33
+ @schema_validator = schema_validator
34
+ @tracker_class = tracker
35
+ @stages = stages.freeze
36
+ end
37
+
38
+ def call(document:, profile:)
39
+ context = compile(document:, profile:)
40
+ report = context.tracker.report
41
+ raise_conformance_error(profile, report) if report.invalid?
42
+
43
+ @schema_validator.call(document: context.xml, profile:)
44
+ context.to_xml
45
+ end
46
+
47
+ def validate(document:, profile:)
48
+ compile(document:, profile:).tracker.report
49
+ end
50
+
51
+ private
52
+
53
+ def compile(document:, profile:)
54
+ tracker = @tracker_class.new(profile:)
55
+ tracker.check_document(document, path: Terms.fetch('BT-24').xpath)
56
+ context = Context.new(document:, profile:, tracker:)
57
+ @stages.each { |stage| stage.new(context).call }
58
+ context
59
+ end
60
+
61
+ def raise_conformance_error(profile, report)
62
+ raise ConformanceError.new(
63
+ 'Factur-X document does not conform to the selected profile',
64
+ profile: profile.id, report:, issues: report.issues
65
+ )
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Facturx
4
+ module Xml
5
+ module Namespaces
6
+ CII = 'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'
7
+ QUALIFIED = 'urn:un:unece:uncefact:data:standard:QualifiedDataType:100'
8
+ REUSABLE = 'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'
9
+ UNQUALIFIED = 'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'
10
+
11
+ MAP = {
12
+ 'qdt' => QUALIFIED,
13
+ 'ram' => REUSABLE,
14
+ 'rsm' => CII,
15
+ 'udt' => UNQUALIFIED
16
+ }.freeze
17
+ end
18
+ end
19
+ end
@@ -2,14 +2,12 @@
2
2
 
3
3
  require_relative '../profiles'
4
4
  require_relative '../error'
5
+ require_relative 'namespaces'
5
6
 
6
7
  module Facturx
7
8
  module Xml
8
9
  class ProfileDetector
9
- NAMESPACES = {
10
- 'ram' => 'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100',
11
- 'rsm' => 'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'
12
- }.freeze
10
+ NAMESPACES = Namespaces::MAP
13
11
  GUIDELINE_XPATH = '/rsm:CrossIndustryInvoice/rsm:ExchangedDocumentContext/' \
14
12
  'ram:GuidelineSpecifiedDocumentContextParameter/ram:ID'
15
13
 
data/lib/facturx.rb CHANGED
@@ -5,6 +5,18 @@ require 'facturx/error'
5
5
  require 'facturx/embedding'
6
6
  require 'facturx/profile'
7
7
  require 'facturx/profiles'
8
+ require 'facturx/profile_resolver'
9
+ require 'facturx/term'
10
+ require 'facturx/group'
11
+ require 'facturx/terms'
12
+ require 'facturx/model'
13
+ require 'facturx/document'
14
+ require 'facturx/builders'
15
+ require 'facturx/diagnostic'
16
+ require 'facturx/reading'
17
+ require 'facturx/coerce'
18
+ require 'facturx/format'
19
+ require 'facturx/conformance'
8
20
  require 'facturx/xml/verifier'
9
21
  require 'facturx/pdf/document'
10
22
  require 'facturx/pdf/inspector'
@@ -12,10 +24,22 @@ require 'facturx/pdf/extractor'
12
24
  require 'facturx/pdf/verifier'
13
25
  require 'facturx/composers/ghostscript'
14
26
  require 'facturx/attach'
27
+ require 'facturx/reader'
28
+ require 'facturx/writer'
29
+ require 'facturx/generate'
15
30
 
16
31
  module Facturx
17
32
  DEFAULT_ATTACHER = Attach.new
33
+ DEFAULT_READER = Reader.new
34
+ DEFAULT_PROFILE_RESOLVER = ProfileResolver.new
35
+ DEFAULT_WRITER = Writer.new
36
+ DEFAULT_GENERATOR = Generate.new(writer: DEFAULT_WRITER, attacher: DEFAULT_ATTACHER,
37
+ profile_resolver: DEFAULT_PROFILE_RESOLVER)
18
38
  private_constant :DEFAULT_ATTACHER
39
+ private_constant :DEFAULT_READER
40
+ private_constant :DEFAULT_PROFILE_RESOLVER
41
+ private_constant :DEFAULT_WRITER
42
+ private_constant :DEFAULT_GENERATOR
19
43
 
20
44
  class << self
21
45
  def verify_xml(xml:)
@@ -30,5 +54,23 @@ module Facturx
30
54
  def extract_xml(pdf:)
31
55
  Pdf::Extractor.new.call(pdf).xml
32
56
  end
57
+
58
+ def read(source, on_unknown_profile: :fallback)
59
+ DEFAULT_READER.call(source, on_unknown_profile:)
60
+ end
61
+
62
+ def validate_document(document:, profile:)
63
+ canonical_profile = DEFAULT_PROFILE_RESOLVER.call(profile)
64
+ DEFAULT_WRITER.validate(document:, profile: canonical_profile)
65
+ end
66
+
67
+ def build_xml(document:, profile:)
68
+ canonical_profile = DEFAULT_PROFILE_RESOLVER.call(profile)
69
+ DEFAULT_WRITER.call(document:, profile: canonical_profile)
70
+ end
71
+
72
+ def generate(pdf:, document:, profile:)
73
+ DEFAULT_GENERATOR.call(pdf:, document:, profile:)
74
+ end
33
75
  end
34
76
  end