cfdi40 0.0.1.alfa → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +2 -2
  3. data/README.md +10 -5
  4. data/README_es-MX.md +84 -0
  5. data/cfdi40.gemspec +2 -2
  6. data/lib/cfdi40/complemento_concepto.rb +12 -0
  7. data/lib/cfdi40/comprobante.rb +225 -20
  8. data/lib/cfdi40/concepto.rb +164 -0
  9. data/lib/cfdi40/conceptos.rb +4 -0
  10. data/lib/cfdi40/emisor.rb +7 -0
  11. data/lib/cfdi40/impuestos.rb +29 -0
  12. data/lib/cfdi40/inst_educativas.rb +17 -0
  13. data/lib/cfdi40/node.rb +155 -0
  14. data/lib/cfdi40/receptor.rb +11 -0
  15. data/lib/cfdi40/sat_csd.rb +89 -0
  16. data/lib/cfdi40/schema_validator.rb +29 -0
  17. data/lib/cfdi40/traslado.rb +9 -0
  18. data/lib/cfdi40/traslados.rb +18 -0
  19. data/lib/cfdi40/version.rb +1 -1
  20. data/lib/cfdi40.rb +14 -0
  21. data/lib/xsd/README.md +34 -0
  22. data/lib/xsd/catCFDI.xsd +162329 -0
  23. data/lib/xsd/cfdv40.xsd +857 -0
  24. data/lib/xsd/iedu.xsd +84 -0
  25. data/lib/xsd/tdCFDI.xsd +157 -0
  26. data/lib/xslt/CartaPorte20.xslt +615 -0
  27. data/lib/xslt/GastosHidrocarburos10.xslt +171 -0
  28. data/lib/xslt/IngresosHidrocarburos.xslt +39 -0
  29. data/lib/xslt/Pagos20.xslt +233 -0
  30. data/lib/xslt/TuristaPasajeroExtranjero.xslt +40 -0
  31. data/lib/xslt/aerolineas.xslt +50 -0
  32. data/lib/xslt/cadenaoriginal.xslt +405 -0
  33. data/lib/xslt/cadenaoriginal_local.xslt +405 -0
  34. data/lib/xslt/certificadodedestruccion.xslt +60 -0
  35. data/lib/xslt/cfdiregistrofiscal.xslt +19 -0
  36. data/lib/xslt/consumodeCombustibles11.xslt +94 -0
  37. data/lib/xslt/detallista.xslt +42 -0
  38. data/lib/xslt/divisas.xslt +13 -0
  39. data/lib/xslt/donat11.xslt +13 -0
  40. data/lib/xslt/iedu.xslt +26 -0
  41. data/lib/xslt/implocal.xslt +39 -0
  42. data/lib/xslt/ine11.xslt +30 -0
  43. data/lib/xslt/leyendasFisc.xslt +28 -0
  44. data/lib/xslt/nomina12.xslt +412 -0
  45. data/lib/xslt/notariospublicos.xslt +301 -0
  46. data/lib/xslt/obrasarteantiguedades.xslt +33 -0
  47. data/lib/xslt/pagoenespecie.xslt +39 -0
  48. data/lib/xslt/pfic.xslt +13 -0
  49. data/lib/xslt/renovacionysustitucionvehiculos.xslt +152 -0
  50. data/lib/xslt/servicioparcialconstruccion.xslt +44 -0
  51. data/lib/xslt/utilerias.xslt +22 -0
  52. data/lib/xslt/valesdedespensa.xslt +70 -0
  53. data/lib/xslt/vehiculousado.xslt +63 -0
  54. metadata +53 -13
