cfdi 0.0.2
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/.gitignore +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +31 -0
- data/Rakefile +5 -0
- data/cfdi.gemspec +25 -0
- data/cfdi.rb +20 -0
- data/examples/crear_factura.rb +78 -0
- data/examples/data/_empty +0 -0
- data/lib/certificado.rb +36 -0
- data/lib/comprobante.rb +175 -0
- data/lib/comun.rb +42 -0
- data/lib/concepto.rb +26 -0
- data/lib/entidad.rb +34 -0
- data/lib/key.rb +22 -0
- data/readme.md +37 -0
- metadata +74 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9f817ca5a0b899f9d7eb858a4093065851a97076
|
4
|
+
data.tar.gz: f3ec315d75256251637af8ba492042dbd00bd322
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4818dd7e9c79070fc32fc3ba853898ec2c48430e4f82718673a9509a4a533141d3f6f2e19001ed0d7ffcf492ef27dedc38b29df9891c80674133ad0610633fdb
|
7
|
+
data.tar.gz: 875fdf5ec3adb21f7a49b16f5cf30abf654a5a9dccc1704f601844791b906e418dc07209dfaf1a4cb725323dcaef0bef393155cf2425e69e4acd8c771420000f
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
2
|
+
Version 2, December 2004
|
3
|
+
|
4
|
+
(C) 2013 Roberto Hidalgo <un@rob.mx>, Aquellos listados en CONTRIBUTORS
|
5
|
+
|
6
|
+
Everyone is permitted to copy and distribute verbatim or modified
|
7
|
+
copies of this license document, and changing it is allowed as long
|
8
|
+
as the name is changed.
|
9
|
+
|
10
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
11
|
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
12
|
+
|
13
|
+
0. You just DO WHAT THE FUCK YOU WANT TO.
|
14
|
+
|
15
|
+
|
16
|
+
----
|
17
|
+
Traducción:
|
18
|
+
|
19
|
+
LICENCIA PÚBLICA HAZ LO QUE TE DE TU CHINGADA GANA
|
20
|
+
Version 2, Diciembre 2004
|
21
|
+
|
22
|
+
(C) 2013 Roberto Hidalgo <un@rob.mx>, Aquellos listados en CONTRIBUTORS
|
23
|
+
|
24
|
+
Se permite la copia y distribución textual o con modificaciones de
|
25
|
+
este documento de licencia, y los cambios son permitidos siempre y
|
26
|
+
cuando el nombre sea cambiado.
|
27
|
+
|
28
|
+
LICENCIA PÚBLICA HAZ LO QUE TE DE TU CHINGADA GANA
|
29
|
+
TÉRMINOS Y CONDICIONES PARA COPIAR, DISTRIBUIR Y MODIFICAR ESTE SOFTWARE
|
30
|
+
|
31
|
+
0. Solamente HAZ LO QUE TE DE TU CHINGADA GANA.
|
data/Rakefile
ADDED
data/cfdi.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require './cfdi.rb'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "cfdi"
|
8
|
+
gem.version = CFDI::VERSION
|
9
|
+
gem.authors = ["Roberto Hidalgo"]
|
10
|
+
gem.email = ["un@rob.mx"]
|
11
|
+
gem.description = "Comprobantes fiscales digitales por internet"
|
12
|
+
gem.summary = "Digitales!! por Internet!!"
|
13
|
+
gem.homepage = "https://github.com/unRob/cfdi"
|
14
|
+
gem.licenses = ['WTFPL', 'GPLv2']
|
15
|
+
gem.has_rdoc = true
|
16
|
+
|
17
|
+
gem.files = `git ls-files`.split($/)
|
18
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
19
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
|
+
gem.require_paths = ["lib"]
|
21
|
+
|
22
|
+
|
23
|
+
gem.add_runtime_dependency 'nokogiri'
|
24
|
+
|
25
|
+
end
|
data/cfdi.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
gem 'nokogiri', '< 1.6', '>= 1.4.0'
|
3
|
+
|
4
|
+
require_relative 'lib/comun.rb'
|
5
|
+
require_relative 'lib/comprobante.rb'
|
6
|
+
require_relative 'lib/entidad.rb'
|
7
|
+
require_relative 'lib/concepto.rb'
|
8
|
+
require_relative 'lib/certificado.rb'
|
9
|
+
require_relative 'lib/key.rb'
|
10
|
+
|
11
|
+
module CFDI
|
12
|
+
|
13
|
+
require 'nokogiri'
|
14
|
+
require 'time'
|
15
|
+
require 'openssl'
|
16
|
+
require 'base64'
|
17
|
+
|
18
|
+
VERSION = '0.0.2'
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require_relative '../cfdi.rb'
|
2
|
+
require 'json'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
# el archivo .cer, tal cual
|
6
|
+
certificado = CFDI::Certificado.new './cfdi/examples/data/cert.cer'
|
7
|
+
# la llave en formato pem, porque no encontré como usar OpenSSL con llaves tipo PKCS8
|
8
|
+
# Esta se convierte de un archivo .key con:
|
9
|
+
# openssl pkcs8 -inform DER -in someKey.key -passin pass:somePassword -out key.pem
|
10
|
+
llave = CFDI::Key.new './cfdi/examples/data/key.pem', 'somePassword'
|
11
|
+
|
12
|
+
# Así instanciamos el comprobante nuevo
|
13
|
+
factura = CFDI::Comprobante.new ({
|
14
|
+
folio: 1,
|
15
|
+
serie: 'A',
|
16
|
+
fecha: Time.now,
|
17
|
+
formaDePago: 'PAGO EN UNA SOLA EXHIBICION',
|
18
|
+
condicionesDePago: 'Sera marcada como pagada en cuanto el receptor haya cubierto el pago.',
|
19
|
+
metodoDePago: 'Transferencia Bancaria',
|
20
|
+
lugarExpedicion: 'Nutopia, Nutopia'
|
21
|
+
})
|
22
|
+
|
23
|
+
# Esto es un domicilio casi completo
|
24
|
+
domicilioEmisor = CFDI::Domicilio.new({
|
25
|
+
calle: 'Calle Feliz',
|
26
|
+
noExterior: '42',
|
27
|
+
noInterior: '314',
|
28
|
+
colonia: 'Centro',
|
29
|
+
localidad: 'No se que sea esto, pero va',
|
30
|
+
referencia: 'Sin Referencia',
|
31
|
+
municipio: 'Nutopía',
|
32
|
+
estado: 'Nutopía',
|
33
|
+
pais: 'Nutopía',
|
34
|
+
codigoPostal: '31415'
|
35
|
+
})
|
36
|
+
|
37
|
+
# y esto es una persona fiscal
|
38
|
+
factura.emisor = CFDI::Entidad.new({
|
39
|
+
rfc: 'XAXX010101000',
|
40
|
+
nombre: 'Me cago en sus estándares S.A. de C.V.',
|
41
|
+
domicilioFiscal: domicilioEmisor,
|
42
|
+
expedidoEn: domicilioEmisor,
|
43
|
+
regimenFiscal: 'Pruebas Fiscales'
|
44
|
+
})
|
45
|
+
|
46
|
+
# misma mierda
|
47
|
+
domicilioReceptor = CFDI::Domicilio.new({
|
48
|
+
referencia: 'Sin Referencia',
|
49
|
+
estado: 'Nutopía',
|
50
|
+
pais: 'Nutopía'
|
51
|
+
})
|
52
|
+
factura.receptor = CFDI::Entidad.new({rfc: 'XAXX010101000', nombre: 'El SAT apesta S. de R.L.', domicilioFiscal: domicilioReceptor})
|
53
|
+
|
54
|
+
|
55
|
+
# Así agregamos conceptos, en este caso, 2 Kg de Verga
|
56
|
+
factura.conceptos << CFDI::Concepto.new({
|
57
|
+
cantidad: 2,
|
58
|
+
unidad: 'Kilos',
|
59
|
+
noIdentificacion: 'KDV',
|
60
|
+
descripcion: 'Verga',
|
61
|
+
valorUnitario:5500.00 #el importe se calcula solo
|
62
|
+
})
|
63
|
+
|
64
|
+
# Todavía no agarro bien el pedo sobre como salen los impuestos, pull request?
|
65
|
+
factura.impuestos << {impuesto: 'IVA'}
|
66
|
+
|
67
|
+
# Esto hace que se le agregue al comprobante el certificado y su número de serie (noCertificado)
|
68
|
+
certificado.certifica factura
|
69
|
+
|
70
|
+
# Para mandarla a un PAC, necesitamos sellarla, y esto lo hace agregando el sello
|
71
|
+
# La cadena original que generamos sólo tiene los datos que u
|
72
|
+
llave.sella factura
|
73
|
+
|
74
|
+
# Esto genera la factura como xml
|
75
|
+
puts factura.to_xml
|
76
|
+
|
77
|
+
# Esto nos da un hash con todo lo que pusimos
|
78
|
+
puts JSON.pretty_generate factura.to_h
|
File without changes
|
data/lib/certificado.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module CFDI
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
class Certificado < OpenSSL::X509::Certificate
|
6
|
+
|
7
|
+
attr_reader :noCertificado, :data
|
8
|
+
|
9
|
+
def initialize (file)
|
10
|
+
|
11
|
+
if file.is_a? String
|
12
|
+
file = File.read(file)
|
13
|
+
end
|
14
|
+
|
15
|
+
super file
|
16
|
+
|
17
|
+
@noCertificado = '';
|
18
|
+
# Normalmente son strings de tipo:
|
19
|
+
# 3230303031303030303030323030303030323933
|
20
|
+
# por eso sólo tomamos cada segundo dígito
|
21
|
+
self.serial.to_s(16).scan(/.{2}/).each {|v| @noCertificado += v[1]; }
|
22
|
+
@data = self.to_s.gsub(/^-.+/, '').gsub(/\n/, '')
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def certifica factura
|
27
|
+
|
28
|
+
factura.noCertificado = @noCertificado
|
29
|
+
factura.certificado = @data
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/comprobante.rb
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
module CFDI
|
2
|
+
class Comprobante
|
3
|
+
|
4
|
+
@@datosCadena = [:version, :fecha, :tipoDeComprobante, :formaDePago, :condicionesDePago, :subTotal, :moneda, :total, :metodoDePago, :lugarExpedicion]
|
5
|
+
@@data = @@datosCadena+[:emisor, :receptor, :conceptos, :serie, :folio, :sello, :noCertificado, :certificado, :impuestos]
|
6
|
+
attr_accessor *@@data
|
7
|
+
|
8
|
+
@@options = {
|
9
|
+
tasa: 0.16,
|
10
|
+
defaults: {
|
11
|
+
moneda: 'pesos',
|
12
|
+
version: '3.2',
|
13
|
+
subTotal: 0.0,
|
14
|
+
conceptos: [],
|
15
|
+
impuestos: [],
|
16
|
+
tipoDeComprobante: 'ingreso'
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
def configure (options)
|
21
|
+
@@options.merge! options
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize (data={}, options={})
|
25
|
+
data = @@options[:defaults].merge data
|
26
|
+
@opciones = @@options.merge options
|
27
|
+
data.each do |k,v|
|
28
|
+
self.send "#{k}=", v
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def subTotal
|
33
|
+
ret = 0
|
34
|
+
@conceptos.each do |c|
|
35
|
+
ret += c.importe
|
36
|
+
end
|
37
|
+
ret
|
38
|
+
end
|
39
|
+
|
40
|
+
def total
|
41
|
+
self.subTotal+(self.subTotal*@opciones[:tasa])
|
42
|
+
end
|
43
|
+
|
44
|
+
def fecha= fecha
|
45
|
+
fecha = fecha.strftime('%FT%R:%S') unless fecha.is_a? String
|
46
|
+
@fecha = fecha
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_xml
|
50
|
+
ns = {
|
51
|
+
'xmlns:cfdi' => "http://www.sat.gob.mx/cfd/3",
|
52
|
+
'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
|
53
|
+
'xsi:schemaLocation' => "http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd",
|
54
|
+
version: @version,
|
55
|
+
folio: @folio,
|
56
|
+
fecha: @fecha,
|
57
|
+
formaDePago: @formaDePago,
|
58
|
+
condicionesDePago: @condicionesDePago,
|
59
|
+
subTotal: self.subTotal,
|
60
|
+
Moneda: @moneda,
|
61
|
+
total: self.total,
|
62
|
+
metodoDePago: @metodoDePago,
|
63
|
+
tipoDeComprobante: @tipoDeComprobante,
|
64
|
+
LugarExpedicion: @lugarExpedicion,
|
65
|
+
}
|
66
|
+
|
67
|
+
if @noCertificado
|
68
|
+
ns[:noCertificado] = @noCertificado
|
69
|
+
ns[:certificado] = @certificado
|
70
|
+
end
|
71
|
+
|
72
|
+
if @sello
|
73
|
+
ns[:sello] = @sello
|
74
|
+
end
|
75
|
+
|
76
|
+
@builder = Nokogiri::XML::Builder.new do |xml|
|
77
|
+
xml.Comprobante(ns) do
|
78
|
+
ins = xml.doc.root.add_namespace_definition('cfdi', 'http://www.sat.gob.mx/cfd/3')
|
79
|
+
xml.doc.root.namespace = ins
|
80
|
+
|
81
|
+
xml.Emisor(@emisor.ns) {
|
82
|
+
xml.DomicilioFiscal(@emisor.domicilioFiscal.to_h.reject {|k,v| v == nil})
|
83
|
+
xml.ExpedidoEn(@emisor.expedidoEn.to_h.reject {|k,v| v == nil})
|
84
|
+
xml.RegimenFiscal({Regimen: @emisor.regimenFiscal})
|
85
|
+
}
|
86
|
+
xml.Receptor(@receptor.ns) {
|
87
|
+
xml.Domicilio(@receptor.domicilioFiscal.to_h.reject {|k,v| v == nil})
|
88
|
+
}
|
89
|
+
xml.Conceptos {
|
90
|
+
@conceptos.each do |concepto|
|
91
|
+
xml.Concepto(concepto.to_h) {
|
92
|
+
xml.ComplementoConcepto
|
93
|
+
}
|
94
|
+
end
|
95
|
+
}
|
96
|
+
xml.Impuestos({totalImpuestosTrasladados: self.subTotal*@opciones[:tasa]}) {
|
97
|
+
xml.Traslados {
|
98
|
+
@impuestos.each do |impuesto|
|
99
|
+
xml.Traslado({impuesto: impuesto[:impuesto], tasa:@opciones[:tasa]*100.to_i, importe: self.subTotal*@opciones[:tasa]})
|
100
|
+
end
|
101
|
+
}
|
102
|
+
}
|
103
|
+
xml.Complemento
|
104
|
+
end
|
105
|
+
end
|
106
|
+
@builder.to_xml
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_h
|
110
|
+
hash = {}
|
111
|
+
@@data.each do |key|
|
112
|
+
data = deep_to_h send(key)
|
113
|
+
hash[key] = data
|
114
|
+
end
|
115
|
+
|
116
|
+
return hash
|
117
|
+
end
|
118
|
+
|
119
|
+
def cadena_original
|
120
|
+
params = []
|
121
|
+
|
122
|
+
@@datosCadena.each {|key| params.push send(key) }
|
123
|
+
params += @emisor.cadena_original
|
124
|
+
params << @regimen
|
125
|
+
params += @receptor.cadena_original
|
126
|
+
|
127
|
+
@conceptos.each do |concepto|
|
128
|
+
params += concepto.cadena_original
|
129
|
+
end
|
130
|
+
|
131
|
+
@impuestos.each do |traslado|
|
132
|
+
params += [traslado[:impuesto], @opciones[:tasa]*100, self.subTotal*@opciones[:tasa], self.subTotal*@opciones[:tasa]]
|
133
|
+
end
|
134
|
+
|
135
|
+
params.select! { |i| i != nil }
|
136
|
+
params.map! do |elem|
|
137
|
+
if elem.is_a? Float
|
138
|
+
elem = sprintf('%.2f', elem)
|
139
|
+
else
|
140
|
+
elem = elem.to_s
|
141
|
+
end
|
142
|
+
elem
|
143
|
+
end
|
144
|
+
|
145
|
+
return "||#{params.join '|'}||"
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
def deep_to_h value
|
150
|
+
|
151
|
+
if value.is_a? ElementoComprobante
|
152
|
+
original = value.to_h
|
153
|
+
value = {}
|
154
|
+
original.each do |k,v|
|
155
|
+
value[k] = deep_to_h v
|
156
|
+
end
|
157
|
+
|
158
|
+
elsif value.is_a?(Array)
|
159
|
+
value = value.map do |v|
|
160
|
+
deep_to_h v
|
161
|
+
end
|
162
|
+
end
|
163
|
+
value
|
164
|
+
|
165
|
+
#value = value.to_h if value.respond_to? :to_h
|
166
|
+
#if value.each do |vi|
|
167
|
+
# value.map do |k,v|
|
168
|
+
# v = deep_to_h v
|
169
|
+
# end
|
170
|
+
#end
|
171
|
+
value
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
data/lib/comun.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
class Float
|
2
|
+
|
3
|
+
def to_s
|
4
|
+
sprintf('%.2f', self)
|
5
|
+
end
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
module CFDI
|
10
|
+
|
11
|
+
class ElementoComprobante
|
12
|
+
|
13
|
+
def initialize data={}
|
14
|
+
#puts self.class
|
15
|
+
data.each do |k,v|
|
16
|
+
self.instance_variable_set "@#{k}", v
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.data
|
21
|
+
@cadenaOriginal
|
22
|
+
end
|
23
|
+
|
24
|
+
def cadena_original
|
25
|
+
params = []
|
26
|
+
data = {}
|
27
|
+
data = self.class.data
|
28
|
+
# puts self.class.cadenaOriginal
|
29
|
+
|
30
|
+
data.each {|key| params.push instance_variable_get('@'+key.to_s) }
|
31
|
+
return params
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_h
|
35
|
+
Hash[*self.class.data.map { |v|
|
36
|
+
[v, self.send(v)]
|
37
|
+
}.flatten]
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/lib/concepto.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module CFDI
|
2
|
+
|
3
|
+
class Concepto < ElementoComprobante
|
4
|
+
|
5
|
+
@cadenaOriginal = [:cantidad, :unidad, :noIdentificacion, :descripcion, :valorUnitario, :importe]
|
6
|
+
@data = @cadenaOriginal
|
7
|
+
attr_accessor *@cadenaOriginal
|
8
|
+
|
9
|
+
def cadena_original
|
10
|
+
return [
|
11
|
+
@cantidad.to_i,
|
12
|
+
@unidad,
|
13
|
+
@noIdentificacion,
|
14
|
+
@descripcion,
|
15
|
+
@valorUnitario,
|
16
|
+
@importe
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
def importe
|
21
|
+
return @valorUnitario*@cantidad
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/lib/entidad.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module CFDI
|
2
|
+
|
3
|
+
class Entidad < ElementoComprobante
|
4
|
+
@cadenaOriginal = [:rfc, :nombre, :domicilioFiscal, :expedidoEn, :regimenFiscal]
|
5
|
+
@data = @cadenaOriginal
|
6
|
+
attr_accessor *@cadenaOriginal
|
7
|
+
|
8
|
+
def cadena_original
|
9
|
+
expedido = @expedidoEn ? @expedidoEn.cadena_original : nil
|
10
|
+
return [
|
11
|
+
@rfc,
|
12
|
+
@nombre,
|
13
|
+
*@domicilioFiscal.cadena_original,
|
14
|
+
expedido,
|
15
|
+
@regimenFiscal
|
16
|
+
].flatten
|
17
|
+
end
|
18
|
+
|
19
|
+
def ns
|
20
|
+
return ({
|
21
|
+
nombre: @nombre,
|
22
|
+
rfc: @rfc
|
23
|
+
})
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
class Domicilio < ElementoComprobante
|
29
|
+
@cadenaOriginal = [:calle, :noExterior, :noInterior, :colonia, :localidad, :referencia, :municipio, :estado, :pais, :codigoPostal]
|
30
|
+
attr_accessor *@cadenaOriginal
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/lib/key.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module CFDI
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
class Key < OpenSSL::PKey::RSA
|
6
|
+
|
7
|
+
def initialize file, password
|
8
|
+
if file.is_a? String
|
9
|
+
file = File.read(file)
|
10
|
+
end
|
11
|
+
super file, password
|
12
|
+
end
|
13
|
+
|
14
|
+
# sella una factura
|
15
|
+
def sella factura
|
16
|
+
cadena_original = factura.cadena_original
|
17
|
+
factura.sello = Base64::encode64(self.sign(OpenSSL::Digest::SHA1.new, cadena_original)).gsub(/\n/, '')
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/readme.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# CFDI para principiantes en CFDI
|
2
|
+
|
3
|
+
El sistema de generación y sellado de facturas es una patada en los genitales. Este gem pretende ser una bolsa de hielos. Igual va a doler, pero espero que al menos no quede moretón.
|
4
|
+
|
5
|
+
## Instalación
|
6
|
+
|
7
|
+
gem install cfdi
|
8
|
+
|
9
|
+
## Uso
|
10
|
+
|
11
|
+
Puedes ver [crear_factura.rb](examples/crear_factura.rb) para darse una mejor idea, pero acá va un resumen:
|
12
|
+
|
13
|
+
require 'cfdi'
|
14
|
+
factura = CFDI::Comprobante.new
|
15
|
+
|
16
|
+
emisor = {
|
17
|
+
rfc: 'un RFC',
|
18
|
+
nombre: 'una razón social o nombre',
|
19
|
+
domicilioFiscal: CFDI::Domicilio.new
|
20
|
+
expedidoEn: CFDI::Domicilio.new
|
21
|
+
regimenFiscal: 'general'
|
22
|
+
}
|
23
|
+
|
24
|
+
# lo mismo para el receptor
|
25
|
+
|
26
|
+
# porque XML! ES LO DE HOY! BIENVENIDOS A 2001!
|
27
|
+
puts factura.to_xml
|
28
|
+
|
29
|
+
# O talvez evolucionamos a un formato de intercambio de datos menos castroso
|
30
|
+
require 'json'
|
31
|
+
puts JSON.pretty_generate(factura.to_h)
|
32
|
+
|
33
|
+
## Licencia
|
34
|
+
|
35
|
+

|
36
|
+
|
37
|
+
Como es costumbre, todo bajo WTFPL. La licencia completa la puedes leer acá: [Licencia](LICENSE.txt)
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cfdi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Roberto Hidalgo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-10-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Comprobantes fiscales digitales por internet
|
28
|
+
email:
|
29
|
+
- un@rob.mx
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- Gemfile
|
36
|
+
- LICENSE.txt
|
37
|
+
- Rakefile
|
38
|
+
- cfdi.gemspec
|
39
|
+
- cfdi.rb
|
40
|
+
- examples/crear_factura.rb
|
41
|
+
- examples/data/_empty
|
42
|
+
- lib/certificado.rb
|
43
|
+
- lib/comprobante.rb
|
44
|
+
- lib/comun.rb
|
45
|
+
- lib/concepto.rb
|
46
|
+
- lib/entidad.rb
|
47
|
+
- lib/key.rb
|
48
|
+
- readme.md
|
49
|
+
homepage: https://github.com/unRob/cfdi
|
50
|
+
licenses:
|
51
|
+
- WTFPL
|
52
|
+
- GPLv2
|
53
|
+
metadata: {}
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
requirements: []
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 2.0.3
|
71
|
+
signing_key:
|
72
|
+
specification_version: 4
|
73
|
+
summary: Digitales!! por Internet!!
|
74
|
+
test_files: []
|