cfdi40 0.0.3 → 0.0.5

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: bb9a12ec72926735a554f302b13993914393c626cc4dcd797791dfcf0ed8191a
4
- data.tar.gz: 4bc4708eb4c2c0671fa11c81dbcb40a8a8c9a585d2fa02531137efeee942a9d0
3
+ metadata.gz: d48bd150dec201d3a2b0812d49080313c5e4aec6efd836e5606e9e2edabd822f
4
+ data.tar.gz: 3f437736ebc91684c7652f217f64a0e04c9e8b18519a18a02397ab4f95ca8db9
5
5
  SHA512:
6
- metadata.gz: f41d5ee33e1e705d2b12b2c623cd6b197d2c93c1a9e5d994ffc38630954fc4cb6ed6a1c2ae084513bfa6991f7c1bf67b4cc609ebbca3c2d1c99d74167897f470
7
- data.tar.gz: 9f55e7a93233c1c8121d26424c2a7604d36b58f6695b104dc64ea39091e16bb45f3da10231ff4367e75c56d843fd724284b2adae053617244a2295227d3bc41d
6
+ metadata.gz: 4491ff366eeb72c4c6d1792227750e5c3f333d6df52f7d974c05eff8d4d94f30a44e98dfefaf03b1aaa41a8d1407e2765a3ef026617faa071fb86049cbbec9f9
7
+ data.tar.gz: 77c1ee1687c4b13af13768afa7a5b3ee0c7ab2071a4a3ddbf45d3bedcf4a18bb2a8e56b07bdf1762d6e833c052cf77c1b3272b04a75cb3f28d2c0494b76d66af
data/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Cfdi40
2
2
 
3
+ Please see `README_es-MX.md`
4
+
3
5
  ## [Unreleased]
4
6
 
5
7
  ### Added
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cfdi40 (0.0.3)
4
+ cfdi40 (0.0.5)
5
5
  nokogiri (>= 1.10.10)
6
6
 
7
7
  GEM
@@ -10,12 +10,12 @@ GEM
10
10
  ast (2.4.2)
11
11
  json (2.3.1)
12
12
  minitest (5.15.0)
13
- nokogiri (1.13.9-x86_64-linux)
13
+ nokogiri (1.14.2-x86_64-linux)
14
14
  racc (~> 1.4)
15
15
  parallel (1.20.1)
16
16
  parser (3.1.2.1)
17
17
  ast (~> 2.4.1)
18
- racc (1.6.0)
18
+ racc (1.6.2)
19
19
  rainbow (3.0.0)
20
20
  rake (13.0.6)
21
21
  regexp_parser (1.8.2)
data/README.md CHANGED
@@ -12,6 +12,8 @@ TODO: Document, document, document
12
12
  ## Features
13
13
 
14
14
  * XML generation and sign.
15
+ * Node 'iedu'
16
+ * Concept is calculated from gross price or net price
15
17
 
16
18
  ## Future features
17
19
 
data/README_es-MX.md CHANGED
@@ -14,7 +14,7 @@ que esta herramienta:
14
14
  información esencial para la elaboración del CFDi.
15
15
  * Realice los cálculos complementarios como impuestos,
16
16
  totales, etcétera.
17
- * Valide el CFDi contra los CSD
17
+ * Valide el CFDi contra los XSD
18
18
  * Selle el CFDi.
19
19
 
20
20
  # Uso
@@ -55,6 +55,23 @@ que esta herramienta:
55
55
 
56
56
  # Cambios
57
57
 
58
+ # 0.0.5
59
+
60
+ * IVA tasa 0 e IVA excento. Los conceptos con iva tasa 0 deben llevar el
61
+ nodo de traslados de impuestos. Los conceptos excentos de iva no
62
+ llevan el nodo de traslado. Al agregar el concepto usar `nil` para el
63
+ IVA cuando sea excento.
64
+ * Nuevo cálculo de impuestos e importes. El PAC solo acepta 2 decimales
65
+ en los nodos de traslado. Esto puede generar discrepancias al sumar
66
+ los conceptos si no se maneja el mismo número de decimales. Solamente
67
+ el valor unitario se manejará con 6 decimales.
68
+
69
+ # 0.0.4
70
+
71
+ * Atributos 'Total' y 'Subtotal' van con 2 decimales. Aunque el anexo 20
72
+ indica que el tipo es `t_Importe` (6 decimales) el PAC acepta solo 2
73
+ decimales si el CFDi está en pesos mexicanos.
74
+
58
75
  # 0.0.3
