cfdi 0.0.9 → 0.1.0

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.
data/lib/comun.rb CHANGED
@@ -1,5 +1,9 @@
1
+ # Mkay, quiero que mis floats tengan dos decimales a huevo
1
2
  class Float
2
3
 
4
+ # Lo que dice la clase
5
+ #
6
+ # @return [String] El string de este float con dos decimales
3
7
  def to_s
4
8
  sprintf('%.2f', self)
5
9
  end
@@ -8,8 +12,15 @@ end
8
12
 
9
13
  module CFDI
10
14
 
15
+
16
+ # Un elemento del comprobante con métodos mágicos y especiales
11
17
  class ElementoComprobante
12
18
 
19
+
20
+ # Crear este elemento y settear lo que le pasemos como hash en un tipo de dato adecuado
21
+ # @param data [Hash] Los datos para este elemento
22
+ #
23
+ # @return [CFDI::ElementoComprobante] El elemento creado
13
24
  def initialize data={}
14
25
  #puts self.class
15
26
  data.each do |k,v|
@@ -18,11 +29,19 @@ module CFDI
18
29
  self.send method, v
19
30
  end
20
31
  end
21
-
32
+
33
+
34
+ # Los elementos para generar la cadena original de este comprobante
35
+ #
36
+ # @return [Array] idem
22
37
  def self.data
23
38
  @cadenaOriginal
24
39
  end
25
40
 
41
+
42
+ # Un array con los datos de la cadena original para este elemento
43
+ #
44
+ # @return [Array] idem
26
45
  def cadena_original
27
46
  params = []
28
47
  data = {}
@@ -32,7 +51,11 @@ module CFDI
32
51
  data.each {|key| params.push instance_variable_get('@'+key.to_s) }
33
52
  return params
34
53
  end
35
-
54
+
55
+
56
+ # Los datos xmleables de este elemento
57
+ #
58
+ # @return [Hash] idem
36
59
  def to_h
37
60
  h = {}
38
61
  self.class.data.each do |v|
data/lib/concepto.rb CHANGED
@@ -1,11 +1,14 @@
1
1
  module CFDI
2
2
 
3
+ # Un concepto del comprobante
3
4
  class Concepto < ElementoComprobante
4
5
 
6
+ # @private
5
7
  @cadenaOriginal = [:cantidad, :unidad, :noIdentificacion, :descripcion, :valorUnitario, :importe]
6
- #@data = @cadenaOriginal
8
+ # @private
7
9
  attr_accessor *@cadenaOriginal
8
10
 
11
+ # @private
9
12
  def cadena_original
10
13
  return [
11
14
  @cantidad.to_i,
@@ -16,17 +19,33 @@ module CFDI
16
19
  self.importe
17
20
  ]
18
21
  end
19
-
20
- def valorUnitario= vu
21
- @valorUnitario = vu.to_f
22
+
23
+
24
+ # Aigna el valor unitario de este concepto
25
+ # @param dineros [String, Float, #to_f] Cualquier cosa que responda a #to_f
26
+ #
27
+ # @return [Float] El valor unitario como Float
28
+ def valorUnitario= dineros
29
+ @valorUnitario = dineros.to_f
30
+ @valorUnitario
22
31
  end
23
-
32
+
33
+
34
+ # El importe de este concepto
35
+ #
36
+ # @return [Float] El valor unitario multiplicado por la cantidad
24
37
  def importe
25
38
  return @valorUnitario*@cantidad
26
39
  end
27
-
40
+
41
+
42
+ # Asigna la cantidad de (tipo) de este concepto
43
+ # @param qty [Integer, String, #to_i] La cantidad, que ahuevo queremos en int, porque no, no podemos vender 1.5 Kilos de verga...
44
+ #
45
+ # @return [Integer] La cantidad
28
46
  def cantidad= qty
29
47
  @cantidad = qty.to_i
48
+ @cantidad
30
49
  end
31
50
 
32
51
  end
data/lib/entidad.rb CHANGED
@@ -1,10 +1,21 @@
1
1
  module CFDI
2
2
 
