fdis2 0.1.34

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.
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