cfdi40 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d48bd150dec201d3a2b0812d49080313c5e4aec6efd836e5606e9e2edabd822f
4
- data.tar.gz: 3f437736ebc91684c7652f217f64a0e04c9e8b18519a18a02397ab4f95ca8db9
3
+ metadata.gz: 55ca7f3a64dc87b4b19cbb5d389f71ccd624b70e7c5af601d97dd4dd6e1599db
4
+ data.tar.gz: 9cab2e03e554d96d47ff328c6c520782036ee9b561f11586c42bd353a3112f1d
5
5
  SHA512:
6
- metadata.gz: 4491ff366eeb72c4c6d1792227750e5c3f333d6df52f7d974c05eff8d4d94f30a44e98dfefaf03b1aaa41a8d1407e2765a3ef026617faa071fb86049cbbec9f9
7
- data.tar.gz: 77c1ee1687c4b13af13768afa7a5b3ee0c7ab2071a4a3ddbf45d3bedcf4a18bb2a8e56b07bdf1762d6e833c052cf77c1b3272b04a75cb3f28d2c0494b76d66af
6
+ metadata.gz: '0310189ea57acabe4fffc77ed24d1b02aab3d49e0afafd47d1abf7df10856d19a5a00b89d302138c6b43b036e8af5d52671d922b274e10613f1d57d76b732e1e'
7
+ data.tar.gz: 719ed8d0e569dd621edf7ee702271b7c468c2e6b4606297f650cac39b6cb199d06d881f07b15e56599d214da3481651987348cfc24997e6a1843939ea933c711
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cfdi40 (0.0.5)
4
+ cfdi40 (0.0.6)
5
5
  nokogiri (>= 1.10.10)
6
6
 
7
7
  GEM
data/README_es-MX.md CHANGED
@@ -53,8 +53,58 @@ que esta herramienta:
53
53
  # Genera CFDI firmado
54
54
  xml_string = cfdi.to_xml
55
55
 
56
+ ## Ejemplo CFDI con complemento de pago
57
+
58
+ # Inicia un cfdi
59
+ cfdi = Cfdi40.new
60
+ cfdi.tipo_de_comprobante = 'P'
61
+
62
+ # Datos del emisor.
63
+ # RFC y Nombre se extraen del certificado si no existen
64
+ cfdi.lugar_expedicion = '06000'
65
+ cfdi.emisor.regimen_fiscal = '612'
66
+
67
+ # Datos del receptor
68
+ cfdi.receptor.nombre = 'JUAN PUEBLO BUENO'
69
+ cfdi.receptor.rfc = 'XAXX010101000'
70
+ cfdi.receptor.domicilio_fiscal = '06000'
71
+ cfdi.receptor.regimen_fiscal = '616'
72
+ cfdi.receptor.uso_cfdi = 'CP01'
73
+
74
+ # Agrega un concepto en pesos,
75
+ # precio final al cliente (neto)
76
+ # causa IVA con tasa de 16% (default)
77
+ cfdi.add_pago(
78
+ monto: 200.17,
79
+ uuid: 'e40229b3-5c4b-46fb-9ba8-707df828a5bc',
80
+ serie: 'A',
81
+ folio: '12345',
82
+ num_parcialidad: 2,
83
+ fecha_pago: '2023-04-01T12:20:34',
84
+ forma_pago: '01',
85
+ importe_saldo_anterior: 845.673
86
+ )
87
+
88
+ # Archivos CSD
89
+ cfdi.cert_path = '/path_to/certificado.cer'
90
+ cfdi.key_path = '/path_to/llave_privada.key'
91
+ cfdi.key_pass = 'contraseña'
92
+
93
+ # Genera CFDI firmado
94
+ xml_string = cfdi.to_xml
95
+
96
+
97
+ # Lo que sigue
98
+
99
+ * Nodo de Traslados cuando hay tasa de IEPS
100
+ * Retenciones de impuestos
101
+ * CFDI de pagos con varios pagos y varios documentos en cada pago
102
+
56
103
  # Cambios
57
104
 
105
+ # 0.0.6
106
+ * CFDi con complemento de pagos
107
+
58
108
  # 0.0.5
59
109
 
60
110
  * IVA tasa 0 e IVA excento. Los conceptos con iva tasa 0 deben llevar el
