fdis2 0.1.34

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +51 -0
  3. data/lib/cadena/CartaPorte20.xslt +615 -0
  4. data/lib/cadena/ComercioExterior11.xslt +181 -0
  5. data/lib/cadena/GastosHidrocarburos10.xslt +171 -0
  6. data/lib/cadena/IngresosHidrocarburos.xslt +39 -0
  7. data/lib/cadena/Pagos10.xslt +165 -0
  8. data/lib/cadena/Pagos20.xsd +532 -0
  9. data/lib/cadena/Pagos20.xslt +233 -0
  10. data/lib/cadena/TuristaPasajeroExtranjero.xslt +40 -0
  11. data/lib/cadena/aerolineas.xslt +50 -0
  12. data/lib/cadena/cadena33.xslt +349 -0
  13. data/lib/cadena/cadenaoriginal_4_0.xslt +405 -0
  14. data/lib/cadena/catCFDI.xsd +162331 -0
  15. data/lib/cadena/certificadodedestruccion.xslt +60 -0
  16. data/lib/cadena/cfdiregistrofiscal.xslt +19 -0
  17. data/lib/cadena/cfdv33.xsd +737 -0
  18. data/lib/cadena/cfdv40.xsd +850 -0
  19. data/lib/cadena/consumodeCombustibles11.xslt +94 -0
  20. data/lib/cadena/consumodecombustibles.xslt +108 -0
  21. data/lib/cadena/detallista.xslt +42 -0
  22. data/lib/cadena/divisas.xslt +13 -0
  23. data/lib/cadena/donat11.xslt +13 -0
  24. data/lib/cadena/ecc11.xslt +102 -0
  25. data/lib/cadena/ecc12.xslt +99 -0
  26. data/lib/cadena/iedu.xslt +26 -0
  27. data/lib/cadena/implocal.xslt +39 -0
  28. data/lib/cadena/ine11.xslt +51 -0
  29. data/lib/cadena/leyendasFisc.xslt +28 -0
  30. data/lib/cadena/nomina12.xslt +412 -0
  31. data/lib/cadena/notariospublicos.xslt +301 -0
  32. data/lib/cadena/obrasarteantiguedades.xslt +33 -0
  33. data/lib/cadena/pagoenespecie.xslt +39 -0
  34. data/lib/cadena/pfic.xslt +13 -0
  35. data/lib/cadena/renovacionysustitucionvehiculos.xslt +152 -0
  36. data/lib/cadena/servicioparcialconstruccion.xslt +44 -0
  37. data/lib/cadena/terceros11.xslt +108 -0
  38. data/lib/cadena/utilerias.xslt +22 -0
  39. data/lib/cadena/valesdedespensa.xslt +70 -0
  40. data/lib/cadena/vehiculousado.xslt +63 -0
  41. data/lib/cadena/ventavehiculos11.xslt +53 -0
  42. data/lib/fdis/config.rb +101 -0
  43. data/lib/fdis/facturacion.rb +785 -0
  44. data/lib/fdis/version.rb +3 -0
  45. data/lib/fdis.rb +17 -0
  46. metadata +144 -0