@@ -0,0 +1,155 @@
1
+ module Cfdi40
2
+ class Node
3
+ # Nokigir XML Document for the xml_node
4
+ attr_accessor :xml_document, :xml_parent, :children_nodes, :parent_node
5
+ attr_writer :element_name
6
+
7
+ def initialize
8
+ self.class.verify_class_variables
9
+ @children_nodes = []
10
+ end
11
+
12
+ # Use class variables to define attributes used to create nodes
13
+ # Class variables are the same for children classes, so are organized by
14
+ # the name of the class.
15
+ def self.verify_class_variables
16
+ @@attributes ||= {}
17
+ @@attributes[name] ||= {}
18
+ @@namespaces ||= {}
19
+ @@namespaces[name] ||= {}
20
+ @@default_values ||= {}
21
+ @@default_values[name] ||= {}
22
+ @@formats ||= {}
23
+ @@formats[name] ||= {}
24
+ @@element_names ||= {}
25
+ end
26
+
27
+ def self.define_attribute(accessor, xml_attribute:, default: nil, format: nil, readonly: false)
28
+ verify_class_variables
29
+ if readonly
30
+ attr_reader accessor.to_sym
31
+ else
32
+ attr_accessor accessor.to_sym
33
+ end
34
+ @@attributes[name][accessor.to_sym] = xml_attribute
35
+ if default
36
+ @@default_values[name][accessor.to_sym] = default
37
+ end
38
+ if format
39
+ @@formats[name][accessor.to_sym] = format
40
+ end
41
+ end
42
+
43
+ def self.define_namespace(namespace, value)
44
+ verify_class_variables
45
+ @@namespaces[name][namespace] = value
46
+ end
47
+
48
+ def self.define_element_name(value)
49
+ verify_class_variables
50
+ @@element_names[name] = value.to_s
51
+ end
52
+
53
+ def self.namespaces
54
+ @@namespaces[name]
55
+ end
56
+
57
+ def self.attributes
58
+ @@attributes[name]
59
+ end
60
+
61
+ def self.default_values
62
+ @@default_values[name]
63
+ end
64
+
65
+ def self.formats
66
+ @@formats[name]
67
+ end
68
+
69
+ def self.element_name
70
+ verify_class_variables
71
+ @@element_names[name]
72
+ end
73
+
74
+ def set_defaults
75
+ return if self.class.default_values.nil?
76
+
77
+ self.class.default_values.each do |accessor, value|
78
+ next unless attibute_is_null?(accessor)
79
+
80
+ instance_variable_set "@#{accessor}".to_sym, value
81
+ end
82
+ end
83
+
84
+ def attibute_is_null?(accessor)
85
+ return true unless instance_variable_defined?("@#{accessor}".to_sym)
86
+
87
+ instance_variable_get("@#{accessor}".to_sym).nil?
88
+ end
89
+
90
+ def current_namespace
91
+ return unless self.class.respond_to?(:namespaces)
92
+ if self.class.namespaces.empty?
93
+ return parent_node.current_namespace unless parent_node.nil?
94
+ end
95
+
96
+ self.class.namespaces.keys.last
97
+ end
98
+
99
+ def create_xml_node
100
+ set_defaults
101
+ if self.respond_to?(:before_add, true)
102
+ self.before_add
103
+ end
104
+ xml_node = xml_document.create_element(expanded_element_name)
105
+ add_namespaces_to(xml_node)
106
+ add_attributes_to(xml_node)
107
+ add_children_to(xml_node)
108
+ xml_parent.add_child xml_node
109
+ end
110
+
111
+ # Returns setted @element_name or use class_name
112
+ def element_name
113
+ return self.class.element_name unless self.class.element_name.nil? || self.class.element_name == ''
114
+
115
+ self.class.name.split('::').last
116
+ end
117
+
118
+ def expanded_element_name
119
+ return element_name unless current_namespace
120
+ "#{current_namespace}:#{element_name}"
121
+ end
122
+
123
+ def add_namespaces_to(xml_node)
124
+ self.class.namespaces.each do |namespace, value|
125
+ xml_node.add_namespace namespace, value
126
+ end
127
+ end
128
+
129
+ def add_attributes_to(node)
130
+ self.class.attributes.each do |object_accessor, xml_attribute|
131
+ next unless respond_to?(object_accessor)
132
+ next if public_send(object_accessor).nil?
133
+
134
+ node[xml_attribute] = formated_value(object_accessor)
135
+ end
136
+ end
137
+
138
+ def add_children_to(xml_node)
139
+ children_nodes.each do |node|
140
+ node.xml_document = xml_document
141
+ node.xml_parent = xml_node
142
+ node.create_xml_node
143
+ end
144
+ end
145
+
146
+ def formated_value(accessor)
147
+ case self.class.formats[accessor]
148
+ when :t_Importe
149
+ sprintf("%0.6f", public_send(accessor).to_f)
150
+ else
151
+ public_send(accessor)
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,11 @@
1
+ module Cfdi40
2
+ class Receptor < Node
3
+ define_attribute :rfc, xml_attribute: 'Rfc'
4
+ define_attribute :nombre, xml_attribute: 'Nombre'
5
+ define_attribute :domicilio_fiscal, xml_attribute: 'DomicilioFiscalReceptor'
6
+ define_attribute :residencia_fiscal, xml_attribute: 'ResidenciaFiscal'
7
+ define_attribute :num_reg_id_trib, xml_attribute: 'NumRegIdTrib'
8
+ define_attribute :regimen_fiscal, xml_attribute: 'RegimenFiscalReceptor'
9
+ define_attribute :uso_cfdi, xml_attribute: 'UsoCFDI'
10
+ end
11
+ end
@@ -0,0 +1,89 @@
1
+ module Cfdi40
2
+ class SatCsd
3
+ attr_reader :x509_cert
4
+ attr_reader :private_key
5
+
6
+ def cert_path=(path)
7
+ @cert_path = path
8
+ @x509_cert = OpenSSL::X509::Certificate.new(File.read(path))
9
+ end
10
+
11
+ def cert_der=(data)
12
+ @x509_cert = OpenSSL::X509::Certificate.new(data)
13
+ end
14
+
15
+ def load_private_key(key_path, key_pass)
16
+ key_pem = key_to_pem(File.read(key_path))
17
+ @private_key = OpenSSL::PKey::RSA.new(key_pem, key_pass)
18
+ end
19
+
20
+ def set_private_key(key_data, key_pass = nil)
21
+ key_pem = (pem_format?(key_data) ? key_data : key_to_pem(key_data))
22
+ @private_key = OpenSSL::PKey::RSA.new(key_pem, key_pass)
23
+ end
24
+
25
+ def rfc
26
+ return unless subject_data
27
+
28
+ unique_identifier = subject_data.select { |data| data[0] == "x500UniqueIdentifier" }.first
29
+ return unless unique_identifier
30
+
31
+ unique_identifier[1].split(" / ").first
32
+ end
33
+
34
+ def name
35
+ return unless subject_data
36
+
37
+ subject_name = subject_data.select { |data| data[0] == "name" }.first
38
+ return unless subject_name
39
+
40
+ subject_name[1]
41
+ end
42
+
43
+ def no_certificado
44
+ return unless x509_cert
45
+
46
+ s = ''
47
+ x509_cert.serial.to_s(16).split('').each_with_index do |c, i|
48
+ next if i.even?
49
+
50
+ s += c
51
+ end
52
+ s
53
+ end
54
+
55
+ def cert64
56
+ return unless x509_cert
57
+
58
+ Base64.strict_encode64 x509_cert.to_der
59
+ end
60
+
61
+ def valid_pair?
62
+ return false unless x509_cert && private_key
63
+
64
+ x509_cert.check_private_key private_key
65
+ end
66
+
67
+ private
68
+
69
+ def subject_data
70
+ return unless x509_cert
71
+
72
+ x509_cert.subject.to_a
73
+ end
74
+
75
+ def key_to_pem(key_der)
76
+ array_key_pem = []
77
+ array_key_pem << '-----BEGIN ENCRYPTED PRIVATE KEY-----'
78
+ array_key_pem += Base64.strict_encode64(key_der).scan(/.{1,64}/)
79
+ array_key_pem << '-----END ENCRYPTED PRIVATE KEY-----'
80
+ array_key_pem.join("\n")
81
+ end
82
+
83
+ def pem_format?(data)
84
+ return false unless data.valid_encoding?
85
+
86
+ data.match?(/BEGIN/)
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,29 @@
1
+ # Validates Schema Using xsd files
2
+ module Cfdi40
3
+ class SchemaValidator
4
+ # options = Nokogiri::XML::ParseOptions.new.nononet
5
+ # schema = Nokogiri::XML::Schema(Net::HTTP.get('www.sat.gob.mx', '/sitio_internet/cfd/4/cfdv40.xsd'), options)
6
+ # schema = Nokogiri::XML::Schema(File.open("/home/israel/git/cfdi40/lib/xsd/cfdv40.xsd"), options)
7
+
8
+ attr_reader :errors
9
+ LOCAL_XSD_PATH = File.join(File.dirname(__FILE__), '..', 'xsd', 'cfdv40.xsd')
10
+
11
+ # Param xml is xml string
12
+ def initialize(xml)
13
+ @xml_doc = Nokogiri::XML(xml)
14
+ @schema = Nokogiri::XML::Schema(File.open(LOCAL_XSD_PATH))
15
+ end
16
+
17
+ def valid?
18
+ validate unless defined?(@errors)
19
+
20
+ @errors.empty?
21
+ end
22
+
23
+ private
24
+
25
+ def validate
26
+ @errors = @schema.validate(@xml_doc)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ module Cfdi40
2
+ class Traslado < Node
3
+ define_attribute :base, xml_attribute: 'Base', format: :t_Importe
4
+ define_attribute :impuesto, xml_attribute: 'Impuesto'
5
+ define_attribute :tipo_factor, xml_attribute: 'TipoFactor', default: 'Tasa'
6
+ define_attribute :tasa_o_cuota, xml_attribute: 'TasaOCuota', format: :t_Importe
7
+ define_attribute :importe, xml_attribute: 'Importe', format: :t_Importe
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ module Cfdi40
2
+ class Traslados < Node
3
+ def traslado_iva
4
+ return @traslado_iva if defined?(@traslado_iva)
5
+
6
+ @traslado_iva = Traslado.new
7
+ # TODO: FIX magic number
8
+ @traslado_iva.impuesto = '002'
9
+ @traslado_iva.parent_node = self
10
+ self.children_nodes << @traslado_iva
11
+ @traslado_iva
12
+ end
13
+
14
+ def traslado_nodes
15
+ children_nodes
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cfdi40
4
- VERSION = "0.0.1.alfa"
4
+ VERSION = "0.0.3"
5
5
  end