@@ -0,0 +1,18 @@
1
+ module Cfdi40
2
+ class Complemento < Node
3
+ # See Comprobante#add_pago
4
+ def add_pago(attributes={})
5
+ pagos.totales_node
6
+ pagos.add_pago(attributes)
7
+ end
8
+
9
+ def pagos
10
+ return @pagos if defined?(@pagos)
11
+
12
+ @pagos = Pagos.new
13
+ @pagos.parent_node = self
14
+ @children_nodes << @pagos
15
+ @pagos
16
+ end
17
+ end
18
+ end
@@ -119,6 +119,8 @@ module Cfdi40
119
119
  # +iedu_rfc_pago+::
120
120
  #
121
121
  def add_concepto(attributes = {})
122
+ raise Error, 'CFDi tipo pago no acepta conceptos' if tipo_de_comprobante == 'P'
123
+
122
124
  concepto = Concepto.new
123
125
  concepto.parent_node = @conceptos
124
126
  attributes.each do |key, value|
@@ -135,6 +137,23 @@ module Cfdi40
135
137
  concepto
136
138
  end
137
139
 
140
+ # TODO: Doc params add_pago
141
+ # monto
142
+ # uuid
143
+ # folio
144
+ # serie
145
+ # num_parcialidad
146
+ # fecha_pago
147
+ # forma_pago
148
+ # importe_saldo_anterior
149
+ # objeto_impuestos
150
+ def add_pago(attributes = {})
151
+ raise Error, "CFDi debe ser tipo 'P'" unless tipo_de_comprobante == 'P'
152
+
153
+ add_node_concepto_actividad_pago
154
+ complemento.add_pago(attributes)
155
+ end
156
+
138
157
  def to_s
139
158
  to_xml
140
159
  end
@@ -167,6 +186,23 @@ module Cfdi40
167
186
 
168
187
  private
169
188
 
189
+ def add_node_concepto_actividad_pago
190
+ return if defined?(@concepto_actividad)
191
+
192
+ @receptor.uso_cfdi = 'CP01'
193
+ @concepto_actividad = Concepto.new
194
+ @concepto_actividad.clave_prod_serv = '84111506'
195
+ @concepto_actividad.cantidad = 1
196
+ @concepto_actividad.clave_unidad = 'ACT'
197
+ @concepto_actividad.descripcion = 'Pago'
198
+ @concepto_actividad.precio_bruto = 0
199
+ @concepto_actividad.tasa_iva = nil
200
+ @concepto_actividad.objeto_impuestos = '01'
201
+ @concepto_actividad.calculate!
202
+ @conceptos.add_child_node @concepto_actividad
203
+ calculate!
204
+ end
205
+
170
206
  def docxml
171
207
  return @docxml if defined?(@docxml) && !@docxml.nil?
172
208
 
@@ -245,7 +281,7 @@ module Cfdi40
245
281
  impuestos_node.traslados
246
282
  end
247
283
 
248
- # Eliminar
284
+ # TODO: Eliminar
249
285
  def traslado_iva_node
250
286
  impuestos_node.traslado_iva
251
287
  end
@@ -256,5 +292,14 @@ module Cfdi40
256
292
  @sat_csd ||= SatCsd.new
257
293
  @sat_csd.set_private_key(@key_data, (defined?(@key_pass) ? @key_pass : nil))
258
294
  end
295
+
296
+ def complemento
297
+ return @complemento if defined?(@complemento)
298
+
299
+ @complemento = Complemento.new
300
+ @complemento.parent_node = self
301
+ @children_nodes << @complemento
302
+ @complemento
303
+ end
259
304
  end
260
305
  end
@@ -71,7 +71,9 @@ module Cfdi40
71
71
  def calculate_from_gross_price
72
72
  @precio_bruto = @precio_bruto.round(6)
73
73
  calculate_taxes
74
- @precio_neto = (@importe_neto / cantidad).round(2)
74
+ # May be can not be rounded with 2 decimals.
75
+ # Example gross_price = 1.99512
76
+ @precio_neto = (@importe_neto / cantidad).round(6)
75
77
  update_xml_attributes
76
78
  end
77
79
 
