cfdi40 0.4.4 → 0.4.6

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
  SHA256:
3
- metadata.gz: c5d902e16b11421579d59b2cf22aac71d40602829d952e43825c38f448689b67
4
- data.tar.gz: 5f1a7d20d12018956c459cce25ea08701778c87a5ac5b665da7760dff72699b4
3
+ metadata.gz: e4048e33cf4c2c3afc08006ea141c96431a4d407db2c15c62f16af9ec443e261
4
+ data.tar.gz: 3d80d6b98b71753869048cf14eaa8fc8b7a6ead278b9d313ceebb1ea517cf701
5
5
  SHA512:
6
- metadata.gz: 77abcb157daa8154ec16bd2c1fc847715034f78905f44a7dd94a29c5274121d38d781bcf5ec504b7dbf0661ecd8964773260be691db742158d2eeef34b3e80fc
7
- data.tar.gz: '0638c1cbaeb96408dac0f3822a8f198d32452d99fa6af66b8a28fc1fcd16ad95ee274221aa8b2069b40ffaf15bd23223cd0fbe0752553f63767b5ea21659e05a'
6
+ metadata.gz: 266289fcbc480d8a0006b4847c72c18edd52deb6c343304a875cc1fc8335b90d5f16385fdeac70d94179e9efcdba9c4baef77efc1c200e107616f235f44ba2f1
7
+ data.tar.gz: 7ea20d0a5949da3af27660205edd3c25086d2f0afe3873031018b795fcb0f3da97bc57e6965b1eb4ebfbcc3cae02c09bd00a76e03a12505ccdde54e41669f302
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cfdi40 (0.4.4)
4
+ cfdi40 (0.4.6)
5
5
  nokogiri (>= 1.10.10)
6
6
 
7
7
  GEM
@@ -314,17 +314,12 @@ module Cfdi40
314
314
  private
315
315
 
316
316
  def add_node_concepto_actividad_pago
317
- return if defined?(@concepto_actividad)
317
+ return if @conceptos.children_nodes.size > 0
318
318
 
319
319
  @receptor.uso_cfdi = "CP01"
320
- @concepto_actividad = Concepto.new
321
- @concepto_actividad.clave_prod_serv = "84111506"
322
- @concepto_actividad.cantidad = 1
323
- @concepto_actividad.clave_unidad = "ACT"
324
- @concepto_actividad.descripcion = "Pago"
320
+ @concepto_actividad = ConceptoRep.new
325
321
  @concepto_actividad.precio_bruto = 0
326
322
  @concepto_actividad.tasa_iva = nil
327
- @concepto_actividad.objeto_impuestos = "01"
328
323
  @concepto_actividad.calculate!
329
324
  @conceptos.add_child_node @concepto_actividad