3
+ # Una persona fiscal
4
+ #
5
+ # @attr rfc [String] El RFC
6
+ # @attr nombre [String] El nombre o razón social de la entidad
7
+ # @attr domicilioFiscal [CFDI::Domicilio, Hash] El domicilio de esta entidad
8
+ # @attr regimenFiscal [String] El régimen fiscal, sólo de un emisor
9
+ # @attr expedidoEn [CFDI::Domicilio, Hash] El domicilio de la sucursal de emisión
3
10
  class Entidad < ElementoComprobante
11
+ # @private
4
12
  @cadenaOriginal = [:rfc, :nombre, :domicilioFiscal, :expedidoEn, :regimenFiscal]
13
+ # @private
5
14
  @data = @cadenaOriginal
15
+ # @private
6
16
  attr_accessor *@cadenaOriginal
7
17
 
18
+ # @private
8
19
  def cadena_original
9
20
  expedido = @expedidoEn ? @expedidoEn.cadena_original : nil
10
21
  return [
@@ -15,19 +26,29 @@ module CFDI
15
26
  @regimenFiscal
16
27
  ].flatten
17
28
  end
18
-
29
+
30
+
31
+ # Asigna un domicilioFiscal
32
+ # @param domicilio [CFDI::Domicilio, Hash] El domicilio de esta entidad
33
+ #
34
+ # @return [CFDI::Domicilio] idem
19
35
  def domicilioFiscal= domicilio
20
36
  domicilio = Domicilio.new domicilio unless domicilio.is_a? Domicilio
21
37
  @domicilioFiscal = domicilio
38
+ @domicilioFiscal
22
39
  end
23
40
 
41
+ # Designa dónde se expidió el comprobante, sólo para Entidades de tipo "Emisor"
42
+ # @param domicilio [CFDI::Domicilio, Hash] El domicilio de expedición de este emisor
43
+ #
44
+ # @return [CFDI::Domicilio] idem
24
45
  def expedidoEn= domicilio
25
46
  return if !domicilio
26
47
  domicilio = Domicilio.new domicilio unless domicilio.is_a? Domicilio
27
48
  @expedidoEn = domicilio
28
49
  end
29
50
 
30
-
51
+ # @private
31
52
  def ns