@@ -0,0 +1,66 @@
1
+ module Cfdi40
2
+ class DoctoRelacionado < Node
3
+ define_attribute :id_documento, xml_attribute: 'IdDocumento'
4
+ define_attribute :serie, xml_attribute: 'Serie'
5
+ define_attribute :folio, xml_attribute: 'Folio'
6
+ define_attribute :moneda_dr, xml_attribute: 'MonedaDR', default: 'MXN'
7
+ define_attribute :equivalencia_dr, xml_attribute: 'EquivalenciaDR'
8
+ define_attribute :num_parcialidad, xml_attribute: 'NumParcialidad'
9
+ define_attribute :imp_saldo_ant, xml_attribute: 'ImpSaldoAnt', format: :t_ImporteMXN
10
+ define_attribute :imp_pagado, xml_attribute: 'ImpPagado', format: :t_ImporteMXN
11
+ define_attribute :imp_saldo_insoluto, xml_attribute: 'ImpSaldoInsoluto', format: :t_ImporteMXN
12
+ define_attribute :objeto_imp_dr, xml_attribute: 'ObjetoImpDR', default: '02'
13
+
14
+ def calculate!
15
+ self.imp_saldo_insoluto = (imp_saldo_ant - imp_pagado).round(2)
16
+ add_impuestos
17
+ end
18
+
19
+ # Add nodes for 'traslado_dr' and/or 'retencion_dr' and intermetiate nodes
20
+ def add_impuestos
21
+ add_traslado if objeto_imp_dr == '02'
22
+ end
23
+
24
+ def add_traslado
25
+ return unless objeto_imp_dr == '02'
26
+
27
+ # Taxes values for IVA rate 0.16 assumming that all 'conceptos' in the realted document
28
+ # has the same tax rate. This could not be true but the is the most common case.
29
+ traslado_dr = TrasladoDR.new
30
+ traslado_dr.monto_pago = imp_pagado.round(2)
31
+ traslado_dr.calculate!
32
+ traslados_dr.add_child_node(traslado_dr)
33
+ traslado_dr
34
+ end
35
+
36
+ # Return a hash. The key is an array [impuesto, tipo_factor, tasa_o_cuot]
37
+ # and the value is another hash with the keys :base, :importe
38
+ def traslados_summary
39
+ return {} unless defined?(@traslados_dr)
40
+
41
+ summary = {}
42
+ @traslados_dr.children_nodes.each do |traslado_dr|
43
+ key = [traslado_dr.impuesto_dr, traslado_dr.tipo_factor_dr, traslado_dr.tasa_o_cuota_dr]
44
+ summary[key] ||= { base: 0, importe: 0 }
45
+ summary[key][:base] += traslado_dr.base_dr.to_f
46
+ summary[key][:importe] += traslado_dr.importe_dr.to_f
47
+ end
48
+ summary
49
+ end
50
+
51
+ def traslados_dr
52
+ return @traslados_dr if defined?(@traslados_dr)
53
+
54
+ @traslados_dr = impuestos_dr.traslados_dr
55
+ end
56
+
57
+ def impuestos_dr
58
+ return @impuestos_dr if defined?(@impuestos_dr)
59
+
60
+ @impuestos_dr = ImpuestosDR.new
61
+ @impuestos_dr.parent_node = self
62
+ @children_nodes << @impuestos_dr
63
+ @impuestos_dr
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,12 @@
1
+ module Cfdi40
2
+ class ImpuestosDR < Node
3
+ def traslados_dr
4
+ return @traslados_dr if defined?(@traslados_dr)
5
+
6
+ @traslados_dr = TrasladosDR.new
7
+ @traslados_dr.parent_node = self
8
+ @children_nodes << @traslados_dr
9
+ @traslados_dr
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Cfdi40
2
+ class ImpuestosP < Node
3
+
4
+ def traslados_p
5
+ return @traslados_p if defined?(@traslados_p)
6
+
7
+ @traslados_p = TrasladosP.new
8
+ add_child_node @traslados_p
9
+ @traslados_p
10
+ end
11
+ end
12
+ end
data/lib/cfdi40/node.rb CHANGED
@@ -7,6 +7,7 @@ module Cfdi40
7
7
  def initialize
8
8
  self.class.verify_class_variables
9
9
  @children_nodes = []