59
76
 
60
77
  * Lee RFC en certificados de personas morales. Los certificados de
@@ -19,11 +19,11 @@ module Cfdi40
19
19
  define_attribute :no_certificado, xml_attribute: 'NoCertificado'
20
20
  define_attribute :certificado, xml_attribute: 'Certificado'
21
21
  define_attribute :condiciones_de_pago, xml_attribute: 'CondicionesDePago'
22
- define_attribute :subtotal, xml_attribute: 'SubTotal', format: :t_Importe
23
- define_attribute :descuento, xml_attribute: 'Descuento', format: :t_Importe
22
+ define_attribute :subtotal, xml_attribute: 'SubTotal', format: :t_ImporteMXN
23
+ define_attribute :descuento, xml_attribute: 'Descuento', format: :t_ImporteMXN
24
24
  define_attribute :moneda, xml_attribute: 'Moneda', default: 'MXN'
25
25
  define_attribute :tipo_cambio, xml_attribute: 'TipoCambio'
26
- define_attribute :total, xml_attribute: 'Total', format: :t_Importe
26
+ define_attribute :total, xml_attribute: 'Total', format: :t_ImporteMXN
27
27
  define_attribute :tipo_de_comprobante, xml_attribute: 'TipoDeComprobante', default: 'I'
28
28
  define_attribute :exportacion, xml_attribute: 'Exportacion', default: '01'
29
29
  define_attribute :metodo_pago, xml_attribute: 'MetodoPago'
@@ -60,7 +60,7 @@ module Cfdi40
60
60
  @sat_csd ||= SatCsd.new
61
61
  @sat_csd.cert_der = cert_data
62
62
  emisor.rfc = @sat_csd.rfc
63
- emisor.nombre = @sat_csd.name
63
+ emisor.nombre ||= @sat_csd.name
64
64
  @no_certificado = @sat_csd.no_certificado
65
65
  @certificado = @sat_csd.cert64
66
66
  true
@@ -82,18 +82,42 @@ module Cfdi40
82
82
  @docxml = nil
83
83
  end
84
84
 
85
- # clave_prod_serv
86
- # no_identificacion
87
- # cantidad
88
- # clave_unidad
89
- # unidad
90
- # descripcion
91
- # valor_unitario
92
- # importe
93
- # descuento
94
- # objeto_imp
85
+ # ## Required attributes
86
+ #
87
+ # +clave_prod_serv+:: From SAT catalogue
88
+ # +clave_unidad+:: From SAT catalogue
89
+ # +cantidad+:: Must be greather than 0
90
+ # +descripcion+:: Product or service description
91
+ #
92
+ # ### Price and Taxes attributes
93
+ #
94
+ # +tasa_iva+:: Decimal between 0 and 1. Nil means exempt. Default value is 0.16
95
+ # +tasa_ieps+:: Decimal between 0 and 1. Nil means exempt. Default value is null
96
+ # +precio_bruto+:: Price before apply taxes or gross price.
97
+ # All quantities are calculated based on this price and taxes rate.
98
+ # +precio_neto+:: Precio after taxes or net price. All quantities are calculated from this prices.
99
+ # When both, +precio_neto+ and +precio_bruto+ exist, +precio_neto+ is used
100
+ #
101
+ # The most common usage requires only the net price (+precio_neto+).
102
+ #
103
+ # ## Optional attributes:
104
+ # +no_identificacion+::
105
+ # +unidad+::
106
+ # +descuento+:: PENDING
107
+ #
108
+ # ## Special attributes
109
+ #
110
+ # ### IEDU attributes
111
+ #
112
+ # IEDU node (path: cfdi:Comprobante/cfdi:Conceptos/cfdi:Concepto/cfdi:ComplementoConcepto/iedu:instEducativas) is
113
+ # generated when one of +iedu_nombre_alumno+, +iedu_curp+, +iedu_nivel_educativo+ exist.
114
+ #
115
+ # +iedu_nombre_alumno+::
116
+ # +iedu_curp+::
117
+ # +iedu_nivel_educativo+::
118
+ # +iedu_aut_rvoe+::
119
+ # +iedu_rfc_pago+::
95
120
  #
