Class: VerifactuRails::Detalle
- Inherits:
-
Object
- Object
- VerifactuRails::Detalle
- Defined in:
- lib/verifactu_rails/desglose.rb
Overview
Una línea del desglose de la factura (sf:DetalleType).
Todos los importes pasan por Importe.formatear: el string que sale de aquí es el mismo que la AEAT usará para recalcular la huella. No formatear por otro lado, nunca.
Constant Summary collapse
- IMPUESTOS =
01 IVA, 02 IPSI, 03 IGIC, 05 Otros
%w[01 02 03 05].freeze
- CALIFICACIONES =
%w[S1 S2 N1 N2].freeze
- EXENCIONES =
Lista L10. E7 y E8 NO son valores generales: solo se admiten con IGIC (Validaciones v1.2.2, ap. 15.5).
%w[E1 E2 E3 E4 E5 E6].freeze
- EXENCIONES_IGIC =
(EXENCIONES + %w[E7 E8]).freeze
- REGIMENES_IVA =
ClaveRegimen admisible por impuesto: L8A para IVA, L8B (+20) para IGIC y el subconjunto del ap. 15.6 para IPSI. Con Impuesto = 05 (Otros) el campo no se puede informar en absoluto.
%w[01 02 03 04 05 06 07 08 09 10 11 14 15 17 18 19 20].freeze
- REGIMENES_IGIC =
(REGIMENES_IVA + %w[21]).freeze
- REGIMENES_IPSI =
%w[01 08 11 18 19 20].freeze
- REGIMENES =
(REGIMENES_IGIC | REGIMENES_IVA).freeze
- TIPOS_IMPOSITIVOS_IVA =
Ap. 15.1 y 15.3, para IVA con operación sujeta y no exenta.
%w[0.00 2.00 4.00 5.00 7.50 10.00 21.00].freeze
- RECARGOS_IVA =
%w[0.00 0.26 0.50 0.62 1.00 1.40 1.75 5.20].freeze
- VENTANAS_TIPO =
Ap. 15.1. Tres de los tipos NO son de aplicación permanente: fueron rebajas temporales y solo se admiten si la fecha de referencia cae dentro de su ventana. Los demás no llevan ventana.
Consecuencia práctica que sorprende: como FechaExpedicionFactura no puede ser anterior al 28-10-2024, el 5 % ya NO es declarable salvo que se informe una FechaOperacion dentro de su ventana. Es el origen de los errores 1235 y 1236 de la AEAT.
{ '5.00' => [Date.new(2022, 7, 1), Date.new(2024, 9, 30)], '2.00' => [Date.new(2024, 10, 1), Date.new(2024, 12, 31)], '7.50' => [Date.new(2024, 10, 1), Date.new(2024, 12, 31)] }.freeze
- RECARGOS_POR_TIPO =
Ap. 15.3. Qué recargo de equivalencia admite cada tipo impositivo. Cada entrada es [ventana, recargos]; ventana nil significa "siempre".
Solo se exige lo que la norma afirma. Para el 0 % y el 5 % fuera de sus ventanas el texto no dice nada, y ahí NO se inventa una restricción: ser más estricto que la norma ya bloqueó casos válidos antes en esta gema.
{ '21.00' => [[nil, %w[5.20 1.75]]], '10.00' => [[nil, %w[1.40]]], '7.50' => [[nil, %w[1.00]]], '4.00' => [[nil, %w[0.50]]], '2.00' => [[nil, %w[0.26]]], '5.00' => [[[nil, Date.new(2022, 12, 31)], %w[0.50]], [[Date.new(2023, 1, 1), Date.new(2024, 9, 30)], %w[0.62]]], '0.00' => [[[Date.new(2023, 1, 1), Date.new(2024, 9, 30)], %w[0.00]]] }.freeze
- CAMPOS_DE_SUJECION =
Campos que solo tienen sentido en una operación sujeta y no exenta.
%i[tipo_impositivo cuota_repercutida tipo_recargo cuota_recargo].freeze
Instance Attribute Summary collapse
-
#base_imponible ⇒ Object
readonly
Returns the value of attribute base_imponible.
-
#calificacion ⇒ Object
readonly
Returns the value of attribute calificacion.
-
#clave_regimen ⇒ Object
readonly
Returns the value of attribute clave_regimen.
-
#cuota_recargo ⇒ Object
readonly
Returns the value of attribute cuota_recargo.
-
#cuota_repercutida ⇒ Object
readonly
Returns the value of attribute cuota_repercutida.
-
#exenta ⇒ Object
readonly
Returns the value of attribute exenta.
-
#impuesto ⇒ Object
readonly
Returns the value of attribute impuesto.
-
#tipo_impositivo ⇒ Object
readonly
Returns the value of attribute tipo_impositivo.
-
#tipo_recargo ⇒ Object
readonly
Returns the value of attribute tipo_recargo.
Instance Method Summary collapse
-
#a_pares ⇒ Object
Orden EXACTO de sf:DetalleType (
). - #exenta? ⇒ Boolean
-
#initialize(base_imponible:, calificacion: nil, exenta: nil, tipo_impositivo: nil, cuota_repercutida: nil, impuesto: '01', clave_regimen: '01', tipo_recargo: nil, cuota_recargo: nil) ⇒ Detalle
constructor
calificacion y exenta son excluyentes: el XSD las modela como
. -
#iva? ⇒ Boolean
si no se informa, se asume IVA.
-
#validar_en_fecha!(fecha) ⇒ Object
Reglas del ap.
Constructor Details
#initialize(base_imponible:, calificacion: nil, exenta: nil, tipo_impositivo: nil, cuota_repercutida: nil, impuesto: '01', clave_regimen: '01', tipo_recargo: nil, cuota_recargo: nil) ⇒ Detalle
calificacion y exenta son excluyentes: el XSD las modela como
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/verifactu_rails/desglose.rb', line 75 def initialize(base_imponible:, calificacion: nil, exenta: nil, tipo_impositivo: nil, cuota_repercutida: nil, impuesto: '01', clave_regimen: '01', tipo_recargo: nil, cuota_recargo: nil) if calificacion.nil? == exenta.nil? raise ValidacionError, 'Indica exactamente una de calificacion: o exenta: ' \ '(el esquema las modela como choice, no caben las dos ni ninguna)' end @base_imponible = Importe.formatear(base_imponible) @calificacion = calificacion && Formato.enumerado(calificacion, 'CalificacionOperacion', CALIFICACIONES) @impuesto = impuesto && Formato.enumerado(impuesto, 'Impuesto', IMPUESTOS) @exenta = exenta && Formato.enumerado(exenta, 'OperacionExenta', exenciones_admitidas) # Sin normalizar todavía: con Impuesto=05 no cabe ninguna clave, y un # enumerado de lista vacía daría "Admitidos: " en lugar del motivo real. # validar_clave_regimen! resuelve primero ese caso. @clave_regimen = clave_regimen&.to_s @tipo_impositivo = tipo_impositivo && Importe.porcentaje(tipo_impositivo, 'TipoImpositivo') @cuota_repercutida = cuota_repercutida && Importe.formatear(cuota_repercutida) @tipo_recargo = tipo_recargo && Importe.porcentaje(tipo_recargo, 'TipoRecargoEquivalencia') @cuota_recargo = cuota_recargo && Importe.formatear(cuota_recargo) validar_coherencia! end |
Instance Attribute Details
#base_imponible ⇒ Object (readonly)
Returns the value of attribute base_imponible.
70 71 72 |
# File 'lib/verifactu_rails/desglose.rb', line 70 def base_imponible @base_imponible end |
#calificacion ⇒ Object (readonly)
Returns the value of attribute calificacion.
70 71 72 |
# File 'lib/verifactu_rails/desglose.rb', line 70 def calificacion @calificacion end |
#clave_regimen ⇒ Object (readonly)
Returns the value of attribute clave_regimen.
70 71 72 |
# File 'lib/verifactu_rails/desglose.rb', line 70 def clave_regimen @clave_regimen end |
#cuota_recargo ⇒ Object (readonly)
Returns the value of attribute cuota_recargo.
70 71 72 |
# File 'lib/verifactu_rails/desglose.rb', line 70 def cuota_recargo @cuota_recargo end |
#cuota_repercutida ⇒ Object (readonly)
Returns the value of attribute cuota_repercutida.
70 71 72 |
# File 'lib/verifactu_rails/desglose.rb', line 70 def cuota_repercutida @cuota_repercutida end |
#exenta ⇒ Object (readonly)
Returns the value of attribute exenta.
70 71 72 |
# File 'lib/verifactu_rails/desglose.rb', line 70 def exenta @exenta end |
#impuesto ⇒ Object (readonly)
Returns the value of attribute impuesto.
70 71 72 |
# File 'lib/verifactu_rails/desglose.rb', line 70 def impuesto @impuesto end |
#tipo_impositivo ⇒ Object (readonly)
Returns the value of attribute tipo_impositivo.
70 71 72 |
# File 'lib/verifactu_rails/desglose.rb', line 70 def tipo_impositivo @tipo_impositivo end |
#tipo_recargo ⇒ Object (readonly)
Returns the value of attribute tipo_recargo.
70 71 72 |
# File 'lib/verifactu_rails/desglose.rb', line 70 def tipo_recargo @tipo_recargo end |
Instance Method Details
#a_pares ⇒ Object
Orden EXACTO de sf:DetalleType (
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/verifactu_rails/desglose.rb', line 117 def a_pares pares = {} pares['Impuesto'] = impuesto if impuesto pares['ClaveRegimen'] = clave_regimen if clave_regimen if exenta? pares['OperacionExenta'] = exenta else pares['CalificacionOperacion'] = calificacion end pares['TipoImpositivo'] = tipo_impositivo if tipo_impositivo pares['BaseImponibleOimporteNoSujeto'] = base_imponible pares['CuotaRepercutida'] = cuota_repercutida if cuota_repercutida pares['TipoRecargoEquivalencia'] = tipo_recargo if tipo_recargo pares['CuotaRecargoEquivalencia'] = cuota_recargo if cuota_recargo pares end |
#exenta? ⇒ Boolean
101 |
# File 'lib/verifactu_rails/desglose.rb', line 101 def exenta? = !@exenta.nil? |
#iva? ⇒ Boolean
si no se informa, se asume IVA
134 |
# File 'lib/verifactu_rails/desglose.rb', line 134 def iva? = impuesto.nil? || impuesto == '01' # si no se informa, se asume IVA |
#validar_en_fecha!(fecha) ⇒ Object
Reglas del ap. 15.1 y 15.3 que dependen de la fecha de la operación, que esta clase no conoce: la aporta RegistroAlta, que es quien sabe si hay FechaOperacion o hay que caer en FechaExpedicionFactura. Se deja aquí y no allí para que la tabla viva junto al resto de reglas del desglose.
Ambas cuelgan de "Impuesto = 01 (IVA) y CalificacionOperacion = S1".
109 110 111 112 113 114 |
# File 'lib/verifactu_rails/desglose.rb', line 109 def validar_en_fecha!(fecha) return unless iva? && calificacion == 'S1' validar_ventana_tipo!(fecha) validar_recargo_en_fecha!(fecha) end |