10
+ set_defaults
10
11
  end
11
12
 
12
13
  # Use class variables to define attributes used to create nodes
@@ -87,6 +88,13 @@ module Cfdi40
87
88
  instance_variable_get("@#{accessor}".to_sym).nil?
88
89
  end
89
90
 
91
+ def add_child_node(child_node)
92
+ raise Error, 'child_node must be a Node object' unless child_node.is_a?(Node)
93
+
94
+ child_node.parent_node = self
95
+ @children_nodes << child_node
96
+ end
97
+
90
98
  def current_namespace
91
99
  return unless self.class.respond_to?(:namespaces)
92
100
  if self.class.namespaces.empty?
@@ -97,7 +105,8 @@ module Cfdi40
97
105
  end
98
106
 
99
107
  def create_xml_node
100
- set_defaults
108
+ # TODO: Quitar la siguiente linea (set_defaults) si funciona poniendo los defaults en initialize
109
+ # set_defaults
101
110
  if self.respond_to?(:before_add, true)
102
111
  self.before_add
103
112
  end
@@ -0,0 +1,56 @@
1
+ module Cfdi40
2
+ class Pago < Node
3
+ define_attribute :monto, xml_attribute: 'Monto'
4
+ define_attribute :fecha_pago, xml_attribute: 'FechaPago'
5
+ define_attribute :forma_pago, xml_attribute: 'FormaDePagoP'
6
+ define_attribute :moneda, xml_attribute: 'MonedaP', readonly: true, default: 'MXN'
7
+ define_attribute :tipo_cambio, xml_attribute: 'TipoCambioP', readonly: true, default: '1'
8
+
9
+ attr_accessor :uuid, :serie, :folio, :num_parcialidad, :importe_saldo_anterior, :objeto_impuestos
10
+
11
+ # Generate docto_relacionado node
12
+ # Data for dcto_relacionado node is obtained from de accessors
13
+ # uuid, serie, folio
14
+ def add_docto_relacionado
15
+ docto_relacionado = DoctoRelacionado.new
16
+ docto_relacionado.parent_node = self
17
+ docto_relacionado.id_documento = uuid
18
+ docto_relacionado.serie = serie
19
+ docto_relacionado.folio = folio
20
+ docto_relacionado.num_parcialidad = num_parcialidad
21
+ docto_relacionado.imp_saldo_ant = importe_saldo_anterior.round(2)
22
+ docto_relacionado.imp_pagado = monto
23
+ docto_relacionado.calculate!
24
+ @children_nodes << docto_relacionado
25
+ end
26
+
27
+ def add_impuestos_p
28
+ impuestos_p = ImpuestosP.new
29
+ add_child_node impuestos_p
30
+ impuestos_p.traslados_p.add_traslado_p_nodes
31
+ end
32
+
33
+ def docto_relacionados
34
+ return @docto_relacionados if defined?(@docto_relacionados)
35
+
36
+ @docto_relacionados =
37
+ @children_nodes.select do |child|
38
+ child if child.class.name == 'Cfdi40::DoctoRelacionado'
39
+ end
40
+ end
41
+
42
+ def traslados_summary
43
+ return @summary if defined?(@summary)
44
+
45
+ summary = {}
46
+ docto_relacionados.each do |docto|
47
+ docto.traslados_summary.each do |key, data|
48
+ summary[key] ||= {base: 0, importe: 0}
49
+ summary[key][:base] += data[:base]
50
+ summary[key][:importe] += data[:importe]
51
+ end
52
+ end
53
+ @summary = summary
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,73 @@
1
+ module Cfdi40
2
+ class Pagos < Node
3
+ define_namespace "pago20", "http://www.sat.gob.mx/Pagos20"
4
+ define_attribute :schema_location,
5
+ xml_attribute: 'xsi:schemaLocation',
6
+ readonly: true,
7
+ default: "http://www.sat.gob.mx/Pagos20 " \
8
+ "http://www.sat.gob.mx/sitio_internet/cfd/Pagos/Pagos20.xsd"
9
+ define_attribute :version, xml_attribute: 'Version', readonly: true, default: '2.0'
10
+
11
+ def add_pago(attributes={})
12
+ pago = Pago.new
13
+ pago.parent_node = self
14
+ attributes.each do |key, value|
15
+ method_name = "#{key}=".to_sym
16
+ if pago.respond_to?(method_name)
17
+ pago.public_send(method_name, value)
18
+ else
19
+ raise Error, ":#{key} no se puede asignar al nodo Pago"
20
+ end
21
+ end
22
+ pago.monto = pago.monto.round(2)
23
+ pago.add_docto_relacionado
24
+ pago.add_impuestos_p
25
+ @children_nodes << pago
26
+ update_totales
27
+ true
28
+ end
29
+
30
+ def update_totales
31
+ update_totales_traslado_iva16
32
+ update_total_monto
33
+ end
34
+
35
+ def update_totales_traslado_iva16
36
+ key = ['002', 'Tasa', '0.160000']
37
+ return if traslados_summary[key].nil?
38
+
39
+ totales_node.base_iva16 = traslados_summary[key][:base]
40
+ totales_node.importe_iva16 = traslados_summary[key][:importe]
41
+ end
42
+
43
+ def update_total_monto
44
+ totales_node.monto_total = pago_nodes.map(&:monto).sum
45
+ end
46
+
47
+ def traslados_summary
48
+ @traslados_summary if defined?(@traslados_summary)
49
+
50
+ summary = {}
51
+ pago_nodes.each do |pago|
52
+ pago.traslados_summary.each do |key, data|
53
+ summary[key] ||= { base: 0, importe: 0 }
54
+ summary[key][:base] += data[:base]
55
+ summary[key][:importe] += data[:importe]
56
+ end
57
+ end
58
+ @traslados_summary = summary
59
+ end
60
+
61
+ def totales_node
62
+ return @totales_node if defined?(@totales_node)
63
+
64
+ @totales_node = Totales.new
65
+ add_child_node @totales_node
66
+ @totales_node
67
+ end
68
+
69
+ def pago_nodes
70
+ @children_nodes.select { |node| node.class.name == 'Cfdi40::Pago' }
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,15 @@
1
+ module Cfdi40
2
+ class Totales < Node
3
+ define_attribute :ret_iva, xml_attribute: 'TotalRetencionesIVA'
4
+ define_attribute :ret_isr, xml_attribute: 'TotalRetencionesISR'
5
+ define_attribute :ret_ieps, xml_attribute: 'TotalRetencionesIEPS'
6
+ define_attribute :base_iva16, xml_attribute: 'TotalTrasladosBaseIVA16'
7
+ define_attribute :importe_iva16, xml_attribute: 'TotalTrasladosImpuestoIVA16'
8
+ define_attribute :base_iva8, xml_attribute: 'TotalTrasladosBaseIVA8'
9
+ define_attribute :importe_iva8, xml_attribute: 'TotalTrasladosImpuestoIVA8'
10
+ define_attribute :base_iva0, xml_attribute: 'TotalTrasladosBaseIVA0'
11
+ define_attribute :importe_iva0, xml_attribute: 'TotalTrasladosImpuestoIVA0'
12
+ define_attribute :base_iva_excento, xml_attribute: 'TotalTrasladosBaseIVAExento'
13
+ define_attribute :monto_total, xml_attribute: 'MontoTotalPagos'
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ module Cfdi40
2
+ class TrasladoDR < Node
3
+ define_attribute :base_dr, xml_attribute: 'BaseDR', format: :t_ImporteMXN
4
+ define_attribute :impuesto_dr, xml_attribute: 'ImpuestoDR', default: '002'
5
+ define_attribute :tipo_factor_dr, xml_attribute: 'TipoFactorDR', default: 'Tasa'
6
+ define_attribute :tasa_o_cuota_dr, xml_attribute: 'TasaOCuotaDR', default: '0.160000', format: :t_Importe
7
+ define_attribute :importe_dr, xml_attribute: 'ImporteDR', format: :t_ImporteMXN
8
+
9
+ attr_accessor :monto_pago
10
+
11
+ def calculate!
12
+ self.base_dr = (monto_pago / (1 + tasa_o_cuota_dr.to_f)).round(2)
13
+ self.importe_dr = (monto_pago - base_dr).round(2)
14
+ end
15
+ end
16
+ end
17
+
18
+
@@ -0,0 +1,9 @@
1
+ module Cfdi40
2
+ class TrasladoP < Node
3
+ define_attribute :base_p, xml_attribute: 'BaseP', format: :t_ImporteMXN
4
+ define_attribute :impuesto_p, xml_attribute: 'ImpuestoP', default: '002'
5
+ define_attribute :tipo_factor_p, xml_attribute: 'TipoFactorP', default: 'Tasa'
6
+ define_attribute :tasa_o_cuota_p, xml_attribute: 'TasaOCuotaP', default: '0.160000', format: :t_Importe
7
+ define_attribute :importe_p, xml_attribute: 'ImporteP', format: :t_ImporteMXN
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module Cfdi40
2
+ class TrasladosDR < Node
3
+ end
4
+ end
5
+
@@ -0,0 +1,16 @@
1
+ module Cfdi40
2
+ class TrasladosP < Node
3
+ def add_traslado_p_nodes
4
+ pago = parent_node.parent_node
5
+ pago.traslados_summary.each do |key, data|
6
+ traslado_p = TrasladoP.new
7
+ traslado_p.impuesto_p = key[0]
8
+ traslado_p.tipo_factor_p = key[1]
9
+ traslado_p.tasa_o_cuota_p = key[2]
10
+ traslado_p.base_p = data[:base]
11
+ traslado_p.importe_p = data[:importe]
12
+ add_child_node traslado_p
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cfdi40
4
- VERSION = "0.0.5"
4
+ VERSION = "0.0.6"
5
5
  end
