ticketbai 0.1.1
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/.rspec +3 -0
- data/.rubocop.yml +32 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +107 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/ticketbai/api/client.rb +63 -0
- data/lib/ticketbai/api/registry.rb +34 -0
- data/lib/ticketbai/api/request.rb +125 -0
- data/lib/ticketbai/api/response_parser.rb +36 -0
- data/lib/ticketbai/checksum_calculator.rb +15 -0
- data/lib/ticketbai/document.rb +21 -0
- data/lib/ticketbai/document_validator.rb +42 -0
- data/lib/ticketbai/documents/annulment.rb +45 -0
- data/lib/ticketbai/documents/api_payload.rb +47 -0
- data/lib/ticketbai/documents/issuance.rb +55 -0
- data/lib/ticketbai/documents/issuance_unsigned.rb +58 -0
- data/lib/ticketbai/errors.rb +27 -0
- data/lib/ticketbai/nodes/breakdown_type.rb +72 -0
- data/lib/ticketbai/nodes/invoice_chaining.rb +22 -0
- data/lib/ticketbai/nodes/invoice_data.rb +24 -0
- data/lib/ticketbai/nodes/invoice_header.rb +26 -0
- data/lib/ticketbai/nodes/issuer.rb +18 -0
- data/lib/ticketbai/nodes/lroe_header.rb +29 -0
- data/lib/ticketbai/nodes/lroe_issued_invoices.rb +28 -0
- data/lib/ticketbai/nodes/receiver.rb +36 -0
- data/lib/ticketbai/nodes/software.rb +17 -0
- data/lib/ticketbai/operation.rb +59 -0
- data/lib/ticketbai/operations/annulment.rb +41 -0
- data/lib/ticketbai/operations/issuance.rb +93 -0
- data/lib/ticketbai/operations/issuance_unsigned.rb +89 -0
- data/lib/ticketbai/signer.rb +273 -0
- data/lib/ticketbai/tbai_identifier.rb +33 -0
- data/lib/ticketbai/tbai_qr.rb +50 -0
- data/lib/ticketbai/version.rb +5 -0
- data/lib/ticketbai/xmldsig-core-schema.xsd +318 -0
- data/lib/ticketbai/xsd_validators/annulment.xsd +392 -0
- data/lib/ticketbai/xsd_validators/issuance.xsd +865 -0
- data/lib/ticketbai.rb +89 -0
- data/ticketbai.gemspec +42 -0
- metadata +186 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
module Documents
|
3
|
+
class Issuance < Document
|
4
|
+
ROOT_NAME = 'T:TicketBai'.freeze
|
5
|
+
XMLNS = {
|
6
|
+
'xmlns:T' => 'urn:ticketbai:emision'
|
7
|
+
}.freeze
|
8
|
+
|
9
|
+
ATTRIBUTES = %i[issuer receiver invoice_header invoice_data breakdown_type invoice_chaining software].freeze
|
10
|
+
|
11
|
+
attr_accessor(*ATTRIBUTES)
|
12
|
+
|
13
|
+
###
|
14
|
+
# @param [Ticketbai::Nodes::Isuer] issuer
|
15
|
+
# @param [Ticketbai::Nodes::Receiver] receiver
|
16
|
+
# @param [Ticketbai::Nodes::InvoiceHeader] invoice_header
|
17
|
+
# @param [Ticketbai::Nodes::InvoiceData] invoice_data
|
18
|
+
# @param [Ticketbai::Nodes::BreakdownType] breakdown_type
|
19
|
+
# @param [Ticketbai::Nodes::InvoiceChaining] invoice_chaining
|
20
|
+
# @param [Ticketbai::Nodes::Software] software
|
21
|
+
###
|
22
|
+
def initialize(**args)
|
23
|
+
super(args)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Nokogiri::XML::Builder]
|
27
|
+
def create
|
28
|
+
builder = Nokogiri::XML::Builder.new(encoding: Encoding::UTF_8.to_s) do |xml|
|
29
|
+
xml.TicketBai(XMLNS) do
|
30
|
+
xml.Cabecera do
|
31
|
+
xml.IDVersionTBAI TBAI_VERSION
|
32
|
+
end
|
33
|
+
xml.Sujetos do
|
34
|
+
@issuer.build_xml(xml)
|
35
|
+
@receiver&.build_xml(xml)
|
36
|
+
end
|
37
|
+
xml.Factura do
|
38
|
+
@invoice_header.build_xml(xml)
|
39
|
+
@invoice_data.build_xml(xml)
|
40
|
+
@breakdown_type.build_xml(xml)
|
41
|
+
end
|
42
|
+
xml.HuellaTBAI do
|
43
|
+
@invoice_chaining&.build_xml(xml)
|
44
|
+
@software.build_xml(xml)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
modify_xml_root_name(builder)
|
50
|
+
|
51
|
+
builder
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
module Documents
|
3
|
+
# This document has the pecualiarity that it does not need to be signed and it must be sent to the API without a root node, so it's not considered
|
4
|
+
# as a valid XML either. The structure of the document is as follows:
|
5
|
+
# <Destinatarios>
|
6
|
+
# ...
|
7
|
+
# ...
|
8
|
+
# </Destinatarios>
|
9
|
+
# <CabeceraFactura>
|
10
|
+
# ...
|
11
|
+
# ...
|
12
|
+
# </CabeceraFactura>
|
13
|
+
# <DatosFactura>
|
14
|
+
# ...
|
15
|
+
# ...
|
16
|
+
# </DatosFactura>
|
17
|
+
# <TipoDesglose>
|
18
|
+
# ...
|
19
|
+
# ...
|
20
|
+
# </TipoDesglose>
|
21
|
+
#
|
22
|
+
# The document is created with a temporary root node (TicketBai), and after creating it, it's returned without the root node.
|
23
|
+
class IssuanceUnsigned
|
24
|
+
###
|
25
|
+
# @param [Ticketbai::Nodes::Receiver] receiver
|
26
|
+
# @param [Ticketbai::Nodes::InvoiceHeader] invoice_header
|
27
|
+
# @param [Ticketbai::Nodes::InvoiceData] invoice_data
|
28
|
+
# @param [Ticketbai::Nodes::BreakdownType] breakdown_type
|
29
|
+
###
|
30
|
+
def initialize(**args)
|
31
|
+
@receiver = args[:receiver]
|
32
|
+
@invoice_header = args[:invoice_header]
|
33
|
+
@invoice_data = args[:invoice_data]
|
34
|
+
@breakdown_type = args[:breakdown_type]
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [String]
|
38
|
+
def create
|
39
|
+
builder = Nokogiri::XML::Builder.new(encoding: Encoding::UTF_8.to_s) do |xml|
|
40
|
+
xml.TicketBai do
|
41
|
+
@receiver&.build_xml(xml)
|
42
|
+
@invoice_header.build_xml(xml)
|
43
|
+
@invoice_data.build_xml(xml)
|
44
|
+
@breakdown_type.build_xml(xml)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
filter_root_node(builder)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def filter_root_node(builder)
|
54
|
+
Nokogiri::XML(builder.to_xml).at('TicketBai').children.to_xml
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
# Generic Ticketbai exception class.
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
# For errors returned by the client library, like HTTP errors
|
6
|
+
class ClientError < Error; end
|
7
|
+
|
8
|
+
# For errors returned by the API
|
9
|
+
class APIError < Error
|
10
|
+
attr_reader :code
|
11
|
+
|
12
|
+
def initialize(message = nil, code = nil)
|
13
|
+
@code = code
|
14
|
+
super(message)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# For errors returned by the TBAIFile validation.
|
19
|
+
class TBAIFileError < Error
|
20
|
+
attr_reader :code
|
21
|
+
|
22
|
+
def initialize(message = nil, code = nil)
|
23
|
+
@code = code
|
24
|
+
super(message)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
module Nodes
|
3
|
+
class BreakdownType
|
4
|
+
# Exemption causes (CausaExencion):
|
5
|
+
# E1: Exenta por el articulo 20 de la Norma Foral del IVA
|
6
|
+
# E2: Exenta por el articulo 21 de la Norma Foral del IVA
|
7
|
+
# E3: Exenta por el articulo 22 de la Norma Foral del IVA
|
8
|
+
# E4: Exenta por el articulo 23 y 24 de la Norma Foral del IVA
|
9
|
+
# E5: Exenta por el articulo 25 de la Norma Foral del IVA
|
10
|
+
# E6: Exenta por otra causa
|
11
|
+
|
12
|
+
# Type of non-exempt (TipoNoExenta):
|
13
|
+
# S1: Sin inversion del sujeto pasivo
|
14
|
+
# S2: Con inversion del sujeto pasivo
|
15
|
+
def initialize(args = {})
|
16
|
+
@receiver_country = args[:receiver_country]
|
17
|
+
@invoice_amount = args[:invoice_amount]
|
18
|
+
@invoice_vat = args[:invoice_vat]
|
19
|
+
@invoice_vat_total = args[:invoice_vat_total]
|
20
|
+
@receiver_in_eu = args[:receiver_in_eu]
|
21
|
+
@simplified_invoice = args[:simplified_invoice]
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Nokogiri::XML::Builder]
|
25
|
+
def build_xml(node)
|
26
|
+
node = Nokogiri::XML::Builder.new if node.nil?
|
27
|
+
node.TipoDesglose do |xml|
|
28
|
+
if (@receiver_country == 'ES') || (@receiver_country != 'ES' && @simplified_invoice)
|
29
|
+
xml.DesgloseFactura do
|
30
|
+
xml.Sujeta do
|
31
|
+
if (@receiver_country != 'ES') && @simplified_invoice
|
32
|
+
xml.Exenta do
|
33
|
+
xml.DetalleExenta do
|
34
|
+
xml.CausaExencion @receiver_in_eu == true ? 'E6' : 'E2'
|
35
|
+
xml.BaseImponible @invoice_amount
|
36
|
+
end
|
37
|
+
end
|
38
|
+
else
|
39
|
+
xml.NoExenta do
|
40
|
+
xml.DetalleNoExenta do
|
41
|
+
xml.TipoNoExenta 'S1'
|
42
|
+
xml.DesgloseIVA do
|
43
|
+
xml.DetalleIVA do
|
44
|
+
xml.BaseImponible @invoice_amount
|
45
|
+
xml.TipoImpositivo @invoice_vat
|
46
|
+
xml.CuotaImpuesto @invoice_vat_total
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
else
|
55
|
+
xml.DesgloseTipoOperacion do
|
56
|
+
xml.PrestacionServicios do
|
57
|
+
xml.Sujeta do
|
58
|
+
xml.Exenta do
|
59
|
+
xml.DetalleExenta do
|
60
|
+
xml.CausaExencion @receiver_in_eu == true ? 'E6' : 'E2'
|
61
|
+
xml.BaseImponible @invoice_amount
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
module Nodes
|
3
|
+
class InvoiceChaining
|
4
|
+
def initialize(args = {})
|
5
|
+
@prev_invoice_serial = args[:prev_invoice_serial]
|
6
|
+
@prev_invoice_number = args[:prev_invoice_number]
|
7
|
+
@prev_invoice_date = args[:prev_invoice_date]
|
8
|
+
@prev_invoice_signature = args[:prev_invoice_signature]
|
9
|
+
end
|
10
|
+
|
11
|
+
def build_xml(node)
|
12
|
+
node = Nokogiri::XML::Builder.new if node.nil?
|
13
|
+
node.EncadenamientoFacturaAnterior do |xml|
|
14
|
+
xml.SerieFacturaAnterior @prev_invoice_serial if @prev_invoice_serial
|
15
|
+
xml.NumFacturaAnterior @prev_invoice_number
|
16
|
+
xml.FechaExpedicionFacturaAnterior @prev_invoice_date
|
17
|
+
xml.SignatureValueFirmaFacturaAnterior @prev_invoice_signature
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
module Nodes
|
3
|
+
class InvoiceData
|
4
|
+
def initialize(args = {})
|
5
|
+
@invoice_description = args[:invoice_description]
|
6
|
+
@invoice_total = args[:invoice_total]
|
7
|
+
@invoice_vat_key = args[:invoice_vat_key]
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_xml(node)
|
11
|
+
node = Nokogiri::XML::Builder.new if node.nil?
|
12
|
+
node.DatosFactura do |xml|
|
13
|
+
xml.DescripcionFactura @invoice_description
|
14
|
+
xml.ImporteTotalFactura @invoice_total
|
15
|
+
xml.Claves do
|
16
|
+
xml.IDClave do
|
17
|
+
xml.ClaveRegimenIvaOpTrascendencia @invoice_vat_key
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
module Nodes
|
3
|
+
class InvoiceHeader
|
4
|
+
SIMPLIFIED_INVOICE = 'S'.freeze
|
5
|
+
|
6
|
+
def initialize(args = {})
|
7
|
+
@invoice_serial = args[:invoice_serial]
|
8
|
+
@invoice_number = args[:invoice_number]
|
9
|
+
@invoice_date = args[:invoice_date]
|
10
|
+
@invoice_time = args[:invoice_time]
|
11
|
+
@simplified_invoice = args[:simplified_invoice]
|
12
|
+
end
|
13
|
+
|
14
|
+
def build_xml(node)
|
15
|
+
node = Nokogiri::XML::Builder.new if node.nil?
|
16
|
+
node.CabeceraFactura do |xml|
|
17
|
+
xml.SerieFactura @invoice_serial
|
18
|
+
xml.NumFactura @invoice_number
|
19
|
+
xml.FechaExpedicionFactura @invoice_date
|
20
|
+
xml.HoraExpedicionFactura @invoice_time if @invoice_time
|
21
|
+
xml.FacturaSimplificada SIMPLIFIED_INVOICE if @simplified_invoice
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
module Nodes
|
3
|
+
class Issuer
|
4
|
+
def initialize(args = {})
|
5
|
+
@issuing_company_nif = args[:issuing_company_nif]
|
6
|
+
@issuing_company_name = args[:issuing_company_name]
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_xml(node)
|
10
|
+
node = Nokogiri::XML::Builder.new if node.nil?
|
11
|
+
node.Emisor do |xml|
|
12
|
+
xml.NIF @issuing_company_nif
|
13
|
+
xml.ApellidosNombreRazonSocial @issuing_company_name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
module Nodes
|
3
|
+
class LroeHeader
|
4
|
+
def initialize(args = {})
|
5
|
+
@year = args[:year]
|
6
|
+
@nif = args[:nif]
|
7
|
+
@company_name = args[:company_name]
|
8
|
+
@operation = args[:operation]
|
9
|
+
@subchapter = args[:subchapter]
|
10
|
+
end
|
11
|
+
|
12
|
+
def build_xml(node)
|
13
|
+
node = Nokogiri::XML::Builder.new if node.nil?
|
14
|
+
node.Cabecera do |xml|
|
15
|
+
xml.Modelo Ticketbai::Api::Request::LROE_MODEL
|
16
|
+
xml.Capitulo Ticketbai::Api::Request::LROE_CHAPTER
|
17
|
+
xml.Subcapitulo @subchapter
|
18
|
+
xml.Operacion @operation
|
19
|
+
xml.Version '1.0'
|
20
|
+
xml.Ejercicio @year
|
21
|
+
xml.ObligadoTributario do
|
22
|
+
xml.NIF @nif
|
23
|
+
xml.ApellidosNombreRazonSocial @company_name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
module Nodes
|
3
|
+
class LroeIssuedInvoices
|
4
|
+
def initialize(args = {})
|
5
|
+
@issued_invoices = args[:issued_invoices]
|
6
|
+
@operation = args[:operation]
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_xml(node)
|
10
|
+
node = Nokogiri::XML::Builder.new if node.nil?
|
11
|
+
node.FacturasEmitidas do |xml|
|
12
|
+
@issued_invoices.each do |issued_invoice|
|
13
|
+
xml.FacturaEmitida do
|
14
|
+
case @operation
|
15
|
+
when :annulment
|
16
|
+
xml.AnulacionTicketBai Base64.strict_encode64(issued_invoice)
|
17
|
+
when :issuance
|
18
|
+
xml.TicketBai Base64.strict_encode64(issued_invoice)
|
19
|
+
when :issuance_unsigned
|
20
|
+
xml << issued_invoice
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
module Nodes
|
3
|
+
class Receiver
|
4
|
+
# ID TYPES
|
5
|
+
# 02: NIF-IVA
|
6
|
+
# 03: Pasaporte
|
7
|
+
# 04: Documento oficial de identificacion expedido por el pais o territorio de residencia
|
8
|
+
# 05: Certificado de residencia
|
9
|
+
# 06: Otro documento probatorio
|
10
|
+
|
11
|
+
def initialize(args = {})
|
12
|
+
@receiver_country = args[:receiver_country]
|
13
|
+
@receiver_nif = args[:receiver_nif]
|
14
|
+
@receiver_name = args[:receiver_name]
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_xml(node)
|
18
|
+
node = Nokogiri::XML::Builder.new if node.nil?
|
19
|
+
node.Destinatarios do |xml|
|
20
|
+
xml.IDDestinatario do
|
21
|
+
if @receiver_country == 'ES'
|
22
|
+
xml.NIF @receiver_nif
|
23
|
+
else
|
24
|
+
xml.IDOtro do
|
25
|
+
xml.CodigoPais @receiver_country
|
26
|
+
xml.IDType '04'
|
27
|
+
xml.ID @receiver_nif
|
28
|
+
end
|
29
|
+
end
|
30
|
+
xml.ApellidosNombreRazonSocial @receiver_name
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
module Nodes
|
3
|
+
class Software
|
4
|
+
def build_xml(node)
|
5
|
+
node = Nokogiri::XML::Builder.new if node.nil?
|
6
|
+
node.Software do |xml|
|
7
|
+
xml.LicenciaTBAI Ticketbai.config.license_key
|
8
|
+
xml.EntidadDesarrolladora do
|
9
|
+
xml.NIF Ticketbai.config.developer_company_nif
|
10
|
+
end
|
11
|
+
xml.Nombre Ticketbai.config.app_name
|
12
|
+
xml.Version Ticketbai.config.app_version
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
class Operation
|
3
|
+
attr_accessor :company_cert
|
4
|
+
|
5
|
+
def initialize(args)
|
6
|
+
self.class::ATTRIBUTES.each do |p|
|
7
|
+
instance_variable_set "@#{p}", args[p]
|
8
|
+
end
|
9
|
+
|
10
|
+
@company_cert = Ticketbai.config.certificates[args[:company_cert]]
|
11
|
+
end
|
12
|
+
|
13
|
+
###
|
14
|
+
# Creates the Ticketbai operation by doing the following:
|
15
|
+
# 1. Build the document
|
16
|
+
# 2. Sign the document
|
17
|
+
# 3. Validate the signed document
|
18
|
+
#
|
19
|
+
# @return [Hash] xml_doc: The signed document. signature_value: The first 100 characters of the signature value
|
20
|
+
###
|
21
|
+
def create
|
22
|
+
document = build_document
|
23
|
+
|
24
|
+
signed_document = sign_document(document)
|
25
|
+
|
26
|
+
validate_document(signed_document)
|
27
|
+
|
28
|
+
signature_value = read_signature_value(signed_document)[0..99]
|
29
|
+
|
30
|
+
{ xml_doc: signed_document, signature_value: signature_value }
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_document
|
34
|
+
raise NotImplementedError, 'Must implement this method'
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def modify_xml_root_name(builder)
|
40
|
+
builder.doc.root.name = self.class::ROOT_NAME
|
41
|
+
end
|
42
|
+
|
43
|
+
def decoded_cert
|
44
|
+
Base64.decode64(@company_cert[:cert])
|
45
|
+
end
|
46
|
+
|
47
|
+
def sign_document(document)
|
48
|
+
Signer.new({ xml: document.to_xml, certificate: decoded_cert, key: @company_cert[:key] }).sign
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate_document(document)
|
52
|
+
DocumentValidator.new(document: document, validator_type: self.class::OPERATION_NAME).validate
|
53
|
+
end
|
54
|
+
|
55
|
+
def read_signature_value(document)
|
56
|
+
Nokogiri::XML(document).children.last.children.last.children[1].children.first.text
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
module Operations
|
3
|
+
class Annulment < Operation
|
4
|
+
OPERATION_NAME = :annulment
|
5
|
+
|
6
|
+
ATTRIBUTES = %i[issuing_company_nif issuing_company_name invoice_serial invoice_number invoice_date].freeze
|
7
|
+
|
8
|
+
attr_accessor(*ATTRIBUTES)
|
9
|
+
|
10
|
+
###
|
11
|
+
# @param [String] issuing_company_nif NIF of the taxpayer's company
|
12
|
+
# @param [String] issuing_company_name Name of the taxpayer's company
|
13
|
+
# @param [String] invoice_serial Invoice's serial number
|
14
|
+
# @param [String] invoice_number Invoice's number
|
15
|
+
# @param [String] invoice_date Invoices emission date (Format: d-m-Y)
|
16
|
+
# @param [String] company_cert The name of the certificate to be used for issuance
|
17
|
+
###
|
18
|
+
def initialize(**args)
|
19
|
+
super(args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_document
|
23
|
+
@issuer = Ticketbai::Nodes::Issuer.new(issuing_company_nif: @issuing_company_nif, issuing_company_name: @issuing_company_name)
|
24
|
+
|
25
|
+
@invoice_header = Ticketbai::Nodes::InvoiceHeader.new(
|
26
|
+
invoice_serial: @invoice_serial,
|
27
|
+
invoice_number: @invoice_number,
|
28
|
+
invoice_date: @invoice_date
|
29
|
+
)
|
30
|
+
|
31
|
+
@software = Ticketbai::Nodes::Software.new
|
32
|
+
|
33
|
+
Ticketbai::Documents::Annulment.new(
|
34
|
+
issuer: @issuer,
|
35
|
+
invoice_header: @invoice_header,
|
36
|
+
software: @software
|
37
|
+
).create
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Ticketbai
|
2
|
+
module Operations
|
3
|
+
class Issuance < Operation
|
4
|
+
DEFAULT_VAT_KEY = '01'.freeze
|
5
|
+
OPERATION_NAME = :issuance
|
6
|
+
|
7
|
+
ATTRIBUTES = %i[company_cert issuing_company_nif issuing_company_name receiver_nif receiver_name receiver_country receiver_in_eu invoice_serial
|
8
|
+
invoice_number invoice_date invoice_time simplified_invoice invoice_description invoice_total invoice_vat_key
|
9
|
+
invoice_amount invoice_vat invoice_vat_total prev_invoice_number prev_invoice_signature prev_invoice_date].freeze
|
10
|
+
|
11
|
+
attr_accessor(*ATTRIBUTES)
|
12
|
+
|
13
|
+
###
|
14
|
+
# @param [String] issuing_company_nif NIF of the taxpayer's company
|
15
|
+
# @param [String] issuing_company_name Name of the taxpayer's company
|
16
|
+
# @param [String] receiver_nif Spanish NIF or official identification document in case of being another country
|
17
|
+
# @param [String] receiver_name Name of the receiver
|
18
|
+
# @param [String] receiver_country Country code of the receiver (Ex: ES|DE)
|
19
|
+
# @param [Boolean] receiver_in_eu The receiver residence is in Europe?
|
20
|
+
# @param [String] invoice_serial Invoices serial number
|
21
|
+
# @param [String] invoice_number Invoices number
|
22
|
+
# @param [String] invoice_date Invoices emission date (Format: d-m-Y)
|
23
|
+
# @param [String] invoice_time Invoices emission time (Format: H:M:S)
|
24
|
+
# @param [Boolean] simplified_invoice is a simplified invoice?
|
25
|
+
# @param [String] invoice_description Invoices description text
|
26
|
+
# @param [String] invoice_total Total invoice amount
|
27
|
+
# @param [String] invoice_vat_key Key of the VAT regime
|
28
|
+
# @param [String] invoice_amount Taxable base of the invoice
|
29
|
+
# @param [Float] invoice_vat Invoice VAT (Ex: 21.0)
|
30
|
+
# @param [String] invoice_vat_total Invoices number
|
31
|
+
# @param [String] prev_invoice_number Number of the last invoice generated
|
32
|
+
# @param [String] prev_invoice_signature Signature of the last invoice generated
|
33
|
+
# @param [String] prev_invoice_date Emission date of the last invoice generated
|
34
|
+
# @param [Symbol] company_cert: The name of the certificate to be used for issuance
|
35
|
+
###
|
36
|
+
def initialize(**args)
|
37
|
+
args[:receiver_nif]&.strip!
|
38
|
+
args[:receiver_country] = args[:receiver_country]&.upcase || 'ES'
|
39
|
+
|
40
|
+
super(args)
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_document
|
44
|
+
@issuer = Ticketbai::Nodes::Issuer.new(issuing_company_nif: @issuing_company_nif, issuing_company_name: @issuing_company_name)
|
45
|
+
|
46
|
+
if @receiver_nif && @receiver_name
|
47
|
+
@receiver = Ticketbai::Nodes::Receiver.new(receiver_country: @receiver_country, receiver_nif: @receiver_nif, receiver_name: @receiver_name)
|
48
|
+
end
|
49
|
+
|
50
|
+
@invoice_header = Ticketbai::Nodes::InvoiceHeader.new(
|
51
|
+
invoice_serial: @invoice_serial,
|
52
|
+
invoice_number: @invoice_number,
|
53
|
+
invoice_date: @invoice_date,
|
54
|
+
invoice_time: @invoice_time,
|
55
|
+
simplified_invoice: @simplified_invoice
|
56
|
+
)
|
57
|
+
@invoice_data = Ticketbai::Nodes::InvoiceData.new(
|
58
|
+
invoice_description: @invoice_description,
|
59
|
+
invoice_total: @invoice_total,
|
60
|
+
invoice_vat_key: @invoice_vat_key
|
61
|
+
)
|
62
|
+
@breakdown_type = Ticketbai::Nodes::BreakdownType.new(
|
63
|
+
receiver_country: @receiver_country,
|
64
|
+
invoice_amount: @invoice_amount,
|
65
|
+
invoice_vat: @invoice_vat,
|
66
|
+
invoice_vat_total: @invoice_vat_total,
|
67
|
+
receiver_in_eu: @receiver_in_eu,
|
68
|
+
simplified_invoice: @simplified_invoice
|
69
|
+
)
|
70
|
+
if @prev_invoice_number && @prev_invoice_date && @prev_invoice_signature
|
71
|
+
@invoice_chaining = Ticketbai::Nodes::InvoiceChaining.new(
|
72
|
+
prev_invoice_serial: @prev_invoice_serial,
|
73
|
+
prev_invoice_number: @prev_invoice_number,
|
74
|
+
prev_invoice_date: @prev_invoice_date,
|
75
|
+
prev_invoice_signature: @prev_invoice_signature
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
@software = Ticketbai::Nodes::Software.new
|
80
|
+
|
81
|
+
Ticketbai::Documents::Issuance.new(
|
82
|
+
issuer: @issuer,
|
83
|
+
receiver: @receiver,
|
84
|
+
invoice_header: @invoice_header,
|
85
|
+
invoice_data: @invoice_data,
|
86
|
+
breakdown_type: @breakdown_type,
|
87
|
+
invoice_chaining: @invoice_chaining,
|
88
|
+
software: @software
|
89
|
+
).create
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|