ticketbai 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +32 -0
  4. data/CHANGELOG.md +13 -0
  5. data/Gemfile +6 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +107 -0
  8. data/Rakefile +12 -0
  9. data/bin/console +15 -0
  10. data/bin/setup +8 -0
  11. data/lib/ticketbai/api/client.rb +63 -0
  12. data/lib/ticketbai/api/registry.rb +34 -0
  13. data/lib/ticketbai/api/request.rb +125 -0
  14. data/lib/ticketbai/api/response_parser.rb +36 -0
  15. data/lib/ticketbai/checksum_calculator.rb +15 -0
  16. data/lib/ticketbai/document.rb +21 -0
  17. data/lib/ticketbai/document_validator.rb +42 -0
  18. data/lib/ticketbai/documents/annulment.rb +45 -0
  19. data/lib/ticketbai/documents/api_payload.rb +47 -0
  20. data/lib/ticketbai/documents/issuance.rb +55 -0
  21. data/lib/ticketbai/documents/issuance_unsigned.rb +58 -0
  22. data/lib/ticketbai/errors.rb +27 -0
  23. data/lib/ticketbai/nodes/breakdown_type.rb +72 -0
  24. data/lib/ticketbai/nodes/invoice_chaining.rb +22 -0
  25. data/lib/ticketbai/nodes/invoice_data.rb +24 -0
  26. data/lib/ticketbai/nodes/invoice_header.rb +26 -0
  27. data/lib/ticketbai/nodes/issuer.rb +18 -0
  28. data/lib/ticketbai/nodes/lroe_header.rb +29 -0
  29. data/lib/ticketbai/nodes/lroe_issued_invoices.rb +28 -0
  30. data/lib/ticketbai/nodes/receiver.rb +36 -0
  31. data/lib/ticketbai/nodes/software.rb +17 -0
  32. data/lib/ticketbai/operation.rb +59 -0
  33. data/lib/ticketbai/operations/annulment.rb +41 -0
  34. data/lib/ticketbai/operations/issuance.rb +93 -0
  35. data/lib/ticketbai/operations/issuance_unsigned.rb +89 -0
  36. data/lib/ticketbai/signer.rb +273 -0
  37. data/lib/ticketbai/tbai_identifier.rb +33 -0
  38. data/lib/ticketbai/tbai_qr.rb +50 -0
  39. data/lib/ticketbai/version.rb +5 -0
  40. data/lib/ticketbai/xmldsig-core-schema.xsd +318 -0
  41. data/lib/ticketbai/xsd_validators/annulment.xsd +392 -0
  42. data/lib/ticketbai/xsd_validators/issuance.xsd +865 -0
  43. data/lib/ticketbai.rb +89 -0
  44. data/ticketbai.gemspec +42 -0
  45. metadata +186 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a1c6cb226a9d92966d7025aa0869b26a9375fc51e7107a14dcec995e82bf632a