data/lib/cfdi40.rb CHANGED
@@ -17,6 +17,17 @@ require_relative "cfdi40/traslados"
17
17
  require_relative "cfdi40/traslado"
18
18
  require_relative "cfdi40/complemento_concepto"
19
19
  require_relative "cfdi40/inst_educativas"
20
+ require_relative "cfdi40/complemento"
21
+ require_relative "cfdi40/pagos"
22
+ require_relative "cfdi40/pago"
23
+ require_relative "cfdi40/docto_relacionado"
24
+ require_relative "cfdi40/impuestos_dr"
25
+ require_relative "cfdi40/traslados_dr"
26
+ require_relative "cfdi40/traslado_dr"
27
+ require_relative "cfdi40/impuestos_p"
28
+ require_relative "cfdi40/traslados_p"
29
+ require_relative "cfdi40/traslado_p"
30
+ require_relative "cfdi40/totales.rb"
20
31
 
21
32
  # Leading module and entry point for all features and classes
22
33
  #
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cfdi40
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Israel Benítez
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-04-08 00:00:00.000000000 Z
11
+ date: 2023-04-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -43,19 +43,30 @@ files:
43
43
  - Rakefile
44
44
  - cfdi40.gemspec
45
45
  - lib/cfdi40.rb
46
+ - lib/cfdi40/complemento.rb
46
47
  - lib/cfdi40/complemento_concepto.rb
