buho_cfdi 0.1.12
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/.coveralls.yml +1 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +80 -0
- data/LICENSE.txt +21 -0
- data/README.md +185 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/buho_cfdi.gemspec +45 -0
- data/dump.rdb +0 -0
- data/lib/buho_cfdi.rb +47 -0
- data/lib/buho_cfdi/base_node.rb +89 -0
- data/lib/buho_cfdi/certificate.rb +27 -0
- data/lib/buho_cfdi/i18n.rb +9 -0
- data/lib/buho_cfdi/key.rb +37 -0
- data/lib/buho_cfdi/locale/es.yml +83 -0
- data/lib/buho_cfdi/nodes/cfdi_related.rb +9 -0
- data/lib/buho_cfdi/nodes/collection.rb +21 -0
- data/lib/buho_cfdi/nodes/concept.rb +24 -0
- data/lib/buho_cfdi/nodes/customs_information.rb +9 -0
- data/lib/buho_cfdi/nodes/detained.rb +10 -0
- data/lib/buho_cfdi/nodes/issuer.rb +11 -0
- data/lib/buho_cfdi/nodes/part.rb +19 -0
- data/lib/buho_cfdi/nodes/property_account.rb +9 -0
- data/lib/buho_cfdi/nodes/receipt.rb +45 -0
- data/lib/buho_cfdi/nodes/receiver.rb +13 -0
- data/lib/buho_cfdi/nodes/related.rb +9 -0
- data/lib/buho_cfdi/nodes/tax_detained.rb +20 -0
- data/lib/buho_cfdi/nodes/tax_transferred.rb +17 -0
- data/lib/buho_cfdi/nodes/taxes.rb +8 -0
- data/lib/buho_cfdi/nodes/transferred.rb +17 -0
- data/lib/buho_cfdi/params_builder.rb +125 -0
- data/lib/buho_cfdi/params_example.json +106 -0
- data/lib/buho_cfdi/strategy.rb +76 -0
- data/lib/buho_cfdi/version.rb +3 -0
- metadata +178 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require 'active_model'
|
|
2
|
+
|
|
3
|
+
module BuhoCfdi
|
|
4
|
+
class BaseNode
|
|
5
|
+
include ActiveModel::Validations
|
|
6
|
+
attr_accessor :node_hash
|
|
7
|
+
|
|
8
|
+
def initialize(params = {})
|
|
9
|
+
@node_hash = {}
|
|
10
|
+
|
|
11
|
+
params.each_pair do |key, value|
|
|
12
|
+
if respond_to? key
|
|
13
|
+
instance_variable_set("@#{key}", value)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_hash
|
|
19
|
+
self.class.params.each do |param|
|
|
20
|
+
node_hash[define_locale(param)] = send(param) unless send(param).blank?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
node_hash
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def build_child!(klass, params)
|
|
27
|
+
node = klass.new(params)
|
|
28
|
+
|
|
29
|
+
if node.valid?
|
|
30
|
+
self.class.send(:attr_reader, klass.name.downcase.sub( '::', '_'))
|
|
31
|
+
instance_variable_set("@#{klass.name.downcase.sub( '::', '_')}", node)
|
|
32
|
+
else
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def build_child(params)
|
|
38
|
+
node = self.class.new(params)
|
|
39
|
+
|
|
40
|
+
if node.valid?
|
|
41
|
+
self.class.send(:attr_reader, self.class.name.downcase.sub( '::', '_'))
|
|
42
|
+
instance_variable_set("@#{self.class.name.downcase.sub( '::', '_')}", self.class.new)
|
|
43
|
+
else
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def build_children(klass)
|
|
49
|
+
self.class.send(:attr_reader, klass.name.downcase.sub( '::', '_'))
|
|
50
|
+
|
|
51
|
+
instance_variable_set(
|
|
52
|
+
"@#{klass.name.downcase.sub( '::', '_')}",
|
|
53
|
+
::Nodes::Collection.new(klass)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def define_locale(param)
|
|
62
|
+
I18n.t("nodes.#{self.class.name.demodulize.underscore}.#{param}")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class << self
|
|
66
|
+
def build_child(params)
|
|
67
|
+
node = self.new(params)
|
|
68
|
+
|
|
69
|
+
if node.valid?
|
|
70
|
+
self.class.send(:attr_reader, self.name.downcase.sub( '::', '_'))
|
|
71
|
+
instance_variable_set("@#{self.name.downcase.sub( '::', '_')}", self.new)
|
|
72
|
+
else
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def attr_accessor(*args)
|
|
78
|
+
@params ||= []
|
|
79
|
+
@params.concat(args)
|
|
80
|
+
|
|
81
|
+
super(*args)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def params
|
|
85
|
+
@params
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module BuhoCfdi
|
|
2
|
+
require 'openssl'
|
|
3
|
+
|
|
4
|
+
class Certificate
|
|
5
|
+
|
|
6
|
+
attr_reader :certificate_number, :certificate_value
|
|
7
|
+
|
|
8
|
+
def initialize (file)
|
|
9
|
+
if file.is_a? String
|
|
10
|
+
file = file
|
|
11
|
+
end
|
|
12
|
+
certificate = OpenSSL::X509::Certificate.new(file)
|
|
13
|
+
@certificate_number = '';
|
|
14
|
+
certificate.serial.to_s(16).scan(/.{2}/).each { |v| @certificate_number += v[1]; }
|
|
15
|
+
@certificate_value = certificate.to_s.gsub(/^-.+/, '').gsub(/\n/, '')
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def certificate(xml)
|
|
20
|
+
xml = Nokogiri::XML(xml)
|
|
21
|
+
xml.at_css('cfdi|Comprobante').set_attribute('NoCertificado',@certificate_number)
|
|
22
|
+
xml.at_css('cfdi|Comprobante').set_attribute('Certificado',@certificate_value)
|
|
23
|
+
xml.to_xml
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module BuhoCfdi
|
|
2
|
+
require 'openssl'
|
|
3
|
+
|
|
4
|
+
class Key
|
|
5
|
+
|
|
6
|
+
attr_reader :private_key
|
|
7
|
+
|
|
8
|
+
def initialize(file, password=nil)
|
|
9
|
+
if file.is_a? String
|
|
10
|
+
@private_key = file
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def seal(xml)
|
|
15
|
+
xml = Nokogiri::XML(xml)
|
|
16
|
+
signature = generate_seal(xml)
|
|
17
|
+
xml.at_css('cfdi|Comprobante').set_attribute('Sello',signature)
|
|
18
|
+
xml.to_xml
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def generate_seal(xml)
|
|
22
|
+
chain = original_chain(xml)
|
|
23
|
+
key = OpenSSL::PKey::RSA.new(@private_key)
|
|
24
|
+
digester = OpenSSL::Digest::SHA256.new
|
|
25
|
+
signature = key.sign(digester, chain)
|
|
26
|
+
signature = Base64.strict_encode64(signature)
|
|
27
|
+
signature
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def original_chain(invoice)
|
|
31
|
+
invoice = invoice
|
|
32
|
+
xslt = Nokogiri::XSLT(File.read('storage/xslt/cadenaoriginal_3_3.xslt'))
|
|
33
|
+
chain = xslt.transform(invoice)
|
|
34
|
+
chain.text.gsub("\n","")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
es:
|
|
2
|
+
nodes:
|
|
3
|
+
receipt:
|
|
4
|
+
version: Version
|
|
5
|
+
serie: Serie
|
|
6
|
+
folio_number: Folio
|
|
7
|
+
date: Fecha
|
|
8
|
+
stamp: Sello
|
|
9
|
+
payment_way: FormaPago
|
|
10
|
+
certificate_number: NoCertificado
|
|
11
|
+
certificate: Certificado
|
|
12
|
+
payment_conditions: CondicionesDePago
|
|
13
|
+
subtotal: SubTotal
|
|
14
|
+
discount: Descuento
|
|
15
|
+
currency: Moneda
|
|
16
|
+
exchange_rate: TipoCambio
|
|
17
|
+
total: Total
|
|
18
|
+
type_of_receipt: TipoDeComprobante
|
|
19
|
+
payment_method: MetodoPago
|
|
20
|
+
expedition_place: LugarExpedicion
|
|
21
|
+
confirmation: Confirmacion
|
|
22
|
+
cfdi_related:
|
|
23
|
+
relationship_type: TipoRelacion
|
|
24
|
+
related:
|
|
25
|
+
uuid: UUID
|
|
26
|
+
issuer:
|
|
27
|
+
rfc: Rfc
|
|
28
|
+
name: Nombre
|
|
29
|
+
fiscal_regime: RegimenFiscal
|
|
30
|
+
receiver:
|
|
31
|
+
rfc: Rfc
|
|
32
|
+
name: Nombre
|
|
33
|
+
tax_residency: ResidenciaFiscal
|
|
34
|
+
tax_identity_registration_number: NumRegIdTrib
|
|
35
|
+
cfdi_usage: UsoCFDI
|
|
36
|
+
concept:
|
|
37
|
+
prod_or_svc_key: ClaveProdServ
|
|
38
|
+
identification_number: NoIdentificacion
|
|
39
|
+
quantity: Cantidad
|
|
40
|
+
unit_measure_key: ClaveUnidad
|
|
41
|
+
unit_measure: Unidad
|
|
42
|
+
description: Descripcion
|
|
43
|
+
unit_value: ValorUnitario
|
|
44
|
+
amount: Importe
|
|
45
|
+
discount: Descuento
|
|
46
|
+
tax_transferred:
|
|
47
|
+
base: Base
|
|
48
|
+
tax: Impuesto
|
|
49
|
+
factor_type: TipoFactor
|
|
50
|
+
rate_or_fee: TasaOCuota
|
|
51
|
+
import: Importe
|
|
52
|
+
tax_detained:
|
|
53
|
+
base: Base
|
|
54
|
+
tax: Impuesto
|
|
55
|
+
factor_type: TipoFactor
|
|
56
|
+
rate_or_fee: TasaOCuota
|
|
57
|
+
import: Importe
|
|
58
|
+
customs_information: # InformacionAduanera
|
|
59
|
+
requirement_number: NumeroPedimento
|
|
60
|
+
property_account: # CuentaPredial
|
|
61
|
+
number: Numero
|
|
62
|
+
part: # Parte
|
|
63
|
+
prod_or_svc_key: ClaveProdServ
|
|
64
|
+
identification_number: NoIdentificacion
|
|
65
|
+
quantity: Cantidad
|
|
66
|
+
unit_measure: Unidad
|
|
67
|
+
description: Descripcion
|
|
68
|
+
unit_value: ValorUnitario
|
|
69
|
+
amount: Importe
|
|
70
|
+
taxes: # Impuestos
|
|
71
|
+
total_taxes_transferred: TotalImpuestosTrasladados
|
|
72
|
+
total_taxes_detained: TotalImpuestosRetenidos
|
|
73
|
+
detained:
|
|
74
|
+
tax: Impuesto
|
|
75
|
+
import: Importe
|
|
76
|
+
transferred:
|
|
77
|
+
tax: Impuesto
|
|
78
|
+
factor_type: TipoFactor
|
|
79
|
+
rate_or_fee: TasaOCuota
|
|
80
|
+
import: Importe
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Nodes
|
|
2
|
+
class Collection
|
|
3
|
+
attr_accessor :_collection, :klass
|
|
4
|
+
|
|
5
|
+
def initialize(klass)
|
|
6
|
+
@klass = klass
|
|
7
|
+
@_collection = []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def all
|
|
11
|
+
_collection
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def add(params)
|
|
15
|
+
instance = klass.new(params)
|
|
16
|
+
_collection << instance
|
|
17
|
+
|
|
18
|
+
instance
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Nodes
|
|
2
|
+
class Concept < ::BuhoCfdi::BaseNode
|
|
3
|
+
attr_accessor(
|
|
4
|
+
:prod_or_svc_key, # ClaveProdServ - required
|
|
5
|
+
:identification_number, # NoIdentificacion - optional
|
|
6
|
+
:quantity, # Cantidad - required
|
|
7
|
+
:unit_measure_key, # ClaveUnidad - required
|
|
8
|
+
:unit_measure, # Unidad - optional
|
|
9
|
+
:description, # Descripcion - required
|
|
10
|
+
:unit_value, # ValorUnitario - required
|
|
11
|
+
:amount, # Importe - required
|
|
12
|
+
:discount # Descuento - optional
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
validates_presence_of(
|
|
16
|
+
:prod_or_svc_key,
|
|
17
|
+
:quantity,
|
|
18
|
+
:unit_measure_key,
|
|
19
|
+
:description,
|
|
20
|
+
:unit_value,
|
|
21
|
+
:amount
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Nodes
|
|
2
|
+
class Part < ::BuhoCfdi::BaseNode
|
|
3
|
+
attr_accessor(
|
|
4
|
+
:prod_or_svc_key, # ClaveProdServ - required
|
|
5
|
+
:identification_number, # NoIdentificacion - optional
|
|
6
|
+
:quantity, # Cantidad - required
|
|
7
|
+
:unit_measure, # Unidad - optional
|
|
8
|
+
:description, # Descripcion - required
|
|
9
|
+
:unit_value, # ValorUnitario - optional
|
|
10
|
+
:amount # Importe - optional
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
validates_presence_of(
|
|
14
|
+
:prod_or_svc_key,
|
|
15
|
+
:quantity,
|
|
16
|
+
:description
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Nodes
|
|
2
|
+
class Receipt < ::BuhoCfdi::BaseNode
|
|
3
|
+
DEFAULT = {
|
|
4
|
+
'xsi:schemaLocation' => 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd',
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
attr_accessor(
|
|
8
|
+
:version, # Version - required
|
|
9
|
+
:serie, # Serie - optional
|
|
10
|
+
:folio_number, # Folio - optional
|
|
11
|
+
:date, # Fecha - required
|
|
12
|
+
:stamp, # Sello - required
|
|
13
|
+
:payment_way, # FormaPago - optional
|
|
14
|
+
:certificate_number, # NoCertificado - required
|
|
15
|
+
:certificate, # Certificado - required
|
|
16
|
+
:payment_conditions, # CondicionesDePago - optional
|
|
17
|
+
:subtotal, # SubTotal - required
|
|
18
|
+
:discount, # Descuento - optional
|
|
19
|
+
:currency, # Moneda - required
|
|
20
|
+
:exchange_rate, # TipoCambio - optional
|
|
21
|
+
:total, # Total - required
|
|
22
|
+
:type_of_receipt, # TipoDeComprobante - required
|
|
23
|
+
:payment_method, # MetodoPago - optional
|
|
24
|
+
:expedition_place, # LugarExpedicion - required
|
|
25
|
+
:confirmation # Confirmacion - optional
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
validates_presence_of(
|
|
29
|
+
:version,
|
|
30
|
+
:date,
|
|
31
|
+
:stamp,
|
|
32
|
+
:certificate_number,
|
|
33
|
+
:certificate,
|
|
34
|
+
:subtotal,
|
|
35
|
+
:currency,
|
|
36
|
+
:total,
|
|
37
|
+
:type_of_receipt,
|
|
38
|
+
:expedition_place
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def to_hash
|
|
42
|
+
super.merge! DEFAULT
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Nodes
|
|
2
|
+
class Receiver < ::BuhoCfdi::BaseNode
|
|
3
|
+
attr_accessor(
|
|
4
|
+
:rfc, # Rfc - required
|
|
5
|
+
:name, # Nombre - optional
|
|
6
|
+
:tax_residency, # ResidenciaFiscal - optional
|
|
7
|
+
:tax_identity_registration_number, # NumRegIdTrib - optional
|
|
8
|
+
:cfdi_usage # UsoCFDI - required
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
validates_presence_of :rfc, :cfdi_usage
|
|
12
|
+
end
|
|
13
|
+
end
|