fm_timbrado_cfdi 0.0.1.alpha → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3f2b840f296b634da2bbba4dde5e1fc075cb9c70
4
- data.tar.gz: 5af90d177466131f1d1085a0bd2961f5febc5fc7
3
+ metadata.gz: d2582e25a56146d9dc81168d01ee98697244367b
4
+ data.tar.gz: 2c34955c53b3db7c4c21f8540259510924421991
5
5
  SHA512:
6
- metadata.gz: 2ab5cbf56f9372c4789b95275809a45291fea49926d3e43d32ecca900b513428f92f5800b1c7999b302d5abe5a4bbbd6b83de0b7812905d4dd5e57ce77b586a9
7
- data.tar.gz: abd639e1e09925c9f75bfe37e405c012f0fa65d041c838cb26097d24900f85884bbb9095c062c95140d69e627770c873edb62031782e059b780a65144f04843d
6
+ metadata.gz: fb32a28f691f19aef58e428385bbc3299a4b0dcdc146fdfc790f787ffa23b1797c3b7d6ed93bd08046fd8c1964da7ec6b4354c00a8a03c7e3d1616d8ebed6813
7
+ data.tar.gz: 7de560ae159cc58026ad86e2a4b2e45894a523cc0996d3f0319d895f0d7fc0afc1918156c9c04296b312f10d12541bac9b61be1de4f22e86248b9c09bef2f76d
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'coveralls', require: false
3
4
  # Specify your gem's dependencies in fm_timbrado_cfdi.gemspec
4
5
  gemspec
data/README.md CHANGED
@@ -1,4 +1,7 @@
1
1
  # FmTimbradoCfdi