47
48
  - lib/cfdi40/comprobante.rb
48
49
  - lib/cfdi40/concepto.rb
49
50
  - lib/cfdi40/conceptos.rb
51
+ - lib/cfdi40/docto_relacionado.rb
50
52
  - lib/cfdi40/emisor.rb
51
53
  - lib/cfdi40/impuestos.rb
54
+ - lib/cfdi40/impuestos_dr.rb
55
+ - lib/cfdi40/impuestos_p.rb
52
56
  - lib/cfdi40/inst_educativas.rb
53
57
  - lib/cfdi40/node.rb
58
+ - lib/cfdi40/pago.rb
59
+ - lib/cfdi40/pagos.rb
54
60
  - lib/cfdi40/receptor.rb
55
61
  - lib/cfdi40/sat_csd.rb
56
62
  - lib/cfdi40/schema_validator.rb
63
+ - lib/cfdi40/totales.rb
57
64
  - lib/cfdi40/traslado.rb
65
+ - lib/cfdi40/traslado_dr.rb
66
+ - lib/cfdi40/traslado_p.rb
58
67
  - lib/cfdi40/traslados.rb
68
+ - lib/cfdi40/traslados_dr.rb
69
+ - lib/cfdi40/traslados_p.rb
59
70
  - lib/cfdi40/version.rb
60
71
  - lib/xsd/Pagos20.xsd
61
72
  - lib/xsd/README.md