32
53
  return ({
33
54
  nombre: @nombre,
@@ -36,8 +57,22 @@ module CFDI
36
57
  end
37
58
 
38
59
  end
39
-
60
+
61
+
62
+ # Un domicilio
63
+ #
64
+ # @attr [String] calle La calle
65
+ # @attr [String] noExterior El número exterior
66
+ # @attr [String] noInterior El número interior
67
+ # @attr [String] colonia La colonia
68
+ # @attr [String] localidad La localidad (o ciudad)
69
+ # @attr [String] referencia La referencia: lotería!.
70
+ # @attr [String] municipio El municipio
71
+ # @attr [String] estado El estado
72
+ # @attr [String] pais El país
73
+ # @attr [String] codigoPostal El código postal
40
74
  class Domicilio < ElementoComprobante
75
+
41
76
  @cadenaOriginal = [:calle, :noExterior, :noInterior, :colonia, :localidad, :referencia, :municipio, :estado, :pais, :codigoPostal]
42
77
  attr_accessor *@cadenaOriginal
43
78
 
data/lib/key.rb CHANGED
@@ -2,8 +2,18 @@ module CFDI
2
2
 
3
3
  require 'openssl'
4
4
 
5
+ # Una llave privada, en formato X509 no PKCS7
6
+ #
7
+ # Para convertirlos, nomás hacemos
8
+ # openssl pkcs8 -inform DER -in nombreGiganteDelSAT.key -passin pass:miFIELCreo >> certX509.pem
5
9
  class Key < OpenSSL::PKey::RSA
6
-
10
+
11
+
12
+ # Crea una llave privada
13
+ # @param file [IO, String] El `path` de esta llave o los bytes de la misma
14
+ # @param password=nil [String, nil] El password de esta llave
15
+ #
16
+ # @return [CFDI::Key] La llave privada
7
17
  def initialize file, password=nil
8
18
  if file.is_a? String
9
19
  file = File.read(file)
@@ -12,6 +22,10 @@ module CFDI
12
22
  end
13
23
 
14
24
  # sella una factura
25
+ #
26
+ # @param factura [CFDI::Comprobante] El comprobante a sellar
27
+ #
28
+ # @return [CFDI::comprobante] El comprobante con el `sello`
15
29
  def sella factura
16
30
  cadena_original = factura.cadena_original
17
31
  factura.sello = Base64::encode64(self.sign(OpenSSL::Digest::SHA1.new, cadena_original)).gsub(/\n/, '')
data/lib/xml.rb CHANGED
@@ -1,5 +1,10 @@
1
1
  module CFDI
2
2
 
3
+
4
+ # Crea un CFDI::Comprobante desde un string XML
5
+ # @param data [String, IO] El XML a parsear, según acepte Nokogiri
6
+ #
7
+ # @return [CFDI::Comprobante] El comprobante parseado
3
8
  def self.from_xml(data)
4
9
  xml = Nokogiri::XML(data);
5
10
  xml.remove_namespaces!
@@ -0,0 +1,57 @@
1
+ require_relative '../lib/cfdi.rb'
2
+
3
+ describe CFDI::Comprobante do
4
+
5
+ it "debe de poder instanciar sin datos" do
6
+ comprobante = CFDI::Comprobante.new
7
+ defaults = {
8
+ :version=>"3.2",
9
+ :fecha=>nil,
10
+ :tipoDeComprobante=>"ingreso",
11
+ :formaDePago=>nil,
12
+ :condicionesDePago=>nil,
13
+ :subTotal=>0,
14
+ :TipoCambio=>1,
15
+ :moneda=>"pesos",
16
+ :total=>0.0,
17
+ :metodoDePago=>nil,
18
+ :lugarExpedicion=>nil,
19
+ :NumCtaPago=>nil,
20
+ :emisor=>nil,
21
+ :receptor=>nil,
22
+ :conceptos=>[],
23
+ :serie=>nil,
24
+ :folio=>nil,
25
+ :sello=>nil,
26
+ :noCertificado=>nil,
27
+ :certificado=>nil,
28
+ :complemento=>nil,
29
+ :cancelada=>nil,
30
+ :impuestos=>[]
31
+ }
32
+ comprobante.to_h.should be_eql(defaults)
33
+ end
34
+
35
+ it "debe de poder settear defaults" do
36
+ opts = CFDI::Comprobante.configure({defaults: {version: '2.0'}})
37
+ comprobante = CFDI::Comprobante.new
38
+ comprobante.version.should == '2.0'
39
+ end
40
+
41
+ it "debe de generar un comprobante desde XML" do
42
+ comprobante = CFDI.from_xml(File.read('./examples/data/cfdi.xml'))
43
+ returns = {:version=>"3.2", :fecha=>"2013-10-15T01:33:32", :tipoDeComprobante=>"ingreso", :formaDePago=>"PAGO EN UNA SOLA EXHIBICION", :condicionesDePago=>"Sera marcada como pagada en cuanto el receptor haya cubierto el pago.", :subTotal=>11000.0, :TipoCambio=>1, :moneda=>"pesos", :total=>12760.0, :metodoDePago=>"Transferencia Bancaria", :lugarExpedicion=>"Nutopia, Nutopia", :NumCtaPago=>nil, :emisor=>{:rfc=>"XAXX010101000", :nombre=>"Me cago en sus estándares S.A. de C.V.", :domicilioFiscal=>{:calle=>"Calle Feliz", :noExterior=>"42", :noInterior=>"314", :colonia=>"Centro", :localidad=>"No se que sea esto, pero va", :referencia=>"Sin Referencia", :municipio=>"Nutopía", :estado=>"Nutopía", :pais=>"Nutopía", :codigoPostal=>"31415"}, :expedidoEn=>{:calle=>"Calle Feliz", :noExterior=>"42", :noInterior=>nil, :colonia=>"Centro", :localidad=>"No se que sea esto, pero va", :referencia=>"Sin Referencia", :municipio=>"Nutopía", :estado=>"Nutopía", :pais=>"Nutopía", :codigoPostal=>"31415"}, :regimenFiscal=>"Pruebas Fiscales"}, :receptor=>{:rfc=>"XAXX010101000", :nombre=>"El SAT apesta S. de R.L.", :domicilioFiscal=>{:calle=>nil, :noExterior=>nil, :noInterior=>nil, :colonia=>nil, :localidad=>nil, :referencia=>"Sin Referencia", :municipio=>nil, :estado=>"Nutopía", :pais=>"Nutopía", :codigoPostal=>nil}, :expedidoEn=>nil, :regimenFiscal=>nil}, :conceptos=>[{:cantidad=>2, :unidad=>"Kilos", :noIdentificacion=>"KDV", :descripcion=>"Verga", :valorUnitario=>5500.0, :importe=>11000.0}], :serie=>nil, :folio=>"1", :sello=>"igFu7Q9Z98n6xFSLMv7a2y8ZlJCO+pgTX3xDAUt5xSpX3dHOKXkTHBAf4P/oHHDm3xkYkaNBfPEzpVFDrRVjL2rvkR5T9rsFqb4cl6DOo4RrRIpSR9vojLp7mFWiON9H6OFPi2b9PVAnyIx1Skb5iGIAmSQIhVYyt2DSauObY2c=", :noCertificado=>"20001000000200000293", :certificado=>"MIIE2jCCA8KgAwIBAgIUMjAwMDEwMDAwMDAyMDAwMDAyOTMwDQYJKoZIhvcNAQEFBQAwggFcMRowGAYDVQQDDBFBLkMuIDIgZGUgcHJ1ZWJhczEvMC0GA1UECgwmU2VydmljaW8gZGUgQWRtaW5pc3RyYWNpw7NuIFRyaWJ1dGFyaWExODA2BgNVBAsML0FkbWluaXN0cmFjacOzbiBkZSBTZWd1cmlkYWQgZGUgbGEgSW5mb3JtYWNpw7NuMSkwJwYJKoZIhvcNAQkBFhphc2lzbmV0QHBydWViYXMuc2F0LmdvYi5teDEmMCQGA1UECQwdQXYuIEhpZGFsZ28gNzcsIENvbC4gR3VlcnJlcm8xDjAMBgNVBBEMBTA2MzAwMQswCQYDVQQGEwJNWDEZMBcGA1UECAwQRGlzdHJpdG8gRmVkZXJhbDESMBAGA1UEBwwJQ295b2Fjw6FuMTQwMgYJKoZIhvcNAQkCDCVSZXNwb25zYWJsZTogQXJhY2VsaSBHYW5kYXJhIEJhdXRpc3RhMB4XDTEyMTAyNjE5MjI0M1oXDTE2MTAyNjE5MjI0M1owggFTMUkwRwYDVQQDE0BBU09DSUFDSU9OIERFIEFHUklDVUxUT1JFUyBERUwgRElTVFJJVE8gREUgUklFR08gMDA0IERPTiBNQVJUSU4gMWEwXwYDVQQpE1hBU09DSUFDSU9OIERFIEFHUklDVUxUT1JFUyBERUwgRElTVFJJVE8gREUgUklFR08gMDA0IERPTiBNQVJUSU4gQ09BSFVJTEEgWSBOVUVWTyBMRU9OIEFDMUkwRwYDVQQKE0BBU09DSUFDSU9OIERFIEFHUklDVUxUT1JFUyBERUwgRElTVFJJVE8gREUgUklFR08gMDA0IERPTiBNQVJUSU4gMSUwIwYDVQQtExxBQUQ5OTA4MTRCUDcgLyBIRUdUNzYxMDAzNFMyMR4wHAYDVQQFExUgLyBIRUdUNzYxMDAzTURGUk5OMDkxETAPBgNVBAsTCFNlcnZpZG9yMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlrI9loozd+UcW7YHtqJimQjzX9wHIUcc1KZyBBB8/5fZsgZ/smWS4Sd6HnPs9GSTtnTmM4bEgx28N3ulUshaaBEtZo3tsjwkBV/yVQ3SRyMDkqBA2NEjbcum+e/MdCMHiPI1eSGHEpdESt55a0S6N24PW732Xm3ZbGgOp1tht1wIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIGwDANBgkqhkiG9w0BAQUFAAOCAQEAuoPXe+BBIrmJn+IGeI+m97OlP3RC4Ct3amjGmZICbvhI9BTBLCL/PzQjjWBwU0MG8uK6e/gcB9f+klPiXhQTeI1YKzFtWrzctpNEJYo0KXMgvDiputKphQ324dP0nzkKUfXlRIzScJJCSgRw9ZifKWN0D9qTdkNkjk83ToPgwnldg5lzU62woXo4AKbcuabAYOVoC7owM5bfNuWJe566UzD6i5PFY15jYMzi1+ICriDItCv3S+JdqyrBrX3RloZhdyXqs2Htxfw4b1OcYboPCu4+9qM3OV02wyGKlGQMhfrXNwYyj8huxS1pHghEROM2Zs0paZUOy+6ajM+Xh0LX2w==", :complemento=>nil, :cancelada=>nil, :impuestos=>[{:impuesto=>"IVA"}]}
44
+ comprobante.to_h.should be_eql(returns)
45
+ end
46
+
47
+ it "no debe de generar atributos vacios" do
48
+ comprobante = CFDI.from_xml(File.read('./examples/data/cfdi.xml'))
49
+ #el comprobante de XML no tiene numero de cuenta de pago
50
+ comprobante.to_xml.should_not =~ /NumCtaPago/
51
+
52
+ #si quitamos referencia
53
+ comprobante.emisor.domicilioFiscal.referencia = nil
54
+ comprobante.to_xml.should_not =~ /DomicilioFiscal.+referencia/
55
+ end
56
+
57
+ end
@@ -0,0 +1,20 @@
1
+ require_relative '../lib/cfdi.rb'
2
+
3
+ describe CFDI::Entidad do
4
+
5
+ it 'debe de crear domicilios desde hashes' do
6
+ entidad = CFDI::Entidad.new({
7
+ rfc: 'XAXX010101000',
8
+ nombre: 'Mira un Salmón S.A. de C.V.',
9
+ domicilioFiscal: {
10
+ referencia: 'Sin Referencia',
11
+ pais: 'México',
12
+ estado: 'Nutopia'
13
+ }
14
+ })
15
+
16
+ entidad.domicilioFiscal.should be_a CFDI::Domicilio
17
+ end
18
+
19
+
20
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cfdi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roberto Hidalgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-30 00:00:00.000000000 Z
11
+ date: 2013-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  description: Comprobantes fiscales digitales por internet
28
42
  email:
29
43
  - un@rob.mx
@@ -36,6 +50,29 @@ files:
36
50
  - LICENSE.txt
37
51
  - Rakefile
38
52
  - cfdi.gemspec
53
+ - doc/CFDI.html
54
+ - doc/CFDI/Certificado.html
55
+ - doc/CFDI/Complemento.html
56
+ - doc/CFDI/Comprobante.html
57
+ - doc/CFDI/Concepto.html
58
+ - doc/CFDI/Domicilio.html
59
+ - doc/CFDI/ElementoComprobante.html
60
+ - doc/CFDI/Entidad.html
61
+ - doc/CFDI/Key.html
62
+ - doc/Float.html
63
+ - doc/_index.html
64
+ - doc/class_list.html
65
+ - doc/css/common.css
66
+ - doc/css/full_list.css
67
+ - doc/css/style.css
68
+ - doc/file_list.html
69
+ - doc/frames.html
70
+ - doc/index.html
71
+ - doc/js/app.js
72
+ - doc/js/full_list.js
73
+ - doc/js/jquery.js
74
+ - doc/method_list.html
75
+ - doc/top-level-namespace.html
39
76
  - examples/crear_factura.rb
40
77
  - examples/data/_empty
41
78
  - examples/data/cfdi.xml
@@ -50,6 +87,8 @@ files:
50
87
  - lib/key.rb
51
88
  - lib/xml.rb
52
89
  - readme.md
90
+ - test/comprobante_spec.rb
91
+ - test/entidar_spec.rb
53
92
  homepage: https://github.com/unRob/cfdi
54
93
  licenses:
55
94
  - WTFPL
@@ -75,4 +114,7 @@ rubygems_version: 2.0.3
75
114
  signing_key:
76
115
  specification_version: 4
77
116
  summary: Digitales!! por Internet!!
78
- test_files: []
117
+ test_files:
118
+ - test/comprobante_spec.rb
119
+ - test/entidar_spec.rb
120
+ has_rdoc: true