96
- # TODO: Document accepted attributes and its use
97
121
  def add_concepto(attributes = {})
98
122
  concepto = Concepto.new
99
123
  concepto.parent_node = @conceptos
@@ -133,7 +157,8 @@ module Cfdi40
133
157
  end
134
158
 
135
159
  def original_content
136
- xslt = Nokogiri::XSLT(File.open('lib/xslt/cadenaoriginal_local.xslt'))
160
+ xslt_path = File.join(File.dirname(__FILE__), '..', '..', 'lib/xslt/cadenaoriginal_local.xslt')
161
+ xslt = Nokogiri::XSLT(File.open(xslt_path))
137
162
  transformed = xslt.transform(docxml)
138
163
  # The ampersand (&) char must be used in original content
139
164
  # even though the documentation indicates otherwise
@@ -226,10 +251,10 @@ module Cfdi40
226
251
  end
227
252
 
228
253
  def load_private_key
229
- return unless defined?(@key_data) && defined?(@key_pass)
254
+ return unless defined?(@key_data)
230
255
 
231
256
  @sat_csd ||= SatCsd.new
232
- @sat_csd.set_private_key(@key_data, @key_pass)
257
+ @sat_csd.set_private_key(@key_data, (defined?(@key_pass) ? @key_pass : nil))
233
258
  end
234
259
  end
235
260
  end
@@ -1,6 +1,6 @@
1
1
  # Represents node 'concepto'
2
2
  #
3
- # * Attribute +Importe+ represente gross amount. Gross amount id before taxes and the result of multiply
3
+ # * Attribute +Importe+ represente gross amount. Gross amount is before taxes and the result of multiply
4
4
  # +ValorUnitario+ by +Cantidad+
5
5
  #
6
6
  module Cfdi40
@@ -24,7 +24,7 @@ module Cfdi40
24
24
 
25
25
  def initialize
26
26
  @tasa_iva = 0.16
27
- @tasa_ieps = 0
27
+ @tasa_ieps = nil
28
28
  super
29
29
  end
30
30
 
@@ -33,6 +33,7 @@ module Cfdi40
33
33
  def calculate!
34
34
  set_defaults
35
35
  assign_objeto_imp
36
+ # TODO: accept discount
36
37
  if defined?(@precio_neto) && !@precio_neto.nil?
37
38
  calculate_from_net_price
38
39
  elsif defined?(@precio_bruto) && !@precio_bruto.nil?
@@ -61,33 +62,26 @@ module Cfdi40
61
62
 
62
63
  def calculate_from_net_price
63
64
  set_defaults
64
- @importe_neto = precio_neto * cantidad
65
- breakdown_taxes
65
+ @precio_neto = @precio_neto.round(2)
66
+ @precio_bruto = (@precio_neto / ((1 + tasa_iva.to_f) * (1 + tasa_ieps.to_f))).round(6)
67
+ calculate_taxes
66
68
  update_xml_attributes
67
69
  end
68
70
 
69
- def breakdown_taxes
70
- @base_iva = @importe_neto / (1 + tasa_iva)
71
- @iva = @importe_neto - @base_iva
72
- @base_ieps = @base_iva / (1 + tasa_ieps)
73
- @ieps = @base_iva - @base_ieps
74
- @importe_bruto = @base_ieps
75
- @precio_bruto = @importe_bruto / @cantidad
76
- end
77
-
78
71
  def calculate_from_gross_price
79
- @importe_bruto = @precio_bruto * cantidad
80
- add_taxes
72
+ @precio_bruto = @precio_bruto.round(6)
73
+ calculate_taxes
74
+ @precio_neto = (@importe_neto / cantidad).round(2)
81
75
  update_xml_attributes