330
325
  calculate!
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Represents node 'concepto'
4
+ #
5
+ # * Attribute +Importe+ represente gross amount. Gross amount is before taxes and the result of multiply
6
+ # +ValorUnitario+ by +Cantidad+
7
+ #
8
+ module Cfdi40
9
+ class ConceptoRep < Node
10
+ define_element_name 'Concepto'
11
+ define_attribute :clave_prod_serv, xml_attribute: "ClaveProdServ", default: "84111506"
12
+ define_attribute :no_identificacion, xml_attribute: "NoIdentificacion"
13
+ define_attribute :cantidad, xml_attribute: "Cantidad", default: 1
14
+ define_attribute :clave_unidad, xml_attribute: "ClaveUnidad", default: "ACT"
15
+ define_attribute :unidad, xml_attribute: "Unidad"
16
+ define_attribute :descripcion, xml_attribute: "Descripcion", no_writer: true, default: "Pago"
17
+ define_attribute :valor_unitario, xml_attribute: "ValorUnitario", format: :t_Importe
18
+ define_attribute :importe, xml_attribute: "Importe", format: :t_Importe
19
+ define_attribute :descuento, xml_attribute: "Descuento", format: :t_Importe
20
+ define_attribute :objeto_impuestos, xml_attribute: "ObjetoImp", default: "01"
21
+
22
+ attr_accessor :tasa_iva, :tasa_ieps, :precio_neto, :precio_bruto
23
+ attr_reader :iva, :ieps, :base_iva, :importe_neto, :importe_bruto
24
+
25
+ # accesors for instEducativas
26
+ attr_accessor :iedu_nombre_alumno, :iedu_curp, :iedu_nivel_educativo, :iedu_aut_rvoe, :iedu_rfc_pago
27
+
28
+ def initialize
29
+ @tasa_iva = 0.16
30
+ @tasa_ieps = nil
31
+ super
32
+ end
33
+
34
+ # Ver Comprobante#add_concepto
35
+ def update(attributes = {})
36
+ attributes.each do |key, value|
37
+ method_name = "#{key}=".to_sym
38
+ raise Error, ":#{key} no se puede asignar al concepto" unless respond_to?(method_name)
39
+
40
+ public_send(method_name, value)
41
+ end
42
+
43
+ calculate!
44
+ parent_node.parent_node.calculate!
45
+ true
46
+ end
47
+
48
+ # Calculate taxes, amounts from gross price
49
+ # or net price
50
+ def calculate!
51
+ set_defaults
52
+ assign_objeto_imp
53
+ # TODO: accept discount
54
+ if defined?(@precio_neto) && !@precio_neto.nil?
55
+ calculate_from_net_price
56
+ elsif defined?(@precio_bruto) && !@precio_bruto.nil?
57
+ calculate_from_gross_price
58
+ elsif !valor_unitario.nil?
59
+ @precio_bruto = valor_unitario
60
+ calculate_from_gross_price
61
+ end
62
+ add_info_to_traslado_iva
63
+ # TODO: add_info_to_traslado_ieps if @ieps > 0
64
+ add_inst_educativas
65
+ true
66
+ end
67
+
68
+ def objeto_impuestos?
69
+ objeto_impuestos == "02"
70
+ end
71
+
72
+ def traslado_nodes
73
+ return [] if impuestos_node.nil?
74
+
75
+ impuestos_node.traslado_nodes
76
+ end
77
+
78
+ def traslado_iva_node
79
+ return nil unless impuestos_node.is_a?(ConceptoImpuestos)
80
+
81
+ impuestos_node.traslado_iva
82
+ end
83
+
84
+ def load_traslado_iva(ng_node)
85
+ traslado_iva_node.load_from_ng_node(ng_node)
86
+ calculate_from_gross_price
87
+ end
88
+
89
+ def descripcion=(desc)
90
+ @descripcion = desc.to_s[0..999]
91
+ end
92
+
93
+ private
94
+
95
+ def calculate_from_net_price
96
+ set_defaults
97
+ @precio_neto = @precio_neto.round(6)
98
+ @precio_bruto = (@precio_neto / ((1 + tasa_iva.to_f) * (1 + tasa_ieps.to_f)))
99
+ calculate_taxes
100
+
101
+ @precio_bruto = (((@precio_neto * cantidad.to_f) - @iva - @ieps) / cantidad.to_f.round(6)).round(6)
102
+ update_xml_attributes
103
+ end
104
+
105
+ def calculate_from_gross_price
106
+ @precio_bruto = @precio_bruto.round(6)
107
+ calculate_taxes
108
+ # May be can not be rounded with 2 decimals.
109
+ # Example gross_price = 1.99512
110
+ @precio_neto = (@importe_neto / cantidad.to_f).round(6)
111
+ update_xml_attributes
112
+ end
113
+
114
+ def calculate_taxes
115
+ @base_ieps = (@precio_bruto * cantidad.to_f).round(6)
116
+ @ieps = (@base_ieps * tasa_ieps.to_f).round(6)
117
+ @base_iva = (@base_ieps + @ieps).round(6)
118
+ @iva = (@base_iva * tasa_iva.to_f).round(6)
119
+ @importe_bruto = @base_ieps
120
+ @importe_neto = (@base_iva + @iva).round(2)
121
+ end
122
+
123
+ def update_xml_attributes
124
+ self.importe = @importe_bruto
125
+ self.valor_unitario = @precio_bruto
126
+ end
127
+
128
+ def add_info_to_traslado_iva
129
+ return if @tasa_iva.nil?
130
+
131
+ traslado_iva_node.importe = @iva
132
+ traslado_iva_node.base = @base_iva
133
+ traslado_iva_node.tasa_o_cuota = @tasa_iva
134
+ end
135
+
136
+ def assign_objeto_imp
137
+ # 01 No objeto de impuesto.
138
+ # 02 Sí objeto de impuesto.
139
+ # 03 Sí objeto del impuesto y no obligado al desglose.
140
+
141
+ return if objeto_impuestos == "03"
142
+
143
+ self.objeto_impuestos = (!@tasa_iva.nil? || !@tasa_ieps.nil? ? "02" : "01")
144
+ end
145
+
146
+ def impuestos_node
147
+ return @impuestos_node if defined?(@impuestos_node)
148
+ return nil unless objeto_impuestos?
149
+
150
+ @impuestos_node = children_nodes.select { |child| child.is_a?(ConceptoImpuestos) }.first
151
+ return if @impuestos_node
152
+
153
+ @impuestos_node = ConceptoImpuestos.new
154
+ @impuestos_node.parent_node = self
155
+ children_nodes << @impuestos_node
156
+ @impuestos_node
157
+ end
158
+
159
+ def add_inst_educativas
160
+ return unless inst_educativas_present?
161
+
162
+ inst_educativas_node.nombre_alumno = iedu_nombre_alumno
163
+ inst_educativas_node.curp = iedu_curp
164
+ inst_educativas_node.nivel_educativo = iedu_nivel_educativo
165
+ inst_educativas_node.aut_rvoe = iedu_aut_rvoe
166
+ inst_educativas_node.rfc_pago = iedu_rfc_pago
167
+ end
168
+
169
+ def inst_educativas_present?
170
+ return false if iedu_nombre_alumno.nil?
171
+ return false if iedu_nivel_educativo.nil?
172
+ return false if iedu_aut_rvoe.nil?
173
+
174
+ true
175
+ end
176
+
177
+ def concepto_complemento_node
178
+ return @complemento_concepto_node if defined?(@complemento_concepto_node)
179
+
180
+ @complemento_concepto_node = ComplementoConcepto.new
181
+ @complemento_concepto_node.parent_node = self
182
+ children_nodes << @complemento_concepto_node
183
+ @complemento_concepto_node
184
+ end
185
+
186
+ def inst_educativas_node
187
+ return nil unless concepto_complemento_node.is_a?(ComplementoConcepto)
188
+
189
+ concepto_complemento_node.inst_educativas_node
190
+ end
191
+ end
192
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cfdi40
4
- VERSION = "0.4.4"
4
+ VERSION = "0.4.6"
5
5
  end
data/lib/cfdi40.rb CHANGED
@@ -22,6 +22,7 @@ require_relative "cfdi40/traslado"
22
22
  require_relative "cfdi40/complemento_concepto"
23
23
  require_relative "cfdi40/inst_educativas"
24
24
  require_relative "cfdi40/complemento"
25
+ require_relative "cfdi40/concepto_rep"
25
26
  require_relative "cfdi40/pagos"
26
27
  require_relative "cfdi40/pago"
27
28
  require_relative "cfdi40/docto_relacionado"
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.4.4
4
+ version: 0.4.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: 2025-12-18 00:00:00.000000000 Z
11
+ date: 2026-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -48,6 +48,7 @@ files:
48
48
  - lib/cfdi40/comprobante.rb
49
49
  - lib/cfdi40/concepto.rb
50
50
  - lib/cfdi40/concepto_impuestos.rb
51
+ - lib/cfdi40/concepto_rep.rb
51
52
  - lib/cfdi40/conceptos.rb
52
53
  - lib/cfdi40/cp_totales.rb
53
54
  - lib/cfdi40/docto_relacionado.rb