verifactu-rails 0.1.0.pre
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 +7 -0
- data/COMPLIANCE.md +149 -0
- data/LICENSE +21 -0
- data/README.md +404 -0
- data/doc/FUENTES.md +505 -0
- data/lib/generators/verifactu/install/install_generator.rb +61 -0
- data/lib/generators/verifactu/install/templates/instalar_verifactu.rb.tt +19 -0
- data/lib/generators/verifactu/install/templates/verifactu.rb.tt +64 -0
- data/lib/verifactu-rails.rb +31 -0
- data/lib/verifactu_rails/certificado.rb +122 -0
- data/lib/verifactu_rails/consulta.rb +279 -0
- data/lib/verifactu_rails/desglose.rb +396 -0
- data/lib/verifactu_rails/envio.rb +153 -0
- data/lib/verifactu_rails/error.rb +31 -0
- data/lib/verifactu_rails/formato.rb +199 -0
- data/lib/verifactu_rails/huella.rb +130 -0
- data/lib/verifactu_rails/importe.rb +80 -0
- data/lib/verifactu_rails/libro/autochequeo.rb +67 -0
- data/lib/verifactu_rails/libro/cadena.rb +110 -0
- data/lib/verifactu_rails/libro/migracion.rb +92 -0
- data/lib/verifactu_rails/libro/reconciliacion.rb +224 -0
- data/lib/verifactu_rails/libro/registro.rb +74 -0
- data/lib/verifactu_rails/libro/remesa.rb +120 -0
- data/lib/verifactu_rails/libro.rb +89 -0
- data/lib/verifactu_rails/qr.rb +56 -0
- data/lib/verifactu_rails/railtie.rb +30 -0
- data/lib/verifactu_rails/registro.rb +833 -0
- data/lib/verifactu_rails/respuesta.rb +154 -0
- data/lib/verifactu_rails/schemas/ConsultaLR.xsd +54 -0
- data/lib/verifactu_rails/schemas/EventosSIF.xsd +823 -0
- data/lib/verifactu_rails/schemas/PROCEDENCIA.md +64 -0
- data/lib/verifactu_rails/schemas/RespuestaConsultaLR.xsd +201 -0
- data/lib/verifactu_rails/schemas/RespuestaSuministro.xsd +139 -0
- data/lib/verifactu_rails/schemas/RespuestaValRegistNoVeriFactu.xsd +103 -0
- data/lib/verifactu_rails/schemas/SuministroInformacion.xsd +1390 -0
- data/lib/verifactu_rails/schemas/SuministroLR.xsd +25 -0
- data/lib/verifactu_rails/schemas/catalog.xml +5 -0
- data/lib/verifactu_rails/schemas/xmldsig-core-schema.xsd +318 -0
- data/lib/verifactu_rails/sistema_informatico.rb +88 -0
- data/lib/verifactu_rails/transporte.rb +127 -0
- data/lib/verifactu_rails/version.rb +5 -0
- metadata +124 -0
data/doc/FUENTES.md
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
# Fuentes y hallazgos
|
|
2
|
+
|
|
3
|
+
Qué dice la documentación oficial de VERI\*FACTU, qué se ha comprobado contra el
|
|
4
|
+
servicio real de la AEAT y qué sigue siendo suposición. Es la justificación de
|
|
5
|
+
las decisiones de diseño de la gema y el respaldo de [COMPLIANCE.md](../COMPLIANCE.md).
|
|
6
|
+
|
|
7
|
+
Cada bloque lleva marcado de dónde sale lo que afirma:
|
|
8
|
+
|
|
9
|
+
- **[doc]** — de la documentación oficial de la AEAT.
|
|
10
|
+
- **[real]** — comprobado contra el entorno de pruebas (preproducción).
|
|
11
|
+
- **[?]** — deducido o supuesto, sin comprobar.
|
|
12
|
+
|
|
13
|
+
## Documentos
|
|
14
|
+
|
|
15
|
+
Los PDF no se versionan aquí: pesan casi 2 MB y no producen diffs legibles. Se
|
|
16
|
+
registran URL, versión y SHA-256 para detectar que han cambiado.
|
|
17
|
+
|
|
18
|
+
| Documento | Versión | SHA-256 |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| [Especificaciones huella/hash](https://www.agenciatributaria.es/static_files/AEAT_Desarrolladores/EEDD/IVA/VERI-FACTU/Veri-Factu_especificaciones_huella_hash_registros.pdf) | 0.1.2 | `f4334c254bb875b417247b54315199f8…` |
|
|
21
|
+
| [Validaciones y errores](https://www.agenciatributaria.es/static_files/AEAT_Desarrolladores/EEDD/IVA/VERI-FACTU/Validaciones_Errores_Veri-Factu.pdf) | 1.2.2 | `426eb926fc098a36a163f66ca5f40d9e…` |
|
|
22
|
+
| [Especificaciones del código QR](https://www.agenciatributaria.es/static_files/AEAT_Desarrolladores/EEDD/IVA/VERI-FACTU/DetalleEspecificacTecnCodigoQRfactura.pdf) | 0.5.0 | `f86b3c260d8a4963dbc18c5007732b53…` |
|
|
23
|
+
| [FAQs de empresas de desarrollo](https://www.agenciatributaria.es/static_files/AEAT_Desarrolladores/EEDD/IVA/VERI-FACTU/FAQs-Desarrolladores.pdf) | 1.3 | `73906dc8afbbb9da35f6cb489980352b…` |
|
|
24
|
+
|
|
25
|
+
Índice por si cambian de nombre:
|
|
26
|
+
[Documentación VERI\*FACTU para desarrolladores](https://www.agenciatributaria.es/AEAT.desarrolladores/Desarrolladores/_menu_/Documentacion/Sistemas_Informaticos_de_Facturacion_y_Sistemas_VERI_FACTU/Sistemas_Informaticos_de_Facturacion_y_Sistemas_VERI_FACTU.html).
|
|
27
|
+
Para comprobar si han cambiado: `curl -sL -A "Mozilla/5.0" "<url>" | shasum -a 256`.
|
|
28
|
+
|
|
29
|
+
Además: `DsRegistroVeriFactu.xlsx` (Diseños de registro v1.0), Descripción SWeb
|
|
30
|
+
v1.0.3 y el
|
|
31
|
+
[listado de errores](https://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/errores.properties)
|
|
32
|
+
(ISO-8859-1, 247 códigos: 44 rechazan el envío completo, 193 la factura y 10
|
|
33
|
+
producen aceptación con obligación de subsanar).
|
|
34
|
+
|
|
35
|
+
## La huella
|
|
36
|
+
|
|
37
|
+
**[doc]** Los ceros a la derecha son irrelevantes: *"se tratarán indistintamente
|
|
38
|
+
los valores con una o dos posiciones en los decimales"*. La exigencia real no es
|
|
39
|
+
un formato concreto sino **coherencia entre la huella y el XML**. "Siempre 2
|
|
40
|
+
decimales" es válido, y el `241.4` del ejemplo oficial también.
|
|
41
|
+
|
|
42
|
+
**[doc]** Los espacios al borde hay que recortarlos. La gema los **rechaza**, que
|
|
43
|
+
es más estricto: casi siempre son un defecto de los datos de origen y recortar en
|
|
44
|
+
silencio lo taparía.
|
|
45
|
+
|
|
46
|
+
**[doc]** Un campo vacío va como `Campo=` (nombre, igual, nada). Es el caso del
|
|
47
|
+
primer registro de la cadena.
|
|
48
|
+
|
|
49
|
+
**[real]** La huella que construye la gema coincide con la que recalcula la AEAT,
|
|
50
|
+
tanto en altas como en anulaciones. La serialización de la anulación es distinta
|
|
51
|
+
—no lleva importes ni tipo de factura, solo IDFactura, fecha de generación y
|
|
52
|
+
huella anterior— y también cuadra.
|
|
53
|
+
|
|
54
|
+
**[doc]** Una huella que no coincide **no provoca rechazo**: es *error admisible*
|
|
55
|
+
(ap. 4.3.1), el registro se acepta y queda anotado, pero obliga a subsanarlo.
|
|
56
|
+
|
|
57
|
+
Los tres vectores oficiales del ap. 6 están en `test/diferencial_test.rb` y se
|
|
58
|
+
reproducen exactamente, encadenamiento incluido.
|
|
59
|
+
|
|
60
|
+
## Validaciones implementadas
|
|
61
|
+
|
|
62
|
+
**[doc]** De Validaciones v1.2.2:
|
|
63
|
+
|
|
64
|
+
| Ap. | Regla | Dónde |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| 3.1.3.1 | `IDEmisorFactura` = NIF del `ObligadoEmision` de la cabecera | `Envio#validar_emisores!` |
|
|
67
|
+
| 3.1.3.1 | `FechaExpedicionFactura` no futura ni anterior a 28-10-2024 | `RegistroAlta#validar_fecha_expedicion!` |
|
|
68
|
+
| 3.1.3.1 | `NumSerieFactura`: ASCII 32-126, prohibidos `"` `'` `<` `>` `=` | `Formato.num_serie` |
|
|
69
|
+
| 3.1.3.3 | `TipoRectificativa` obligatorio y exclusivo de R1-R5 | `RegistroAlta#validar_rectificativa!` |
|
|
70
|
+
| 3.1.3.4 | `FacturasRectificadas` **no obligatoria**, exclusiva de R1-R5 | ídem |
|
|
71
|
+
| 3.1.3.5 | `FacturasSustituidas` **no obligatoria**, exclusiva de F3 | `#validar_sustitutiva!` |
|
|
72
|
+
| 3.1.3.6 | `ImporteRectificacion` obligatorio y exclusivo de `TipoRectificativa=S` | `#validar_importe_rectificacion!` |
|
|
73
|
+
| 3.1.3.13 | Destinatarios obligatorios en F1/F3/R1-R4, prohibidos en F2/R5 | `#validar_destinatarios!` |
|
|
74
|
+
| 2 | `RechazoPrevio` distinto de "N" solo dentro de una subsanación | `#validar_subsanacion!` |
|
|
75
|
+
| 8 y 9 | `FacturaSimplificadaArt7273` solo en F1/F3/R1-R4; `FacturaSinIdentifDestinatarioArt61d` solo en F2/R5 | `#validar_marcas!` |
|
|
76
|
+
| 10 | `Macrodato` obligatorio si `ImporteTotal >= |100.000.000,00|` | `#validar_macrodato!` |
|
|
77
|
+
| 11 | `EmitidaPorTerceroODestinatario` "T" exige `Tercero`; "D" exige `Destinatarios` | `#validar_emisor_tercero!` |
|
|
78
|
+
| 12 | `Tercero` solo con "T", NIF distinto del emisor, sin `IDType` 07, y desde ES solo 03 | ídem y `Tercero` |
|
|
79
|
+
| 13 | Reglas de `IDOtro` del destinatario (07 exige ES; desde ES solo 03 o 07) | `IdOtro.normalizar` |
|
|
80
|
+
| 14 | `Cupon` solo "S" y solo con R1 o R5 | `#validar_cupon!` |
|
|
81
|
+
| 15.1 | Ventanas temporales de `TipoImpositivo` | `Detalle#validar_en_fecha!` |
|
|
82
|
+
| 15.3 | El recargo de equivalencia tiene que cuadrar con el tipo impositivo | ídem |
|
|
83
|
+
| 15.4 | `CalificacionOperacion` S2 solo en F1/F3/R1-R4 | `#validar_inversion_sujeto_pasivo!` |
|
|
84
|
+
| 15.4-15.7 | Coherencia de `Calificacion`, `OperacionExenta` y recargo | `Detalle#validar_coherencia!` |
|
|
85
|
+
| 15.6 | `ClaveRegimen`: obligatoria con IVA/IPSI/IGIC, prohibida con Otros, y contenida en la lista que corresponda al impuesto | `Detalle#validar_clave_regimen!` |
|
|
86
|
+
| 15.6.1-3, 15.6.5-6, 15.6.8, 15.6.10 | Cada clave de régimen ata la calificación o la exención de su línea | `Detalle#validar_regimen_de_la_linea!` |
|
|
87
|
+
| 15.6.4, 15.6.7, 15.6.9 | Claves 06, 10 y 14: tipo de factura, destinatarios y fecha de operación | `RegistroAlta#validar_desglose_cruzado!` |
|
|
88
|
+
|
|
89
|
+
Tres cosas que conviene entender:
|
|
90
|
+
|
|
91
|
+
- **El 5 % ya no es declarable a secas.** Fue una rebaja temporal cuya ventana
|
|
92
|
+
cerró el 30-09-2024, y como `FechaExpedicionFactura` no puede ser anterior al
|
|
93
|
+
28-10-2024, hoy solo cabe informando una `FechaOperacion` dentro de la ventana.
|
|
94
|
+
Igual para el 2 % y el 7,5 %. Se mide contra `FechaOperacion`, o la de
|
|
95
|
+
expedición si falta.
|
|
96
|
+
- **Las reglas cruzadas no caben en el objeto del que hablan.** Un `Detalle` no
|
|
97
|
+
conoce el `TipoFactura` ni la fecha de operación, así que 15.1, 15.3 y 15.4 las
|
|
98
|
+
dispara `RegistroAlta`, igual que `Envio` comprueba que el emisor de cada
|
|
99
|
+
registro sea el obligado de la cabecera.
|
|
100
|
+
- **Fuera de las ventanas que la norma menciona no se impone nada.** Inventar
|
|
101
|
+
restricciones donde el texto calla repetiría el error que ya hubo al hacer
|
|
102
|
+
obligatorias 3.1.3.4 y 3.1.3.5, que no lo son.
|
|
103
|
+
|
|
104
|
+
**Una regla del ap. 13 se decidió NO implementar**: "si un destinatario se
|
|
105
|
+
identifica con `IDType=02`, `TipoFactura` debe ser F1/F3/R1-R4" es redundante,
|
|
106
|
+
porque los tipos se reparten entre los que exigen destinatario y los que lo
|
|
107
|
+
prohíben. Escribirla habría dejado una comprobación incapaz de fallar, que
|
|
108
|
+
aparenta cobertura sin cubrir nada.
|
|
109
|
+
|
|
110
|
+
**Sin implementar**: ap. 15.2 `BaseImponibleACoste`, porque el campo no está
|
|
111
|
+
soportado por la gema. Arrastra una consecuencia: la clave de régimen **`06`
|
|
112
|
+
(grupo de entidades, nivel avanzado) exige ese campo**, así que un registro que
|
|
113
|
+
la use no se puede construir bien. La gema lo rechaza en local diciendo por qué,
|
|
114
|
+
en vez de armar algo que la AEAT va a rechazar de todas formas.
|
|
115
|
+
|
|
116
|
+
**Sobre el alcance de la 15.6, para no confundirlo:** sus reglas se acotan a
|
|
117
|
+
"Impuesto = 01 (IVA), 03 (IGIC) o no se cumplimenta". En IPSI **no aplican**, y
|
|
118
|
+
eso importa porque ahí las claves 18, 19 y 20 significan otra cosa —art. 73.4 y
|
|
119
|
+
5 de la Ordenanza fiscal de Ceuta, operaciones interiores exentas y régimen de
|
|
120
|
+
estimación objetiva—. Aplicarles las reglas de IVA sería inventar restricciones
|
|
121
|
+
sobre una lista distinta.
|
|
122
|
+
|
|
123
|
+
### Qué hacen las otras implementaciones con la 15.6
|
|
124
|
+
|
|
125
|
+
Comprobado leyendo su código fuente, no su documentación:
|
|
126
|
+
|
|
127
|
+
- **`josemmo/Verifactu-PHP`**: no implementa **ninguna**. `ClaveRegimen` es un
|
|
128
|
+
enum puro; la única regla cruzada que tiene es la de la clave 18 con el recargo
|
|
129
|
+
de equivalencia.
|
|
130
|
+
- **`mybooking-es/verifactu-rb`**: implementa la mayoría, y es la referencia más
|
|
131
|
+
completa que hemos encontrado. Le faltan las tres del bloque de la clave 14
|
|
132
|
+
(destinatarios con NIF que empiece por P/Q/S/V y tipo de factura) y la de la
|
|
133
|
+
clave 20 con IGIC. Su regla de exenciones del criterio de caja queda además en
|
|
134
|
+
una rama inalcanzable, porque su constructor exige `calificacion_operacion` y
|
|
135
|
+
la comprobación de `OperacionExenta` vive en el `elsif`.
|
|
136
|
+
|
|
137
|
+
**[doc] Otros errores admisibles** (se aceptan, obligan a subsanar): `ImporteTotal`
|
|
138
|
+
o `CuotaTotal` que no cuadran con el desglose, con margen de ±10,00 € (no aplica
|
|
139
|
+
si `ClaveRegimen` es 03, 05, 06, 08 o 09); `PrimerRegistro="S"` cuando ya existen
|
|
140
|
+
registros para ese SIF y NIF; y `FechaHoraHusoGenRegistro` posterior a la hora de
|
|
141
|
+
la AEAT (este exceptuado de subsanación).
|
|
142
|
+
|
|
143
|
+
## Listas de códigos
|
|
144
|
+
|
|
145
|
+
**[doc]** De la hoja "6)Listas" del Excel de diseños, que es la fuente autorizada
|
|
146
|
+
de los enumerados que las Validaciones citan sin desarrollar:
|
|
147
|
+
|
|
148
|
+
| Lista | Campo | Valores |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| L1 | `Impuesto` | 01 IVA, 02 IPSI, 03 IGIC, 05 Otros |
|
|
151
|
+
| L2 | `TipoFactura` | F1, F2, F3, R1–R5 |
|
|
152
|
+
| L3 | `TipoRectificativa` | S sustitución, I diferencias |
|
|
153
|
+
| L6 | `EmitidaPorTerceroODestinatario` | D, T |
|
|
154
|
+
| L7 | `IDType` | 02 NIF-IVA, 03 pasaporte, 04 doc. oficial, 05 cert. residencia, 06 otro, 07 no censado |
|
|
155
|
+
| L8A | `ClaveRegimen` con IVA | 01–11, 14, 15, 17, 18, 19, 20 |
|
|
156
|
+
| L8B | `ClaveRegimen` con IGIC | 01–11, 14, 15, 17, 18, 19 (+ 20 según Validaciones) |
|
|
157
|
+
| L9 | `CalificacionOperacion` | S1, S2, N1, N2 |
|
|
158
|
+
| **L10** | `OperacionExenta` | **E1–E6 únicamente** |
|
|
159
|
+
| L12 | `TipoHuella` | 01 SHA-256 |
|
|
160
|
+
| L15 | `IDVersion` | 1.0 |
|
|
161
|
+
| L16 | `GeneradoPor` | E expedidor, D destinatario, T tercero |
|
|
162
|
+
| L17 | `RechazoPrevio` | N, S, X |
|
|
163
|
+
|
|
164
|
+
L10 confirma que **E7 y E8 solo se admiten con IGIC**, y así lo hace `Detalle`:
|
|
165
|
+
`EXENCIONES` es E1–E6 y `EXENCIONES_IGIC` añade las dos, según el impuesto de la
|
|
166
|
+
línea.
|
|
167
|
+
|
|
168
|
+
`ClaveRegimen = 21` no aparece en L8A ni L8B pero sí en el XSD y en el ap. 15.6.11
|
|
169
|
+
de Validaciones. El Excel es v1.0 y Validaciones v1.2.2: se sigue a la más
|
|
170
|
+
reciente.
|
|
171
|
+
|
|
172
|
+
## Las operativas de subsanación y rechazo
|
|
173
|
+
|
|
174
|
+
**[doc]** Los cuadros de operativa del Excel definen las variantes de alta y de
|
|
175
|
+
anulación, cada una distinguida por una combinación de campos:
|
|
176
|
+
|
|
177
|
+
| Operativa de alta | `Subsanacion` | `RechazoPrevio` |
|
|
178
|
+
|---|---|---|
|
|
179
|
+
| Alta inicial ("normal") | ausente o N | ausente o N |
|
|
180
|
+
| Alta de subsanación | S | ausente o N |
|
|
181
|
+
| Alta por rechazo de subsanación | S | S |
|
|
182
|
+
| Alta por rechazo / sin registro previo | S | X |
|
|
183
|
+
|
|
184
|
+
Las de anulación combinan `SinRegistroPrevio` y `RechazoPrevio` del mismo modo.
|
|
185
|
+
|
|
186
|
+
**Las construye todas**: `RegistroAlta` admite `subsanacion:` y
|
|
187
|
+
`rechazo_previo:`, y `RegistroAnulacion` admite `sin_registro_previo:` y
|
|
188
|
+
`rechazo_previo:`. Importa porque una huella que no cuadra es error admisible y
|
|
189
|
+
**obliga a subsanar**: sin estos campos, quien recibiera un `AceptadoConErrores`
|
|
190
|
+
no tendría con qué corregirlo. La subsanación está además contrastada contra el
|
|
191
|
+
servicio real (ver más abajo).
|
|
192
|
+
|
|
193
|
+
`RechazoPrevio = X` es el camino de migración desde NO VERI\*FACTU: registros que
|
|
194
|
+
existen en el SIF pero nunca se remitieron.
|
|
195
|
+
|
|
196
|
+
## Endpoints, TLS y certificados
|
|
197
|
+
|
|
198
|
+
**[doc]** Los dominios de preproducción se corresponden uno a uno con producción:
|
|
199
|
+
|
|
200
|
+
| Preproducción | Producción | Uso |
|
|
201
|
+
|---|---|---|
|
|
202
|
+
| `prewww1.aeat.es` | `www1.agenciatributaria.gob.es` | Web services, certificado normal |
|
|
203
|
+
| `prewww2.aeat.es` | `www2.agenciatributaria.gob.es` | Estáticos (de aquí salen los XSD) y cotejo del QR |
|
|
204
|
+
| `prewww10.aeat.es` | `www10.agenciatributaria.gob.es` | Web services con **certificado de sello** |
|
|
205
|
+
|
|
206
|
+
**Aviso operativo del propio portal:** preproducción es para pruebas *puntuales*.
|
|
207
|
+
Nada de pruebas masivas ni de validaciones integradas en procesos de producción;
|
|
208
|
+
un uso que consideren abusivo puede acabar en bloqueo de acceso.
|
|
209
|
+
|
|
210
|
+
**[real] `ca_file` no hace falta.** Tras la renovación de noviembre de 2025, los
|
|
211
|
+
cinco endpoints validan con el almacén de confianza del sistema (`Verify return
|
|
212
|
+
code: 0`). Sirven cadenas de CA públicas: Entrust OV TLS y Sectigo, ambas bajo
|
|
213
|
+
USERTrust RSA. `ca_file` sigue existiendo por si hay que anclar la cadena en algún
|
|
214
|
+
entorno, pero ante un fallo de verificación lo probable es un almacén anticuado o
|
|
215
|
+
un proxy interceptando el TLS. Nunca `VERIFY_NONE`.
|
|
216
|
+
|
|
217
|
+
**[real]** El mTLS funciona con certificado de representante de la FNMT, y
|
|
218
|
+
`Certificado#sello?` acierta el caso negativo (va a `prewww1`, que es lo
|
|
219
|
+
correcto).
|
|
220
|
+
|
|
221
|
+
**[?] El caso del sello queda sin contrastar, y seguirá así.** La AEAT no emite
|
|
222
|
+
certificados de prueba —preproducción exige un certificado real de una CA
|
|
223
|
+
reconocida— y el de sello de entidad de la FNMT es de pago y solo para personas
|
|
224
|
+
jurídicas. Con él hay que declarar `sello: true` explícitamente en vez de fiarse
|
|
225
|
+
de la heurística sobre el sujeto del certificado, que nunca ha visto un sello
|
|
226
|
+
real.
|
|
227
|
+
|
|
228
|
+
## Cadenas, instalaciones y varias fuentes de facturación
|
|
229
|
+
|
|
230
|
+
**[doc]** La cadena es **una por SIF + NIF obligado**, no una por serie. Pero
|
|
231
|
+
"SIF" no es el producto: lo identifica el bloque `SistemaInformatico` completo, y
|
|
232
|
+
dentro de él el `NumeroInstalacion` distingue instalaciones. Las FAQs:
|
|
233
|
+
|
|
234
|
+
> cada una de esas facturaciones distintas (sean de distintos OEF o del mismo OEF
|
|
235
|
+
> pero de distintos centros de facturación independientes, como tiendas) debe
|
|
236
|
+
> tener un nº de instalación propio y distinto al resto (pasado, presente o
|
|
237
|
+
> futuro) **porque se consideran SIF independientes, como si fueran "SIF
|
|
238
|
+
> virtuales"**
|
|
239
|
+
|
|
240
|
+
Con varias fuentes (tiendas, TPV, web, un job) hay dos arquitecturas válidas: un
|
|
241
|
+
solo SIF, que obliga a serializar todas las fuentes contra una cadena con un lock
|
|
242
|
+
de base de datos; o un SIF virtual por fuente, con cadenas independientes y sin
|
|
243
|
+
lock entre ellas. La segunda es la que la AEAT contempla expresamente y la que
|
|
244
|
+
evita el problema de raíz.
|
|
245
|
+
|
|
246
|
+
El `NumeroInstalacion` **no puede repetirse nunca**, ni al reinstalar sobre la
|
|
247
|
+
misma máquina. Las FAQs recomiendan un timestamp de instalación o un secuencial
|
|
248
|
+
propio del obligado. `IndicadorMultiplesOT` va a "S" cuando un SIF en la nube
|
|
249
|
+
atiende a varios obligados a la vez.
|
|
250
|
+
|
|
251
|
+
**Regla dura de diseño: la gema no autogenera nunca un `NumeroInstalacion`.** Si
|
|
252
|
+
lo hiciera, un contenedor que se recrea en cada despliegue produciría una
|
|
253
|
+
instalación por despliegue —y en el límite una por factura—, cada registro saldría
|
|
254
|
+
`PrimerRegistro="S"`, la AEAT lo aceptaría y la cadena dejaría de demostrar nada.
|
|
255
|
+
Lo que lo impide no es técnico: las FAQs prohíben que la identidad del SIF cambie
|
|
256
|
+
"con cada factura ni con cada sesión o arranque del producto", la trazabilidad es
|
|
257
|
+
obligación legal del productor (art. 29.2.j LGT) y certificar por declaración
|
|
258
|
+
responsable un SIF que no cumple el RD 1007/2023 es sancionable.
|
|
259
|
+
|
|
260
|
+
## La AEAT no impide bifurcar la cadena
|
|
261
|
+
|
|
262
|
+
**[real]** Se enviaron dos altas distintas apuntando ambas al **mismo**
|
|
263
|
+
predecesor. La AEAT aceptó las dos con `Correcto`, sin aviso ni error admisible.
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
Registro 1 (PrimerRegistro)
|
|
267
|
+
├── Registro 2 -> anterior: Registro 1
|
|
268
|
+
└── Registro 3 -> anterior: Registro 1 <-- cadena bifurcada, aceptada
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Es el hallazgo con más consecuencias de diseño de toda la integración:
|
|
272
|
+
|
|
273
|
+
- **No hay red de seguridad.** La AEAT no valida al recibir que el
|
|
274
|
+
`RegistroAnterior` sea de verdad el último anotado. Una condición de carrera
|
|
275
|
+
produce una cadena rota que se acepta en silencio y no se descubre al enviar.
|
|
276
|
+
- **El lock no es una optimización, es lo único que sostiene la integridad**, y
|
|
277
|
+
dentro de la gema lo respalda un índice único sobre `(cadena_id,
|
|
278
|
+
huella_anterior)`.
|
|
279
|
+
- **Que no lo rechacen no significa que no lo vean.** La AEAT conserva todos los
|
|
280
|
+
registros, y la lista L1E de tipos de anomalía incluye "el campo huella del
|
|
281
|
+
registro anterior no se corresponde con la huella del registro anterior". Lo que
|
|
282
|
+
no hay es detección síncrona.
|
|
283
|
+
|
|
284
|
+
**La huella anterior no la da la AEAT**: hay que guardarla. Cada registro almacena
|
|
285
|
+
la suya y el siguiente lee la última de su cadena bajo lock.
|
|
286
|
+
|
|
287
|
+
## Qué queda anotado: la consulta devuelve una foto, no un libro
|
|
288
|
+
|
|
289
|
+
**[real]** Consultada una cadena con **6 registros de facturación sobre 4
|
|
290
|
+
facturas**, la consulta devolvió **4 filas**: una por factura, con su estado
|
|
291
|
+
actual, no el histórico. De ahí tres conclusiones:
|
|
292
|
+
|
|
293
|
+
- **La subsanación sustituye al original, no convive con él.** La factura
|
|
294
|
+
subsanada aparece una sola vez, con los importes corregidos y
|
|
295
|
+
`TimestampUltimaModificacion` a la hora de la subsanación. `Correcto` al
|
|
296
|
+
subsanar significa de verdad "he reemplazado el registro anterior".
|
|
297
|
+
- **La anulación surte efecto**: la factura queda en `Anulado`, un estado que
|
|
298
|
+
`RespuestaSuministro` ni siquiera puede expresar (solo conoce Correcto,
|
|
299
|
+
AceptadoConErrores e Incorrecto). Sin este servicio no hay forma de observarlo.
|
|
300
|
+
- **La cadena NO se puede reconstruir entera desde la consulta.** Es la cara B: los
|
|
301
|
+
eslabones sustituidos desaparecen, así que los registros que encadenaban tras
|
|
302
|
+
ellos *parecen* huérfanos aunque la cadena esté intacta. Por lo mismo, se
|
|
303
|
+
informaron **cero** registros marcados `PrimerRegistro`, porque el primero de la
|
|
304
|
+
cadena había sido sustituido.
|
|
305
|
+
|
|
306
|
+
**Corolario para la capa Rails: el histórico de la cadena hay que guardarlo uno
|
|
307
|
+
mismo.** La consulta sirve para reconciliar el estado de cada factura, no para
|
|
308
|
+
auditar el encadenamiento. Detectar una bifurcación sigue dependiendo del SIF.
|
|
309
|
+
|
|
310
|
+
Detalles confirmados de paso: las huellas devueltas **coinciden** con las
|
|
311
|
+
almacenadas; los importes vuelven **sin ceros a la derecha** (`181.5`, `121`,
|
|
312
|
+
`133.1`); y el orden de las filas **no es cronológico**, así que no conviene
|
|
313
|
+
apoyarse en él.
|
|
314
|
+
|
|
315
|
+
**[real] `Subsanacion="S"` evita el rechazo por duplicado.** Reenviar una factura
|
|
316
|
+
con el mismo `IDFactura` e importes distintos se anota como `Correcto`, sin
|
|
317
|
+
`RegistroDuplicado`. El mismo cuerpo sin la marca habría chocado con "Registro de
|
|
318
|
+
facturación duplicado".
|
|
319
|
+
|
|
320
|
+
## Reconciliación
|
|
321
|
+
|
|
322
|
+
**[real]** Dos altas remitidas y reconciliadas a continuación: 2 facturas
|
|
323
|
+
locales, 2 filas de la AEAT, 0 divergencias.
|
|
324
|
+
|
|
325
|
+
**[real] La AEAT imputa el periodo por FECHA DE EXPEDICIÓN.** Es lo que asume
|
|
326
|
+
`Reconciliacion#vigentes` al elegir qué facturas locales revisar.
|
|
327
|
+
|
|
328
|
+
**[?] El cotejo del `SistemaInformatico` lo aplica el servidor: probable, no
|
|
329
|
+
comprobado.** El razonamiento es indirecto: había 4 facturas anotadas bajo el
|
|
330
|
+
mismo NIF y el mismo periodo en otra instalación, y la consulta filtrada devolvió
|
|
331
|
+
solo las 2 de la instalación consultada. **Falta la premisa**: que esas 4
|
|
332
|
+
siguieran almacenadas ese día. Preproducción no tiene trascendencia tributaria y
|
|
333
|
+
nada garantiza que no purguen datos, así que "las purgaron" explica lo observado
|
|
334
|
+
igual de bien. Se cierra pidiendo el mismo periodo con el SIF de la otra
|
|
335
|
+
instalación.
|
|
336
|
+
|
|
337
|
+
Consecuencia práctica que no depende de eso: **el SIF que se manda en el filtro
|
|
338
|
+
tiene que ser el mismo con el que se anotaron los registros**. Con uno distinto la
|
|
339
|
+
respuesta viene `SinDatos`, y eso se disfraza de "no consta ninguna factura". Por
|
|
340
|
+
eso `Reconciliacion` filtra además en cliente por el `NumeroInstalacion` de cada
|
|
341
|
+
fila.
|
|
342
|
+
|
|
343
|
+
## El código QR: la AEAT NO devuelve la URL
|
|
344
|
+
|
|
345
|
+
**[doc]** Conviene decirlo explícito porque es la suposición natural y es falsa:
|
|
346
|
+
la URL de cotejo no llega en ninguna respuesta. No está en
|
|
347
|
+
`RespuestaSuministro.xsd`, ni en `RespuestaConsultaLR.xsd`, ni se menciona en
|
|
348
|
+
Validaciones. La construye el propio SIF con datos que ya tiene.
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
Pruebas: https://prewww2.aeat.es/wlpl/TIKE-CONT/ValidarQR?
|
|
352
|
+
Producción: https://www2.agenciatributaria.gob.es/wlpl/TIKE-CONT/ValidarQR?
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Cuatro parámetros: `nif`, `numserie`, `fecha` (DD-MM-AAAA) e `importe`.
|
|
356
|
+
|
|
357
|
+
- **El host no es el del SOAP.** El cotejo va por `prewww2`/`www2`, los envíos por
|
|
358
|
+
`prewww1`/`prewww10`.
|
|
359
|
+
- **El URL encoding no es opcional y el caso es alcanzable.** `Formato.num_serie`
|
|
360
|
+
admite `&`, `%`, `+` y espacios, así que un número de serie válido para el envío
|
|
361
|
+
puede partir la URL si se concatena sin codificar. UTF-8.
|
|
362
|
+
- Hay una URL distinta para NO VERI\*FACTU (`ValidarQRNoVerifactu`), fuera de
|
|
363
|
+
alcance.
|
|
364
|
+
|
|
365
|
+
**Consecuencia de diseño.** Como el QR no depende de la respuesta de la AEAT, se
|
|
366
|
+
genera al crear el registro y bajo el mismo lock, antes de enviar nada: la
|
|
367
|
+
impresión de la factura queda desacoplada del envío asíncrono. Ojo al matiz: que
|
|
368
|
+
el QR sea *válido* no significa que la factura *conste*. Si el envío nunca se
|
|
369
|
+
completa, quien escanee obtendrá un "no consta".
|
|
370
|
+
|
|
371
|
+
## Ritmo de remisión: los lotes NO son el modo normal
|
|
372
|
+
|
|
373
|
+
**[doc]** Las FAQs son tajantes:
|
|
374
|
+
|
|
375
|
+
> debe asegurarse que la generación del RF se produzca de forma "simultánea"
|
|
376
|
+
> (entiéndase inmediata o sin demora apreciable) a la expedición de la factura
|
|
377
|
+
> para su instantáneo almacenamiento o remisión a la AEAT
|
|
378
|
+
|
|
379
|
+
Un comercio que factura cada diez minutos mandará siempre un registro por
|
|
380
|
+
petición. El tope de 1000 es un techo para quien factura rápido, no un objetivo:
|
|
381
|
+
el tamaño del lote lo dicta el ritmo de facturación, no una decisión de diseño.
|
|
382
|
+
|
|
383
|
+
**[real] Un envío admite una cadena entera, no solo un eslabón.** Varios registros
|
|
384
|
+
encadenados entre sí dentro del mismo `RegFactuSistemaFacturacion` se anotan
|
|
385
|
+
todos. Esto separa dos cosas fáciles de confundir: el **cálculo** de la cadena
|
|
386
|
+
sigue necesitando el lock por SIF+NIF, pero el **transporte** puede agrupar.
|
|
387
|
+
|
|
388
|
+
**[real] `TiempoEsperaEnvio` no escala con el tamaño del lote**: 60 s tanto tras
|
|
389
|
+
un lote de tres como tras un envío de uno. Es el valor inicial que fija el art.
|
|
390
|
+
16.2 de la Orden. Agrupar sale más barato que encadenar peticiones: mismo coste de
|
|
391
|
+
espera, más registros dentro. Medido solo en preproducción, y la AEAT lo devuelve
|
|
392
|
+
en cada respuesta precisamente porque puede variarlo.
|
|
393
|
+
|
|
394
|
+
**[doc]** `TiempoEsperaEnvio` significa esperar esos segundos **o** acumular hasta
|
|
395
|
+
el límite de lote, lo que ocurra primero.
|
|
396
|
+
|
|
397
|
+
## Obligaciones del SIF que no se leen en el esquema (OM art. 7.i)
|
|
398
|
+
|
|
399
|
+
**[doc]** Dos comprobaciones **antes de generar cada registro**, que no se deducen
|
|
400
|
+
de ningún XSD:
|
|
401
|
+
|
|
402
|
+
> 1.º El último registro de facturación generado está correctamente encadenado.
|
|
403
|
+
> 2.º La fecha y hora de generación del último registro de facturación generado
|
|
404
|
+
> no es superior en más de un minuto a la fecha y hora actuales que se utilizarán
|
|
405
|
+
> para fechar el registro de facturación a generar.
|
|
406
|
+
|
|
407
|
+
En la primera se mira **un eslabón hacia atrás**, no la cadena entera: que la
|
|
408
|
+
`Huella` del `RegistroAnterior` del RF n-1 se corresponda con la huella del RF
|
|
409
|
+
n-2.
|
|
410
|
+
|
|
411
|
+
La segunda confunde y las FAQs lo aclaran: que pasen horas entre registros **no es
|
|
412
|
+
problema**. Lo que no se admite es que el registro a generar tenga fecha anterior
|
|
413
|
+
en más de un minuto al ya generado. Es un control de que el reloj no va hacia
|
|
414
|
+
atrás, no de que factures rápido.
|
|
415
|
+
|
|
416
|
+
**Y lo más importante: detectar una anomalía NO puede parar la caja.**
|
|
417
|
+
|
|
418
|
+
> será preciso generar el siguiente RF, ya que la facturación por este motivo
|
|
419
|
+
> **NUNCA debe interrumpirse**
|
|
420
|
+
|
|
421
|
+
Conviene no confundir dos clases de fallo:
|
|
422
|
+
|
|
423
|
+
- **Datos inválidos del registro** (un tipo impositivo inexistente, un desglose
|
|
424
|
+
incoherente): no se puede generar el registro, y ahí sí hay que fallar. Con esos
|
|
425
|
+
datos tampoco se puede emitir la factura.
|
|
426
|
+
- **Anomalía de trazabilidad** (el eslabón anterior no cuadra): se anota, se avisa
|
|
427
|
+
y se sigue. Nunca bloquea.
|
|
428
|
+
|
|
429
|
+
Las FAQs añaden que el orden de generación debe seguir el orden cronológico de
|
|
430
|
+
expedición, lo que encaja con serializar bajo lock.
|
|
431
|
+
|
|
432
|
+
## Recuperación ante desastre
|
|
433
|
+
|
|
434
|
+
**[doc]** Si se pierde la base de datos local a mitad de año hay dos salidas:
|
|
435
|
+
|
|
436
|
+
1. **Recuperar el último eslabón desde la AEAT.** La consulta devuelve, de cada
|
|
437
|
+
registro, su `Huella` y su `FechaHoraHusoGenRegistro`. El de marca temporal
|
|
438
|
+
mayor es el último de la cadena, y con su `IDFactura` + `Huella` se reanuda.
|
|
439
|
+
Solo vale en modalidad VERI\*FACTU, que es donde la AEAT los conserva.
|
|
440
|
+
2. **No recuperarlo: abrir instalación nueva.** Reinstalar exige un nº de
|
|
441
|
+
instalación nuevo, y una instalación nueva arranca su propia cadena con
|
|
442
|
+
`PrimerRegistro="S"`.
|
|
443
|
+
|
|
444
|
+
De aquí sale una idea que ordena bastante: **la cadena no es un hilo eterno del
|
|
445
|
+
contribuyente, es por instalación**. Romperla no es un pecado irreparable; es
|
|
446
|
+
motivo para abrir una instalación nueva. Lo que sí es irreparable es reutilizar un
|
|
447
|
+
número de factura, y eso sí lo detecta la AEAT.
|
|
448
|
+
|
|
449
|
+
## Respuestas y errores del servicio
|
|
450
|
+
|
|
451
|
+
**[doc]** Estados: `EstadoEnvio` (Correcto / ParcialmenteCorrecto / Incorrecto),
|
|
452
|
+
`EstadoRegistro` (Correcto / AceptadoConErrores / Incorrecto) y
|
|
453
|
+
`EstadoRegistroDuplicado` (Correcta / AceptadaConErrores / Anulada). La consulta
|
|
454
|
+
usa otros: Correcto, AceptadoConErrores y **Anulado**, que el canal de envío no
|
|
455
|
+
sabe expresar.
|
|
456
|
+
|
|
457
|
+
**[doc]** Solo hay que escapar `&` como `&` y `<` como `<`, lo que respalda
|
|
458
|
+
la asimetría huella-cruda / XML-escapado. Ceros a la izquierda prohibidos en
|
|
459
|
+
numéricos, irrelevantes tras el separador decimal.
|
|
460
|
+
|
|
461
|
+
**[real] Los fallos de servicio llegan como SOAP Fault**, no como
|
|
462
|
+
`RespuestaSuministro`, con el código dentro de `faultstring` en el formato
|
|
463
|
+
`Codigo[NNNN].descripción`.
|
|
464
|
+
|
|
465
|
+
**[real] La identificación del obligado es por el PAR NIF + NombreRazon.** Con el
|
|
466
|
+
NIF correcto y un nombre que no cuadre con el censo, la AEAT responde `4104` "el
|
|
467
|
+
NIF no está identificado", que apunta al campo equivocado; el detalle del error sí
|
|
468
|
+
devuelve los dos campos. Lo mismo para el destinatario, con el código `1239`. Un
|
|
469
|
+
NIF con dígito de control incorrecto da `4116`.
|
|
470
|
+
|
|
471
|
+
**[real]** Un sobre SOAP con dos declaraciones XML devuelve `Codigo[102].Error
|
|
472
|
+
interno en el servidor`: es un fallo de parseo, no de negocio, y el mensaje no
|
|
473
|
+
orienta en absoluto.
|
|
474
|
+
|
|
475
|
+
**[real]** Estos códigos existen como error propio de la AEAT, lo que respalda las
|
|
476
|
+
validaciones implementadas leyendo el ap. 15:
|
|
477
|
+
|
|
478
|
+
| Código | Regla |
|
|
479
|
+
|---|---|
|
|
480
|
+
| 1237 | N1/N2 con IVA no admiten tipo, cuota ni recargo |
|
|
481
|
+
| 1238 | Una exenta no admite esos cuatro campos |
|
|
482
|
+
| 1245 | `ClaveRegimen` obligatoria con IVA/IPSI/IGIC |
|
|
483
|
+
| 1232-1234 | Reglas cruzadas de `IDType` y `CodigoPais` |
|
|
484
|
+
| 1235-1236 | Ventanas temporales de `TipoImpositivo` |
|
|
485
|
+
|
|
486
|
+
**[doc]** WSDL de pruebas:
|
|
487
|
+
`https://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SistemaFacturacion.wsdl`
|
|
488
|
+
|
|
489
|
+
## Lo que sigue sin comprobarse
|
|
490
|
+
|
|
491
|
+
- **El certificado de sello** y su endpoint (`prewww10`/`www10`). No hay forma
|
|
492
|
+
barata de probarlo: la AEAT no emite certificados de prueba.
|
|
493
|
+
- **El cotejo del SIF en servidor**, por la premisa que falta (ver arriba).
|
|
494
|
+
- **Las operativas de subsanación por rechazo y de anulación sin registro
|
|
495
|
+
previo**, que la gema no construye.
|
|
496
|
+
- **Los caminos de rechazo**: una factura válida no los recorre, así que las
|
|
497
|
+
validaciones de error solo están ejercitadas contra los tests propios.
|
|
498
|
+
|
|
499
|
+
## Nota operativa
|
|
500
|
+
|
|
501
|
+
Los guiones de `examples/` contra preproducción y la suite de tests **no deben
|
|
502
|
+
compartir base de datos**. La suite tira las tablas, las recrea y vacía cadenas y
|
|
503
|
+
registros en cada test; correrla contra una base con datos de pruebas reales los
|
|
504
|
+
destruye. La gema lo impide abortando si el nombre de la base no parece de tests,
|
|
505
|
+
pero la separación conviene hacerla explícita con `VF_DATABASE_URL`.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails/generators'
|
|
4
|
+
require 'rails/generators/active_record'
|
|
5
|
+
|
|
6
|
+
module Verifactu
|
|
7
|
+
module Generators
|
|
8
|
+
# `rails g verifactu:install`
|
|
9
|
+
#
|
|
10
|
+
# Deja dos ficheros y nada más: la migración del libro registro y un
|
|
11
|
+
# initializer con la identificación del SIF.
|
|
12
|
+
#
|
|
13
|
+
# Lo que este generador NO hace, y es deliberado:
|
|
14
|
+
#
|
|
15
|
+
# - No abre ninguna cadena. El NumeroInstalacion no se autogenera en
|
|
16
|
+
# ninguna parte de esta gema (ver Cadena.abrir!): un contenedor que se
|
|
17
|
+
# recrea en cada despliegue acabaría con una instalación por despliegue,
|
|
18
|
+
# y eso vacía de sentido el encadenamiento.
|
|
19
|
+
# - No toca el certificado ni escribe dónde vive. Certificado recibe los
|
|
20
|
+
# bytes ya cargados y no sabe leer ficheros ni ENV a propósito: la
|
|
21
|
+
# política de almacenamiento de la credencial es de quien despliega.
|
|
22
|
+
class InstallGenerator < Rails::Generators::Base
|
|
23
|
+
include ActiveRecord::Generators::Migration
|
|
24
|
+
|
|
25
|
+
source_root File.expand_path('templates', __dir__)
|
|
26
|
+
|
|
27
|
+
desc 'Instala el libro registro VERI*FACTU: migración e initializer.'
|
|
28
|
+
|
|
29
|
+
def crear_initializer
|
|
30
|
+
template 'verifactu.rb.tt', 'config/initializers/verifactu.rb'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def crear_migracion
|
|
34
|
+
migration_template 'instalar_verifactu.rb.tt',
|
|
35
|
+
'db/migrate/instalar_verifactu.rb'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def siguientes_pasos
|
|
39
|
+
say <<~TXT
|
|
40
|
+
|
|
41
|
+
Instalado. Quedan dos cosas, y ninguna se puede hacer por ti:
|
|
42
|
+
|
|
43
|
+
1. Edita config/initializers/verifactu.rb. Los valores que trae son
|
|
44
|
+
inválidos a propósito, para que falle pronto y no mande a la AEAT
|
|
45
|
+
una identificación de SIF inventada.
|
|
46
|
+
2. rails db:migrate
|
|
47
|
+
|
|
48
|
+
Después, abre una cadena por cada fuente de facturación (una por
|
|
49
|
+
tienda, TPV o sede: son SIF virtuales distintos). En un seed, en una
|
|
50
|
+
tarea rake o en la consola, NO en el initializer: una cadena es un
|
|
51
|
+
dato, se abre una vez y a conciencia.
|
|
52
|
+
|
|
53
|
+
VerifactuRails::Libro::Cadena.abrir!(
|
|
54
|
+
numero_instalacion: 'TIENDA-VALENCIA-20260810120000',
|
|
55
|
+
nif_obligado: '...', nombre_obligado: '...')
|
|
56
|
+
|
|
57
|
+
TXT
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'verifactu_rails/libro'
|
|
4
|
+
|
|
5
|
+
# Libro registro VERI*FACTU.
|
|
6
|
+
#
|
|
7
|
+
# El esquema no se copia aquí: se HEREDA de la gema. Así lo que ejecuta tu
|
|
8
|
+
# db:migrate es exactamente el mismo esquema que ejercita la suite de tests de
|
|
9
|
+
# verifactu-rails, incluidos los índices únicos que impiden bifurcar la cadena
|
|
10
|
+
# (que son la única red que hay: está comprobado que la AEAT acepta una cadena
|
|
11
|
+
# bifurcada sin avisar).
|
|
12
|
+
#
|
|
13
|
+
# El contrato que hace esto seguro: VerifactuRails::Libro::Migracion es el
|
|
14
|
+
# esquema v1 y está CONGELADO. Ningún cambio futuro de la gema lo tocará; los
|
|
15
|
+
# cambios de esquema llegarán como migraciones nuevas y aparte. Si no fuera así,
|
|
16
|
+
# una app ya migrada y una instalación nueva acabarían con esquemas distintos sin
|
|
17
|
+
# que nadie se enterara.
|
|
18
|
+
class <%= migration_class_name %> < VerifactuRails::Libro::Migracion
|
|
19
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Identificación del SISTEMA INFORMÁTICO DE FACTURACIÓN, que viaja en CADA
|
|
4
|
+
# registro remitido a la AEAT.
|
|
5
|
+
#
|
|
6
|
+
# Describe al SIF que despliegas TÚ, no a esta gema: verifactu-rails es un
|
|
7
|
+
# componente de tu sistema de facturación, no el sistema.
|
|
8
|
+
#
|
|
9
|
+
# LOS VALORES DE ABAJO SON INVÁLIDOS A PROPÓSITO. Con ellos, el primer
|
|
10
|
+
# `anotar_alta!` levanta una ValidacionError antes de construir nada. Es
|
|
11
|
+
# preferible a un valor de relleno plausible, que se remitiría a la AEAT como
|
|
12
|
+
# identificación real de tu sistema.
|
|
13
|
+
#
|
|
14
|
+
# Va dentro de `on_load(:active_record)` porque la capa Libro se carga con
|
|
15
|
+
# ActiveRecord, no antes: el núcleo de la gema no depende de Rails y se sigue
|
|
16
|
+
# pudiendo usar suelto.
|
|
17
|
+
#
|
|
18
|
+
# AQUÍ NO SE ABRE NINGUNA CADENA. Esto es configuración; una cadena es un dato, y
|
|
19
|
+
# `Cadena.abrir!` va en un seed, en una tarea rake de alta de tienda o en tu
|
|
20
|
+
# panel de administración, una sola vez y a conciencia. Ponerlo en este fichero
|
|
21
|
+
# rompe de dos maneras: el segundo arranque choca contra el índice único de
|
|
22
|
+
# numero_instalacion, y un initializer que consulta la base de datos revienta
|
|
23
|
+
# donde todavía no la hay (assets:precompile, db:create, un contenedor de CI).
|
|
24
|
+
ActiveSupport.on_load(:active_record) do
|
|
25
|
+
VerifactuRails::Libro.configure do |c|
|
|
26
|
+
# Quién PRODUCE el software. Si lo has desarrollado tú para tu propio uso,
|
|
27
|
+
# eres tú; si lo vendes, eres tú igualmente (no tu cliente).
|
|
28
|
+
c.productor_nombre = 'CAMBIAME: razón social del productor'
|
|
29
|
+
c.productor_nif = 'CAMBIAME'
|
|
30
|
+
|
|
31
|
+
# Nombre comercial del SIF (máx. 30 caracteres).
|
|
32
|
+
c.nombre_sistema = 'CAMBIAME'
|
|
33
|
+
|
|
34
|
+
# IdSistemaInformatico: EXACTAMENTE dos posiciones, mayúscula (sin Ñ) o
|
|
35
|
+
# dígito. Distingue entre sí los sistemas de un mismo productor.
|
|
36
|
+
c.id_sistema = 'CAMBIAME'
|
|
37
|
+
|
|
38
|
+
# Versión del SIF (máx. 50). Tiene que cambiar cuando cambie el sistema:
|
|
39
|
+
# es lo que permite a la AEAT saber qué versión emitió cada registro.
|
|
40
|
+
c.version = 'CAMBIAME'
|
|
41
|
+
|
|
42
|
+
# Preproducción hasta que esto vaya en serio. Aviso de la propia AEAT: el
|
|
43
|
+
# entorno de pruebas es para pruebas PUNTUALES, no para carga.
|
|
44
|
+
c.entorno = Rails.env.production? ? :produccion : :pruebas
|
|
45
|
+
|
|
46
|
+
# ¿Puede este SIF usarse para VARIOS obligados tributarios? Y, si puede,
|
|
47
|
+
# ¿se está usando así ahora mismo? Declarar `multiples_ot` sin `multi_ot`
|
|
48
|
+
# es incoherente y la gema lo rechaza.
|
|
49
|
+
c.multi_ot = false
|
|
50
|
+
c.multiples_ot = false
|
|
51
|
+
|
|
52
|
+
# Anomalías de trazabilidad del art. 7.i) de la Orden HAC/1177/2024.
|
|
53
|
+
#
|
|
54
|
+
# NO interrumpen la facturación: la norma dice que ante una anomalía la
|
|
55
|
+
# facturación "nunca debe interrumpirse". Se anotan en el registro y se
|
|
56
|
+
# avisan por aquí. Por defecto no se hace nada, que en la práctica es
|
|
57
|
+
# perderlas: mándalas a donde de verdad las vayas a mirar.
|
|
58
|
+
c.al_detectar_anomalia = lambda do |anomalias, registro|
|
|
59
|
+
Rails.logger.warn(
|
|
60
|
+
"[verifactu] anomalías #{anomalias.inspect} en el registro #{registro.id}"
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|