cfdi40 0.0.5 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README_es-MX.md +60 -3
- data/lib/cfdi40/complemento.rb +18 -0
- data/lib/cfdi40/comprobante.rb +46 -1
- data/lib/cfdi40/concepto.rb +3 -1
- data/lib/cfdi40/docto_relacionado.rb +66 -0
- data/lib/cfdi40/impuestos_dr.rb +12 -0
- data/lib/cfdi40/impuestos_p.rb +12 -0
- data/lib/cfdi40/node.rb +12 -3
- data/lib/cfdi40/pago.rb +56 -0
- data/lib/cfdi40/pagos.rb +73 -0
- data/lib/cfdi40/totales.rb +15 -0
- data/lib/cfdi40/traslado_dr.rb +18 -0
- data/lib/cfdi40/traslado_p.rb +9 -0
- data/lib/cfdi40/traslados_dr.rb +5 -0
- data/lib/cfdi40/traslados_p.rb +16 -0
- data/lib/cfdi40/version.rb +1 -1
- data/lib/cfdi40.rb +11 -0
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7901aac708d9a35630373ea7d4f38a43fdddd801defb286068fe7b96350db91c
|
4
|
+
data.tar.gz: 34ffabb2c0a58898656c5a652afdbc2e84d04bc0848c5927b7f673a0ebd5875e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e04325fa2210081dc36519e8d9773110f386964d45e541b8273fbd7c22112d258ba249f0f04ad68cd8f12039648d466228c1759aad8b1b84a9eb69e23edd3a1
|
7
|
+
data.tar.gz: c1f87bc293e895fc4df9d3440413477f1d540142e879b85e3d13e534a69c1f9376080f41212bf26e3bc161726883c238f625121198e2de7db98aec99b0122bd7
|
data/Gemfile.lock
CHANGED
data/README_es-MX.md
CHANGED
@@ -17,6 +17,12 @@ que esta herramienta:
|
|
17
17
|
* Valide el CFDi contra los XSD
|
18
18
|
* Selle el CFDi.
|
19
19
|
|
20
|
+
Hasta ahora:
|
21
|
+
|
22
|
+
* Genera CFDIs de ingreso básicos con IVA
|
23
|
+
* Genera CFDIs con complemento de pago para colegiaturas
|
24
|
+
* Genera CFDIs complementos de pago básicos.
|
25
|
+
|
20
26
|
# Uso
|
21
27
|
|
22
28
|
## Ejemplo básico
|
@@ -53,8 +59,61 @@ que esta herramienta:
|
|
53
59
|
# Genera CFDI firmado
|
54
60
|
xml_string = cfdi.to_xml
|
55
61
|
|
62
|
+
## Ejemplo CFDI con complemento de pago
|
63
|
+
|
64
|
+
# Inicia un cfdi
|
65
|
+
cfdi = Cfdi40.new
|
66
|
+
cfdi.tipo_de_comprobante = 'P'
|
67
|
+
|
68
|
+
# Datos del emisor.
|
69
|
+
# RFC y Nombre se extraen del certificado si no existen
|
70
|
+
cfdi.lugar_expedicion = '06000'
|
71
|
+
cfdi.emisor.regimen_fiscal = '612'
|
72
|
+
|
73
|
+
# Datos del receptor
|
74
|
+
cfdi.receptor.nombre = 'JUAN PUEBLO BUENO'
|
75
|
+
cfdi.receptor.rfc = 'XAXX010101000'
|
76
|
+
cfdi.receptor.domicilio_fiscal = '06000'
|
77
|
+
cfdi.receptor.regimen_fiscal = '616'
|
78
|
+
cfdi.receptor.uso_cfdi = 'CP01'
|
79
|
+
|
80
|
+
# Agrega un concepto en pesos,
|
81
|
+
# precio final al cliente (neto)
|
82
|
+
# causa IVA con tasa de 16% (default)
|
83
|
+
cfdi.add_pago(
|
84
|
+
monto: 200.17,
|
85
|
+
uuid: 'e40229b3-5c4b-46fb-9ba8-707df828a5bc',
|
86
|
+
serie: 'A',
|
87
|
+
folio: '12345',
|
88
|
+
num_parcialidad: 2,
|
89
|
+
fecha_pago: '2023-04-01T12:20:34',
|
90
|
+
forma_pago: '01',
|
91
|
+
importe_saldo_anterior: 845.673
|
92
|
+
)
|
93
|
+
|
94
|
+
# Archivos CSD
|
95
|
+
cfdi.cert_path = '/path_to/certificado.cer'
|
96
|
+
cfdi.key_path = '/path_to/llave_privada.key'
|
97
|
+
cfdi.key_pass = 'contraseña'
|
98
|
+
|
99
|
+
# Genera CFDI firmado
|
100
|
+
xml_string = cfdi.to_xml
|
101
|
+
|
102
|
+
|
103
|
+
# Lo que sigue
|
104
|
+
|
105
|
+
* Nodo de Traslados cuando hay tasa de IEPS
|
106
|
+
* Retenciones de impuestos
|
107
|
+
* CFDI de pagos con varios pagos y varios documentos en cada pago
|
108
|
+
|
56
109
|
# Cambios
|
57
110
|
|
111
|
+
# 0.0.7
|
112
|
+
* Ajustes al CFDi con complemento de pagos por validaciones del PAC.
|
113
|
+
|
114
|
+
# 0.0.6
|
115
|
+
* CFDi con complemento de pagos
|
116
|
+
|
58
117
|
# 0.0.5
|
59
118
|
|
60
119
|
* IVA tasa 0 e IVA excento. Los conceptos con iva tasa 0 deben llevar el
|
@@ -95,7 +154,5 @@ que esta herramienta:
|
|
95
154
|
|
96
155
|
# ¿Que sigue?
|
97
156
|
|
98
|
-
* Complemento de pagos
|
99
157
|
* Retenciones
|
100
|
-
*
|
101
|
-
* Complemento para colegiaturas
|
158
|
+
* Complemento de pagos con retenciones
|
@@ -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
|
data/lib/cfdi40/comprobante.rb
CHANGED
@@ -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
|
data/lib/cfdi40/concepto.rb
CHANGED
@@ -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
|
-
|
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', default: '1'
|
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 intermediate 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
|
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
|
@@ -146,9 +155,9 @@ module Cfdi40
|
|
146
155
|
def formated_value(accessor)
|
147
156
|
case self.class.formats[accessor]
|
148
157
|
when :t_Importe
|
149
|
-
sprintf("%0.6f", public_send(accessor).to_f)
|
158
|
+
public_send(accessor).to_f == 0.0 ? '0' : sprintf("%0.6f", public_send(accessor).to_f)
|
150
159
|
when :t_ImporteMXN
|
151
|
-
sprintf("%0.2f", public_send(accessor).to_f)
|
160
|
+
public_send(accessor).to_f == 0.0 ? '0' : sprintf("%0.2f", public_send(accessor).to_f)
|
152
161
|
else
|
153
162
|
public_send(accessor)
|
154
163
|
end
|
data/lib/cfdi40/pago.rb
ADDED
@@ -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
|
data/lib/cfdi40/pagos.rb
ADDED
@@ -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,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
|
data/lib/cfdi40/version.rb
CHANGED
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.
|
4
|
+
version: 0.0.7
|
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-
|
11
|
+
date: 2023-04-22 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
|