data/lib/cfdi40.rb CHANGED
@@ -1,8 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "nokogiri"
4
+ require "base64"
5
+ require "openssl"
4
6
  require_relative "cfdi40/version"
7
+ require_relative "cfdi40/schema_validator"
8
+ require_relative "cfdi40/sat_csd"
9
+ require_relative "cfdi40/node"
5
10
  require_relative "cfdi40/comprobante"
11
+ require_relative "cfdi40/emisor"
12
+ require_relative "cfdi40/receptor"
13
+ require_relative "cfdi40/conceptos"
14
+ require_relative "cfdi40/concepto"
15
+ require_relative "cfdi40/impuestos"
16
+ require_relative "cfdi40/traslados"
17
+ require_relative "cfdi40/traslado"
18
+ require_relative "cfdi40/complemento_concepto"
19
+ require_relative "cfdi40/inst_educativas"
6
20
 
7
21
  # Leading module and entry point for all features and classes
8
22
  #
data/lib/xsd/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # Local XSD files
2
+
3
+ ## Why?
4
+
5
+ In order to avoid innecesary and redundant reading from SAT site the
6
+ file `cfdv40.xsd` referenced in cfdi standard, has been
7
+ prepared for local access.
8
+
9
+ The `cfdv40.xsd` has been modified to reference the local files:
10
+
11
+ * `tdCFDI.xsd`,
12
+ * `catCFDI.xsd` this file has 5.8MB
13
+ * `iedu.xsd`
14
+
15
+ Local files are imported schemas.
16
+
17
+ ## Use external files
18
+
19
+ If you want to use the schema in the original location must enable the
20
+ external references for Nokogiri:
21
+
22
+ require 'net/http'
23
+
24
+ xml_doc = Nokogiri::XML(xml_string)
25
+
26
+ options = Nokogiri::XML::ParseOptions.new.nononet
27
+ schema = Nokogiri::XML::Schema(Net::HTTP.get('www.sat.gob.mx', '/sitio_internet/cfd/4/cfdv40.xsd'), options)
28
+
29
+ schema.validate(xml_doc)
30
+
31
+ References:
32
+
33
+ https://nokogiri.org/rdoc/Nokogiri/XML/Schema.html
34
+ https://nokogiri.org/rdoc/Nokogiri/XML/ParseOptions.html