sw_fac 0.3.58 → 0.3.58.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,738 +1,742 @@
1
1
 
2
2
  module SwFac
3
- class Facturacion < Tools
4
-
5
- def comp_pago(params={})
6
- # Sample params
7
- # params = {
8
- # uuid: '',
9
- # venta_folio: '',
10
- # cp: '',
11
- # receptor_razon: 'Car zone',
12
- # receptor_rfc: 'XAXX010101000',
13
- # forma_pago: '01',
14
- # total: 100.00,
15
- # time: '',
16
- # modena: '',
17
- # line_items: [
18
- # {
19
- # monto: 60.00,
20
- # moneda: '',
21
- # },
22
- # ]
23
- # }
24
-
25
- puts " Datos --------"
26
- puts "-- Total params: #{params[:total]}"
27
- puts "--- Line items: "
28
- params[:line_items].each do |line|
29
- puts "--- #{line[:monto]}"
30
- end
31
- lines_total = params[:line_items].inject(0) {|sum, x| sum + x[:monto].to_f}
32
-
33
- puts "-- Suma de line_items: #{lines_total.round(2)}"
34
-
35
- if (lines_total.round(2) > params[:total].to_f)
36
- raise 'Error SW - la suma de los complementos de pago es mayor al total reportado'
37
- end
38
-
39
- uri = @production ? URI("#{SwFac::UrlProduction}cfdi33/stamp/customv1/b64") : URI("#{SwFac::UrlDev}cfdi33/stamp/customv1/b64")
40
- token = @production ? @production_token : @dev_token
41
- time = params.fetch(:time, (Time.now).strftime("%Y-%m-%dT%H:%M:%S"))
42
-
43
-
44
- base_doc = %(<?xml version="1.0" encoding="UTF-8"?>
45
- <cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:pago10="http://www.sat.gob.mx/Pagos" xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd http://www.sat.gob.mx/Pagos http://www.sat.gob.mx/sitio_internet/cfd/Pagos/Pagos10.xsd" Version="3.3" SubTotal="0" Total="0" Moneda="XXX" TipoDeComprobante="P" >
46
- <cfdi:Emisor />
47
- <cfdi:Receptor UsoCFDI="P01"/>
48
- <cfdi:Conceptos>
49
- <cfdi:Concepto ClaveProdServ="84111506" Cantidad="1" ClaveUnidad="ACT" Descripcion="Pago" ValorUnitario="0" Importe="0" />
50
- </cfdi:Conceptos>
51
- <cfdi:Complemento>
52
- <pago10:Pagos Version="1.0">
53
- <pago10:Pago>
54
- </pago10:Pago>
55
- </pago10:Pagos>
56
- </cfdi:Complemento>
57
- </cfdi:Comprobante>)
58
-
59
- base_doc.delete!("\n")
60
- base_doc.delete!("\t")
61
-
62
- xml = Nokogiri::XML(base_doc)
63
- comprobante = xml.at_xpath("//cfdi:Comprobante")
64
- comprobante['Serie'] = 'P'
65
- comprobante['Folio'] = params[:venta_folio].to_s
66
- comprobante['Fecha'] = time
67
- comprobante['LugarExpedicion'] = params[:cp].to_s
68
- comprobante['NoCertificado'] = @serial
69
- comprobante['Certificado'] = @cadena
70
- emisor = xml.at_xpath("//cfdi:Emisor")
71
- emisor['Rfc'] = @rfc
72
- emisor['Nombre'] = @razon
73
- emisor['RegimenFiscal'] = @regimen_fiscal
74
- receptor = xml.at_xpath("//cfdi:Receptor")
75
- receptor['Nombre'] = params[:receptor_razon].to_s
76
- receptor['Rfc'] = params[:receptor_rfc].to_s
77
-
78
- child_pago = xml.at_xpath("//pago10:Pago")
79
- child_pago['FechaPago'] = time
80
- child_pago['FormaDePagoP'] = params[:forma_pago].to_s
81
- child_pago['MonedaP'] = params.fetch(:moneda, 'MXN')
82
- child_pago['Monto'] = params[:total].round(2).to_s
83
-
84
- saldo_anterior = params[:total]
85
-
86
- params[:line_items].each_with_index do |line, index|
87
- monto = line[:monto].to_f
88
- child_pago_relacionado = Nokogiri::XML::Node.new "pago10:DoctoRelacionado", xml
89
- child_pago_relacionado['IdDocumento'] = params[:uuid]
90
- child_pago_relacionado['MonedaDR'] = line.fetch(:moneda, 'MXN')
91
- child_pago_relacionado['MetodoDePagoDR'] = 'PPD'
92
- child_pago_relacionado['NumParcialidad'] = (index + 1).to_s
93
-
94
- child_pago_relacionado['ImpSaldoAnt'] = (saldo_anterior).round(2).to_s
95
- child_pago_relacionado['ImpPagado'] = monto.round(2).to_s
96
- child_pago_relacionado['ImpSaldoInsoluto'] = (saldo_anterior - monto).round(2).to_s
97
- saldo_anterior -= monto
98
-
99
- child_pago.add_child(child_pago_relacionado)
100
- end
101
-
102
- # puts '---------------- Xml resultante comprobante de pago -----------------------'
103
- # puts xml.to_xml
104
- # puts '--------------------------------------------------------'
105
-
106
- path = File.join(File.dirname(__FILE__), *%w[.. tmp])
107
- id = SecureRandom.hex
108
-
109
- FileUtils.mkdir_p(path) unless File.exist?(path)
110
- File.write("#{path}/tmp_c_#{id}.xml", xml.to_xml)
111
- xml_path = "#{path}/tmp_c_#{id}.xml"
112
- cadena_path = File.join(File.dirname(__FILE__), *%w[.. cadena cadena33.xslt])
113
-
114
- File.write("#{path}/pem_#{id}.pem", @pem)
115
- key_pem_url = "#{path}/pem_#{id}.pem"
116
- sello = %x[xsltproc #{cadena_path} #{xml_path} | openssl dgst -sha256 -sign #{key_pem_url} | openssl enc -base64 -A]
117
- comprobante['Sello'] = sello
118
-
119
- File.delete("#{xml_path}")
120
- File.delete("#{key_pem_url}")
121
-
122
- # puts '------ comprobante de pago antes de timbre -------'
123
- # puts xml.to_xml
124
-
125
- base64_xml = Base64.encode64(xml.to_xml)
126
- request = Net::HTTP::Post.new(uri)
127
- request.basic_auth(token, "")
128
- request.content_type = "application/json"
129
- request["cache-control"] = 'no-cache'
130
- request.body = JSON.dump({
131
- "credentials" => {
132
- "id" => params[:venta_folio].to_s,
133
- "token" => token.to_s
134
- },
135
- "issuer" => {
136
- "rfc" => @rfc
137
- },
138
- "document" => {
139
- "ref-id": params[:venta_folio].to_s,
140
- "certificate-number": @serial,
141
- "section": "all",
142
- "format": "xml",
143
- "template": "letter",
144
- "type": "application/xml",
145
- "content": base64_xml
146
- }
147
- })
148
-
149
- req_options = {
150
- use_ssl: false,
151
- }
152
-
153
- json_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
154
- http.request(request)
155
- end
156
- puts "-- #{json_response.code} --"
157
- puts "-- #{json_response.message} --"
158
- # puts "-- Body --"
159
- # puts json_response.body
160
- # puts '---'
161
- response = JSON.parse(json_response.body)
162
-
163
- if json_response.code == '200'
164
- decoded_xml = Nokogiri::XML(Base64.decode64(response['content']))
165
- timbre = decoded_xml.at_xpath("//cfdi:Complemento").children[1]
166
- response = {
167
- status: 200,
168
- message_error: '',
169
- xml: decoded_xml.to_xml,
170
- uuid: response['uuid'],
171
- fecha_timbrado: timbre['FechaTimbrado'],
172
- sello_cfd: timbre['SelloCFD'],
173
- sello_sat: timbre['SelloSAT'],
174
- no_certificado_sat: timbre['NoCertificadoSAT'],
175
- }
176
- return response
177
- else
178
- response = {
179
- status: json_response.code,
180
- message_error: "Error message: #{json_response.message}, #{response['message']} #{response['error_details']}",
181
- xml: '',
182
- uuid: '',
183
- fecha_timbrado: '',
184
- sello_cfd: '',
185
- sello_sat: '',
186
- no_certificado_sat: '',
187
- }
188
- return response
189
- end
190
-
191
- end
192
-
193
- def nota_credito(params={})
194
- # Sample params
195
- # params = {
196
- # uuid_relacionado: '',
197
- # desc: '',
198
- # motivo: 'dev, mod',
199
- # series: '',
200
- # folio: '',
201
- # cp: '',
202
- # time: '',
203
- # receptor_razon: '',
204
- # receptor_rfc: '',
205
- # uso_cfdi: '',
206
- # }
207
-
208
- total = (params[:monto]).to_f
209
- subtotal = total / 1.16
210
- tax = total - subtotal
211
-
212
- uri = @production ? URI("#{SwFac::UrlProduction}cfdi33/stamp/customv1/b64") : URI("#{SwFac::UrlDev}cfdi33/stamp/customv1/b64")
213
- token = @production ? @production_token : @dev_token
214
- time = params.fetch(:time, (Time.now).strftime("%Y-%m-%dT%H:%M:%S"))
215
-
216
-
217
- base_doc = %(<?xml version="1.0" encoding="utf-8"?>
218
- <cfdi:Comprobante xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd" Version="3.3" Serie="#{params.fetch(:series, 'N')}" Folio="#{params[:folio]}" Fecha="#{time}" FormaPago="99" NoCertificado="#{@serial}" Certificado="#{@cadena}" SubTotal="#{subtotal.round(2)}" Moneda="#{params.fetch(:moneda, 'MXN')}" Total="#{total.round(2)}" TipoDeComprobante="E" MetodoPago="PUE" LugarExpedicion="#{params[:cp]}" xmlns:cfdi="http://www.sat.gob.mx/cfd/3">
219
- <cfdi:CfdiRelacionados TipoRelacion="01">
220
- <cfdi:CfdiRelacionado UUID="#{params[:uuid_relacionado]}" />
221
- </cfdi:CfdiRelacionados>
222
- <cfdi:Emisor Rfc="#{@rfc}" Nombre="#{@razon}" RegimenFiscal="#{@regimen_fiscal}" />
223
- <cfdi:Receptor Rfc="#{params[:receptor_rfc]}" Nombre="#{params[:receptor_razon]}" UsoCFDI="#{params.fetch(:uso_cfdi, 'G03')}" />
3
+ class Facturacion < Tools
4
+
5
+ def comp_pago(params={})
6
+ # Sample params
7
+ # params = {
8
+ # uuid: '',
9
+ # venta_folio: '',
10
+ # cp: '',
11
+ # receptor_razon: 'Car zone',
12
+ # receptor_rfc: 'XAXX010101000',
13
+ # forma_pago: '01',
14
+ # total: 100.00,
15
+ # time: '',
16
+ # modena: '',
17
+ # line_items: [
18
+ # {
19
+ # monto: 60.00,
20
+ # moneda: '',
21
+ # },
22
+ # ]
23
+ # }
24
+
25
+ puts " Datos --------"
26
+ puts "-- Total params: #{params[:total]}"
27
+ puts "--- Line items: "
28
+ params[:line_items].each do |line|
29
+ puts "--- #{line[:monto]}"
30
+ end
31
+ lines_total = params[:line_items].inject(0) {|sum, x| sum + x[:monto].to_f}
32
+
33
+ puts "-- Suma de line_items: #{lines_total.round(2)}"
34
+
35
+ if (lines_total.round(2) > params[:total].to_f)
36
+ raise 'Error SW - la suma de los complementos de pago es mayor al total reportado'
37
+ end
38
+
39
+ uri = @production ? URI("#{SwFac::UrlProduction}cfdi33/stamp/customv1/b64") : URI("#{SwFac::UrlDev}cfdi33/stamp/customv1/b64")
40
+ token = @production ? @production_token : @dev_token
41
+ time = params.fetch(:time, (Time.now).strftime("%Y-%m-%dT%H:%M:%S"))
42
+
43
+
44
+ base_doc = %(<?xml version="1.0" encoding="UTF-8"?>
45
+ <cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:pago10="http://www.sat.gob.mx/Pagos" xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd http://www.sat.gob.mx/Pagos http://www.sat.gob.mx/sitio_internet/cfd/Pagos/Pagos10.xsd" Version="3.3" SubTotal="0" Total="0" Moneda="XXX" TipoDeComprobante="P" >
46
+ <cfdi:Emisor />
47
+ <cfdi:Receptor UsoCFDI="P01"/>
224
48
  <cfdi:Conceptos>
225
- <cfdi:Concepto ClaveUnidad="ACT" ClaveProdServ="84111506" NoIdentificacion="C" Cantidad="1.00" Unidad="Pieza" Descripcion="#{params.fetch(:desc, 'DICTAMEN CC FACTURA ORIGEN 0')}" ValorUnitario="#{subtotal.round(2)}" Importe="#{subtotal.round(2)}">
226
- <cfdi:Impuestos>
227
- <cfdi:Traslados>
228
- <cfdi:Traslado Base="#{subtotal.round(2)}" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="#{tax.round(2)}" />
229
- </cfdi:Traslados>
230
- </cfdi:Impuestos>
231
- </cfdi:Concepto>
49
+ <cfdi:Concepto ClaveProdServ="84111506" Cantidad="1" ClaveUnidad="ACT" Descripcion="Pago" ValorUnitario="0" Importe="0" />
232
50
  </cfdi:Conceptos>
233
- <cfdi:Impuestos TotalImpuestosTrasladados="#{tax.round(2)}">
234
- <cfdi:Traslados>
235
- <cfdi:Traslado Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="#{tax.round(2)}" />
236
- </cfdi:Traslados>
237
- </cfdi:Impuestos>
238
- </cfdi:Comprobante>
239
- )
51
+ <cfdi:Complemento>
52
+ <pago10:Pagos Version="1.0">
53
+ <pago10:Pago>
54
+ </pago10:Pago>
55
+ </pago10:Pagos>
56
+ </cfdi:Complemento>
57
+ </cfdi:Comprobante>)
240
58
 
241
59
  base_doc.delete!("\n")
242
- base_doc.delete!("\t")
60
+ base_doc.delete!("\t")
61
+
62
+ xml = Nokogiri::XML(base_doc)
63
+ comprobante = xml.at_xpath("//cfdi:Comprobante")
64
+ comprobante['Serie'] = 'P'
65
+ comprobante['Folio'] = params[:venta_folio].to_s
66
+ comprobante['Fecha'] = time
67
+ comprobante['LugarExpedicion'] = params[:cp].to_s
68
+ comprobante['NoCertificado'] = @serial
69
+ comprobante['Certificado'] = @cadena
70
+ emisor = xml.at_xpath("//cfdi:Emisor")
71
+ emisor['Rfc'] = @rfc
72
+ emisor['Nombre'] = @razon
73
+ emisor['RegimenFiscal'] = @regimen_fiscal
74
+ receptor = xml.at_xpath("//cfdi:Receptor")
75
+ receptor['Nombre'] = params[:receptor_razon].to_s
76
+ receptor['Rfc'] = params[:receptor_rfc].to_s
77
+
78
+ child_pago = xml.at_xpath("//pago10:Pago")
79
+ child_pago['FechaPago'] = time
80
+ child_pago['FormaDePagoP'] = params[:forma_pago].to_s
81
+ child_pago['MonedaP'] = params.fetch(:moneda, 'MXN')
82
+ child_pago['Monto'] = params[:total].round(2).to_s
83
+
84
+ saldo_anterior = params[:total]
85
+
86
+ params[:line_items].each_with_index do |line, index|
87
+ monto = line[:monto].to_f
88
+ child_pago_relacionado = Nokogiri::XML::Node.new "pago10:DoctoRelacionado", xml
89
+ child_pago_relacionado['IdDocumento'] = params[:uuid]
90
+ child_pago_relacionado['MonedaDR'] = line.fetch(:moneda, 'MXN')
91
+ child_pago_relacionado['MetodoDePagoDR'] = 'PPD'
92
+ child_pago_relacionado['NumParcialidad'] = (index + 1).to_s
93
+
94
+ child_pago_relacionado['ImpSaldoAnt'] = (saldo_anterior).round(2).to_s
95
+ child_pago_relacionado['ImpPagado'] = monto.round(2).to_s
96
+ child_pago_relacionado['ImpSaldoInsoluto'] = (saldo_anterior - monto).round(2).to_s
97
+ saldo_anterior -= monto
98
+
99
+ child_pago.add_child(child_pago_relacionado)
100
+ end
101
+
102
+ # puts '---------------- Xml resultante comprobante de pago -----------------------'
103
+ # puts xml.to_xml
104
+ # puts '--------------------------------------------------------'
105
+
106
+ path = File.join(File.dirname(__FILE__), *%w[.. tmp])
107
+ id = SecureRandom.hex
108
+
109
+ FileUtils.mkdir_p(path) unless File.exist?(path)
110
+ File.write("#{path}/tmp_c_#{id}.xml", xml.to_xml)
111
+ xml_path = "#{path}/tmp_c_#{id}.xml"
112
+ cadena_path = File.join(File.dirname(__FILE__), *%w[.. cadena cadena33.xslt])
113
+
114
+ File.write("#{path}/pem_#{id}.pem", @pem)
115
+ key_pem_url = "#{path}/pem_#{id}.pem"
116
+ sello = %x[xsltproc #{cadena_path} #{xml_path} | openssl dgst -sha256 -sign #{key_pem_url} | openssl enc -base64 -A]
117
+ comprobante['Sello'] = sello
118
+
119
+ File.delete("#{xml_path}")
120
+ File.delete("#{key_pem_url}")
121
+
122
+ # puts '------ comprobante de pago antes de timbre -------'
123
+ # puts xml.to_xml
124
+
125
+ base64_xml = Base64.encode64(xml.to_xml)
126
+ request = Net::HTTP::Post.new(uri)
127
+ request.basic_auth(token, "")
128
+ request.content_type = "application/json"
129
+ request["cache-control"] = 'no-cache'
130
+ request.body = JSON.dump({
131
+ "credentials" => {
132
+ "id" => params[:venta_folio].to_s,
133
+ "token" => token.to_s
134
+ },
135
+ "issuer" => {
136
+ "rfc" => @rfc
137
+ },
138
+ "document" => {
139
+ "ref-id": params[:venta_folio].to_s,
140
+ "certificate-number": @serial,
141
+ "section": "all",
142
+ "format": "xml",
143
+ "template": "letter",
144
+ "type": "application/xml",
145
+ "content": base64_xml
146
+ }
147
+ })
148
+
149
+ req_options = {
150
+ use_ssl: false,
151
+ }
152
+
153
+ json_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
154
+ http.request(request)
155
+ end
156
+ puts "-- #{json_response.code} --"
157
+ puts "-- #{json_response.message} --"
158
+ # puts "-- Body --"
159
+ # puts json_response.body
160
+ # puts '---'
161
+ response = JSON.parse(json_response.body)
162
+
163
+ if json_response.code == '200'
164
+ decoded_xml = Nokogiri::XML(Base64.decode64(response['content']))
165
+ timbre = decoded_xml.at_xpath("//cfdi:Complemento").children[1]
166
+ response = {
167
+ status: 200,
168
+ message_error: '',
169
+ xml: decoded_xml.to_xml,
170
+ uuid: response['uuid'],
171
+ fecha_timbrado: timbre['FechaTimbrado'],
172
+ sello_cfd: timbre['SelloCFD'],
173
+ sello_sat: timbre['SelloSAT'],
174
+ no_certificado_sat: timbre['NoCertificadoSAT'],
175
+ }
176
+ return response
177
+ else
178
+ response = {
179
+ status: json_response.code,
180
+ message_error: "Error message: #{json_response.message}, #{response['message']} #{response['error_details']}",
181
+ xml: '',
182
+ uuid: '',
183
+ fecha_timbrado: '',
184
+ sello_cfd: '',
185
+ sello_sat: '',
186
+ no_certificado_sat: '',
187
+ }
188
+ return response
189
+ end
243
190
 
244
- xml = Nokogiri::XML(base_doc)
245
- comprobante = xml.at_xpath("//cfdi:Comprobante")
191
+ end
246
192
 
247
- path = File.join(File.dirname(__FILE__), *%w[.. tmp])
248
- id = SecureRandom.hex
193
+ def nota_credito(params={})
194
+ # Sample params
195
+ # params = {
196
+ # uuid_relacionado: '',
197
+ # desc: '',
198
+ # motivo: 'dev, mod',
199
+ # series: '',
200
+ # folio: '',
201
+ # cp: '',
202
+ # time: '',
203
+ # receptor_razon: '',
204
+ # receptor_rfc: '',
205
+ # uso_cfdi: '',
206
+ # }
207
+
208
+ total = (params[:monto]).to_f
209
+ subtotal = total / 1.16
210
+ tax = total - subtotal
211
+
212
+ uri = @production ? URI("#{SwFac::UrlProduction}cfdi33/stamp/customv1/b64") : URI("#{SwFac::UrlDev}cfdi33/stamp/customv1/b64")
213
+ token = @production ? @production_token : @dev_token
214
+ time = params.fetch(:time, (Time.now).strftime("%Y-%m-%dT%H:%M:%S"))
215
+
216
+
217
+ base_doc = %(<?xml version="1.0" encoding="utf-8"?>
218
+ <cfdi:Comprobante xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd" Version="3.3" Serie="#{params.fetch(:series, 'N')}" Folio="#{params[:folio]}" Fecha="#{time}" FormaPago="99" NoCertificado="#{@serial}" Certificado="#{@cadena}" SubTotal="#{subtotal.round(2)}" Moneda="#{params.fetch(:moneda, 'MXN')}" Total="#{total.round(2)}" TipoDeComprobante="E" MetodoPago="PUE" LugarExpedicion="#{params[:cp]}" xmlns:cfdi="http://www.sat.gob.mx/cfd/3">
219
+ <cfdi:CfdiRelacionados TipoRelacion="01">
220
+ <cfdi:CfdiRelacionado UUID="#{params[:uuid_relacionado]}" />
221
+ </cfdi:CfdiRelacionados>
222
+ <cfdi:Emisor Rfc="#{@rfc}" Nombre="#{@razon}" RegimenFiscal="#{@regimen_fiscal}" />
223
+ <cfdi:Receptor Rfc="#{params[:receptor_rfc]}" Nombre="#{params[:receptor_razon]}" UsoCFDI="#{params.fetch(:uso_cfdi, 'G03')}" />
224
+ <cfdi:Conceptos>
225
+ <cfdi:Concepto ClaveUnidad="ACT" ClaveProdServ="84111506" NoIdentificacion="C" Cantidad="1.00" Unidad="Pieza" Descripcion="#{params.fetch(:desc, 'DICTAMEN CC FACTURA ORIGEN 0')}" ValorUnitario="#{subtotal.round(2)}" Importe="#{subtotal.round(2)}">
226
+ <cfdi:Impuestos>
227
+ <cfdi:Traslados>
228
+ <cfdi:Traslado Base="#{subtotal.round(2)}" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="#{tax.round(2)}" />
229
+ </cfdi:Traslados>
230
+ </cfdi:Impuestos>
231
+ </cfdi:Concepto>
232
+ </cfdi:Conceptos>
233
+ <cfdi:Impuestos TotalImpuestosTrasladados="#{tax.round(2)}">
234
+ <cfdi:Traslados>
235
+ <cfdi:Traslado Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="#{tax.round(2)}" />
236
+ </cfdi:Traslados>
237
+ </cfdi:Impuestos>
238
+ </cfdi:Comprobante>
239
+ )
240
+
241
+ base_doc.delete!("\n")
242
+ base_doc.delete!("\t")
243
+
244
+ xml = Nokogiri::XML(base_doc)
245
+ comprobante = xml.at_xpath("//cfdi:Comprobante")
246
+
247
+ path = File.join(File.dirname(__FILE__), *%w[.. tmp])
248
+ id = SecureRandom.hex
249
+
250
+ FileUtils.mkdir_p(path) unless File.exist?(path)
251
+ File.write("#{path}/tmp_n_#{id}.xml", xml.to_xml)
252
+ xml_path = "#{path}/tmp_n_#{id}.xml"
253
+ cadena_path = File.join(File.dirname(__FILE__), *%w[.. cadena cadena33.xslt])
254
+
255
+ File.write("#{path}/pem_#{id}.pem", @pem)
256
+ key_pem_url = "#{path}/pem_#{id}.pem"
257
+ sello = %x[xsltproc #{cadena_path} #{xml_path} | openssl dgst -sha256 -sign #{key_pem_url} | openssl enc -base64 -A]
258
+ comprobante['Sello'] = sello
259
+
260
+ File.delete("#{xml_path}")
261
+ File.delete("#{key_pem_url}")
262
+
263
+ puts '------ nota antes de timbre -------'
264
+ puts xml.to_xml
265
+
266
+ base64_xml = Base64.encode64(xml.to_xml)
267
+
268
+ request = Net::HTTP::Post.new(uri)
269
+ request.basic_auth(token, "")
270
+ request.content_type = "application/json"
271
+ request["cache-control"] = 'no-cache'
272
+ request.body = JSON.dump({
273
+ "credentials" => {
274
+ "id" => "#{params[:folio]}",
275
+ "token" => token
276
+ },
277
+ "issuer" => {
278
+ "rfc" => @rfc
279
+ },
280
+ "document" => {
281
+ "ref-id": "#{params[:folio]}",
282
+ "certificate-number": @serial,
283
+ "section": "all",
284
+ "format": "xml",
285
+ "template": "letter",
286
+ "type": "application/xml",
287
+ "content": base64_xml
288
+ }
289
+ })
290
+
291
+ req_options = {
292
+ use_ssl: false,
293
+ }
294
+
295
+ json_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
296
+ http.request(request)
297
+ end
298
+
299
+
300
+ puts "-- #{json_response.code} --"
301
+ puts "-- #{json_response.message} --"
302
+ # puts "-- Body --"
303
+ # puts json_response.body
304
+ # puts '---'
305
+ response = JSON.parse(json_response.body)
306
+
307
+ if json_response.code == '200'
308
+ decoded_xml = Nokogiri::XML(Base64.decode64(response['content']))
309
+ timbre = decoded_xml.at_xpath("//cfdi:Complemento").children.first
310
+
311
+ response = {
312
+ status: 200,
313
+ message_error: '',
314
+ xml: decoded_xml.to_xml,
315
+ uuid: response['uuid'],
316
+ fecha_timbrado: timbre['FechaTimbrado'],
317
+ sello_cfd: timbre['SelloCFD'],
318
+ sello_sat: timbre['SelloSAT'],
319
+ no_certificado_sat: timbre['NoCertificadoSAT'],
320
+ }
321
+ return response
322
+ else
323
+ response = {
324
+ status: json_response.code,
325
+ message_error: "Error message: #{json_response.message}, #{response['message']} #{response['error_details']}",
326
+ xml: '',
327
+ uuid: '',
328
+ fecha_timbrado: '',
329
+ sello_cfd: '',
330
+ sello_sat: '',
331
+ no_certificado_sat: '',
332
+ }
333
+ return response
334
+ end
249
335
 
250
- FileUtils.mkdir_p(path) unless File.exist?(path)
251
- File.write("#{path}/tmp_n_#{id}.xml", xml.to_xml)
252
- xml_path = "#{path}/tmp_n_#{id}.xml"
253
- cadena_path = File.join(File.dirname(__FILE__), *%w[.. cadena cadena33.xslt])
254
336
 
255
- File.write("#{path}/pem_#{id}.pem", @pem)
256
- key_pem_url = "#{path}/pem_#{id}.pem"
257
- sello = %x[xsltproc #{cadena_path} #{xml_path} | openssl dgst -sha256 -sign #{key_pem_url} | openssl enc -base64 -A]
258
- comprobante['Sello'] = sello
337
+ end
259
338
 
260
- File.delete("#{xml_path}")
261
- File.delete("#{key_pem_url}")
339
+ def cancela_doc(params={})
340
+ # Sample params
341
+ # params = {
342
+ # uuid: '',
343
+ # rfc_emisor: '',
344
+ # key_password: '', # optional
345
+ # cer_cadena: '', # optional
346
+ # key_pem: '' # optional
347
+ # }
348
+
349
+ uri = @production ? URI("#{SwFac::UrlProduction}cfdi33/cancel/csd") : URI("#{SwFac::UrlDev}cfdi33/cancel/csd")
350
+ token = @production ? @production_token : @dev_token
351
+ # time = params.fetch(:time, (Time.now).strftime("%Y-%m-%dT%H:%M:%S"))
352
+
353
+
354
+ request = Net::HTTP::Post.new(uri)
355
+ request["Authorization"] = "bearer #{token}"
356
+ request.content_type = "application/json"
357
+ request["Cache-Control"] = 'no-cache'
358
+ request["Postman-Token"] = '30b35bb8-534d-51c7-6a5c-e2c98a0c9395'
359
+ request.body = JSON.dump({
360
+ 'uuid': params[:uuid],
361
+ "password": params.fetch(:key_password, @key_pass),
362
+ "rfc": params.fetch(:rfc_emisor, @rfc),
363
+ "b64Cer": params.fetch(:cer_cadena, @cadena),
364
+ "b64Key": params.fetch(:key_pem, @pem_cadena)
365
+ })
366
+
367
+ req_options = {
368
+ use_ssl: false,
369
+ }
370
+
371
+ json_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
372
+ http.request(request)
373
+ end
374
+
375
+ puts "-- #{json_response.code} --"
376
+ puts "-- #{json_response.message} --"
377
+ # puts "-- Body --"
378
+ # puts json_response.body
379
+ # puts '---'
380
+ response = JSON.parse(json_response.body)
381
+
382
+ if json_response.code == '200'
383
+ decoded_xml = response['data']['acuse']
384
+
385
+ response = {
386
+ status: 200,
387
+ message_error: '',
388
+ xml: decoded_xml,
389
+ }
390
+
391
+ return response
392
+ else
393
+
394
+ response ={
395
+ status: json_response.code,
396
+ message_error: "Error message: #{json_response.message}, #{response['message']} #{response['error_details']}",
397
+ xml: '',
398
+ }
399
+
400
+ return response
401
+ end
262
402
 
263
- puts '------ nota antes de timbre -------'
264
- puts xml.to_xml
403
+ end
265
404
 
266
- base64_xml = Base64.encode64(xml.to_xml)
405
+ def timbra_doc(params={})
406
+ ### sample params
407
+ #
408
+ # params = {
409
+ # moneda: 'MXN',
410
+ # series: 'FA',
411
+ # folio: '003',
412
+ # forma_pago: '',
413
+ # metodo_pago: 'PUE',
414
+ # cp: '47180',
415
+ # receptor_razon: 'Car zone',
416
+ # receptor_rfc: '',
417
+ # uso_cfdi: 'G03',
418
+ # time: "%Y-%m-%dT%H:%M:%S",
419
+ # line_items: [
420
+ # {
421
+ # clave_prod_serv: '78181500',
422
+ # clave_unidad: 'E48',
423
+ # unidad: 'Servicio',
424
+ # sku: 'serv001',
425
+ # cantidad: 1,
426
+ # descripcion: 'Servicio mano de obra',
427
+ # valor_unitario: 100.00,
428
+ # descuento: 0.00,
429
+ # tax_included: true,
430
+ # retencion_iva: 0, 6, 16
431
+ # # Optional parameters
432
+ # },
433
+ # ]
434
+
435
+ # }
436
+
437
+ puts "---- SwFacturacion:facturacion:timbra_doc"
438
+
439
+
440
+ uri = @production ? URI("#{SwFac::UrlProduction}cfdi33/stamp/customv1/b64") : URI("#{SwFac::UrlDev}cfdi33/stamp/customv1/b64")
441
+ token = @production ? @production_token : @dev_token
442
+ time = params.fetch(:time, (Time.now).strftime("%Y-%m-%dT%H:%M:%S"))
443
+
444
+ xml = Nokogiri::XML(SwFac::DocBase)
445
+ comprobante = xml.at_xpath("//cfdi:Comprobante")
446
+ comprobante['TipoCambio'] = '1'
447
+ comprobante['TipoDeComprobante'] = 'I'
448
+ comprobante['Serie'] = params.fetch(:series, 'FA').to_s
449
+ comprobante['Folio'] = params.fetch(:folio).to_s
450
+ comprobante['Fecha'] = time.to_s
451
+ comprobante['FormaPago'] = params.fetch(:forma_pago, '01')
452
+ comprobante['MetodoPago'] = params.fetch(:metodo_pago, 'PUE')
453
+ comprobante['LugarExpedicion'] = params.fetch(:cp, '')
454
+ comprobante['NoCertificado'] = @serial
455
+ comprobante['Certificado'] = @cadena
456
+
457
+ emisor = xml.at_xpath("//cfdi:Emisor")
458
+ emisor['Nombre'] = @razon
459
+ emisor['RegimenFiscal'] = @regimen_fiscal
460
+ emisor['Rfc'] = @rfc
461
+
462
+ receptor = xml.at_xpath("//cfdi:Receptor")
463
+ receptor['Nombre'] = params.fetch(:receptor_razon, '')
464
+ receptor['Rfc'] = params.fetch(:receptor_rfc, '')
465
+ receptor['UsoCFDI'] = params.fetch(:uso_cfdi, 'G03')
466
+
467
+ # retencion_iva = params.fetch(:retencion_iva, 0)
468
+
469
+ impuestos = xml.at_xpath("//cfdi:Impuestos")
470
+ traslados = Nokogiri::XML::Node.new "cfdi:Traslados", xml
471
+
472
+
473
+ puts '--- sw_fac time -----'
474
+ puts time
475
+ puts '--------'
476
+
477
+ conceptos = xml.at_xpath("//cfdi:Conceptos")
478
+
479
+ line_items = params[:line_items]
480
+
481
+ suma_total = 0.00
482
+ subtotal = 0.00
483
+ suma_iva = 0.00
484
+ suma_ret = 0.00
485
+
486
+ line_items.each do |line|
487
+ ret_iva = line.fetch(:retencion_iva, 0)
488
+ puts ret_iva
489
+
490
+
491
+ ## revisando si la linea tiene iva 0
492
+ if line[:tax_included] == true
493
+ # if line[:tipo_impuesto] == '004'
494
+ # valor_unitario = (line[:valor_unitario].to_f)
495
+ # else
496
+ # end
497
+ valor_unitario = ((line[:valor_unitario]).to_f) / 1.16
498
+ else
499
+ valor_unitario = (line[:valor_unitario].to_f)
500
+ end
501
+
502
+ cantidad = line[:cantidad].to_f
503
+ total_line = cantidad * valor_unitario
504
+
505
+ # if line[:tipo_impuesto] == '004'
506
+ # total_acumulator = cantidad * valor_unitario
507
+ # else
508
+ # total_acumulator = cantidad * valor_unitario * 1.16
509
+ # end
267
510
 
268
- request = Net::HTTP::Post.new(uri)
269
- request.basic_auth(token, "")
270
- request.content_type = "application/json"
271
- request["cache-control"] = 'no-cache'
272
- request.body = JSON.dump({
273
- "credentials" => {
274
- "id" => "#{params[:folio]}",
275
- "token" => token
276
- },
277
- "issuer" => {
278
- "rfc" => @rfc
279
- },
280
- "document" => {
281
- "ref-id": "#{params[:folio]}",
282
- "certificate-number": @serial,
283
- "section": "all",
284
- "format": "xml",
285
- "template": "letter",
286
- "type": "application/xml",
287
- "content": base64_xml
288
- }
289
- })
290
-
291
- req_options = {
292
- use_ssl: false,
293
- }
294
-
295
- json_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
296
- http.request(request)
297
- end
298
-
299
-
300
- puts "-- #{json_response.code} --"
301
- puts "-- #{json_response.message} --"
302
- # puts "-- Body --"
303
- # puts json_response.body
304
- # puts '---'
305
- response = JSON.parse(json_response.body)
306
-
307
- if json_response.code == '200'
308
- decoded_xml = Nokogiri::XML(Base64.decode64(response['content']))
309
- timbre = decoded_xml.at_xpath("//cfdi:Complemento").children.first
310
-
311
- response = {
312
- status: 200,
313
- message_error: '',
314
- xml: decoded_xml.to_xml,
315
- uuid: response['uuid'],
316
- fecha_timbrado: timbre['FechaTimbrado'],
317
- sello_cfd: timbre['SelloCFD'],
318
- sello_sat: timbre['SelloSAT'],
319
- no_certificado_sat: timbre['NoCertificadoSAT'],
320
- }
321
- return response
322
- else
323
- response = {
324
- status: json_response.code,
325
- message_error: "Error message: #{json_response.message}, #{response['message']} #{response['error_details']}",
326
- xml: '',
327
- uuid: '',
328
- fecha_timbrado: '',
329
- sello_cfd: '',
330
- sello_sat: '',
331
- no_certificado_sat: '',
332
- }
333
- return response
334
- end
335
-
336
-
337
- end
338
-
339
- def cancela_doc(params={})
340
- # Sample params
341
- # params = {
342
- # uuid: '',
343
- # rfc_emisor: '',
344
- # key_password: '', # optional
345
- # cer_cadena: '', # optional
346
- # key_pem: '' # optional
347
- # }
348
-
349
- uri = @production ? URI("#{SwFac::UrlProduction}cfdi33/cancel/csd") : URI("#{SwFac::UrlDev}cfdi33/cancel/csd")
350
- token = @production ? @production_token : @dev_token
351
- # time = params.fetch(:time, (Time.now).strftime("%Y-%m-%dT%H:%M:%S"))
352
-
353
-
354
- request = Net::HTTP::Post.new(uri)
355
- request["Authorization"] = "bearer #{token}"
356
- request.content_type = "application/json"
357
- request["Cache-Control"] = 'no-cache'
358
- request["Postman-Token"] = '30b35bb8-534d-51c7-6a5c-e2c98a0c9395'
359
- request.body = JSON.dump({
360
- 'uuid': params[:uuid],
361
- "password": params.fetch(:key_password, @key_pass),
362
- "rfc": params.fetch(:rfc_emisor, @rfc),
363
- "b64Cer": params.fetch(:cer_cadena, @cadena),
364
- "b64Key": params.fetch(:key_pem, @pem_cadena)
365
- })
366
-
367
- req_options = {
368
- use_ssl: false,
369
- }
370
-
371
- json_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
372
- http.request(request)
373
- end
374
-
375
- puts "-- #{json_response.code} --"
376
- puts "-- #{json_response.message} --"
377
- # puts "-- Body --"
378
- # puts json_response.body
379
- # puts '---'
380
- response = JSON.parse(json_response.body)
381
-
382
- if json_response.code == '200'
383
- decoded_xml = response['data']['acuse']
384
-
385
- response = {
386
- status: 200,
387
- message_error: '',
388
- xml: decoded_xml,
389
- }
390
-
391
- return response
392
- else
393
-
394
- response ={
395
- status: json_response.code,
396
- message_error: "Error message: #{json_response.message}, #{response['message']} #{response['error_details']}",
397
- xml: '',
398
- }
399
-
400
- return response
401
- end
402
-
403
- end
404
-
405
- def timbra_doc(params={})
406
- ### sample params
407
- #
408
- # params = {
409
- # moneda: 'MXN',
410
- # series: 'FA',
411
- # folio: '003',
412
- # forma_pago: '',
413
- # metodo_pago: 'PUE',
414
- # cp: '47180',
415
- # receptor_razon: 'Car zone',
416
- # receptor_rfc: '',
417
- # uso_cfdi: 'G03',
418
- # time: "%Y-%m-%dT%H:%M:%S",
419
- # line_items: [
420
- # {
421
- # clave_prod_serv: '78181500',
422
- # clave_unidad: 'E48',
423
- # unidad: 'Servicio',
424
- # sku: 'serv001',
425
- # cantidad: 1,
426
- # descripcion: 'Servicio mano de obra',
427
- # valor_unitario: 100.00,
428
- # descuento: 0.00,
429
- # tax_included: true,
430
- # retencion_iva: 0, 6, 16
431
- # # Optional parameters
432
- # },
433
- # ]
434
-
435
- # }
436
-
437
-
438
- uri = @production ? URI("#{SwFac::UrlProduction}cfdi33/stamp/customv1/b64") : URI("#{SwFac::UrlDev}cfdi33/stamp/customv1/b64")
439
- token = @production ? @production_token : @dev_token
440
- time = params.fetch(:time, (Time.now).strftime("%Y-%m-%dT%H:%M:%S"))
441
-
442
- xml = Nokogiri::XML(SwFac::DocBase)
443
- comprobante = xml.at_xpath("//cfdi:Comprobante")
444
- comprobante['TipoCambio'] = '1'
445
- comprobante['TipoDeComprobante'] = 'I'
446
- comprobante['Serie'] = params.fetch(:series, 'FA').to_s
447
- comprobante['Folio'] = params.fetch(:folio).to_s
448
- comprobante['Fecha'] = time.to_s
449
- comprobante['FormaPago'] = params.fetch(:forma_pago, '01')
450
- comprobante['MetodoPago'] = params.fetch(:metodo_pago, 'PUE')
451
- comprobante['LugarExpedicion'] = params.fetch(:cp, '')
452
- comprobante['NoCertificado'] = @serial
453
- comprobante['Certificado'] = @cadena
454
-
455
- emisor = xml.at_xpath("//cfdi:Emisor")
456
- emisor['Nombre'] = @razon
457
- emisor['RegimenFiscal'] = @regimen_fiscal
458
- emisor['Rfc'] = @rfc
459
-
460
- receptor = xml.at_xpath("//cfdi:Receptor")
461
- receptor['Nombre'] = params.fetch(:receptor_razon, '')
462
- receptor['Rfc'] = params.fetch(:receptor_rfc, '')
463
- receptor['UsoCFDI'] = params.fetch(:uso_cfdi, 'G03')
464
-
465
- # retencion_iva = params.fetch(:retencion_iva, 0)
466
-
467
- impuestos = xml.at_xpath("//cfdi:Impuestos")
468
- traslados = Nokogiri::XML::Node.new "cfdi:Traslados", xml
469
-
470
-
471
- puts '--- sw_fac time -----'
472
- puts time
473
- puts '--------'
474
-
475
- conceptos = xml.at_xpath("//cfdi:Conceptos")
476
-
477
- line_items = params[:line_items]
478
-
479
- suma_total = 0.00
480
- subtotal = 0.00
481
- suma_iva = 0.00
482
- suma_ret = 0.00
483
-
484
-
485
-
486
- line_items.each do |line|
487
- ret_iva = line.fetch(:retencion_iva, 0)
488
- puts ret_iva
489
-
490
-
491
- ## revisando si la linea tiene iva 0
492
- if line[:tax_included] == true
493
- # if line[:tipo_impuesto] == '004'
494
- # valor_unitario = (line[:valor_unitario].to_f)
495
- # else
496
- # end
497
- valor_unitario = ((line[:valor_unitario]).to_f) / 1.16
498
- else
499
- valor_unitario = (line[:valor_unitario].to_f)
500
- end
501
-
502
- cantidad = line[:cantidad].to_f
503
- total_line = cantidad * valor_unitario
504
-
505
- # if line[:tipo_impuesto] == '004'
506
- # total_acumulator = cantidad * valor_unitario
507
- # else
508
- # total_acumulator = cantidad * valor_unitario * 1.16
509
- # end
510
-
511
- total_acumulator = cantidad * valor_unitario * 1.16
512
-
513
- importe_iva = total_acumulator - total_line
514
- subtotal += total_line
515
- suma_iva += importe_iva
516
- suma_total += total_acumulator
517
-
518
- puts "--- 01"
519
- ## calculando retencion de IVA en caso de tener
520
- if ret_iva > 0
521
- if ret_iva == 6
522
- importe_ret_linea = (total_line * 1.06) - total_line
523
- elsif ret_iva == 16
524
- importe_ret_linea = importe_iva
525
- end
526
- else
527
- importe_ret_linea = 0
528
- end
529
- puts "--- 02"
530
- suma_ret += importe_ret_linea
531
-
532
-
533
- ## Creando y poblando CFDI:CONCEPTO
534
- child_concepto = Nokogiri::XML::Node.new "cfdi:Concepto", xml
535
- child_concepto['ClaveProdServ'] = line[:clave_prod_serv].to_s
536
- child_concepto['NoIdentificacion'] = line[:sku].to_s
537
- child_concepto['ClaveUnidad'] = line[:clave_unidad].to_s
538
- child_concepto['Unidad'] = line[:unidad].to_s
539
- child_concepto['Descripcion'] = line[:descripcion].to_s
540
- child_concepto['Cantidad'] = cantidad.to_s
541
- child_concepto['ValorUnitario'] = valor_unitario.round(4).to_s
542
- child_concepto['Importe'] = total_line.round(4).to_s
543
- # child_concepto['Descuento'] = line.fetch(:descuento, 0.00).round(6).to_s
544
-
545
-
546
- ## Creando cdfi:Impuestos para cada linea
547
- child_impuestos = Nokogiri::XML::Node.new "cfdi:Impuestos", xml
548
-
549
- ## Creando cfdi:Traslados para cada linea
550
- child_traslados = Nokogiri::XML::Node.new "cfdi:Traslados", xml
551
- child_traslado = Nokogiri::XML::Node.new "cfdi:Traslado", xml
552
- child_traslado['Base'] = total_line.round(4).to_s
553
- child_traslado['Impuesto'] = '002'
554
- child_traslado['TipoFactor'] = "Tasa"
555
- child_traslado['TasaOCuota'] = '0.160000'
556
- child_traslado['Importe'] = importe_iva.round(4).to_s
557
-
558
- # if line[:tipo_impuesto] == '004'
559
- # child_traslado['TasaOCuota'] = '0.000000'
560
- # else
561
- # end
562
-
563
-
564
- # Joining all up
565
- child_traslados.add_child(child_traslado)
566
- child_impuestos.add_child(child_traslados)
567
- child_concepto.add_child(child_impuestos)
568
- conceptos.add_child(child_concepto)
569
-
570
- ## Creando cfdi:Retenciones para cada linea en caso de tener
571
- if ret_iva > 0
572
- child_retenciones = Nokogiri::XML::Node.new "cfdi:Retenciones", xml
573
- child_retencion = Nokogiri::XML::Node.new "cfdi:Retencion", xml
574
- child_retencion['Base'] = total_line.round(4).to_s
575
- child_retencion['Impuesto'] = '002'
576
- child_retencion['TipoFactor'] = "Tasa"
577
-
578
- if ret_iva == 6
579
- child_retencion['TasaOCuota'] = "0.060000"
580
- elsif ret_iva == 16
581
- child_retencion['TasaOCuota'] = "0.160000"
582
- end
583
-
584
- child_retencion['Importe'] = importe_ret_linea.round(4).to_s
585
-
586
- child_retenciones.add_child(child_retencion)
587
- child_impuestos.add_child(child_retenciones)
588
- end
589
-
590
-
591
-
592
- end
593
-
594
- puts '------ Totales -----'
595
- puts "Total suma = #{suma_total.round(2)}"
596
- puts "SubTotal suma = #{subtotal.round(2)}"
597
- puts "Suma iva = #{suma_iva.round(2)}"
598
- puts "Suma restenciones = #{suma_ret.round(2)}"
599
-
600
- comprobante['Moneda'] = params.fetch(:moneda, 'MXN')
601
- comprobante['SubTotal'] = subtotal.round(2).to_s
602
-
603
-
604
- ## Poblanco cfdi:Impuestos
605
- impuestos['TotalImpuestosRetenidos'] = suma_ret.round(2).to_s if suma_ret > 0
606
- impuestos['TotalImpuestosTrasladados'] = suma_iva.round(2).to_s
607
-
608
- ## filling default retencion info
609
- if suma_ret > 0
610
- retenciones = Nokogiri::XML::Node.new "cfdi:Retenciones", xml
611
- retencion_child = Nokogiri::XML::Node.new "cfdi:Retencion", xml
612
- retencion_child['Impuesto'] = "002"
613
- retencion_child['Importe'] = suma_ret.round(2).to_s
614
- # retencion_child['TipoFactor'] = "Tasa"
615
-
616
- retenciones.add_child(retencion_child)
617
- impuestos.add_child(retenciones)
618
- comprobante['Total'] = (suma_total - suma_ret).round(2).to_s
619
- else
620
- comprobante['Total'] = suma_total.round(2).to_s
621
- end
622
-
623
-
624
- ## filling traslado info
625
- traslado_child = Nokogiri::XML::Node.new "cfdi:Traslado", xml
626
- traslado_child['Impuesto'] = '002'
627
- traslado_child['TipoFactor'] = 'Tasa'
628
- traslado_child['TasaOCuota'] = '0.160000'
629
- traslado_child['Importe'] = suma_iva.round(2).to_s
630
- traslados.add_child(traslado_child)
631
- impuestos.add_child(traslados)
632
-
633
-
634
-
635
- # puts '------ Totales -----'
636
- # puts "Total suma = #{comprobante['Total']}"
637
- # puts "SubTotal suma = #{subtotal}"
638
- # puts "Suma iva = #{suma_iva}"
639
- # puts "Suma retenciones = #{impuestos['TotalImpuestosRetenidos']}" if suma_ret > 0
640
-
641
-
642
-
643
- path = File.join(File.dirname(__FILE__), *%w[.. tmp])
644
- id = SecureRandom.hex
645
-
646
- FileUtils.mkdir_p(path) unless File.exist?(path)
647
- File.write("#{path}/tmp_#{id}.xml", xml.to_xml)
648
- xml_path = "#{path}/tmp_#{id}.xml"
649
- cadena_path = File.join(File.dirname(__FILE__), *%w[.. cadena cadena33.xslt])
650
-
651
- # puts File.read(cadena_path)
652
- File.write("#{path}/pem_#{id}.pem", @pem)
653
- key_pem_url = "#{path}/pem_#{id}.pem"
654
- sello = %x[xsltproc #{cadena_path} #{xml_path} | openssl dgst -sha256 -sign #{key_pem_url} | openssl enc -base64 -A]
655
- comprobante['Sello'] = sello
656
-
657
- File.delete("#{xml_path}")
658
- File.delete("#{key_pem_url}")
659
-
660
- puts '---- SW GEM comprobante sin timbrar ------'
661
- puts xml.to_xml
662
- puts '-------------------------'
663
-
664
- base64_xml = Base64.encode64(xml.to_xml)
665
- request = Net::HTTP::Post.new(uri)
666
- request.basic_auth(token, "")
667
- request.content_type = "application/json"
668
- request["cache-control"] = 'no-cache'
669
- request.body = JSON.dump({
670
- "credentials" => {
671
- "id" => params.fetch(:folio).to_s,
672
- "token" => token
673
- },
674
- "issuer" => {
675
- "rfc" => emisor['Rfc']
676
- },
677
- "document" => {
678
- "ref-id": params.fetch(:folio).to_s,
679
- "certificate-number": comprobante['NoCertificado'],
680
- "section": "all",
681
- "format": "xml",
682
- "template": "letter",
683
- "type": "application/xml",
684
- "content": base64_xml
685
- }
686
- })
687
-
688
- req_options = {
689
- use_ssl: false,
690
- }
691
-
692
- json_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
693
- http.request(request)
694
- end
695
-
696
- puts "-- #{json_response.code} --"
697
- puts "-- #{json_response.message} --"
698
- # puts "-- Body --"
699
- # puts json_response.body
700
- # puts '---'
701
- response = JSON.parse(json_response.body)
702
-
703
- if json_response.code == '200'
704
- decoded_xml = Nokogiri::XML(Base64.decode64(response['content']))
705
- timbre = decoded_xml.at_xpath("//cfdi:Complemento").children.first
706
-
707
- response = {
708
- status: 200,
709
- message_error: '',
710
- xml: decoded_xml.to_xml,
711
- uuid: response['uuid'],
712
- fecha_timbrado: timbre['FechaTimbrado'],
713
- sello_cfd: timbre['SelloCFD'],
714
- sello_sat: timbre['SelloSAT'],
715
- no_certificado_sat: timbre['NoCertificadoSAT'],
716
- }
717
-
718
- return response
719
- else
720
-
721
- response ={
722
- status: json_response.code,
723
- message_error: "Error message: #{json_response.message}, #{response['message']} #{response['error_details']}",
724
- xml: '',
725
- uuid: '',
726
- fecha_timbrado: '',
727
- sello_cfd: '',
728
- sello_sat: '',
729
- no_certificado_sat: '',
730
- }
731
-
732
- return response
733
- end
734
-
735
- end
736
-
737
- end
511
+ total_acumulator = cantidad * valor_unitario * 1.16
512
+
513
+ importe_iva = total_acumulator - total_line
514
+ subtotal += total_line
515
+ suma_iva += importe_iva
516
+ suma_total += total_acumulator
517
+
518
+ puts "--- 01"
519
+ ## calculando retencion de IVA en caso de tener
520
+ if ret_iva > 0
521
+ if ret_iva == 6
522
+ importe_ret_linea = (total_line * 1.06) - total_line
523
+ elsif ret_iva == 16
524
+ importe_ret_linea = importe_iva
525
+ end
526
+ else
527
+ importe_ret_linea = 0
528
+ end
529
+ puts "--- 02"
530
+ suma_ret += importe_ret_linea
531
+
532
+
533
+ ## Creando y poblando CFDI:CONCEPTO
534
+ child_concepto = Nokogiri::XML::Node.new "cfdi:Concepto", xml
535
+ child_concepto['ClaveProdServ'] = line[:clave_prod_serv].to_s
536
+ child_concepto['NoIdentificacion'] = line[:sku].to_s
537
+ child_concepto['ClaveUnidad'] = line[:clave_unidad].to_s
538
+ child_concepto['Unidad'] = line[:unidad].to_s
539
+ child_concepto['Descripcion'] = line[:descripcion].to_s
540
+ child_concepto['Cantidad'] = cantidad.to_s
541
+ child_concepto['ValorUnitario'] = valor_unitario.round(4).to_s
542
+ child_concepto['Importe'] = total_line.round(4).to_s
543
+ # child_concepto['Descuento'] = line.fetch(:descuento, 0.00).round(6).to_s
544
+
545
+
546
+ ## Creando cdfi:Impuestos para cada linea
547
+ child_impuestos = Nokogiri::XML::Node.new "cfdi:Impuestos", xml
548
+
549
+ ## Creando cfdi:Traslados para cada linea
550
+ child_traslados = Nokogiri::XML::Node.new "cfdi:Traslados", xml
551
+ child_traslado = Nokogiri::XML::Node.new "cfdi:Traslado", xml
552
+ child_traslado['Base'] = total_line.round(4).to_s
553
+ child_traslado['Impuesto'] = '002'
554
+ child_traslado['TipoFactor'] = "Tasa"
555
+ child_traslado['TasaOCuota'] = '0.160000'
556
+ child_traslado['Importe'] = importe_iva.round(4).to_s
557
+
558
+ # if line[:tipo_impuesto] == '004'
559
+ # child_traslado['TasaOCuota'] = '0.000000'
560
+ # else
561
+ # end
562
+
563
+
564
+ # Joining all up
565
+ child_traslados.add_child(child_traslado)
566
+ child_impuestos.add_child(child_traslados)
567
+ child_concepto.add_child(child_impuestos)
568
+ conceptos.add_child(child_concepto)
569
+
570
+ ## Creando cfdi:Retenciones para cada linea en caso de tener
571
+ if ret_iva > 0
572
+ child_retenciones = Nokogiri::XML::Node.new "cfdi:Retenciones", xml
573
+ child_retencion = Nokogiri::XML::Node.new "cfdi:Retencion", xml
574
+ child_retencion['Base'] = total_line.round(4).to_s
575
+ child_retencion['Impuesto'] = '002'
576
+ child_retencion['TipoFactor'] = "Tasa"
577
+
578
+ if ret_iva == 6
579
+ child_retencion['TasaOCuota'] = "0.060000"
580
+ elsif ret_iva == 16
581
+ child_retencion['TasaOCuota'] = "0.160000"
582
+ end
583
+
584
+ child_retencion['Importe'] = importe_ret_linea.round(4).to_s
585
+
586
+ child_retenciones.add_child(child_retencion)
587
+ child_impuestos.add_child(child_retenciones)
588
+ end
589
+
590
+
591
+ # ???
592
+ end
593
+
594
+ puts '------ Totales -----'
595
+ puts "Total suma = #{suma_total.round(2)}"
596
+ puts "SubTotal suma = #{subtotal.round(2)}"
597
+ puts "Suma iva = #{suma_iva.round(2)}"
598
+ puts "Suma restenciones = #{suma_ret.round(2)}"
599
+
600
+ comprobante['Moneda'] = params.fetch(:moneda, 'MXN')
601
+ comprobante['SubTotal'] = subtotal.round(2).to_s
602
+
603
+
604
+ ## Poblanco cfdi:Impuestos
605
+ impuestos['TotalImpuestosRetenidos'] = suma_ret.round(2).to_s if suma_ret > 0
606
+ impuestos['TotalImpuestosTrasladados'] = suma_iva.round(2).to_s
607
+
608
+ ## filling default retencion info
609
+ if suma_ret > 0
610
+ retenciones = Nokogiri::XML::Node.new "cfdi:Retenciones", xml
611
+ retencion_child = Nokogiri::XML::Node.new "cfdi:Retencion", xml
612
+ retencion_child['Impuesto'] = "002"
613
+ retencion_child['Importe'] = suma_ret.round(2).to_s
614
+ # retencion_child['TipoFactor'] = "Tasa"
615
+
616
+ retenciones.add_child(retencion_child)
617
+ impuestos.add_child(retenciones)
618
+ comprobante['Total'] = (suma_total - suma_ret).round(2).to_s
619
+ else
620
+ comprobante['Total'] = suma_total.round(2).to_s
621
+ end
622
+
623
+
624
+ ## filling traslado info
625
+ traslado_child = Nokogiri::XML::Node.new "cfdi:Traslado", xml
626
+ traslado_child['Impuesto'] = '002'
627
+ traslado_child['TipoFactor'] = 'Tasa'
628
+ traslado_child['TasaOCuota'] = '0.160000'
629
+ traslado_child['Importe'] = suma_iva.round(2).to_s
630
+ traslados.add_child(traslado_child)
631
+ impuestos.add_child(traslados)
632
+
633
+
634
+
635
+ # puts '------ Totales -----'
636
+ # puts "Total suma = #{comprobante['Total']}"
637
+ # puts "SubTotal suma = #{subtotal}"
638
+ # puts "Suma iva = #{suma_iva}"
639
+ # puts "Suma retenciones = #{impuestos['TotalImpuestosRetenidos']}" if suma_ret > 0
640
+
641
+
642
+
643
+ path = File.join(File.dirname(__FILE__), *%w[.. tmp])
644
+ id = SecureRandom.hex
645
+
646
+ FileUtils.mkdir_p(path) unless File.exist?(path)
647
+ File.write("#{path}/tmp_#{id}.xml", xml.to_xml)
648
+ xml_path = "#{path}/tmp_#{id}.xml"
649
+ cadena_path = File.join(File.dirname(__FILE__), *%w[.. cadena cadena33.xslt])
650
+
651
+ # puts File.read(cadena_path)
652
+ File.write("#{path}/pem_#{id}.pem", @pem)
653
+ key_pem_url = "#{path}/pem_#{id}.pem"
654
+ sello = %x[xsltproc #{cadena_path} #{xml_path} | openssl dgst -sha256 -sign #{key_pem_url} | openssl enc -base64 -A]
655
+ comprobante['Sello'] = sello
656
+
657
+ File.delete("#{xml_path}")
658
+ File.delete("#{key_pem_url}")
659
+
660
+ puts '---- SW GEM comprobante sin timbrar ------'
661
+ puts xml.to_xml
662
+ puts '-------------------------'
663
+
664
+ base64_xml = Base64.encode64(xml.to_xml)
665
+ request = Net::HTTP::Post.new(uri)
666
+ request.basic_auth(token, "")
667
+ request.content_type = "application/json"
668
+ request["cache-control"] = 'no-cache'
669
+ request.body = JSON.dump({
670
+ "credentials" => {
671
+ "id" => params.fetch(:folio).to_s,
672
+ "token" => token
673
+ },
674
+ "issuer" => {
675
+ "rfc" => emisor['Rfc']
676
+ },
677
+ "document" => {
678
+ "ref-id": params.fetch(:folio).to_s,
679
+ "certificate-number": comprobante['NoCertificado'],
680
+ "section": "all",
681
+ "format": "xml",
682
+ "template": "letter",
683
+ "type": "application/xml",
684
+ "content": base64_xml
685
+ }
686
+ })
687
+
688
+ req_options = {
689
+ use_ssl: false,
690
+ }
691
+
692
+ json_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
693
+ http.request(request)
694
+ end
695
+
696
+ puts "-- #{json_response.code} --"
697
+ puts "-- #{json_response.message} --"
698
+ # puts "-- Body --"
699
+ # puts json_response.body
700
+ # puts '---'
701
+ response = JSON.parse(json_response.body)
702
+
703
+ if json_response.code == '200'
704
+ decoded_xml = Nokogiri::XML(Base64.decode64(response['content']))
705
+ timbre = decoded_xml.at_xpath("//cfdi:Complemento").children.first
706
+
707
+ response = {
708
+ status: 200,
709
+ message_error: '',
710
+ xml: decoded_xml.to_xml,
711
+ uuid: response['uuid'],
712
+ fecha_timbrado: timbre['FechaTimbrado'],
713
+ sello_cfd: timbre['SelloCFD'],
714
+ sello_sat: timbre['SelloSAT'],
715
+ no_certificado_sat: timbre['NoCertificadoSAT'],
716
+ }
717
+
718
+ return response
719
+ else
720
+
721
+ response ={
722
+ status: json_response.code,
723
+ message_error: "Error message: #{json_response.message}, #{response['message']} #{response['error_details']}",
724
+ xml: '',
725
+ uuid: '',
726
+ fecha_timbrado: '',
727
+ sello_cfd: '',
728
+ sello_sat: '',
729
+ no_certificado_sat: '',
730
+ }
731
+
732
+ return response
733
+ end
734
+
735
+
736
+
737
+ end
738
+
739
+
740
+ end
741
+
738
742
  end