cfdi 0.1.8 → 0.1.9
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 +4 -4
- data/examples/crear_factura.rb +1 -1
- data/lib/comprobante.rb +74 -63
- data/lib/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 10a721b62d24a67b264b69d2e04c3a08fbcb115c
|
4
|
+
data.tar.gz: f664e747c70a95880fddda44a722ee3cf9d18894
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c85d9c7eeeace5094efa16869101e732bfe695622c70dbf9dad3e8feebb6c984b7887cecb73627d382910c70c1992d087d1426d87a5e33519ef7c360b5898860
|
7
|
+
data.tar.gz: 7e68eb183eae021e6c0ec4d950843540499c54048addcdf8a893ea6dd653162684f32c8bb8b93035f17fbab62c517d2d736e67b0edba2e769b45a1e32b336d64
|
data/examples/crear_factura.rb
CHANGED
@@ -58,7 +58,7 @@ factura.conceptos << CFDI::Concepto.new({
|
|
58
58
|
unidad: 'Kilos',
|
59
59
|
noIdentificacion: 'KDV',
|
60
60
|
descripcion: 'Verga',
|
61
|
-
valorUnitario:5500.00 #el importe se calcula solo
|
61
|
+
valorUnitario: 5500.00 #el importe se calcula solo
|
62
62
|
})
|
63
63
|
|
64
64
|
# Todavía no agarro bien el pedo sobre como salen los impuestos, pull request?
|
data/lib/comprobante.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
module CFDI
|
2
2
|
# La clase principal para crear Comprobantes
|
3
3
|
class Comprobante
|
4
|
-
|
4
|
+
|
5
5
|
# los datos para la cadena original en el órden correcto
|
6
6
|
# @private
|
7
7
|
@@datosCadena = [:version, :fecha, :tipoDeComprobante, :formaDePago, :condicionesDePago, :subTotal, :TipoCambio, :moneda, :total, :metodoDePago, :lugarExpedicion, :NumCtaPago]
|
8
8
|
# Todos los datos del comprobante
|
9
9
|
# @private
|
10
10
|
@@data = @@datosCadena+[:emisor, :receptor, :conceptos, :serie, :folio, :sello, :noCertificado, :certificado, :conceptos, :complemento, :cancelada, :impuestos]
|
11
|
-
attr_accessor
|
12
|
-
|
11
|
+
attr_accessor(*@@data)
|
12
|
+
|
13
13
|
@addenda = nil
|
14
|
-
|
14
|
+
|
15
15
|
@@options = {
|
16
16
|
tasa: 0.16,
|
17
17
|
defaults: {
|
@@ -24,9 +24,9 @@ module CFDI
|
|
24
24
|
tipoDeComprobante: 'ingreso'
|
25
25
|
}
|
26
26
|
}
|
27
|
-
|
27
|
+
|
28
28
|
# Configurar las opciones default de los comprobantes
|
29
|
-
#
|
29
|
+
#
|
30
30
|
# == Parameters:
|
31
31
|
# options::
|
32
32
|
# Las opciones del comprobante: tasa (de impuestos), defaults: un Hash con la moneda (pesos), version (3.2), TipoCambio (1), y tipoDeComprobante (ingreso)
|
@@ -37,7 +37,7 @@ module CFDI
|
|
37
37
|
@@options = Comprobante.rmerge @@options, options
|
38
38
|
@@options
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
# Crear un comprobante nuevo
|
42
42
|
#
|
43
43
|
# @param data [Hash] Los datos de un comprobante
|
@@ -51,7 +51,7 @@ module CFDI
|
|
51
51
|
# @option data [String] :metodoDePago ('') El método de pago (depósito bancario? efectivo?)
|
52
52
|
# @option data [String] :lugarExpedicion ('') El lugar dónde se expide la factura (Nutopía, México?)
|
53
53
|
# @option data [String] :NumCtaPago (nil) El número de cuenta para el pago
|
54
|
-
#
|
54
|
+
#
|
55
55
|
# @param options [Hash] Las opciones para este comprobante
|
56
56
|
# @see [Comprobante@@options] Opciones
|
57
57
|
#
|
@@ -67,16 +67,16 @@ module CFDI
|
|
67
67
|
self.send method, v
|
68
68
|
end
|
69
69
|
end
|
70
|
-
|
71
|
-
|
70
|
+
|
71
|
+
|
72
72
|
def addenda= addenda
|
73
73
|
addenda = Addenda.new addenda unless addenda.is_a? Addenda
|
74
74
|
@addenda = addenda
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
|
78
78
|
# Regresa el subtotal de este comprobante, tomando el importe de cada concepto
|
79
|
-
#
|
79
|
+
#
|
80
80
|
# @return [Float] El subtotal del comprobante
|
81
81
|
def subTotal
|
82
82
|
ret = 0
|
@@ -85,41 +85,41 @@ module CFDI
|
|
85
85
|
end
|
86
86
|
ret
|
87
87
|
end
|
88
|
-
|
89
|
-
|
88
|
+
|
89
|
+
|
90
90
|
# Regresa el total
|
91
|
-
#
|
91
|
+
#
|
92
92
|
# @return [Float] El subtotal multiplicado por la tasa
|
93
93
|
def total
|
94
94
|
self.subTotal+(self.subTotal*@opciones[:tasa])
|
95
95
|
end
|
96
|
-
|
97
|
-
|
96
|
+
|
97
|
+
|
98
98
|
# Asigna un emisor de tipo {CFDI::Entidad}
|
99
99
|
# @param emisor [Hash, CFDI::Entidad] Los datos de un emisor
|
100
|
-
#
|
100
|
+
#
|
101
101
|
# @return [CFDI::Entidad] Una entidad
|
102
|
-
def emisor= emisor
|
102
|
+
def emisor= emisor
|
103
103
|
emisor = Entidad.new emisor unless emisor.is_a? Entidad
|
104
104
|
@emisor = emisor;
|
105
105
|
end
|
106
|
-
|
107
|
-
|
106
|
+
|
107
|
+
|
108
108
|
# Asigna un receptor
|
109
109
|
# @param receptor [Hash, CFDI::Entidad] Los datos de un receptor
|
110
|
-
#
|
110
|
+
#
|
111
111
|
# @return [CFDI::Entidad] Una entidad
|
112
|
-
def receptor= receptor
|
112
|
+
def receptor= receptor
|
113
113
|
receptor = Entidad.new receptor unless receptor.is_a? Entidad
|
114
114
|
@receptor = receptor;
|
115
115
|
receptor
|
116
116
|
end
|
117
|
-
|
117
|
+
|
118
118
|
# Agrega uno o varios conceptos
|
119
119
|
# @param conceptos [Array, Hash, CFDI::Concepto] Uno o varios conceptos
|
120
|
-
#
|
120
|
+
#
|
121
121
|
# En caso de darle un Hash o un {CFDI::Concepto}, agrega este a los conceptos, de otro modo, sobreescribe los conceptos pre-existentes
|
122
|
-
#
|
122
|
+
#
|
123
123
|
# @return [Array] Los conceptos de este comprobante
|
124
124
|
def conceptos= conceptos
|
125
125
|
if conceptos.is_a? Array
|
@@ -131,26 +131,26 @@ module CFDI
|
|
131
131
|
elsif conceptos.is_a? Concepto
|
132
132
|
conceptos << conceptos
|
133
133
|
end
|
134
|
-
|
134
|
+
|
135
135
|
@conceptos = conceptos
|
136
136
|
conceptos
|
137
137
|
end
|
138
|
-
|
138
|
+
|
139
139
|
|
140
140
|
# Asigna un complemento al comprobante
|
141
141
|
# @param complemento [Hash, CFDI::Complemento] El complemento a agregar
|
142
|
-
#
|
142
|
+
#
|
143
143
|
# @return [CFDI::Complemento]
|
144
144
|
def complemento= complemento
|
145
145
|
complemento = Complemento.new complemento unless complemento.is_a? Complemento
|
146
146
|
@complemento = complemento
|
147
147
|
complemento
|
148
148
|
end
|
149
|
-
|
149
|
+
|
150
150
|
|
151
151
|
# Asigna una fecha al comprobante
|
152
152
|
# @param fecha [Time, String] La fecha y hora (YYYY-MM-DDTHH:mm:SS) de la emisión
|
153
|
-
#
|
153
|
+
#
|
154
154
|
# @return [String] la fecha en formato '%FT%R:%S'
|
155
155
|
def fecha= fecha
|
156
156
|
fecha = fecha.strftime('%FT%R:%S') unless fecha.is_a? String
|
@@ -159,7 +159,7 @@ module CFDI
|
|
159
159
|
|
160
160
|
|
161
161
|
# El comprobante como XML
|
162
|
-
#
|
162
|
+
#
|
163
163
|
# @return [String] El comprobante namespaceado en versión 3.2 (porque soy un huevón)
|
164
164
|
def to_xml
|
165
165
|
ns = {
|
@@ -171,9 +171,9 @@ module CFDI
|
|
171
171
|
fecha: @fecha,
|
172
172
|
formaDePago: @formaDePago,
|
173
173
|
condicionesDePago: @condicionesDePago,
|
174
|
-
subTotal: self.subTotal,
|
174
|
+
subTotal: sprintf('%.2f', self.subTotal),
|
175
175
|
Moneda: @moneda,
|
176
|
-
total: self.total,
|
176
|
+
total: sprintf('%.2f', self.total),
|
177
177
|
metodoDePago: @metodoDePago,
|
178
178
|
tipoDeComprobante: @tipoDeComprobante,
|
179
179
|
LugarExpedicion: @lugarExpedicion,
|
@@ -181,27 +181,27 @@ module CFDI
|
|
181
181
|
ns[:serie] = @serie if @serie
|
182
182
|
ns[:TipoCambio] = @TipoCambio if @TipoCambio
|
183
183
|
ns[:NumCtaPago] = @NumCtaPago if @NumCtaPago && @NumCtaPago!=''
|
184
|
-
|
184
|
+
|
185
185
|
if (@addenda)
|
186
186
|
# Si tenemos addenda, entonces creamos el campo "xmlns:ElNombre" y agregamos sus definiciones al SchemaLocation
|
187
187
|
ns["xmlns:#{@addenda.nombre}"] = @addenda.namespace
|
188
188
|
ns['xsi:schemaLocation'] += ' '+[@addenda.namespace, @addenda.xsd].join(' ')
|
189
189
|
end
|
190
|
-
|
190
|
+
|
191
191
|
if @noCertificado
|
192
192
|
ns[:noCertificado] = @noCertificado
|
193
193
|
ns[:certificado] = @certificado
|
194
194
|
end
|
195
|
-
|
195
|
+
|
196
196
|
if @sello
|
197
197
|
ns[:sello] = @sello
|
198
198
|
end
|
199
|
-
|
199
|
+
|
200
200
|
@builder = Nokogiri::XML::Builder.new do |xml|
|
201
201
|
xml.Comprobante(ns) do
|
202
202
|
ins = xml.doc.root.add_namespace_definition('cfdi', 'http://www.sat.gob.mx/cfd/3')
|
203
203
|
xml.doc.root.namespace = ins
|
204
|
-
|
204
|
+
|
205
205
|
xml.Emisor(@emisor.ns) {
|
206
206
|
xml.DomicilioFiscal(@emisor.domicilioFiscal.to_h.reject {|k,v| v == nil})
|
207
207
|
xml.ExpedidoEn(@emisor.expedidoEn.to_h.reject {|k,v| v == nil || v == ''})
|
@@ -213,32 +213,43 @@ module CFDI
|
|
213
213
|
xml.Conceptos {
|
214
214
|
@conceptos.each do |concepto|
|
215
215
|
# select porque luego se caga el xml si incluyo noIdentificacion y es empty
|
216
|
-
|
216
|
+
|
217
|
+
cc = concepto.to_h.select {|k,v| v!=nil && v != ''}
|
218
|
+
|
219
|
+
cc = cc.map {|k,v|
|
220
|
+
v = sprintf('%.2f', v) if v.is_a? Float
|
221
|
+
[k,v]
|
222
|
+
}.to_h
|
223
|
+
|
224
|
+
xml.Concepto(cc) {
|
217
225
|
xml.ComplementoConcepto
|
218
226
|
}
|
219
227
|
end
|
220
228
|
}
|
221
|
-
xml.Impuestos({totalImpuestosTrasladados: self.subTotal*@opciones[:tasa]}) {
|
229
|
+
xml.Impuestos({totalImpuestosTrasladados: sprintf('%.2f', self.subTotal*@opciones[:tasa])}) {
|
222
230
|
xml.Traslados {
|
223
231
|
@impuestos.each do |impuesto|
|
224
|
-
xml.Traslado({
|
232
|
+
xml.Traslado({
|
233
|
+
impuesto: impuesto[:impuesto],
|
234
|
+
tasa:(@opciones[:tasa]*100).to_i,
|
235
|
+
importe: sprintf('%.2f', self.subTotal*@opciones[:tasa])})
|
225
236
|
end
|
226
237
|
}
|
227
238
|
}
|
228
239
|
xml.Complemento {
|
229
|
-
|
240
|
+
|
230
241
|
if @complemento
|
231
242
|
nsTFD = {
|
232
243
|
'xsi:schemaLocation' => 'http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/TimbreFiscalDigital/TimbreFiscalDigital.xsd',
|
233
244
|
'xmlns:tfd' => 'http://www.sat.gob.mx/TimbreFiscalDigital',
|
234
|
-
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
|
245
|
+
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
|
235
246
|
}
|
236
247
|
xml['tfd'].TimbreFiscalDigital(@complemento.to_h.merge nsTFD) {
|
237
248
|
}
|
238
|
-
|
249
|
+
|
239
250
|
end
|
240
251
|
}
|
241
|
-
|
252
|
+
|
242
253
|
if (@addenda)
|
243
254
|
xml.Addenda {
|
244
255
|
@addenda.data.each do |k,v|
|
@@ -252,7 +263,7 @@ module CFDI
|
|
252
263
|
end
|
253
264
|
}
|
254
265
|
end
|
255
|
-
|
266
|
+
|
256
267
|
end
|
257
268
|
end
|
258
269
|
@builder.to_xml
|
@@ -260,7 +271,7 @@ module CFDI
|
|
260
271
|
|
261
272
|
|
262
273
|
# Un hash con todos los datos del comprobante, listo para Hash.to_json
|
263
|
-
#
|
274
|
+
#
|
264
275
|
# @return [Hash] El comprobante como Hash
|
265
276
|
def to_h
|
266
277
|
hash = {}
|
@@ -268,30 +279,30 @@ module CFDI
|
|
268
279
|
data = deep_to_h send(key)
|
269
280
|
hash[key] = data
|
270
281
|
end
|
271
|
-
|
282
|
+
|
272
283
|
return hash
|
273
284
|
end
|
274
|
-
|
285
|
+
|
275
286
|
|
276
287
|
# La cadena original del CFDI
|
277
|
-
#
|
288
|
+
#
|
278
289
|
# @return [String] Separada por pipes, because fuck you that's why
|
279
290
|
def cadena_original
|
280
291
|
params = []
|
281
|
-
|
282
|
-
@@datosCadena.each {|key| params.push send(key) }
|
292
|
+
|
293
|
+
@@datosCadena.each {|key| params.push send(key) }
|
283
294
|
params += @emisor.cadena_original
|
284
295
|
params << @regimen
|
285
296
|
params += @receptor.cadena_original
|
286
|
-
|
297
|
+
|
287
298
|
@conceptos.each do |concepto|
|
288
299
|
params += concepto.cadena_original
|
289
300
|
end
|
290
|
-
|
301
|
+
|
291
302
|
@impuestos.each do |traslado|
|
292
303
|
params += [traslado[:impuesto], (@opciones[:tasa]*100).to_i, self.subTotal*@opciones[:tasa], self.subTotal*@opciones[:tasa]]
|
293
304
|
end
|
294
|
-
|
305
|
+
|
295
306
|
params.select! { |i| i != nil && i != '' }
|
296
307
|
params.map! do |elem|
|
297
308
|
if elem.is_a? Float
|
@@ -301,14 +312,14 @@ module CFDI
|
|
301
312
|
end
|
302
313
|
elem
|
303
314
|
end
|
304
|
-
|
315
|
+
|
305
316
|
return "||#{params.join '|'}||"
|
306
317
|
end
|
307
318
|
|
308
319
|
|
309
320
|
# Revisa que el timbre de un comprobante sea válido
|
310
321
|
# @param [String] El certificado del PAC
|
311
|
-
#
|
322
|
+
#
|
312
323
|
# @return [Boolean] El resultado de la validación
|
313
324
|
def timbre_valido? cert=nil
|
314
325
|
return false unless complemento && complemento.selloSAT
|
@@ -343,21 +354,21 @@ module CFDI
|
|
343
354
|
|
344
355
|
private
|
345
356
|
def deep_to_h value
|
346
|
-
|
357
|
+
|
347
358
|
if value.is_a? ElementoComprobante
|
348
359
|
original = value.to_h
|
349
360
|
value = {}
|
350
361
|
original.each do |k,v|
|
351
362
|
value[k] = deep_to_h v
|
352
363
|
end
|
353
|
-
|
364
|
+
|
354
365
|
elsif value.is_a?(Array)
|
355
366
|
value = value.map do |v|
|
356
367
|
deep_to_h v
|
357
368
|
end
|
358
369
|
end
|
359
|
-
value
|
360
|
-
|
370
|
+
# value
|
371
|
+
|
361
372
|
#value = value.to_h if value.respond_to? :to_h
|
362
373
|
#if value.each do |vi|
|
363
374
|
# value.map do |k,v|
|
@@ -366,6 +377,6 @@ module CFDI
|
|
366
377
|
#end
|
367
378
|
value
|
368
379
|
end
|
369
|
-
|
380
|
+
|
370
381
|
end
|
371
382
|
end
|
data/lib/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cfdi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roberto Hidalgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|