4
+ data.tar.gz: 57dd345f66be10521fe9b862450cb64680b1d6b37819ec112e6830b688ffbeea
5
+ SHA512:
6
+ metadata.gz: f15970bada7f78a0d74a3b6f8f84f33afa1f39dc5c973db302f7142ec85116080152854cd474c3d41e022de0c10659f5d51327e18de8e83bc4210e9bcc486149
7
+ data.tar.gz: 214ef909a9e59c490441c6a74179959022ac6d7144ae928d0058619b27be25b03636e24b0dcf555e2d70947a2e828366b786918519e840b647df48233fe44fb0
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,32 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 2.6.7
4
+ SuggestExtensions: false
5
+ Exclude:
6
+ - spec/**/*.rb
7
+
8
+ Style/StringLiterals:
9
+ Enabled: true
10
+ EnforcedStyle: single_quotes
11
+
12
+ Style/StringLiteralsInInterpolation:
13
+ Enabled: true
14
+ EnforcedStyle: single_quotes
15
+
16
+ Metrics:
17
+ Enabled: false
18
+
19
+ Style/Documentation:
20
+ Enabled: false
21
+
22
+ Style/FrozenStringLiteralComment:
23
+ Enabled: false
24
+
25
+ Layout/LineLength:
26
+ Max: 155
27
+
28
+ Style/FormatStringToken:
29
+ Enabled: false
30
+
31
+ Style/IfUnlessModifier:
32
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.1] - 2022-03-08
10
+ 🎉 First release!
11
+
12
+ [Unreleased]: https://github.com/ecommerce-ventures/ticketbai/compare/v0.1.1...HEAD
13
+ [0.1.1]: https://github.com/ecommerce-ventures/ticketbai/releases/tag/v0.1.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in ticketbai.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 franvega
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # Ticketbai
2
+
3
+ Ticketbai is a gem that gives you the ability to generate Ticketbai files and upload them to the Regional Treasury (currently only Bizkaia is supported)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'ticketbai'
11
+ ```
12
+ Then run:
13
+
14
+ ```bash
15
+ bundle install
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ $ bundle install
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install ticketbai
25
+
26
+ ## Configuration
27
+
28
+ Encode your certificate to be able to add the resulting string to the secrets file:
29
+
30
+ ```
31
+ certificate = File.read('path/my-certificate.pfx')
32
+ encoded_certificate = Base64.strict_encode64(certificate)
33
+ ```
34
+
35
+ Add the encoded_certificate string, the key and the rest of your ticketbai license params to the secrets file:
36
+
37
+ ```
38
+ ticketbai:
39
+ license_key: xxxxxxxxxx
40
+ app_name: xxxxxxxxxx
41
+ app_version: xxxxxxxxxx
42
+ developer_company_nif: xxxxxxxxxx
43
+ certificates:
44
+ my_certificate_name:
45
+ cert: encoded_certificate string
46
+ key: xxxxxxxxxx
47
+ ```
48
+
49
+ ## Usage
50
+ The supported TicketBAI operations are: issuance, annulment and issuance unsigned.
51
+
52
+ #### Issuance operation
53
+ ```
54
+ Ticketbai::Operations::Issuance.new(
55
+ company_cert: 'my_certificate_name',
56
+ issuing_company_nif: 'B34576372',
57
+ issuing_company_name: 'FooBar SL',
58
+ invoice_serial: '2022',
59
+ invoice_number: '10001',
60
+ invoice_date: '11-01-2022',
61
+ invoice_time: '13:05:22',
62
+ invoice_description: 'La descripción de la factura',
63
+ invoice_total: 12.21,
64
+ invoice_vat_key: '01',
65
+ invoice_amount: 11.0,
66
+ invoice_vat: 21.0,
67
+ invoice_vat_total: 2.31,
68
+ simplified_invoice: true
69
+ ).create
70
+ ```
71
+ If everything is ok, the response of `Ticketbai::Operations::Issuance.new(params).create` is a Hash with two keys:
72
+ - xml_doc: The signed TicketBAI XML string.
73
+ - signature_value: The first 100 characters of the signature value needed for the chaining of TicketBAI files.
74
+
75
+ #### Annulment operation
76
+ ```
77
+ Ticketbai::Operations::Annulment.new(
78
+ issuing_company_nif: 'B12345678',
79
+ issuing_company_name: 'Test SL',
80
+ invoice_serial: '2022',
81
+ invoice_number: '000002',
82
+ invoice_date: '10-11-2022',
83
+ company_cert: 'my_certificate_name'
84
+ ).create
85
+ ```
86
+
87
+ #### API Upload
88
+
89
+ Sending the TicketBAI files to the LROE is done by executing the following request to the API:
90
+ ```
91
+ Ticketbai::Api::Request.new(
92
+ issued_invoices: xml_doc,
93
+ nif: 'B34576372',
94
+ company_name: 'FooBar SL',
95
+ certificate_name: 'my_certificate_name',
96
+ year: '2022',
97
+ operation: :issuance
98
+ ).execute
99
+ ```
100
+
101
+ ## Contributing
102
+
103
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ecommerce-ventures/ticketbai.
104
+
105
+ ## License
106
+
107
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'ticketbai'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,63 @@
1
+ module Ticketbai
2
+ module Api
3
+ class Client
4
+ KO_RESPONSE = :incorrecto
5
+ OK_RESPONSE = :correcto
6
+ PARTIALLY_OK_RESPONSE = :parcialmentecorrecto
7
+
8
+ def initialize(url:, headers:, body:, company_cert:)
9
+ @url = url
10
+ @headers = headers
11
+ @body = body
12
+ @company_cert = company_cert
13
+ end
14
+
15
+ def execute
16
+ response = connection.post do |req|
17
+ req.url(@url)
18
+ req.headers = @headers
19
+ req.body = @body
20
+ end
21
+
22
+ ResponseParser.new(response).to_h
23
+ end
24
+
25
+ private
26
+
27
+ def connection
28
+ Faraday.new(ssl: ssl) do |builder|
29
+ builder.request :multipart
30
+ builder.response :logger, Ticketbai.logger if Ticketbai.debug
31
+ builder.adapter Faraday.default_adapter
32
+ end
33
+ end
34
+
35
+ def ssl
36
+ {
37
+ client_key: client_key,
38
+ client_cert: client_cert
39
+ }
40
+ end
41
+
42
+ def client_key
43
+ p12.key
44
+ end
45
+
46
+ def client_cert
47
+ p12.certificate
48
+ end
49
+
50
+ def p12
51
+ OpenSSL::PKCS12.new(p12_file, p12_password)
52
+ end
53
+
54
+ def p12_file
55
+ Base64.decode64(@company_cert[:cert])
56
+ end
57
+
58
+ def p12_password
59
+ @company_cert[:key]
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,34 @@
1
+ module Ticketbai
2
+ module Api
3
+ class Registry
4
+ def initialize(attributes)
5
+ @attributes = attributes
6
+ end
7
+
8
+ def number
9
+ @attributes.at_css('NumFactura')&.children&.last&.text
10
+ end
11
+
12
+ def uploaded
13
+ @attributes.at_css('CodigoErrorRegistro').nil?
14
+ end
15
+
16
+ def error_code
17
+ @attributes.at_css('CodigoErrorRegistro')&.children&.last&.text
18
+ end
19
+
20
+ def error_message
21
+ @attributes.at_css('DescripcionErrorRegistroES')&.children&.last&.text
22
+ end
23
+
24
+ def to_h
25
+ {
26
+ number: number,
27
+ uploaded: uploaded,
28
+ error_code: error_code,
29
+ error_message: error_message
30
+ }
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,125 @@
1
+ require 'faraday'
2
+ require 'json'
3
+ require 'zlib'
4
+
5
+ module Ticketbai
6
+ module Api
7
+ class Request
8
+ TEST_PRESENTATION_ENDPOINT = 'https://pruesarrerak.bizkaia.eus/N3B4000M/aurkezpena'.freeze
9
+ LIVE_PRESENTATION_ENDPOINT = 'https://sarrerak.bizkaia.eus/N3B4000M/aurkezpena'.freeze
10
+ TEST_QUERY_ENDPOINT = 'https://pruesarrerak.bizkaia.eus/N3B4001M/kontsulta'.freeze
11
+
12
+ # OPERATIONS
13
+ # issuance_unsigned: (invoices issued without guarantor software): When a TicketBai file has been rejected due to a poorly generated XML result,
14
+ # taking into account that the TicketBAI file cannot be generated again and that its information must be sent, it must be done by
15
+ # indicating the subchapter Invoices issued no guarantor software
16
+ # issuance: New invoices issued that we want to register using the guarantor software
17
+ # annulment: Cancel invoices that we have previously registered using the guarantor software
18
+ SUPPORTED_OPERATIONS = [
19
+ Ticketbai::Operations::Issuance::OPERATION_NAME,
20
+ Ticketbai::Operations::Annulment::OPERATION_NAME,
21
+ Ticketbai::Operations::IssuanceUnsigned::OPERATION_NAME
22
+ ].freeze
23
+
24
+ OPERATION_MAPPING = {
25
+ issuance: 'A00',
26
+ annulment: 'AN0',
27
+ modify: 'M00',
28
+ query: 'C00',
29
+ issuance_unsigned: 'A00'
30
+ }.freeze
31
+
32
+ LROE_MODEL = '240'.freeze
33
+ LROE_CHAPTER = '1'.freeze
34
+
35
+ ###
36
+ # @param [Array] issued_invoices TicketBAI XML file(s) string (Max: 1000)
37
+ # @param [String] company_name Name of the taxpayer's company
38
+ # @param [String] year Fiscal year
39
+ # @param [Symbol] certificate_name Taxpayer's certificate name
40
+ # @param [Symbol] operation Operation name
41
+ ###
42
+ def initialize(issued_invoices:, nif:, company_name:, year:, certificate_name:, operation:)
43
+ raise ArgumentError, "Unsupported operation: #{operation}" unless SUPPORTED_OPERATIONS.include? operation.downcase.to_sym
44
+
45
+ @issued_invoices = Array(issued_invoices)
46
+ @nif = nif
47
+ @company_name = company_name
48
+ @year = year
49
+ @company_cert = Ticketbai.config.certificates[certificate_name.to_sym]
50
+ @operation = operation.downcase.to_sym
51
+ end
52
+
53
+ def execute
54
+ client = Client.new(
55
+ url: presentation_endpoint,
56
+ headers: headers,
57
+ body: gzip_body,
58
+ company_cert: @company_cert
59
+ )
60
+
61
+ client.execute
62
+ end
63
+
64
+ private
65
+
66
+ def gzip_body
67
+ gz = StringIO.new('')
68
+ z = Zlib::GzipWriter.new(gz)
69
+ z.write Nokogiri::XML(build_document)
70
+ z.close
71
+
72
+ StringIO.new(gz.string).read
73
+ end
74
+
75
+ def build_document
76
+ @lroe_header = Ticketbai::Nodes::LroeHeader.new(
77
+ year: @year,
78
+ nif: @nif,
79
+ company_name: @company_name,
80
+ operation: OPERATION_MAPPING[@operation],
81
+ subchapter: subchapter
82
+ )
83
+
84
+ @lroe_issued_invoices = Ticketbai::Nodes::LroeIssuedInvoices.new(operation: @operation, issued_invoices: @issued_invoices)
85
+
86
+ Ticketbai::Documents::ApiPayload.new(
87
+ operation: @operation,
88
+ lroe_issued_invoices: @lroe_issued_invoices,
89
+ lroe_header: @lroe_header
90
+ ).create
91
+ end
92
+
93
+ def presentation_endpoint
94
+ if Ticketbai.live?
95
+ LIVE_PRESENTATION_ENDPOINT
96
+ else
97
+ TEST_PRESENTATION_ENDPOINT
98
+ end
99
+ end
100
+
101
+ def subchapter
102
+ @operation == :issuance_unsigned ? '1.2' : '1.1'
103
+ end
104
+
105
+ def headers
106
+ {
107
+ 'Content-Type' => 'application/octet-stream',
108
+ 'Content-Encoding' => 'gzip',
109
+ 'eus-bizkaia-n3-version' => '1.0',
110
+ 'eus-bizkaia-n3-content-type' => 'application/xml',
111
+ 'eus-bizkaia-n3-data' => lroe_header_data
112
+ }
113
+ end
114
+
115
+ def lroe_header_data
116
+ {
117
+ con: 'LROE',
118
+ apa: subchapter,
119
+ inte: { nif: @nif, nrs: @company_name },
120
+ drs: { mode: LROE_MODEL, ejer: @year }
121
+ }.to_json
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,36 @@
1
+ module Ticketbai
2
+ module Api
3
+ class ResponseParser
4
+ def initialize(raw_response)
5
+ @raw_response = raw_response
6
+ end
7
+
8
+ def status
9
+ @raw_response.headers['eus-bizkaia-n3-tipo-respuesta'].downcase.to_sym
10
+ end
11
+
12
+ def identifier
13
+ @raw_response.headers['eus-bizkaia-n3-identificativo']
14
+ end
15
+
16
+ def message
17
+ @raw_response.headers['eus-bizkaia-n3-mensaje-respuesta']&.force_encoding('ISO-8859-1')&.encode('UTF-8')
18
+ end
19
+
20
+ def registries
21
+ return [] unless @raw_response.body
22
+
23
+ Nokogiri::XML(@raw_response.body).css('Registros Registro').map { |attributes| Registry.new(attributes) }
24
+ end
25
+
26
+ def to_h
27
+ {
28
+ status: status,
29
+ identifier: identifier,
30
+ message: message,
31
+ registries: registries.map(&:to_h)
32
+ }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ module Ticketbai
2
+ class ChecksumCalculator
3
+ attr_accessor :data
4
+
5
+ def initialize(data)
6
+ @data = data
7
+ end
8
+
9
+ def calculate
10
+ checksum = Digest::CRC8.checksum(@data)
11
+
12
+ format('%03d', checksum.to_s)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ module Ticketbai
2
+ class Document
3
+ TBAI_VERSION = '1.2'.freeze
4
+
5
+ def initialize(args)
6
+ self.class::ATTRIBUTES.each do |p|
7
+ instance_variable_set "@#{p}", args[p]
8
+ end
9
+ end
10
+
11
+ def create
12
+ raise NotImplementedError, 'Must implement this method'
13
+ end
14
+
15
+ private
16
+
17
+ def modify_xml_root_name(builder)
18
+ builder.doc.root.name = self.class::ROOT_NAME
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ module Ticketbai
2
+ class DocumentValidator
3
+ attr_accessor :document, :validator_type
4
+
5
+ SUPPORTED_VALIDATORS = [Operations::Issuance::OPERATION_NAME, Operations::Annulment::OPERATION_NAME].freeze
6
+
7
+ ###
8
+ # @param [String] document The xml document to be validated
9
+ # @param [String] validator_type The validator name to be used to validate the document (.xsd file)
10
+ ###
11
+ def initialize(document:, validator_type:)
12
+ raise ArgumentError, 'Unsupported validator' unless SUPPORTED_VALIDATORS.include? validator_type.downcase.to_sym
13
+
14
+ @document = document
15
+ @validator_type = validator_type.downcase.to_sym
16
+ end
17
+
18
+ ###
19
+ # Raises an exception with the validation error(s)
20
+ ###
21
+ def validate
22
+ errors = []
23
+
24
+ xsd_validator.validate(Nokogiri::XML(@document)).each do |error|
25
+ errors << error.message
26
+ end
27
+
28
+ return unless errors.any?
29
+
30
+ raise Ticketbai::TBAIFileError.new(
31
+ errors.uniq.join(','),
32
+ 'TBAIFile'
33
+ )
34
+ end
35
+
36
+ private
37
+
38
+ def xsd_validator
39
+ Nokogiri::XML::Schema(File.read("#{__dir__}/xsd_validators/#{@validator_type}.xsd"))
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,45 @@
1
+ module Ticketbai
2
+ module Documents
3
+ class Annulment < Document
4
+ ROOT_NAME = 'T:AnulaTicketBai'.freeze
5
+ XMLNS = {
6
+ 'xmlns:T' => 'urn:ticketbai:anulacion'
7
+ }.freeze
8
+
9
+ ATTRIBUTES = %i[issuer invoice_header software].freeze
10
+
11
+ attr_accessor(*ATTRIBUTES)
12
+
13
+ ###
14
+ # @param [Ticketbai::Nodes::Isuer] issuer
15
+ # @param [Ticketbai::Nodes::InvoiceHeader] invoice_header
16
+ # @param [Ticketbai::Nodes::Software] software
17
+ ###
18
+ def initialize(**args)
19
+ super(args)
20
+ end
21
+
22
+ # @return [Nokogiri::XML::Builder]
23
+ def create
24
+ builder = Nokogiri::XML::Builder.new(encoding: Encoding::UTF_8.to_s) do |xml|
25
+ xml.TicketBai(XMLNS) do
26
+ xml.Cabecera do
27
+ xml.IDVersionTBAI TBAI_VERSION
28
+ end
29
+ xml.IDFactura do
30
+ @issuer.build_xml(xml)
31
+ @invoice_header.build_xml(xml)
32
+ end
33
+ xml.HuellaTBAI do
34
+ @software.build_xml(xml)
35
+ end
36
+ end
37
+ end
38
+
39
+ modify_xml_root_name(builder)
40
+
41
+ builder
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,47 @@
1
+ module Ticketbai
2
+ module Documents
3
+ class ApiPayload
4
+ ROOT_NAME_MAPPING = {
5
+ issuance: 'lrpjfecsgap:LROEPJ240FacturasEmitidasConSGAltaPeticion',
6
+ annulment: 'lrpjfecsgap:LROEPJ240FacturasEmitidasConSGAnulacionPeticion',
7
+ issuance_unsigned: 'lrpjfecsgap:LROEPJ240FacturasEmitidasSinSGAltaModifPeticion'
8
+ }.freeze
9
+ SCHEME_MAPPING = {
10
+ issuance: 'https://www.batuz.eus/fitxategiak/batuz/LROE/esquemas/LROE_PJ_240_1_1_FacturasEmitidas_ConSG_AltaPeticion_V1_0_2.xsd',
11
+ annulment: 'https://www.batuz.eus/fitxategiak/batuz/LROE/esquemas/LROE_PJ_240_1_1_FacturasEmitidas_ConSG_AnulacionPeticion_V1_0_0.xsd',
12
+ issuance_unsigned: 'https://www.batuz.eus/fitxategiak/batuz/LROE/esquemas/LROE_PJ_240_1_2_FacturasEmitidas_SinSG_AltaModifPeticion_V1_0_1.xsd'
13
+ }.freeze
14
+
15
+ ###
16
+ # @param [Ticketbai::Nodes::LroeHeader] lroe_header
17
+ # @param [Ticketbai::Nodes::LroeIssuedInvoices] lroe_issued_invoices
18
+ # @param [Symbol] operation
19
+ ###
20
+ def initialize(**args)
21
+ @lroe_header = args[:lroe_header]
22
+ @lroe_issued_invoices = args[:lroe_issued_invoices]
23
+ @operation = args[:operation]
24
+ end
25
+
26
+ # @return [Nokogiri::XML::Builder]
27
+ def create
28
+ builder = Nokogiri::XML::Builder.new(encoding: 'utf-8') do |xml|
29
+ xml.ROOT_NAME('xmlns:lrpjfecsgap' => SCHEME_MAPPING[@operation]) do
30
+ @lroe_header.build_xml(xml)
31
+ @lroe_issued_invoices.build_xml(xml)
32
+ end
33
+ end
34
+
35
+ modify_xml_root_name(builder)
36
+
37
+ builder.to_xml
38
+ end
39
+
40
+ private
41
+
42
+ def modify_xml_root_name(builder)
43
+ builder.doc.root.name = ROOT_NAME_MAPPING[@operation]
44
+ end
45
+ end
46
+ end
47
+ end