2
+ [![Build Status](https://travis-ci.org/LogicalBricks/fm_timbrado_cfdi.png?branch=master)](https://travis-ci.org/LogicalBricks/fm_timbrado_cfdi)
3
+ [![Code Climate](https://codeclimate.com/github/LogicalBricks/fm_timbrado_cfdi.png)](https://codeclimate.com/github/LogicalBricks/fm_timbrado_cfdi)
4
+ [![Coverage Status](https://coveralls.io/repos/LogicalBricks/fm_timbrado_cfdi/badge.png)](https://coveralls.io/r/LogicalBricks/fm_timbrado_cfdi)
2
5
 
3
6
  Implementa la conexión con el servicio de timbrado cfdi con el PAC Facturación Moderna [Guía de Desarrollo de FM](http://developers.facturacionmoderna.com).
4
7
 
@@ -22,7 +25,7 @@ o instala de forma independiente:
22
25
 
23
26
  Para usar la gema es necesario realizar la configuración con los valores de conexión:
24
27
 
25
- ```
28
+ ```ruby
26
29
  FmTimbradoCfdi.configurar do |config|
27
30
  config.user_id = 'user_id'
28
31
  config.user_pass = 'password'
@@ -36,27 +39,27 @@ end # configurar
36
39
 
37
40
  Y realizar la petición de timbrado:
38
41
 
39
- ```
42
+ ```ruby
40
43
  respuesta = FmTimbradoCfdi.timbra_cfdi_layout rfc, 'layout_file', false
41
- => # Petición sin generación del CBB
44
+ # => Petición sin generación del CBB
42
45
  respuesta = FmTimbradoCfdi.timbra_cfdi_layout rfc, 'layout_file', true
43
- => # Petición con generación del CBB
46
+ # => Petición con generación del CBB
44
47
  ```
45
48
 
46
49
  Si cuentas con el XML sellado puedes hacer lo siguiente:
47
50
 
48
- ```
51
+ ```ruby
49
52
  respuesta = FmTimbradoCfdi.timbra_cfdi_xml 'archivo_xml', false
50
- => # Petición sin generación del CBB
53
+ # => Petición sin generación del CBB
51
54
  respuesta = FmTimbradoCfdi.timbra_cfdi_xml 'archivo_xml', true
52
- => # Petición con generación del CBB
55
+ # => Petición con generación del CBB
53
56
  ```
54
57
 
55
58
  Para un método más general de timbrado que se encuentre más acorde a la documentación de [Facturación Moderna](http://developers.facturacionmoderna.com):
56
59
 
57
- ```
60
+ ```ruby
58
61
  respuesta = FmTimbradoCfdi.timbrar rfc_emisor, 'archivo_xml_o_layout', 'generarCBB' => false, 'generarPDF' => true, 'generarTXT' => false
59
- => # Generar la respuesta con formato PDF, pero sin formato CBB ni TXT
62
+ # => Generar la respuesta con formato PDF, pero sin formato CBB ni TXT
60
63
  ```
61
64
 
62
65
 
@@ -6,7 +6,7 @@ require 'fm_timbrado_cfdi/version'
6
6
  Gem::Specification.new do |gem|
7
7
  gem.name = "fm_timbrado_cfdi"
8
8
  gem.version = FmTimbradoCfdi::VERSION
9
- gem.authors = ["Carlos García", "Hermes Ojeda Ruiz"]
9
+ gem.authors = ["Carlos García", "Hermes Ojeda Ruiz", "Crisóforo Gaspar Hernández @mitogh"]
10
10
  gem.email = ["hermes.ojeda@logicalbricks.com"]
11
11
  gem.homepage = "https://github.com/LogicalBricks/fm_timbrado_cfdi"
12
12
  gem.summary = %q{Implementación en ruby de la conexión con el servicio de timbrado de cfdi con el PAC Facturación Moderna}
@@ -1,9 +1,9 @@
1
1
  require 'savon'
2
2
  require 'fm_timbrado_cfdi/fm_respuesta'
3
+ require 'fm_timbrado_cfdi/fm_respuesta_cancelacion'
3
4
 
4
5
  module FmTimbradoCfdi
5
6
  class FmCliente
6
- #attrs
7
7
  attr_accessor :user_id, :user_pass, :namespace, :fm_wsdl, :endpoint, :ssl_verify_mode, :log, :log_level
8
8
 
9
9
  def initialize
@@ -39,10 +39,37 @@ module FmTimbradoCfdi
39
39
  FmRespuesta.new(response)
40
40
  end
41
41
 
42
- def peticion_timbrar rfc_emisor, documento, generar_cbb
43
- timbrar(rfc_emisor, documento, 'generarCBB' => generar_cbb)
42
+ def subir_certificado(rfc, certificado, llave, password, opciones = {})
43
+ configurar_cliente
44
+ respuesta = @client.call(:activar_cancelacion,
45
+ message:
46
+ { "param0" => {
47
+ "UserPass" => user_pass,
48
+ "UserID" => user_id,
49
+ "emisorRFC" => rfc,
50
+ "archivoCer" => Base64::encode64(certificado),
51
+ "archivoKey" => Base64::encode64(llave),
52
+ "clave" => password,
53
+ }.merge(opciones)
54
+ })
55
+ FmRespuestaCancelacion.new(respuesta)
44
56
  end
45
57
 
58
+ def cancelar(rfc, uuid, opciones = {})
59
+ configurar_cliente
60
+ respuesta = @client.call(:request_cancelar_cfdi,
61
+ message:
62
+ { "param0" => {
63
+ "UserPass" => user_pass,
64
+ "UserID" => user_id,
65
+ "emisorRFC" => rfc,
66
+ "uuid" => uuid,
67
+ }.merge(opciones)
68
+ })
69
+ FmRespuestaCancelacion.new(respuesta)
70
+ end
71
+
72
+
46
73
  private
47
74
  def configurar_cliente
48
75
  @client = Savon.client(
@@ -55,5 +82,5 @@ module FmTimbradoCfdi
55
82
  )
56
83
  end
57
84
 
58
- end #class
59
- end#module
85
+ end
86
+ end
@@ -17,11 +17,11 @@ module FmTimbradoCfdi
17
17
  @doc = Nokogiri::XML(savon_response.to_xml)
18
18
  @xml = obtener_xml(@doc)
19
19
  @no_csd_emisor = obtener_no_csd_emisor(@xml) if @xml
20
- @timbre = obtener_timbre(@doc)
20
+ @timbre = obtener_timbre(@xml)
21
21
  @pdf = obtener_pdf(@doc)
22
22
  @cbb = obtener_cbb(@doc)
23
23
  else
24
- @errors << savon_response.soap_fault.to_s if savon_response.soap_fault.present?
24
+ @errors << savon_response.soap_fault.to_s if savon_response.soap_fault?
25
25
  @doc = @xml = @no_csd_emisor = @timbre = @pdf = @cbb = nil
26
26
  end
27
27
  rescue Exception => e
@@ -34,27 +34,27 @@ module FmTimbradoCfdi
34
34
  end
35
35
 
36
36
  def xml?
37
- @xml
37
+ !!@xml
38
38
  end
39
39
  alias :xml_present? :xml?
40
40
 
41
41
  def cbb?
42
- @cbb
42
+ !!@cbb
43
43
  end
44
44
  alias :cbb_present? :cbb?
45
45
 
46
46
  def pdf?
47
- @pdf
47
+ !!@pdf
48
48
  end
49
49
  alias :pdf_present? :pdf?
50
50
 
51
51
  def timbre?
52
- @timbre
52
+ !!@timbre
53
53
  end
54
54
  alias :timbre_present? :timbre?
55
55
 
56
56
  def no_csd_emisor?
57
- @no_csd_emisor
57
+ !!@no_csd_emisor
58
58
  end
59
59
  alias :no_csd_emisor_present? :no_csd_emisor?
60
60
 
@@ -69,10 +69,8 @@ module FmTimbradoCfdi
69
69
  end
70
70
  end
71
71
 
72
- def obtener_timbre(doc)
73
- unless doc.xpath("//txt").empty?
74
- FmTimbre.new Base64::decode64( doc.xpath("//txt")[0].content )
75
- end
72
+ def obtener_timbre(xml)
73
+ FmTimbre.new(xml) if xml
76
74
  end
77
75
 
78
76
  def obtener_pdf(doc)
@@ -0,0 +1,29 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'nokogiri'
3
+ require 'savon'
4
+
5
+ module FmTimbradoCfdi
6
+ class FmRespuestaCancelacion
7
+ attr_reader :error
8
+ def initialize(savon_response)
9
+ @respuesta = savon_response
10
+ unless valid?
11
+ #@error = @respuesta.http_error.to_s if @respuesta.http_error?
12
+ @error = @respuesta.to_s if @respuesta.soap_fault?
13
+ end
14
+ end
15
+
16
+ def codigo
17
+ @error.match(/\((.*)\).*/)[0] if @error
18
+ end
19
+
20
+ def mensaje
21
+ @error
22
+ end
23
+
24
+ def valid?
25
+ @respuesta.success?
26
+ end
27
+
28
+ end
29
+ end
@@ -6,50 +6,75 @@ module FmTimbradoCfdi
6
6
  attr_reader :no_certificado_sat, :fecha_timbrado, :uuid, :sello_sat, :sello_cfd, :fecha_comprobante, :serie, :folio, :trans_id
7
7
 
8
8
  def initialize ( nodo_timbre )
9
- parse( nodo_timbre )
10
- end # initialize
11
-
9
+ parse( nodo_timbre )
10
+ end
11
+
12
12
  def parse ( nodo_timbre )
13
- valores = nodo_timbre.split("\n")
14
- return unless valores.size == 9
13
+ xml = Nokogiri::XML(nodo_timbre)
14
+ ns = generar_namespaces(xml)
15
+ atributos.each do |variable|
16
+ instance_variable_set("@#{variable}", send("obtener_#{variable}", xml, ns))
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def generar_namespaces(xml)
23
+ namespaces = xml.collect_namespaces
24
+ ns = {}
25
+ namespaces.each_pair do |key, value|
26
+ ns[key.sub(/^xmlns:/, '')] = value
27
+ end
28
+ ns
29
+ end
15
30
 
16
- # TransID
17
- temp_value = valores[0].chomp.split('|')
18
- @trans_id = temp_value[1] if temp_value.size > 1
31
+ def atributos
32
+ [ 'trans_id',
33
+ 'no_certificado_sat',
34
+ 'fecha_timbrado',
35
+ 'uuid',
36
+ 'sello_sat',
37
+ 'sello_cfd',
38
+ 'fecha_comprobante',
39
+ 'serie',
40
+ 'folio' ]
41
+ end
19
42
 
20
- # noCertificadoSAT
21
- temp_value = valores[1].chomp.split('|')
22
- @no_certificado_sat = temp_value[1] if temp_value.size > 1
43
+ def obtener_trans_id(xml,ns)
44
+ xml.xpath("//cfdi:Comprobante",ns).attribute('TransID').value rescue nil
45
+ end
23
46
 
24
- # FechaTimbrado
25
- temp_value = valores[2].chomp.split('|')
26
- @fecha_timbrado = temp_value[1] if temp_value.size > 1
47
+ def obtener_no_certificado_sat(xml, ns)
48
+ xml.xpath("//tfd:TimbreFiscalDigital", ns).attribute('noCertificadoSAT').value rescue nil
49
+ end
27
50
 
28
- # uuid
29
- temp_value = valores[3].chomp.split('|')
30
- @uuid = temp_value[1] if temp_value.size > 1
51
+ def obtener_uuid(xml,ns)
52
+ xml.xpath("//tfd:TimbreFiscalDigital", ns).attribute('UUID').value rescue nil
53
+ end
31
54
 
32
- # selloSAT
33
- temp_value = valores[4].chomp.split('|')
34
- @sello_sat = temp_value[1] if temp_value.size > 1
55
+ def obtener_fecha_timbrado(xml, ns)
56
+ xml.xpath("//tfd:TimbreFiscalDigital", ns).attribute('FechaTimbrado').value rescue nil
57
+ end
35
58
 
36
- # selloCFD
37
- temp_value = valores[5].chomp.split('|')
38
- @sello_cfd = temp_value[1] if temp_value.size > 1
59
+ def obtener_sello_sat(xml, ns)
60
+ xml.xpath("//tfd:TimbreFiscalDigital", ns).attribute('selloSAT').value rescue nil
61
+ end
39
62
 
40
- # Fecha
41
- temp_value = valores[6].chomp.split('|')
42
- @fecha_comprobante = temp_value[1] if temp_value.size > 1
63
+ def obtener_fecha_comprobante(xml, ns)
64
+ xml.xpath("//cfdi:Comprobante", ns).attribute('fecha').value rescue nil
65
+ end
43
66
 
44
- # Serie
45
- temp_value = valores[7].chomp.split('|')
46
- @serie = temp_value[1] if temp_value.size > 1
67
+ def obtener_sello_cfd(xml, ns)
68
+ xml.xpath("//tfd:TimbreFiscalDigital", ns).attribute('selloCFD').value rescue nil
69
+ end
47
70
 
48
- # folio
49
- temp_value = valores[8].chomp.split('|')
50
- @folio = temp_value[1] if temp_value.size > 1
51
- end # parse
71
+ def obtener_serie(xml, ns )
72
+ xml.xpath("//cfdi:Comprobante", ns).attribute('serie').value rescue nil
73
+ end
52
74
 
53
- end # class
54
- end # module
75
+ def obtener_folio(xml, ns)
76
+ xml.xpath("//cfdi:Comprobante", ns).attribute('folio').value rescue nil
77
+ end
78
+ end
79
+ end
55
80
 
@@ -1,3 +1,3 @@
1
1
  module FmTimbradoCfdi
2
- VERSION = "0.0.1.alpha"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -50,8 +50,27 @@ module FmTimbradoCfdi
50
50
  respuesta = cliente.timbrar rfc, archivo, opciones
51
51
  end
52
52
 
53
- #no implementado todavía
54
- def cancela_cfdi (uuid)
53
+ # Public: Envía CSD para que lo almacene el PAC
54
+ #
55
+ # rfc - Es el RFC del emisor
56
+ # certificado - El contenido del archivo de certificado
57
+ # llave - La llave privada del certificado
58
+ # password - Contraseña de la llave privada
59
+ #
60
+ # Regresa un objeto de tipo FmRespuestaCancelacion
61
+ def activar_cancelacion(rfc, certificado, llave, password)
62
+ respuesta = cliente.subir_certificado rfc, certificado, llave, password
63
+ end
64
+ alias :subir_certificado :activar_cancelacion
65
+
66
+ # Public: Envía una petición de cancelación de factura
67
+ #
68
+ # rfc - Es el RFC del emisor
69
+ # uuid - Es el identificador de la factura a cancelar
70
+ #
71
+ # Regresa una respuesta SOAP
72
+ def cancelar(rfc, uuid)
73
+ respuesta = cliente.cancelar(rfc, uuid)
55
74
  end
56
75
 
57
76
  end
@@ -0,0 +1,15 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICXQIBAAKBgQCumKgINyxzj6QCg/ThZHDYAecI/t5OKBzyt2xX5LuEwhLMLTIS
3
+ LeS52QreFCtHKfUmKSjGtpGVWJupOs6YVoPW8796bx8LfZgepBRsgKVO9XDpNerp
4
+ 6i9qP6Q4+uczbVAXw8ipCgVBkDGILar1aumlyYqaijkvzbAyhFLdKtYIUwIDAQAB
5
+ AoGAdvLn35j/PsfInDIcXoZYXsGFDlrdR3AKsM2IEaVyn7K8rb/I0hvULU2SGtSw
6
+ 223ZZ6M49ZCiRJY2jqfS4nRmoqYNRqxYhYt9r2caoqtyL2ZOsmFil1uuzueeYi0r
7
+ Sj6WvLwlmET+EC6acHBvvo5Y/hsZrtxCTWmOY9LFtn32+KECQQDYZ4l6VnwAc6gV
8
+ XPqedRrm7Htpam334lKKvomkSCdnYfcaUB6OZ0iGR5T43DLq0VbUk2Sj6L7LIRh/
9
+ 2fAO6hzRAkEAzorPuHnoTuHhGcSU9KDgG6tuQ0YYM/kUktlwaKgTNURZB6BXXr8W
10
+ 0i36pLuAJ/6wJB1GtgB7fem8g0eR3EmL4wJBAIqUQwWEvIPNjKWhlRxEAaY/aghn
11
+ j2ReGl2fBqWdTx51Lbg79lVa7qaO0GdNG44j4GbgvGH6ebO6zeWRupjScwECQQCH
12
+ 30dg+0l4wx2bHvhCqaZkbXU+6li3sA75v/Mz1gW0z/6lhrSTTrALmlW9kt9u8Bwd
13
+ 1eiTiGO05FOwJR/uQ6+zAkBkVwBY0+2ONgEff7gJKLNHEvG2DdQdDS+Pl1/udDyW
14
+ /GqzhGFeif2+IjROyLwsqbRD5hMynXVwc60YnMIcALNr
15
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="https://t2demo.facturacionmoderna.com/timbrado/soap" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:requestTimbrarCFDIResponse><return xsi:type="SOAP-ENC:Struct"><xml xsi:type="xsd:string">PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGNmZGk6Q29tcHJvYmFudGUgeG1sbnM6Y2ZkaT0iaHR0cDovL3d3dy5zYXQuZ29iLm14L2NmZC8zIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhzaTpzY2hlbWFMb2NhdGlvbj0iaHR0cDovL3d3dy5zYXQuZ29iLm14L2NmZC8zIGh0dHA6Ly93d3cuc2F0LmdvYi5teC9zaXRpb19pbnRlcm5ldC9jZmQvMy9jZmR2MzIueHNkIiB2ZXJzaW9uPSIzLjIiIGZlY2hhPSIyMDEzLTEyLTI3VDE2OjEzOjQ2IiBmb2xpbz0iMTAxIiBzZXJpZT0iQTEiIHN1YlRvdGFsPSIxMDAuMDAiIGRlc2N1ZW50bz0iMC4wMCIgdG90YWw9IjExNi4wMCIgTW9uZWRhPSJNWE4iIFRpcG9DYW1iaW89IjAuMDAiIGNvbmRpY2lvbmVzRGVQYWdvPSJQYWdvIGVuIHVuYSBzb2xhIEV4aGliaWNpw7NuIiB0aXBvRGVDb21wcm9iYW50ZT0iaW5ncmVzbyIgbm9DZXJ0aWZpY2Fkbz0iMjAwMDEwMDAwMDAyMDAwMDAxOTIiIGNlcnRpZmljYWRvPSJNSUlFUmpDQ0F5NmdBd0lCQWdJVU1qQXdNREV3TURBd01EQXlNREF3TURBeE9USXdEUVlKS29aSWh2Y05BUUVGQlFBd2dnRmNNUm93R0FZRFZRUUREQkZCTGtNdUlESWdaR1VnY0hKMVpXSmhjekV2TUMwR0ExVUVDZ3dtVTJWeWRtbGphVzhnWkdVZ1FXUnRhVzVwYzNSeVlXTnB3N051SUZSeWFXSjFkR0Z5YVdFeE9EQTJCZ05WQkFzTUwwRmtiV2x1YVhOMGNtRmphY096YmlCa1pTQlRaV2QxY21sa1lXUWdaR1VnYkdFZ1NXNW1iM0p0WVdOcHc3TnVNU2t3SndZSktvWklodmNOQVFrQkZocGhjMmx6Ym1WMFFIQnlkV1ZpWVhNdWMyRjBMbWR2WWk1dGVERW1NQ1FHQTFVRUNRd2RRWFl1SUVocFpHRnNaMjhnTnpjc0lFTnZiQzRnUjNWbGNuSmxjbTh4RGpBTUJnTlZCQkVNQlRBMk16QXdNUXN3Q1FZRFZRUUdFd0pOV0RFWk1CY0dBMVVFQ0F3UVJHbHpkSEpwZEc4Z1JtVmtaWEpoYkRFU01CQUdBMVVFQnd3SlEyOTViMkZqdzZGdU1UUXdNZ1lKS29aSWh2Y05BUWtDRENWU1pYTndiMjV6WVdKc1pUb2dRWEpoWTJWc2FTQkhZVzVrWVhKaElFSmhkWFJwYzNSaE1CNFhEVEV5TVRBeU1qSXdORGd3TmxvWERURTJNVEF5TWpJd05EZ3dObG93Z2NBeElUQWZCZ05WQkFNVEdFVkVTVlJQVWtsQlRDQlRTVk5VUVNCVFFTQkVSU0JEVmpFaE1COEdBMVVFS1JNWVJVUkpWRTlTU1VGTUlGTkpVMVJCSUZOQklFUkZJRU5XTVNFd0h3WURWUVFLRXhoRlJFbFVUMUpKUVV3Z1UwbFRWRUVnVTBFZ1JFVWdRMVl4SlRBakJnTlZCQzBUSEVWVFNUa3lNRFF5TnpnNE5pQXZJRWhGUjFRM05qRXdNRE0wVXpJeEhqQWNCZ05WQkFVVEZTQXZJRWhGUjFRM05qRXdNRE5OUkVaU1RrNHdPVEVPTUF3R0ExVUVDeE1GUVcxcFoyRXdnWjh3RFFZSktvWklodmNOQVFFQkJRQURnWTBBTUlHSkFvR0JBSzZZcUFnM0xIT1BwQUtEOU9Ga2NOZ0I1d2orM2s0b0hQSzNiRmZrdTRUQ0Vzd3RNaEl0NUxuWkN0NFVLMGNwOVNZcEtNYTJrWlZZbTZrNnpwaFdnOWJ6djNwdkh3dDltQjZrRkd5QXBVNzFjT2sxNnVucUwyby9wRGo2NXpOdFVCZkR5S2tLQlVHUU1ZZ3RxdlZxNmFYSmlwcUtPUy9Oc0RLRVV0MHExZ2hUQWdNQkFBR2pIVEFiTUF3R0ExVWRFd0VCL3dRQ01BQXdDd1lEVlIwUEJBUURBZ2JBTUEwR0NTcUdTSWIzRFFFQkJRVUFBNElCQVFDZ0NDcFlEYm1OOG5wTFg2dkhDRUpBRi8xRzNLaHVBR1k0OHdWdDF2YTFZWFJ5NnlqK3RKWjlIWjNsOFRZN0o2bjEwWGtyYnplZE9Bckt5amRwZnFXYk5hYlpIdzZiMklsU044SHJIbGlpQUVvcjVod1RVSm5rZzZTMW5tMG1taXJJbnhDbVdvVlYrcXJFWC9YSHlsSitPaklxbC9HeVpySDlzRVBDWStMWUZmVmhPMFU3M2pLcWFqTUVlWnRXWXE1d3JvNFVoUFVobFlnd2hselVONlhOV3RpQzhvaGpFNlFKYWY4allSc0pqSHJhWkwyNTdPMW81VC8zVUxNSmhBU043UjIxMUZteUF1aVlzcThIM2J1UEUwa2wrRW1OOERVTnBURGtCOVhmMWJhNmdYWjBQUlhGMW9HVXd4dlg5RmlJNWVJYXUyUkxmeTlBZkpQY3BBWE4iIGZvcm1hRGVQYWdvPSJDb250YWRvIiBtZXRvZG9EZVBhZ289IkVmZWN0aXZvIiBOdW1DdGFQYWdvPSIwMDA5IC0gQmFuYW1leCIgTHVnYXJFeHBlZGljaW9uPSJTYW4gUGVkcm8gR2FyemEgR2FyY8OtYSIgc2VsbG89ImZXb1RaN1NUWlpCb09hbHJ4SnF0V0hDckxYYW4vMWE4RnNiMkpDNjhqc3pSZ3VKclkrdXpYVDAxd2ZWZ1ZmM2lCdFM2MVVjTStNVlk4R21UQWVPOE9OaVJMc3lMci9kYjk4aVdRWlZ0OFlha3dySVJlT1Z0UVdnS0pZR3Z5cmx6U1BVYWJsS0E5bUtaWTVVNGtNd0ZzVXZmWmdBR3N5UGRxV0JDaUV3OFU3VT0iPjxjZmRpOkVtaXNvciBub21icmU9IkNPTUVSQ0lBTElaQURPUkEgU0EgREUgQ1YiIHJmYz0iRVNJOTIwNDI3ODg2Ij48Y2ZkaTpEb21pY2lsaW9GaXNjYWwgY2FsbGU9IkNhbHphZGEgZGVsIFZhbGxlIiBub0V4dGVyaW9yPSI5MCIgbm9JbnRlcmlvcj0iaW50LTEwIiBjb2xvbmlhPSJDb2wuIERlbCBWYWxsZSIgbXVuaWNpcGlvPSJTYW4gUGVkcm8gR2FyemEgR2FyY2lhLiIgZXN0YWRvPSJOdWV2byBMZcOzbiIgcGFpcz0iTcOpeGljbyIgY29kaWdvUG9zdGFsPSI3Njg4OCIvPjxjZmRpOkV4cGVkaWRvRW4gY2FsbGU9IkNhbHphZGEgZGVsIFZhbGxlKFN1Y3Vyc2FsKSIgbm9FeHRlcmlvcj0iOTAiIG5vSW50ZXJpb3I9ImludC0xMCIgY29sb25pYT0iQ29sLiBEZWwgVmFsbGUiIG11bmljaXBpbz0iU2FuIFBlZHJvIEdhcnphIEdhcmNpYS4iIGVzdGFkbz0iTnVldm8gTGXDs24iIHBhaXM9Ik3DqXhpY28iIGNvZGlnb1Bvc3RhbD0iNzY4ODgiLz48Y2ZkaTpSZWdpbWVuRmlzY2FsIFJlZ2ltZW49IlBFUlNPTkEgTU9SQUwgUkVHSU1FTiBHRU5FUkFMIERFIExFWS4iLz48L2NmZGk6RW1pc29yPjxjZmRpOlJlY2VwdG9yIG5vbWJyZT0iUFVCTElDTyBFTiBHRU5FUkFMIiByZmM9IlhBWFgwMTAxMDEwMDAiPjxjZmRpOkRvbWljaWxpbyBjYWxsZT0iQ2FsbGUiIG5vRXh0ZXJpb3I9Ik51bSBFeHQiIG5vSW50ZXJpb3I9Ik51bSBJbnQiIGNvbG9uaWE9IkNvbG9uaWEiIG11bmljaXBpbz0iTXVuaWNpcGlvIiBlc3RhZG89IkVzdGFkbyIgcGFpcz0iUGFpcyIgY29kaWdvUG9zdGFsPSI2MDAwMCIvPjwvY2ZkaTpSZWNlcHRvcj48Y2ZkaTpDb25jZXB0b3M+PGNmZGk6Q29uY2VwdG8gbm9JZGVudGlmaWNhY2lvbj0iNzg5OTcwMSIgdW5pZGFkPSJQaWV6YSIgZGVzY3JpcGNpb249IkNhamEgZGUgQ2hvY29sYXRlcyIgY2FudGlkYWQ9IjEuMDAiIHZhbG9yVW5pdGFyaW89IjUwLjAwIiBpbXBvcnRlPSI1MC4wMCIvPjxjZmRpOkNvbmNlcHRvIHVuaWRhZD0iTm8gYXBsaWNhIiBkZXNjcmlwY2lvbj0iRW52aW8iIGNhbnRpZGFkPSIxLjAwIiB2YWxvclVuaXRhcmlvPSI1MC4wMCIgaW1wb3J0ZT0iNTAuMDAiLz48L2NmZGk6Q29uY2VwdG9zPjxjZmRpOkltcHVlc3RvcyB0b3RhbEltcHVlc3Rvc1JldGVuaWRvcz0iMjAwLjAwIiB0b3RhbEltcHVlc3Rvc1RyYXNsYWRhZG9zPSIxNi4wMCI+PGNmZGk6UmV0ZW5jaW9uZXM+PGNmZGk6UmV0ZW5jaW9uIGltcHVlc3RvPSJJU1IiIGltcG9ydGU9IjIwMC4wMCIvPjwvY2ZkaTpSZXRlbmNpb25lcz48Y2ZkaTpUcmFzbGFkb3M+PGNmZGk6VHJhc2xhZG8gaW1wdWVzdG89IklWQSIgdGFzYT0iMTYuMDAiIGltcG9ydGU9IjE2LjAwIi8+PC9jZmRpOlRyYXNsYWRvcz48L2NmZGk6SW1wdWVzdG9zPjxjZmRpOkNvbXBsZW1lbnRvPjx0ZmQ6VGltYnJlRmlzY2FsRGlnaXRhbCB4bWxuczp0ZmQ9Imh0dHA6Ly93d3cuc2F0LmdvYi5teC9UaW1icmVGaXNjYWxEaWdpdGFsIiB4c2k6c2NoZW1hTG9jYXRpb249Imh0dHA6Ly93d3cuc2F0LmdvYi5teC9UaW1icmVGaXNjYWxEaWdpdGFsIGh0dHA6Ly93d3cuc2F0LmdvYi5teC9UaW1icmVGaXNjYWxEaWdpdGFsL1RpbWJyZUZpc2NhbERpZ2l0YWwueHNkIiB2ZXJzaW9uPSIxLjAiIFVVSUQ9IjZGMzI4QjY1LTY1RDMtNDI3RS04NTYzLTZDQkFCRURBODZFQyIgRmVjaGFUaW1icmFkbz0iMjAxMy0xMi0yN1QxNjoxMzo0NiIgc2VsbG9DRkQ9ImZXb1RaN1NUWlpCb09hbHJ4SnF0V0hDckxYYW4vMWE4RnNiMkpDNjhqc3pSZ3VKclkrdXpYVDAxd2ZWZ1ZmM2lCdFM2MVVjTStNVlk4R21UQWVPOE9OaVJMc3lMci9kYjk4aVdRWlZ0OFlha3dySVJlT1Z0UVdnS0pZR3Z5cmx6U1BVYWJsS0E5bUtaWTVVNGtNd0ZzVXZmWmdBR3N5UGRxV0JDaUV3OFU3VT0iIG5vQ2VydGlmaWNhZG9TQVQ9IjIwMDAxMDAwMDAwMTAwMDA1NzYxIiBzZWxsb1NBVD0icEhyOXcwNWpwSnRGeVRUc0tURTRxYytsWW0rL3B6Y2VBOXNobXpEQXYrc0tlbU1wU3YyWjI2UFBiNlp1TFBEdUVKZGpsZ05leDJROUpmMXVxM2Z4aXdYSEZLM1JlK3paZHNYdWRjbTVjaTNNSU5VU21pNkpLVWx2OGpSajdRbGFHTFdqTWNsMFo5RjJ6RlMrQjljTDNzV1d1OWtrVEJYcjdFSHFHWmduaThJPSIvPjwvY2ZkaTpDb21wbGVtZW50bz48L2NmZGk6Q29tcHJvYmFudGU+Cg==</xml></return></ns1:requestTimbrarCFDIResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="https://t2demo.facturacionmoderna.com/timbrado/soap" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:requestTimbrarCFDIResponse><return xsi:type="SOAP-ENC:Struct"></return></ns1:requestTimbrarCFDIResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'ostruct'
4
+
5
+ describe FmTimbradoCfdi::FmRespuesta do
6
+ context "respuesta no satisfactoria" do
7
+ let(:respuesta_cliente) do
8
+ prueba = OpenStruct.new
9
+ prueba.stub(:success?).and_return(false)
10
+ prueba.stub_chain(:soap_fault?).and_return(true)
11
+ prueba.stub_chain(:soap_fault, :to_s).and_return('Test Error')
12
+ prueba
13
+ end
14
+ let(:respuesta) { FmTimbradoCfdi::FmRespuesta.new(respuesta_cliente) }
15
+ it { expect(respuesta.valid?).to eq(false) }
16
+ it { expect(respuesta.xml?).to eq(false) }
17
+ it { expect(respuesta.pdf?).to eq(false) }
18
+ it { expect(respuesta.cbb?).to eq(false) }
19
+ it { expect(respuesta.timbre?).to eq(false) }
20
+ it { expect(respuesta.no_csd_emisor?).to eq(false) }
21
+ end
22
+
23
+ context "respuesta sin xml" do
24
+ let(:texto_respuesta){File.open('spec/fixtures/soap_response_sin_xml.txt').read}
25
+ let(:respuesta) { FmTimbradoCfdi::FmRespuesta.new(texto_respuesta) }
26
+ it { expect(respuesta.valid?).to eq(false) }
27
+ end
28
+
29
+ end
@@ -3,8 +3,10 @@ require 'spec_helper'
3
3
 
4
4
  describe FmTimbradoCfdi::FmTimbre do
5
5
  context "debe crear un objeto válido" do
6
- let(:timbre_as_text) { File.open('spec/fixtures/timbre_example.txt').read }
7
- let(:timbre) { FmTimbradoCfdi::FmTimbre.new timbre_as_text }
6
+ let(:plantilla){File.open('spec/fixtures/layout_example.txt').read}
7
+ let(:layout){ plantilla.gsub('--fecha-comprobante--', 'asignarFecha' )}
8
+ let(:respuesta){ FmTimbradoCfdi.timbra_cfdi_layout 'ESI920427886', layout }
9
+ let(:timbre) { FmTimbradoCfdi::FmTimbre.new(respuesta.xml)}
8
10
  it { timbre.no_certificado_sat.should_not be_nil }
9
11
  it { timbre.fecha_timbrado.should_not be_nil }
10
12
  it { timbre.uuid.should_not be_nil }
@@ -10,7 +10,7 @@ describe FmTimbradoCfdi do
10
10
  FmTimbradoCfdi.cliente.endpoint.should == 'https://t2demo.facturacionmoderna.com/timbrado/soap'
11
11
  FmTimbradoCfdi.cliente.fm_wsdl.should == 'https://t2demo.facturacionmoderna.com/timbrado/wsdl'
12
12
  end
13
- end #describe .cliente"
13
+ end
14
14
 
15
15
 
16
16
  describe ".timbra_cfdi_layout" do
@@ -91,29 +91,84 @@ describe FmTimbradoCfdi do
91
91
 
92
92
  context 'timbrado incorrecto' do
93
93
  it "no timbra el comprobante si tiene más de 72 horas de haber sido generado" do
94
- fecha_comprobante = Time.now - 73*3600
94
+ fecha_comprobante = Time.now - 120*3600
95
95
  layout = File.open('spec/fixtures/layout_example.txt').read.gsub('--fecha-comprobante--', fecha_comprobante.strftime("%FT%T"))
96
96
  respuesta = FmTimbradoCfdi.timbrar 'ESI920427886', layout
97
97
  respuesta.should_not be_valid
98
98
  end
99
99
  end
100
- end
101
100
 
102
- describe ".configurar" do
103
- it "cambia los valores de conexión por los proporcionados" do
104
- FmTimbradoCfdi.configurar do |config|
105
- config.user_id = "mi_usuario"
106
- config.user_pass = "secret"
107
- config.namespace = "http://logicalbricks.com/soap"
108
- config.endpoint = "http://logicalbricks.com/endpoint"
109
- config.fm_wsdl = "http://logicalbricks.com/wsdl"
101
+ context 'subir certificados' do
102
+ let(:archivo_certificado) { File.read('spec/fixtures/certificados/20001000000200000192.cer') }
103
+ let(:archivo_llave) { File.read('spec/fixtures/certificados/20001000000200000192.key') }
104
+ let(:password) { '12345678a' }
105
+ describe '.activar_cancelacion' do
106
+ context 'petición válida' do
107
+ let(:respuesta){ FmTimbradoCfdi.activar_cancelacion('ESI920427886', archivo_certificado, archivo_llave, password) }
108
+ it{ expect(respuesta.valid?).to eq(true) }
109
+ end
110
+
111
+ context 'petición inválida' do
112
+ let(:respuesta){ FmTimbradoCfdi.activar_cancelacion('ESI920427886', archivo_llave, archivo_certificado, password) }
113
+ it{ expect(respuesta.valid?).to eq(false) }
114
+ end
115
+ end
116
+
117
+ describe '.subir_certificado' do
118
+ context 'petición válida' do
119
+ let(:respuesta){ FmTimbradoCfdi.subir_certificado('ESI920427886', archivo_certificado, archivo_llave, password) }
120
+ it{ expect(respuesta.valid?).to eq(true) }
121
+ end
122
+
123
+ context 'petición inválida' do
124
+ let(:respuesta){ FmTimbradoCfdi.subir_certificado('ESI920427886', archivo_llave, archivo_certificado, password) }
125
+ it{ expect(respuesta.valid?).to eq(false) }
126
+ end
110
127
  end
111
128
 
112
- FmTimbradoCfdi.cliente.user_id.should == 'mi_usuario'
113
- FmTimbradoCfdi.cliente.user_pass.should == 'secret'
114
- FmTimbradoCfdi.cliente.namespace.should == 'http://logicalbricks.com/soap'
115
- FmTimbradoCfdi.cliente.endpoint.should == 'http://logicalbricks.com/endpoint'
116
- FmTimbradoCfdi.cliente.fm_wsdl.should == 'http://logicalbricks.com/wsdl'
117
129
  end
130
+
131
+ describe ".cancelar" do
132
+ context "petición inválida" do
133
+ let(:respuesta){ FmTimbradoCfdi.cancelar('ESI920427886', 'asdasasdsa') }
134
+ it{ expect(respuesta.valid?).to eq(false) }
135
+ end
136
+
137
+ context "formato válido" do
138
+ let(:respuesta){ FmTimbradoCfdi.cancelar('ESI920427886', '00000000-0000-0000-0000-00000000000000') }
139
+ it{ expect(respuesta.valid?).to eq(false) }
140
+ end
141
+
142
+ context "peticion válida" do
143
+ let(:uuid) do
144
+ plantilla = File.open('spec/fixtures/layout_example.txt').read
145
+ layout = plantilla.gsub('--fecha-comprobante--', 'asignarFecha' )
146
+ respuesta = FmTimbradoCfdi.timbrar 'ESI920427886', layout
147
+ respuesta.timbre.uuid
148
+ end
149
+ let(:respuesta){ FmTimbradoCfdi.cancelar('ESI920427886', uuid) }
150
+ it{ expect(respuesta.valid?).to eq(true) }
151
+ end
152
+ end
153
+
154
+ describe ".configurar" do
155
+ it "cambia los valores de conexión por los proporcionados" do
156
+ FmTimbradoCfdi.configurar do |config|
157
+ config.user_id = "mi_usuario"
158
+ config.user_pass = "secret"
159
+ config.namespace = "http://logicalbricks.com/soap"
160
+ config.endpoint = "http://logicalbricks.com/endpoint"
161
+ config.fm_wsdl = "http://logicalbricks.com/wsdl"
162
+ end
163
+
164
+ FmTimbradoCfdi.cliente.user_id.should == 'mi_usuario'
165
+ FmTimbradoCfdi.cliente.user_pass.should == 'secret'
166
+ FmTimbradoCfdi.cliente.namespace.should == 'http://logicalbricks.com/soap'
167
+ FmTimbradoCfdi.cliente.endpoint.should == 'http://logicalbricks.com/endpoint'
168
+ FmTimbradoCfdi.cliente.fm_wsdl.should == 'http://logicalbricks.com/wsdl'
169
+ end
170
+ end
171
+
118
172
  end
119
173
 
174
+
data/spec/spec_helper.rb CHANGED
@@ -1,2 +1,4 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
1
3
  require 'fm_timbrado_cfdi'
2
4
 
metadata CHANGED
@@ -1,15 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fm_timbrado_cfdi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.alpha
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carlos García
8
8
  - Hermes Ojeda Ruiz
9
+ - Crisóforo Gaspar Hernández @mitogh
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2013-12-26 00:00:00.000000000 Z
13
+ date: 2014-01-02 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: nokogiri
@@ -64,6 +65,7 @@ files:
64
65
  - .gitignore
65
66
  - .ruby-gemset
66
67
  - .ruby-version
68
+ - .travis.yml
67
69
  - Gemfile
68
70
  - LICENSE.txt
69
71
  - README.md
@@ -72,13 +74,20 @@ files:
72
74
  - lib/fm_timbrado_cfdi.rb
73
75
  - lib/fm_timbrado_cfdi/fm_cliente.rb
74
76
  - lib/fm_timbrado_cfdi/fm_respuesta.rb
77
+ - lib/fm_timbrado_cfdi/fm_respuesta_cancelacion.rb
75
78
  - lib/fm_timbrado_cfdi/fm_timbre.rb
76
79
  - lib/fm_timbrado_cfdi/version.rb
80
+ - spec/fixtures/certificados/20001000000200000192.cer
81
+ - spec/fixtures/certificados/20001000000200000192.key
82
+ - spec/fixtures/certificados/20001000000200000192.key.pem
77
83
  - spec/fixtures/constructora_layout_example.txt
78
84
  - spec/fixtures/layout_example.txt
79
85
  - spec/fixtures/layout_example.txt~
80
86
  - spec/fixtures/pipes.txt
87
+ - spec/fixtures/soap_response.txt
88
+ - spec/fixtures/soap_response_sin_xml.txt
81
89
  - spec/fixtures/timbre_example.txt
90
+ - spec/fm_timbrado_cfdi/fm_respuesta_spec.rb
82
91
  - spec/fm_timbrado_cfdi/fm_timbre_spec.rb
83
92
  - spec/fm_timbrado_cfdi_spec.rb
84
93
  - spec/spec_helper.rb
@@ -96,9 +105,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
96
105
  version: '0'
97
106
  required_rubygems_version: !ruby/object:Gem::Requirement
98
107
  requirements:
99
- - - '>'
108
+ - - '>='
100
109
  - !ruby/object:Gem::Version
101
- version: 1.3.1
110
+ version: '0'
102
111
  requirements: []
103
112
  rubyforge_project:
104
113
  rubygems_version: 2.1.11
@@ -107,11 +116,17 @@ specification_version: 4
107
116
  summary: Implementación en ruby de la conexión con el servicio de timbrado de cfdi
108
117
  con el PAC Facturación Moderna
109
118
  test_files:
119
+ - spec/fixtures/certificados/20001000000200000192.cer
120
+ - spec/fixtures/certificados/20001000000200000192.key
121
+ - spec/fixtures/certificados/20001000000200000192.key.pem
110
122
  - spec/fixtures/constructora_layout_example.txt
111
123
  - spec/fixtures/layout_example.txt
112
124
  - spec/fixtures/layout_example.txt~
113
125
  - spec/fixtures/pipes.txt
126
+ - spec/fixtures/soap_response.txt
127
+ - spec/fixtures/soap_response_sin_xml.txt
114
128
  - spec/fixtures/timbre_example.txt
129
+ - spec/fm_timbrado_cfdi/fm_respuesta_spec.rb
115
130
  - spec/fm_timbrado_cfdi/fm_timbre_spec.rb
116
131
  - spec/fm_timbrado_cfdi_spec.rb
117
132
  - spec/spec_helper.rb