82
76
  end
83
77
 
84
- def add_taxes
85
- @base_ieps = @importe_bruto
86
- @ieps = @base_ieps * tasa_ieps
87
- @base_iva = @base_ieps + @ieps
88
- @iva = @base_iva * tasa_iva
89
- @importe_neto = @base_iva + @iva
90
- @precio_neto = @importe_neto / cantidad
78
+ def calculate_taxes
79
+ @base_ieps = (@precio_bruto * cantidad).round(2)
80
+ @ieps = (@base_ieps * tasa_ieps.to_f).round(2)
81
+ @base_iva = (@base_ieps + @ieps).round(2)
82
+ @iva = (@base_iva * tasa_iva.to_f).round(2)
83
+ @importe_bruto = @base_ieps
84
+ @importe_neto = (@base_iva + @iva).round(2)
91
85
  end
92
86
 
93
87
  def update_xml_attributes
@@ -96,7 +90,7 @@ module Cfdi40
96
90
  end
97
91
 
98
92
  def add_info_to_traslado_iva
99
- return unless @iva > 0
93
+ return if @tasa_iva.nil?
100
94
 
101
95
  traslado_iva_node.importe = @iva
102
96
  traslado_iva_node.base = @base_iva
@@ -104,9 +98,13 @@ module Cfdi40
104
98
  end
105
99
 
106
100
  def assign_objeto_imp
101
+ #01 No objeto de impuesto.
102
+ #02 Sí objeto de impuesto.
103
+ #03 Sí objeto del impuesto y no obligado al desglose.
104
+
107
105
  return if objeto_impuestos == '03'
108
106
 
109
- self.objeto_impuestos = (@tasa_iva > 0 || @tasa_ieps > 0 ? '02' : '01')
107
+ self.objeto_impuestos = (!@tasa_iva.nil? || !@tasa_ieps.nil? ? '02' : '01')
110
108
  end
111
109
 
112
110
  def impuestos_node
@@ -1,7 +1,7 @@
1
1
  module Cfdi40
2
2
  class Impuestos < Node
3
- define_attribute :total_impuestos_retenidos, xml_attribute: 'TotalImpuestosRetenidos', format: :t_Importe
4
- define_attribute :total_impuestos_trasladados, xml_attribute: 'TotalImpuestosTrasladados', format: :t_Importe
3
+ define_attribute :total_impuestos_retenidos, xml_attribute: 'TotalImpuestosRetenidos', format: :t_ImporteMXN
4
+ define_attribute :total_impuestos_trasladados, xml_attribute: 'TotalImpuestosTrasladados', format: :t_ImporteMXN
5
5
 
6
6
  def traslados
7
7
  return @traslados if defined?(@traslados)
data/lib/cfdi40/node.rb CHANGED
@@ -147,6 +147,8 @@ module Cfdi40
147
147
  case self.class.formats[accessor]
148
148
  when :t_Importe
149
149
  sprintf("%0.6f", public_send(accessor).to_f)
150
+ when :t_ImporteMXN
151
+ sprintf("%0.2f", public_send(accessor).to_f)
150
152
  else
151
153
  public_send(accessor)
152
154
  end
@@ -1,9 +1,9 @@
1
1
  module Cfdi40
2
2
  class Traslado < Node
3
- define_attribute :base, xml_attribute: 'Base', format: :t_Importe
3
+ define_attribute :base, xml_attribute: 'Base', format: :t_ImporteMXN
4
4
  define_attribute :impuesto, xml_attribute: 'Impuesto'
5
5
  define_attribute :tipo_factor, xml_attribute: 'TipoFactor', default: 'Tasa'
6
6
  define_attribute :tasa_o_cuota, xml_attribute: 'TasaOCuota', format: :t_Importe
7
- define_attribute :importe, xml_attribute: 'Importe', format: :t_Importe
7
+ define_attribute :importe, xml_attribute: 'Importe', format: :t_ImporteMXN
8
8
  end
9
9
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cfdi40
4
- VERSION = "0.0.3"
4
+ VERSION = "0.0.5"
5
5
  end