@@ -0,0 +1,785 @@
1
+
2
+ module Fdis
3
+ class Facturacion < Config
4
+
5
+ def comp_pago(params={})
6
+ # Sample params
7
+ # params = {
8
+ # uuid: '',
9
+ # folio: '',
10
+ # cp: '',
11
+ # receptor_razon: 'Car zone',
12
+ # receptor_rfc: 'XAXX010101000',
13
+ # receptor_cp: '47180',
14
+ # receptor_regimen: '616',
15
+ # tasa_iva: 0, 16, se toma tasa iva de factura madre
16
+ # forma_pago: '01',
17
+ # total: 100.00,
18
+ # monto_pago: 50.0,
19
+ # saldo_anterior: 100.0,
20
+ # num_parcialidad: '1',
21
+ # time_pago: '',
22
+ # time_now: '',
23
+ # modena: '',
24
+ # line_items: [
25
+ # {
26
+ # monto: 60.00,
27
+ # moneda: '',
28
+ # id: ,
29
+ # },
30
+ # ]
31
+ # }
32
+
33
+ puts "----- Fdis::Facturacion::com_pago"
34
+ puts "--- Fdis:: Total venta: #{params[:total]}"
35
+ puts "--- Fdis:: Monto de pago a procesar: #{params[:monto_pago]}"
36
+ puts "--- Fdis:: Line items: "
37
+ params[:line_items].each do |line|
38
+ puts "-- #{line[:monto]}"
39
+ end
40
+ lines_total = params[:line_items].inject(0) {|sum, x| sum + x[:monto].to_f}
41
+
42
+ puts "--- Fdis::Suma de line_items: #{lines_total}"
43
+
44
+ if (lines_total > params[:total].to_f)
45
+ raise 'Error Fdis - la suma de los complementos de pago es mayor al total de la venta'
46
+ end
47
+
48
+ unless params[:time_pago] and params[:time_pago].size > 0
49
+ raise "Error Fdis - la fecha de timbrado debe de estar presente"
50
+ end
51
+
52
+ if params[:num_parcialidad].empty?
53
+ raise "Error Fdis - Se debe registrar el número de parcialidad que corresponde al pago"
54
+ end
55
+
56
+
57
+
58
+
59
+ time_now = params.fetch(:time_now, (Time.now).strftime("%Y-%m-%dT%H:%M:%S"))
60
+ time_pago = params[:time_pago]
61
+
62
+
63
+ base_doc = %(<?xml version="1.0" encoding="UTF-8"?>
64
+ <cfdi:Comprobante xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:pago20="http://www.sat.gob.mx/Pagos20" xsi:schemaLocation="http://www.sat.gob.mx/Pagos20 http://www.sat.gob.mx/sitio_internet/cfd/Pagos/Pagos20.xsd http://www.sat.gob.mx/cfd/4 http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd" Version="4.0" Serie="" Folio="" Fecha="" Sello="" NoCertificado="" Certificado="" SubTotal="0" Moneda="XXX" Total="0" TipoDeComprobante="P" Exportacion="01" LugarExpedicion="" xmlns:cfdi="http://www.sat.gob.mx/cfd/4">
65
+
66
+ <cfdi:Emisor Rfc="" Nombre="" RegimenFiscal="" />
67
+ <cfdi:Receptor Rfc="" Nombre="" UsoCFDI="CP01" DomicilioFiscalReceptor="" RegimenFiscalReceptor="616" />
68
+
69
+ <cfdi:Conceptos>
70
+ <cfdi:Concepto ClaveProdServ="84111506" Cantidad="1" ClaveUnidad="ACT" Descripcion="Pago" ValorUnitario="0" Importe="0" ObjetoImp="01" />
71
+ </cfdi:Conceptos>
72
+
73
+ <cfdi:Complemento>
74
+
75
+ <pago20:Pagos Version="2.0">
76
+ <pago20:Totales MontoTotalPagos="" />
77
+
78
+ <pago20:Pago FechaPago="" FormaDePagoP="" MonedaP="MXN" Monto="" TipoCambioP="1">
79
+
80
+ <pago20:DoctoRelacionado IdDocumento="" MonedaDR="MXN" NumParcialidad="" ImpSaldoAnt="" ImpPagado="" ImpSaldoInsoluto="" ObjetoImpDR="02" EquivalenciaDR="1">
81
+
82
+
83
+ </pago20:DoctoRelacionado>
84
+
85
+ </pago20:Pago>
86
+
87
+ </pago10:Pagos>
88
+
89
+ </cfdi:Complemento>
90
+
91
+ </cfdi:Comprobante>)
92
+
93
+ base_doc.delete!("\n")
94
+ base_doc.delete!("\t")
95
+
96
+ xml = Nokogiri::XML(base_doc)
97
+ comprobante = xml.at_xpath("//cfdi:Comprobante")
98
+ comprobante['Serie'] = 'P'
99
+ comprobante['Folio'] = params[:folio].to_s
100
+ comprobante['Fecha'] = time_now
101
+ comprobante['LugarExpedicion'] = params[:cp].to_s
102
+ comprobante['NoCertificado'] = @serial
103
+ comprobante['Certificado'] = @cadena
104
+
105
+ # Emisor datos
106
+ emisor = xml.at_xpath("//cfdi:Emisor")
107
+ emisor['Rfc'] = @rfc
108
+ emisor['Nombre'] = @razon
109
+ emisor['RegimenFiscal'] = @regimen_fiscal
110
+
111
+ # Receptor datos
112
+ receptor = xml.at_xpath("//cfdi:Receptor")
113
+ receptor['Nombre'] = params[:receptor_razon].to_s
114
+ receptor['Rfc'] = params[:receptor_rfc].to_s
115
+ receptor['DomicilioFiscalReceptor'] = params.fetch(:receptor_cp, '47180')
116
+ receptor['RegimenFiscalReceptor'] = params.fetch(:receptor_regimen, '616')
117
+
118
+
119
+ # totales
120
+ total = params[:monto_pago].to_f
121
+ iva_id = params.fetch(:tasa_iva, 16)
122
+
123
+ pago_totales = xml.at_xpath("//pago20:Totales")
124
+ pago_totales['MontoTotalPagos'] = total.round(2).to_s # total
125
+
126
+
127
+
128
+
129
+ if iva_id == 0
130
+ subtotal = total
131
+ iva = 0.00
132
+
133
+ pago_totales['TotalTrasladosBaseIVA0'] = subtotal.round(2).to_s # subtotal
134
+ pago_totales['TotalTrasladosImpuestoIVA0'] = iva.round(2).to_s # iva
135
+
136
+ else
137
+ subtotal = total / 1.16
138
+ iva = total - subtotal
139
+
140
+ pago_totales['TotalTrasladosBaseIVA16'] = subtotal.round(2).to_s # subtotal
141
+ pago_totales['TotalTrasladosImpuestoIVA16'] = iva.round(2).to_s # iva
142
+ end
143
+
144
+
145
+ # pagos = xml.at_xpath("//pago20:Pagos")
146
+
147
+ # pago
148
+ child_pago = xml.at_xpath("//pago20:Pago")
149
+
150
+ child_pago['FechaPago'] = time_pago
151
+ child_pago['FormaDePagoP'] = params[:forma_pago].to_s
152
+ child_pago['MonedaP'] = params.fetch(:moneda, 'MXN')
153
+ child_pago['Monto'] = total.round(2).to_s
154
+ child_pago['TipoCambioP'] = "1"
155
+
156
+ child_pago_relacionado = xml.at_xpath("//pago20:DoctoRelacionado")
157
+
158
+ child_pago_relacionado['IdDocumento'] = params[:uuid]
159
+ child_pago_relacionado['MonedaDR'] = 'MXN'
160
+ child_pago_relacionado['NumParcialidad'] = params[:num_parcialidad]
161
+ child_pago_relacionado['Serie'] = "Depo"
162
+ child_pago_relacionado['Folio'] = params[:folio]
163
+ child_pago_relacionado['EquivalenciaDR'] = "1"
164
+
165
+ saldo_anterior = params[:saldo_anterior].to_f
166
+
167
+ child_pago_relacionado['ImpSaldoAnt'] = saldo_anterior.round(2).to_s
168
+ child_pago_relacionado['ImpPagado'] = total.round(2).to_s
169
+ child_pago_relacionado['ImpSaldoInsoluto'] = (saldo_anterior - total).round(2).to_s
170
+ child_pago_relacionado['ObjetoImpDR'] = '02'
171
+
172
+ impuestos_dr = Nokogiri::XML::Node.new "pago20:ImpuestosDR", xml
173
+ traslados_dr = Nokogiri::XML::Node.new "pago20:TrasladosDR", xml
174
+ traslado = Nokogiri::XML::Node.new "pago20:TrasladoDR", xml
175
+
176
+ impuestos_p = Nokogiri::XML::Node.new "pago20:ImpuestosP", xml
177
+ traslados_p = Nokogiri::XML::Node.new "pago20:TrasladosP", xml
178
+ traslado_p = Nokogiri::XML::Node.new "pago20:TrasladoP", xml
179
+
180
+ traslado['TipoFactorDR'] = 'Tasa'
181
+ traslado['ImpuestoDR'] = '002'
182
+
183
+ traslado['BaseDR'] = subtotal.round(4).to_s #subtotal
184
+ traslado['ImporteDR'] = iva.round(4).to_s # tax
185
+
186
+ traslado_p['BaseP'] = subtotal.round(4).to_s
187
+ traslado_p['ImpuestoP'] = '002'
188
+ traslado_p['TipoFactorP'] = 'Tasa'
189
+ traslado_p['ImporteP'] = iva.round(4).to_s # tax
190
+
191
+ if iva_id == 16
192
+ traslado['TasaOCuotaDR'] = '0.160000'
193
+ # t_subtotal = total / 1.16
194
+ # t_tax = total - t_subtotal
195
+
196
+ traslado_p['TasaOCuotaP'] = '0.160000'
197
+
198
+ else
199
+ traslado['TasaOCuotaDR'] = '0.000000'
200
+ traslado_p['TasaOCuotaP'] = '0.000000'
201
+ end
202
+
203
+ traslados_dr.add_child(traslado)
204
+ impuestos_dr.add_child(traslados_dr)
205
+ child_pago_relacionado.add_child(impuestos_dr)
206
+
207
+ traslados_p.add_child(traslado_p)
208
+ impuestos_p.add_child(traslados_p)
209
+ child_pago.add_child(impuestos_p)
210
+
211
+
212
+
213
+ # puts '---------------- Xml resultante comprobante de pago -----------------------'
214
+ # puts xml.to_xml
215
+ # puts '--------------------------------------------------------'
216
+
217
+ path = File.join(File.dirname(__FILE__), *%w[.. tmp])
218
+ id = SecureRandom.hex
219
+
220
+ FileUtils.mkdir_p(path) unless File.exist?(path)
221
+ File.write("#{path}/tmp_c_#{id}.xml", xml.to_xml)
222
+ xml_path = "#{path}/tmp_c_#{id}.xml"
223
+ cadena_path = File.join(File.dirname(__FILE__), *%w[.. cadena cadenaoriginal_4_0.xslt])
224
+
225
+ File.write("#{path}/pem_#{id}.pem", @pem)
226
+ key_pem_url = "#{path}/pem_#{id}.pem"
227
+ sello = %x[xsltproc #{cadena_path} #{xml_path} | openssl dgst -sha256 -sign #{key_pem_url} | openssl enc -base64 -A]
228
+ comprobante['Sello'] = sello
229
+
230
+ File.delete("#{xml_path}")
231
+ File.delete("#{key_pem_url}")
232
+
233
+ puts '------ Fdis: comprobante de pago antes de timbre -------'
234
+ puts xml.to_xml
235
+ base64_xml = Base64.encode64(xml.to_xml)
236
+
237
+ # haciendo llamada a API
238
+ uri = URI("#{Fdis::UrlPro}/Timbrar40")
239
+ request = Net::HTTP::Post.new(uri)
240
+ # request.basic_auth(token, "")
241
+ request.content_type = "application/json"
242
+ # request["cache-control"] = 'no-cache'
243
+ request.body = JSON.dump({
244
+ "testMode": !@production,
245
+ "idServicio": @id_servicio,
246
+ "base64XmlFile": base64_xml,
247
+ })
248
+
249
+ req_options = {
250
+ use_ssl: uri.scheme == "https",
251
+ }
252
+
253
+ json_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
254
+ http.request(request)
255
+ end
256
+
257
+ puts "---- Fdis: request"
258
+ puts "-- body: #{request.body} --"
259
+
260
+ puts "---- Fdis: Respuesta"
261
+ puts "-- Codigo: #{json_response.code} --"
262
+ puts "-- Mensaje: #{json_response.message} --"
263
+ puts "-- Body: "
264
+ p json_response.body
265
+ response = JSON.parse(json_response.body)
266
+
267
+ case json_response
268
+ when Net::HTTPSuccess, Net::HTTPRedirection
269
+ if response['success'] == true
270
+ decoded_xml = Nokogiri::XML(Base64.decode64(response['base64XmlFile']))
271
+ timbre = decoded_xml.at_xpath("//cfdi:Complemento").children[0]
272
+ response = {
273
+ status: 200,
274
+ message_error: '',
275
+ xml: decoded_xml.to_xml,
276
+ uuid: timbre['UUID'],
277
+ fecha_timbrado: timbre['FechaTimbrado'],
278
+ sello_cfd: timbre['SelloCFD'],
279
+ sello_sat: timbre['SelloSAT'],
280
+ no_certificado_sat: timbre['NoCertificadoSAT'],
281
+ }
282
+ return response
283
+ else
284
+ response = {
285
+ status: 400,
286
+ message_error: "Error: #{response['errorMessages']}",
287
+ xml: '',
288
+ uuid: '',
289
+ fecha_timbrado: '',
290
+ sello_cfd: '',
291
+ sello_sat: '',
292
+ no_certificado_sat: '',
293
+ }
294
+ return response
295
+
296
+ end
297
+ else
298
+ response = {
299
+ status: json_response.code,
300
+ message_error: "Error: #{response['errorMessages']}",
301
+ xml: '',
302
+ uuid: '',
303
+ fecha_timbrado: '',
304
+ sello_cfd: '',
305
+ sello_sat: '',
306
+ no_certificado_sat: '',
307
+ }
308
+ return response
309
+
310
+ end
311
+
312
+
313
+ end
314
+
315
+
316
+
317
+ def cancela_doc(params={})
318
+ puts "---- Fdis:facturacion:cancela_doc"
319
+
320
+ # Sample params
321
+ # params = {
322
+ # uuid: '',
323
+ # rfcReceptor: 'XAXX010101000',
324
+ # total_sale: 100.0,
325
+ # motivo: '02',
326
+ # uuid_sustituye: '',
327
+ # key_password: '', # optional
328
+ # cer_cadena: '', # optional
329
+ # key_pem: '' # optional
330
+ # }
331
+
332
+
333
+ uri = URI(Fdis::UrlCancel)
334
+ request = Net::HTTP::Post.new(uri)
335
+ request.content_type = "application/json"
336
+
337
+ request.body = JSON.dump({
338
+ rfcEmisor: @rfc,
339
+ rfcReceptor: params[:rfcReceptor],
340
+ keyPassword: @key_pass,
341
+ totalCfdi: params[:total_sale],
342
+ uuid: params[:uuid],
343
+ motivoCancelacion: params.fetch(:motivo, '02'),
344
+ FolioFiscalSustituye: params.fetch(:uuid_sustituye, SecureRandom.uuid.upcase),
345
+ testMode: !@production,
346
+ base64CerFile: params.fetch(:cer_cadena, @cadena),
347
+ base64KeyFile: params.fetch(:key_pem, @pem_cadena),
348
+ })
349
+
350
+ req_options = {
351
+ use_ssl: uri.scheme == "https",
352
+ }
353
+
354
+ json_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
355
+ http.request(request)
356
+ end
357
+
358
+ puts "---- Fdis: request"
359
+ puts "-- body: #{request.body} --"
360
+
361
+ puts "---- Fdis: Respuesta"
362
+ puts "-- Codigo: #{json_response.code} --"
363
+ puts "-- Mensaje: #{json_response.message} --"
364
+ puts "-- Body: "
365
+ p json_response.body
366
+
367
+ response = JSON.parse(json_response.body)
368
+
369
+ case json_response
370
+ when Net::HTTPSuccess, Net::HTTPRedirection
371
+ if response['success'] == true
372
+ acuse = response['acuseCancelacion']
373
+ response = {
374
+ status: 200,
375
+ message_error: '',
376
+ xml: acuse,
377
+ }
378
+
379
+ return response
380
+ else
381
+ response = {
382
+ status: 400,
383
+ message_error: "Error: #{response['errors']}",
384
+ xml: '',
385
+
386
+ }
387
+ return response
388
+ end
389
+
390
+ else
391
+ response = {
392
+ status: 400,
393
+ message_error: "Error: #{response['errors']}",
394
+ xml: '',
395
+
396
+ }
397
+ return response
398
+ end
399
+
400
+ end
401
+
402
+
403
+ def timbra_doc(params={})
404
+ ### sample params
405
+ #
406
+ # params = {
407
+ # moneda: 'MXN',
408
+ # series: 'FA',
409
+ # folio: '003',
410
+ # forma_pago: '',
411
+ # metodo_pago: 'PUE',
412
+ # cp: '47180',
413
+ # receptor_cp: '47180',
414
+ # receptor_razon: 'Car zone',
415
+ # receptor_rfc: '',
416
+ # receptor_regimen: '',
417
+ # uso_cfdi: 'G03',
418
+ # tasa_iva: 0, 16,
419
+ # time: "%Y-%m-%dT%H:%M:%S",
420
+ # line_items: [
421
+ # {
422
+ # clave_prod_serv: '78181500',
423
+ # clave_unidad: 'E48',
424
+ # unidad: 'Servicio',
425
+ # sku: 'serv001',
426
+ # cantidad: 1,
427
+ # descripcion: 'Servicio mano de obra',
428
+ # valor_unitario: 100.00,
429
+ # descuento: 0.00,
430
+ # tax_included: true,
431
+ # retencion_iva: 0, 6, 16
432
+ # # Optional parameters
433
+ # },
434
+ # ]
435
+
436
+ # }
437
+
438
+ puts "---- Fdis:facturacion:timbra_doc"
439
+
440
+ puts "--- Fdis: Datos --------"
441
+ puts "--- Fdis: Line items: "
442
+ params[:line_items].each do |line|
443
+ puts "----- valor unitario: #{line[:valor_unitario]}"
444
+ puts "----- cantidad: #{line[:cantidad]}"
445
+ end
446
+
447
+ time = params.fetch(:time, (Time.now).strftime("%Y-%m-%dT%H:%M:%S"))
448
+
449
+
450
+ base_doc = %(<?xml version="1.0" encoding="utf-8"?>
451
+ <cfdi:Comprobante xsi:schemaLocation="http://www.sat.gob.mx/cfd/4 http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd" Version="4.0" xmlns:cfdi="http://www.sat.gob.mx/cfd/4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Fecha="" Serie="" Folio="" FormaPago="" SubTotal="" Moneda="MXN" Total="" TipoDeComprobante="I" MetodoPago="" LugarExpedicion="" Certificado="" NoCertificado="" Sello="" Exportacion="01">
452
+
453
+ <cfdi:Emisor Rfc="" Nombre="" RegimenFiscal="" />
454
+ <cfdi:Receptor Rfc="" Nombre="" DomicilioFiscalReceptor="" RegimenFiscalReceptor="" UsoCFDI="" />
455
+
456
+ <cfdi:Conceptos></cfdi:Conceptos>
457
+ <cfdi:Impuestos></cfdi:Impuestos>
458
+
459
+ </cfdi:Comprobante>)
460
+
461
+ base_doc.delete!("\n")
462
+ base_doc.delete!("\t")
463
+
464
+ xml = Nokogiri::XML(base_doc)
465
+ comprobante = xml.at_xpath("//cfdi:Comprobante")
466
+ comprobante['TipoCambio'] = '1'
467
+ comprobante['TipoDeComprobante'] = 'I'
468
+ comprobante['Serie'] = params.fetch(:series, 'FA').to_s
469
+ comprobante['Folio'] = params.fetch(:folio).to_s
470
+ comprobante['Fecha'] = time.to_s
471
+ comprobante['FormaPago'] = params.fetch(:forma_pago, '01')
472
+ comprobante['MetodoPago'] = params.fetch(:metodo_pago, 'PUE')
473
+ comprobante['LugarExpedicion'] = params.fetch(:cp, '47180')
474
+ comprobante['NoCertificado'] = @serial
475
+ comprobante['Certificado'] = @cadena
476
+
477
+ # emisor
478
+ emisor = xml.at_xpath("//cfdi:Emisor")
479
+ emisor['Nombre'] = @razon
480
+ emisor['RegimenFiscal'] = @regimen_fiscal
481
+ emisor['Rfc'] = @rfc
482
+
483
+ # receptor
484
+ receptor = xml.at_xpath("//cfdi:Receptor")
485
+ receptor['Rfc'] = params.fetch(:receptor_rfc, '')
486
+ receptor['Nombre'] = params.fetch(:receptor_razon, '')
487
+ receptor['DomicilioFiscalReceptor'] = params.fetch(:receptor_cp, '47180')
488
+ if params[:receptor_rfc] == 'XAXX010101000'
489
+ receptor['UsoCFDI'] = 'S01'
490
+ receptor['RegimenFiscalReceptor'] = '616'
491
+ else
492
+ receptor['UsoCFDI'] = params.fetch(:uso_cfdi, 'G03')
493
+ # receptor['RegimenFiscalReceptor'] = params.fetch(:receptor_regimen, '616')
494
+ receptor['RegimenFiscalReceptor'] = params[:receptor_regimen]
495
+ end
496
+
497
+ # retencion_iva = params.fetch(:retencion_iva, 0)
498
+
499
+ impuestos = xml.at_xpath("//cfdi:Impuestos")
500
+ traslados = Nokogiri::XML::Node.new "cfdi:Traslados", xml
501
+
502
+
503
+ puts '--- Fdis time -----'
504
+ puts time
505
+ puts '--------'
506
+
507
+ conceptos = xml.at_xpath("//cfdi:Conceptos")
508
+
509
+ line_items = params[:line_items]
510
+
511
+ suma_total = 0.00
512
+ subtotal = 0.00
513
+ suma_iva = 0.00
514
+ suma_ret = 0.00
515
+
516
+ iva_id = params.fetch(:tasa_iva, 16)
517
+
518
+ line_items.each do |line|
519
+ ret_iva = line.fetch(:retencion_iva, 0)
520
+
521
+ cantidad = line[:cantidad].to_f
522
+
523
+
524
+
525
+ # if line[:tipo_impuesto] == '004'
526
+ # total_acumulator = cantidad * valor_unitario
527
+ # else
528
+ # total_acumulator = cantidad * valor_unitario * 1.16
529
+ # end
530
+
531
+ ## TODO: ajustar todo a facturacion con iva cero
532
+
533
+ total_acumulator = cantidad * line[:valor_unitario].to_f
534
+
535
+ if iva_id == 16
536
+ valor_unitario = (line[:valor_unitario].to_f) / 1.16
537
+ else
538
+ valor_unitario = line[:valor_unitario].to_f
539
+ end
540
+
541
+ total_line = cantidad * valor_unitario
542
+
543
+ importe_iva = total_acumulator - total_line
544
+
545
+ subtotal += total_line
546
+ suma_iva += importe_iva
547
+ suma_total += total_acumulator
548
+
549
+ puts "--- 01"
550
+ ## calculando retencion de IVA en caso de tener
551
+ if ret_iva > 0
552
+ if ret_iva == 6
553
+ importe_ret_linea = (total_line * 1.06) - total_line
554
+ elsif ret_iva == 16
555
+ importe_ret_linea = importe_iva
556
+ end
557
+ else
558
+ importe_ret_linea = 0
559
+ end
560
+ puts "--- 02"
561
+ suma_ret += importe_ret_linea
562
+
563
+
564
+ ## Creando y poblando CFDI:CONCEPTO
565
+ child_concepto = Nokogiri::XML::Node.new "cfdi:Concepto", xml
566
+ child_concepto['ClaveProdServ'] = line[:clave_prod_serv].to_s
567
+ child_concepto['NoIdentificacion'] = line[:sku].to_s
568
+ child_concepto['ClaveUnidad'] = line[:clave_unidad].to_s
569
+ child_concepto['Unidad'] = line[:unidad].to_s
570
+ child_concepto['Descripcion'] = line[:descripcion].to_s
571
+ child_concepto['Cantidad'] = cantidad.to_s
572
+ child_concepto['ValorUnitario'] = valor_unitario.round(4).to_s
573
+ child_concepto['Importe'] = total_line.round(4).to_s
574
+ child_concepto['ObjetoImp'] = '02'
575
+
576
+
577
+ ## Creando cdfi:Impuestos para cada linea
578
+ child_impuestos = Nokogiri::XML::Node.new "cfdi:Impuestos", xml
579
+
580
+ ## Creando cfdi:Traslados para cada linea
581
+ child_traslados = Nokogiri::XML::Node.new "cfdi:Traslados", xml
582
+
583
+
584
+ child_traslado = Nokogiri::XML::Node.new "cfdi:Traslado", xml
585
+ child_traslado['Impuesto'] = '002'
586
+ child_traslado['TipoFactor'] = "Tasa"
587
+ child_traslado['Base'] = total_line.round(4).to_s
588
+
589
+ if iva_id == 0
590
+ child_traslado['Importe'] = "0.00"
591
+ child_traslado['TasaOCuota'] = '0.000000'
592
+ else
593
+ child_traslado['Importe'] = importe_iva.round(4).to_s
594
+ child_traslado['TasaOCuota'] = '0.160000'
595
+ end
596
+
597
+
598
+ # Mezclando todo lo anterios
599
+ child_traslados.add_child(child_traslado)
600
+ child_impuestos.add_child(child_traslados)
601
+ child_concepto.add_child(child_impuestos)
602
+ conceptos.add_child(child_concepto)
603
+
604
+ ## Creando cfdi:Retenciones para cada linea en caso de tener
605
+ if ret_iva > 0
606
+ child_retenciones = Nokogiri::XML::Node.new "cfdi:Retenciones", xml
607
+ child_retencion = Nokogiri::XML::Node.new "cfdi:Retencion", xml
608
+ child_retencion['Base'] = total_line.round(4).to_s
609
+ child_retencion['Impuesto'] = '002'
610
+ child_retencion['TipoFactor'] = "Tasa"
611
+
612
+ if ret_iva == 6
613
+ child_retencion['TasaOCuota'] = "0.060000"
614
+ elsif ret_iva == 16
615
+ child_retencion['TasaOCuota'] = "0.160000"
616
+ end
617
+
618
+ child_retencion['Importe'] = importe_ret_linea.round(4).to_s
619
+
620
+ child_retenciones.add_child(child_retencion)
621
+ child_impuestos.add_child(child_retenciones)
622
+ end
623
+
624
+
625
+ end
626
+
627
+ puts '------ Totales -----'
628
+ puts "Total suma = #{suma_total.round(2)}"
629
+ puts "SubTotal suma = #{subtotal.round(2)}"
630
+ puts "Suma iva = #{suma_iva.round(2)}"
631
+ puts "Suma restenciones = #{suma_ret.round(2)}"
632
+
633
+ comprobante['Moneda'] = params.fetch(:moneda, 'MXN')
634
+ comprobante['SubTotal'] = subtotal.round(2).to_s
635
+
636
+
637
+ ## Poblanco cfdi:Impuestos
638
+ impuestos['TotalImpuestosRetenidos'] = suma_ret.round(2).to_s if suma_ret > 0
639
+
640
+ if iva_id == 0
641
+ impuestos['TotalImpuestosTrasladados'] = "0.00"
642
+ else
643
+ impuestos['TotalImpuestosTrasladados'] = suma_iva.round(2).to_s
644
+ end
645
+
646
+
647
+ ## filling default retencion info
648
+ if suma_ret > 0
649
+ retenciones = Nokogiri::XML::Node.new "cfdi:Retenciones", xml
650
+ retencion_child = Nokogiri::XML::Node.new "cfdi:Retencion", xml
651
+ retencion_child['Impuesto'] = "002"
652
+ retencion_child['Importe'] = suma_ret.round(2).to_s
653
+ # retencion_child['TipoFactor'] = "Tasa"
654
+
655
+ retenciones.add_child(retencion_child)
656
+ impuestos.add_child(retenciones)
657
+ comprobante['Total'] = (suma_total - suma_ret).round(2).to_s
658
+ else
659
+ comprobante['Total'] = suma_total.round(2).to_s
660
+ end
661
+
662
+
663
+ ## filling traslado info
664
+ traslado_child = Nokogiri::XML::Node.new "cfdi:Traslado", xml
665
+ traslado_child['Impuesto'] = '002'
666
+ traslado_child['TipoFactor'] = 'Tasa'
667
+ traslado_child['Base'] = subtotal.round(2)
668
+
669
+ if iva_id == 0
670
+ traslado_child['Importe'] = "0.00"
671
+ traslado_child['TasaOCuota'] = '0.000000'
672
+ else
673
+ traslado_child['Importe'] = suma_iva.round(2).to_s
674
+ traslado_child['TasaOCuota'] = '0.160000'
675
+ end
676
+
677
+ traslados.add_child(traslado_child)
678
+ impuestos.add_child(traslados)
679
+
680
+
681
+ path = File.join(File.dirname(__FILE__), *%w[.. tmp])
682
+ id = SecureRandom.hex
683
+
684
+ FileUtils.mkdir_p(path) unless File.exist?(path)
685
+ File.write("#{path}/tmp_#{id}.xml", xml.to_xml)
686
+ xml_path = "#{path}/tmp_#{id}.xml"
687
+ cadena_path = File.join(File.dirname(__FILE__), *%w[.. cadena cadenaoriginal_4_0.xslt])
688
+
689
+ # puts File.read(cadena_path)
690
+ File.write("#{path}/pem_#{id}.pem", @pem)
691
+ key_pem_url = "#{path}/pem_#{id}.pem"
692
+ sello = %x[xsltproc #{cadena_path} #{xml_path} | openssl dgst -sha256 -sign #{key_pem_url} | openssl enc -base64 -A]
693
+ comprobante['Sello'] = sello
694
+
695
+ File.delete("#{xml_path}")
696
+ File.delete("#{key_pem_url}")
697
+
698
+ puts '---- Fdis: comprobante sin timbrar ------'
699
+ puts xml.to_xml
700
+ puts '-------------------------'
701
+
702
+ base64_xml = Base64.encode64(xml.to_xml)
703
+
704
+ # haciendo llamada a API
705
+ uri = URI("#{Fdis::UrlPro}/Timbrar40")
706
+ request = Net::HTTP::Post.new(uri)
707
+ request.content_type = "application/json"
708
+ request.body = JSON.dump({
709
+ "testMode": !@production,
710
+ "idServicio": @id_servicio,
711
+ "base64XmlFile": base64_xml,
712
+ })
713
+
714
+ req_options = {
715
+ use_ssl: uri.scheme == "https",
716
+ }
717
+
718
+ json_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
719
+ http.request(request)
720
+ end
721
+
722
+ puts "---- Fdis: request"
723
+ puts "-- body: #{request.body} --"
724
+
725
+ puts "---- Fdis: Respuesta"
726
+ puts "-- Codigo: #{json_response.code} --"
727
+ puts "-- Mensaje: #{json_response.message} --"
728
+ puts "-- Body: "
729
+ p json_response.body
730
+
731
+ response = JSON.parse(json_response.body)
732
+
733
+ case json_response
734
+ when Net::HTTPSuccess, Net::HTTPRedirection
735
+ if response['success'] == true
736
+ decoded_xml = Nokogiri::XML(Base64.decode64(response['base64XmlFile']))
737
+ timbre = decoded_xml.at_xpath("//cfdi:Complemento").children[0]
738
+ response = {
739
+ status: 200,
740
+ message_error: '',
741
+ xml: decoded_xml.to_xml,
742
+ uuid: timbre['UUID'],
743
+ fecha_timbrado: timbre['FechaTimbrado'],
744
+ sello_cfd: timbre['SelloCFD'],
745
+ sello_sat: timbre['SelloSAT'],
746
+ no_certificado_sat: timbre['NoCertificadoSAT'],
747
+ }
748
+ return response
749
+ else
750
+ response = {
751
+ status: 400,
752
+ message_error: "Error: #{response['errorMessages']}",
753
+ xml: '',
754
+ uuid: '',
755
+ fecha_timbrado: '',
756
+ sello_cfd: '',
757
+ sello_sat: '',
758
+ no_certificado_sat: '',
759
+ }
760
+ return response
761
+
762
+ end
763
+ else
764
+ response = {
765
+ status: json_response.code,
766
+ message_error: "Error: #{response['errorMessages']}",
767
+ xml: '',
768
+ uuid: '',
769
+ fecha_timbrado: '',
770
+ sello_cfd: '',
771
+ sello_sat: '',
772
+ no_certificado_sat: '',
773
+ }
774
+ return response
775
+
776
+ end
777
+
778
+
779
+ end
780
+
781
+
782
+
783
+ end
784
+
785
+ end