@dwast/verifactu-lib 0.1.0-beta.0 → 0.1.0-beta.2
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.
- package/dist/cjs/aeat/client.d.ts +162 -0
- package/dist/cjs/aeat/client.d.ts.map +1 -0
- package/dist/cjs/aeat/client.js +267 -0
- package/dist/cjs/aeat/client.js.map +1 -0
- package/dist/cjs/aeat/index.d.ts +3 -0
- package/dist/cjs/aeat/index.d.ts.map +1 -0
- package/dist/cjs/aeat/index.js +19 -0
- package/dist/cjs/aeat/index.js.map +1 -0
- package/dist/cjs/aeat/response.d.ts +78 -0
- package/dist/cjs/aeat/response.d.ts.map +1 -0
- package/dist/cjs/aeat/response.js +117 -0
- package/dist/cjs/aeat/response.js.map +1 -0
- package/dist/{core → cjs/core}/hash/chain.d.ts +42 -0
- package/dist/cjs/core/hash/chain.d.ts.map +1 -0
- package/dist/cjs/core/hash/chain.js +167 -0
- package/dist/cjs/core/hash/chain.js.map +1 -0
- package/dist/cjs/core/hash/index.d.ts.map +1 -0
- package/dist/cjs/core/hash/index.js +18 -0
- package/dist/cjs/core/hash/index.js.map +1 -0
- package/dist/{core → cjs/core}/index.d.ts +2 -0
- package/dist/cjs/core/index.d.ts.map +1 -0
- package/dist/cjs/core/index.js +23 -0
- package/dist/cjs/core/index.js.map +1 -0
- package/dist/cjs/core/models/event.d.ts.map +1 -0
- package/dist/cjs/core/models/event.js +3 -0
- package/dist/{core → cjs/core}/models/event.js.map +1 -1
- package/dist/cjs/core/models/index.d.ts.map +1 -0
- package/dist/cjs/core/models/index.js +19 -0
- package/dist/cjs/core/models/index.js.map +1 -0
- package/dist/{core → cjs/core}/models/invoice.d.ts +42 -17
- package/dist/cjs/core/models/invoice.d.ts.map +1 -0
- package/dist/cjs/core/models/invoice.js +3 -0
- package/dist/{core → cjs/core}/models/invoice.js.map +1 -1
- package/dist/cjs/core/qr/index.d.ts.map +1 -0
- package/dist/cjs/core/qr/index.js +18 -0
- package/dist/cjs/core/qr/index.js.map +1 -0
- package/dist/cjs/core/qr/qr.d.ts.map +1 -0
- package/dist/cjs/core/qr/qr.js +19 -0
- package/dist/cjs/core/qr/qr.js.map +1 -0
- package/dist/cjs/core/utils/date.d.ts +16 -0
- package/dist/cjs/core/utils/date.d.ts.map +1 -0
- package/dist/cjs/core/utils/date.js +27 -0
- package/dist/cjs/core/utils/date.js.map +1 -0
- package/dist/cjs/core/utils/index.d.ts +2 -0
- package/dist/cjs/core/utils/index.d.ts.map +1 -0
- package/dist/cjs/core/utils/index.js +18 -0
- package/dist/cjs/core/utils/index.js.map +1 -0
- package/dist/cjs/core/validation/index.d.ts +2 -0
- package/dist/cjs/core/validation/index.d.ts.map +1 -0
- package/dist/cjs/core/validation/index.js +18 -0
- package/dist/cjs/core/validation/index.js.map +1 -0
- package/dist/cjs/core/validation/validate.d.ts +18 -0
- package/dist/cjs/core/validation/validate.d.ts.map +1 -0
- package/dist/cjs/core/validation/validate.js +65 -0
- package/dist/cjs/core/validation/validate.js.map +1 -0
- package/dist/cjs/core/xml/generator.d.ts +140 -0
- package/dist/cjs/core/xml/generator.d.ts.map +1 -0
- package/dist/cjs/core/xml/generator.js +269 -0
- package/dist/cjs/core/xml/generator.js.map +1 -0
- package/dist/cjs/core/xml/index.d.ts.map +1 -0
- package/dist/cjs/core/xml/index.js +18 -0
- package/dist/cjs/core/xml/index.js.map +1 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +20 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/integration/accountability.d.ts +41 -0
- package/dist/cjs/integration/accountability.d.ts.map +1 -0
- package/dist/cjs/integration/accountability.js +92 -0
- package/dist/cjs/integration/accountability.js.map +1 -0
- package/dist/cjs/integration/index.d.ts +5 -0
- package/dist/cjs/integration/index.d.ts.map +1 -0
- package/dist/cjs/integration/index.js +21 -0
- package/dist/cjs/integration/index.js.map +1 -0
- package/dist/cjs/integration/p12.d.ts +58 -0
- package/dist/cjs/integration/p12.d.ts.map +1 -0
- package/dist/cjs/integration/p12.js +127 -0
- package/dist/cjs/integration/p12.js.map +1 -0
- package/dist/{integration → cjs/integration}/repository.d.ts +2 -0
- package/dist/cjs/integration/repository.d.ts.map +1 -0
- package/dist/cjs/integration/repository.js +3 -0
- package/dist/cjs/integration/repository.js.map +1 -0
- package/dist/{integration → cjs/integration}/service.d.ts +11 -2
- package/dist/cjs/integration/service.d.ts.map +1 -0
- package/dist/cjs/integration/service.js +230 -0
- package/dist/cjs/integration/service.js.map +1 -0
- package/dist/cjs/integration/tenant-credentials.d.ts +44 -0
- package/dist/cjs/integration/tenant-credentials.d.ts.map +1 -0
- package/dist/cjs/integration/tenant-credentials.js +62 -0
- package/dist/cjs/integration/tenant-credentials.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/esm/aeat/client.d.ts +162 -0
- package/dist/esm/aeat/client.d.ts.map +1 -0
- package/dist/esm/aeat/client.js +259 -0
- package/dist/esm/aeat/client.js.map +1 -0
- package/dist/esm/aeat/index.d.ts +3 -0
- package/dist/esm/aeat/index.d.ts.map +1 -0
- package/dist/esm/aeat/index.js +3 -0
- package/dist/esm/aeat/index.js.map +1 -0
- package/dist/esm/aeat/response.d.ts +78 -0
- package/dist/esm/aeat/response.d.ts.map +1 -0
- package/dist/esm/aeat/response.js +114 -0
- package/dist/esm/aeat/response.js.map +1 -0
- package/dist/esm/core/hash/chain.d.ts +139 -0
- package/dist/esm/core/hash/chain.d.ts.map +1 -0
- package/dist/{core → esm/core}/hash/chain.js +37 -0
- package/dist/esm/core/hash/chain.js.map +1 -0
- package/dist/esm/core/hash/index.d.ts +2 -0
- package/dist/esm/core/hash/index.d.ts.map +1 -0
- package/dist/esm/core/hash/index.js.map +1 -0
- package/dist/esm/core/index.d.ts +7 -0
- package/dist/esm/core/index.d.ts.map +1 -0
- package/dist/{core → esm/core}/index.js +2 -0
- package/dist/esm/core/index.js.map +1 -0
- package/dist/esm/core/models/event.d.ts +25 -0
- package/dist/esm/core/models/event.d.ts.map +1 -0
- package/dist/esm/core/models/event.js.map +1 -0
- package/dist/esm/core/models/index.d.ts +3 -0
- package/dist/esm/core/models/index.d.ts.map +1 -0
- package/dist/esm/core/models/index.js.map +1 -0
- package/dist/esm/core/models/invoice.d.ts +115 -0
- package/dist/esm/core/models/invoice.d.ts.map +1 -0
- package/dist/esm/core/models/invoice.js.map +1 -0
- package/dist/esm/core/qr/index.d.ts +2 -0
- package/dist/esm/core/qr/index.d.ts.map +1 -0
- package/dist/esm/core/qr/index.js.map +1 -0
- package/dist/esm/core/qr/qr.d.ts +13 -0
- package/dist/esm/core/qr/qr.d.ts.map +1 -0
- package/dist/esm/core/qr/qr.js.map +1 -0
- package/dist/esm/core/utils/date.d.ts +16 -0
- package/dist/esm/core/utils/date.d.ts.map +1 -0
- package/dist/esm/core/utils/date.js +24 -0
- package/dist/esm/core/utils/date.js.map +1 -0
- package/dist/esm/core/utils/index.d.ts +2 -0
- package/dist/esm/core/utils/index.d.ts.map +1 -0
- package/dist/esm/core/utils/index.js +2 -0
- package/dist/esm/core/utils/index.js.map +1 -0
- package/dist/esm/core/validation/index.d.ts +2 -0
- package/dist/esm/core/validation/index.d.ts.map +1 -0
- package/dist/esm/core/validation/index.js +2 -0
- package/dist/esm/core/validation/index.js.map +1 -0
- package/dist/esm/core/validation/validate.d.ts +18 -0
- package/dist/esm/core/validation/validate.d.ts.map +1 -0
- package/dist/esm/core/validation/validate.js +62 -0
- package/dist/esm/core/validation/validate.js.map +1 -0
- package/dist/esm/core/xml/generator.d.ts +140 -0
- package/dist/esm/core/xml/generator.d.ts.map +1 -0
- package/dist/esm/core/xml/generator.js +259 -0
- package/dist/esm/core/xml/generator.js.map +1 -0
- package/dist/esm/core/xml/index.d.ts +2 -0
- package/dist/esm/core/xml/index.d.ts.map +1 -0
- package/dist/esm/core/xml/index.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/integration/accountability.d.ts +41 -0
- package/dist/esm/integration/accountability.d.ts.map +1 -0
- package/dist/esm/integration/accountability.js +88 -0
- package/dist/esm/integration/accountability.js.map +1 -0
- package/dist/esm/integration/index.d.ts +5 -0
- package/dist/esm/integration/index.d.ts.map +1 -0
- package/dist/esm/integration/index.js +5 -0
- package/dist/esm/integration/index.js.map +1 -0
- package/dist/esm/integration/p12.d.ts +58 -0
- package/dist/esm/integration/p12.d.ts.map +1 -0
- package/dist/esm/integration/p12.js +119 -0
- package/dist/esm/integration/p12.js.map +1 -0
- package/dist/esm/integration/repository.d.ts +79 -0
- package/dist/esm/integration/repository.d.ts.map +1 -0
- package/dist/esm/integration/repository.js.map +1 -0
- package/dist/esm/integration/service.d.ts +45 -0
- package/dist/esm/integration/service.d.ts.map +1 -0
- package/dist/{integration → esm/integration}/service.js +101 -15
- package/dist/esm/integration/service.js.map +1 -0
- package/dist/esm/integration/tenant-credentials.d.ts +44 -0
- package/dist/esm/integration/tenant-credentials.d.ts.map +1 -0
- package/dist/esm/integration/tenant-credentials.js +58 -0
- package/dist/esm/integration/tenant-credentials.js.map +1 -0
- package/package.json +45 -13
- package/readme.md +566 -42
- package/src/aeat/client.ts +296 -55
- package/src/aeat/index.ts +1 -0
- package/src/aeat/response.ts +195 -0
- package/src/core/hash/chain.ts +68 -0
- package/src/core/index.ts +2 -0
- package/src/core/models/invoice.ts +40 -18
- package/src/core/utils/date.ts +27 -0
- package/src/core/utils/index.ts +1 -0
- package/src/core/validation/index.ts +1 -0
- package/src/core/validation/validate.ts +71 -0
- package/src/core/xml/generator.ts +306 -73
- package/src/integration/accountability.ts +125 -0
- package/src/integration/index.ts +2 -0
- package/src/integration/p12.ts +155 -0
- package/src/integration/repository.ts +2 -0
- package/src/integration/service.ts +113 -14
- package/dist/aeat/client.d.ts +0 -57
- package/dist/aeat/client.d.ts.map +0 -1
- package/dist/aeat/client.js +0 -87
- package/dist/aeat/client.js.map +0 -1
- package/dist/aeat/index.d.ts +0 -2
- package/dist/aeat/index.d.ts.map +0 -1
- package/dist/aeat/index.js +0 -2
- package/dist/aeat/index.js.map +0 -1
- package/dist/core/hash/chain.d.ts.map +0 -1
- package/dist/core/hash/chain.js.map +0 -1
- package/dist/core/hash/index.d.ts.map +0 -1
- package/dist/core/hash/index.js.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js.map +0 -1
- package/dist/core/models/event.d.ts.map +0 -1
- package/dist/core/models/index.d.ts.map +0 -1
- package/dist/core/models/index.js.map +0 -1
- package/dist/core/models/invoice.d.ts.map +0 -1
- package/dist/core/qr/index.d.ts.map +0 -1
- package/dist/core/qr/index.js.map +0 -1
- package/dist/core/qr/qr.d.ts.map +0 -1
- package/dist/core/qr/qr.js.map +0 -1
- package/dist/core/xml/generator.d.ts +0 -32
- package/dist/core/xml/generator.d.ts.map +0 -1
- package/dist/core/xml/generator.js +0 -103
- package/dist/core/xml/generator.js.map +0 -1
- package/dist/core/xml/index.d.ts.map +0 -1
- package/dist/core/xml/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/integration/index.d.ts +0 -3
- package/dist/integration/index.d.ts.map +0 -1
- package/dist/integration/index.js +0 -3
- package/dist/integration/index.js.map +0 -1
- package/dist/integration/repository.d.ts.map +0 -1
- package/dist/integration/repository.js.map +0 -1
- package/dist/integration/service.d.ts.map +0 -1
- package/dist/integration/service.js.map +0 -1
- /package/dist/{core → cjs/core}/hash/index.d.ts +0 -0
- /package/dist/{core → cjs/core}/models/event.d.ts +0 -0
- /package/dist/{core → cjs/core}/models/index.d.ts +0 -0
- /package/dist/{core → cjs/core}/qr/index.d.ts +0 -0
- /package/dist/{core → cjs/core}/qr/qr.d.ts +0 -0
- /package/dist/{core → cjs/core}/xml/index.d.ts +0 -0
- /package/dist/{index.d.ts → cjs/index.d.ts} +0 -0
- /package/dist/{core → esm/core}/hash/index.js +0 -0
- /package/dist/{core → esm/core}/models/event.js +0 -0
- /package/dist/{core → esm/core}/models/index.js +0 -0
- /package/dist/{core → esm/core}/models/invoice.js +0 -0
- /package/dist/{core → esm/core}/qr/index.js +0 -0
- /package/dist/{core → esm/core}/qr/qr.js +0 -0
- /package/dist/{core → esm/core}/xml/index.js +0 -0
- /package/dist/{index.js → esm/index.js} +0 -0
- /package/dist/{integration → esm/integration}/repository.js +0 -0
package/src/core/hash/chain.ts
CHANGED
|
@@ -124,6 +124,64 @@ export function verifyRegistroAnulacion(
|
|
|
124
124
|
return computeRegistroAnulacion(input).toLowerCase() === expectedHash.toLowerCase();
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Campos de un registro de evento (RegistroEvento).
|
|
129
|
+
*
|
|
130
|
+
* Nota: el nombre `NIF` aparece DOS veces (posiciones 1 y 6): una para el
|
|
131
|
+
* sistema informático y otra para el obligado a emitir.
|
|
132
|
+
*/
|
|
133
|
+
export interface RegistroEventoInput {
|
|
134
|
+
/** NIF del sistema informático (NIF, posición 1). */
|
|
135
|
+
nifSistemaInformatico: string;
|
|
136
|
+
/** Identificador del evento (ID). */
|
|
137
|
+
id: string;
|
|
138
|
+
/** Identificador del sistema informático. */
|
|
139
|
+
idSistemaInformatico: string;
|
|
140
|
+
/** Versión del sistema informático. */
|
|
141
|
+
version: string;
|
|
142
|
+
/** Número de instalación. */
|
|
143
|
+
numeroInstalacion: string;
|
|
144
|
+
/** NIF del obligado a emitir (NIF, posición 6). */
|
|
145
|
+
nifObligado: string;
|
|
146
|
+
/** Tipo de evento. */
|
|
147
|
+
tipoEvento: string;
|
|
148
|
+
/** Huella (hash) del registro anterior, o null para el primero. */
|
|
149
|
+
huellaAnterior: string | null;
|
|
150
|
+
/** Fecha/hora de generación con huso horario. */
|
|
151
|
+
fechaHoraHusoGenEvento: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Construye la cadena canónica a hashear de un registro de evento.
|
|
156
|
+
*/
|
|
157
|
+
export function concatenateRegistroEvento(input: RegistroEventoInput): string {
|
|
158
|
+
return concatenateFields([
|
|
159
|
+
['NIF', input.nifSistemaInformatico],
|
|
160
|
+
['ID', input.id],
|
|
161
|
+
['IdSistemaInformatico', input.idSistemaInformatico],
|
|
162
|
+
['Version', input.version],
|
|
163
|
+
['NumeroInstalacion', input.numeroInstalacion],
|
|
164
|
+
['NIF', input.nifObligado],
|
|
165
|
+
['TipoEvento', input.tipoEvento],
|
|
166
|
+
['HuellaEvento', input.huellaAnterior],
|
|
167
|
+
['FechaHoraHusoGenEvento', input.fechaHoraHusoGenEvento],
|
|
168
|
+
]);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Calcula la huella (hash) de un registro de evento.
|
|
173
|
+
*/
|
|
174
|
+
export function computeRegistroEvento(input: RegistroEventoInput): string {
|
|
175
|
+
return sha256(concatenateRegistroEvento(input));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Verifica que el hash de un registro de evento coincide con el esperado.
|
|
180
|
+
*/
|
|
181
|
+
export function verifyRegistroEvento(input: RegistroEventoInput, expectedHash: string): boolean {
|
|
182
|
+
return computeRegistroEvento(input).toLowerCase() === expectedHash.toLowerCase();
|
|
183
|
+
}
|
|
184
|
+
|
|
127
185
|
/**
|
|
128
186
|
* Cadena de hash con estado, para encadenar registros de forma secuencial.
|
|
129
187
|
*/
|
|
@@ -161,6 +219,16 @@ export class HashChain {
|
|
|
161
219
|
return hash;
|
|
162
220
|
}
|
|
163
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Encadena un nuevo registro de evento y devuelve su huella.
|
|
224
|
+
*/
|
|
225
|
+
pushEvento(input: Omit<RegistroEventoInput, 'huellaAnterior'>): string {
|
|
226
|
+
const hash = computeRegistroEvento({ ...input, huellaAnterior: this._lastHash });
|
|
227
|
+
this._lastHash = hash;
|
|
228
|
+
this._count += 1;
|
|
229
|
+
return hash;
|
|
230
|
+
}
|
|
231
|
+
|
|
164
232
|
/** Reinicia la cadena. */
|
|
165
233
|
reset(): void {
|
|
166
234
|
this._lastHash = null;
|
package/src/core/index.ts
CHANGED
|
@@ -51,39 +51,51 @@ export type TipoFactura =
|
|
|
51
51
|
export type TipoDesglose = 'S1' | 'S2' | 'S3' | 'S4' | 'S5' | 'S6';
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
-
* Detalle de
|
|
54
|
+
* Detalle de desglose de una línea (DetalleType del XSD de AEAT).
|
|
55
55
|
*/
|
|
56
|
-
export interface
|
|
56
|
+
export interface DetalleDesglose {
|
|
57
|
+
/** Calificación de la operación (p. ej. '01'). */
|
|
58
|
+
calificacionOperacion: string;
|
|
59
|
+
/** Clave de régimen (obligatoria si Impuesto es IVA/IPSI/IGIC). */
|
|
60
|
+
claveRegimen?: string;
|
|
57
61
|
/** Tipo impositivo (%). */
|
|
58
|
-
tipoImpositivo
|
|
59
|
-
/** Base imponible. */
|
|
62
|
+
tipoImpositivo?: number;
|
|
63
|
+
/** Base imponible o importe no sujeto. */
|
|
60
64
|
baseImponible: number;
|
|
61
65
|
/** Cuota repercutida. */
|
|
62
|
-
cuotaRepercutida
|
|
66
|
+
cuotaRepercutida?: number;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
/**
|
|
66
|
-
* Desglose de la
|
|
67
|
-
*/
|
|
68
|
-
export interface DesgloseTipoOperacion {
|
|
69
|
-
/** Clave de tipo de operación (p. ej. '01'). */
|
|
70
|
-
tipoOperacion: string;
|
|
71
|
-
/** Detalles de IVA. */
|
|
72
|
-
detallesIva: DetalleIva[];
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Desglose completo de la factura.
|
|
70
|
+
* Desglose completo de la factura (DesgloseType del XSD de AEAT).
|
|
77
71
|
*/
|
|
78
72
|
export interface Desglose {
|
|
79
|
-
/**
|
|
80
|
-
|
|
73
|
+
/** Detalles de desglose (DetalleDesglose). */
|
|
74
|
+
detalles: DetalleDesglose[];
|
|
81
75
|
/** Cuota total de la factura. */
|
|
82
76
|
cuotaTotal: number;
|
|
83
77
|
/** Importe total de la factura. */
|
|
84
78
|
importeTotal: number;
|
|
85
79
|
}
|
|
86
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Sistema informático que genera el registro (SistemaInformaticoType del XSD).
|
|
83
|
+
*/
|
|
84
|
+
export interface SistemaInformatico {
|
|
85
|
+
/** Nombre o razón social del titular. */
|
|
86
|
+
nombreRazon: string;
|
|
87
|
+
/** NIF del titular. */
|
|
88
|
+
nif: string;
|
|
89
|
+
/** Nombre del sistema informático. */
|
|
90
|
+
nombreSistemaInformatico: string;
|
|
91
|
+
/** Identificador del sistema informático. */
|
|
92
|
+
idSistemaInformatico: string;
|
|
93
|
+
/** Versión del sistema informático. */
|
|
94
|
+
version: string;
|
|
95
|
+
/** Número de instalación. */
|
|
96
|
+
numeroInstalacion: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
87
99
|
/**
|
|
88
100
|
* Factura en el modelo de datos Verifactu.
|
|
89
101
|
*/
|
|
@@ -100,6 +112,16 @@ export interface Invoice {
|
|
|
100
112
|
tipoDesglose: TipoDesglose;
|
|
101
113
|
/** Descripción de la operación. */
|
|
102
114
|
descripcion?: string;
|
|
115
|
+
/** Destinatarios de la operación (obligatorio para F1/F3/R1-R4). */
|
|
116
|
+
destinatarios?: { nombreRazon: string; nif: string }[];
|
|
117
|
+
/** Sistema informático que genera el registro. */
|
|
118
|
+
sistemaInformatico: SistemaInformatico;
|
|
119
|
+
/** Fecha/hora de generación con huso horario (ISO 8601). */
|
|
120
|
+
fechaHoraHusoGenRegistro: string;
|
|
121
|
+
/** Huella (hash) del registro. */
|
|
122
|
+
huella: string;
|
|
123
|
+
/** Versión del esquema (por defecto '1.0'). */
|
|
124
|
+
idVersion?: string;
|
|
103
125
|
/** Metadatos adicionales libres. */
|
|
104
126
|
metadata?: Record<string, unknown>;
|
|
105
127
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilidades de fecha/hora para Verifactu.
|
|
3
|
+
*
|
|
4
|
+
* AEAT exige que `FechaHoraHusoGenRegistro` sea la fecha/hora actual del
|
|
5
|
+
* sistema con su huso horario (margen de 240 segundos). Este helper genera
|
|
6
|
+
* ese valor detectando automáticamente el offset de zona, sin asumir un
|
|
7
|
+
* huso fijo (p. ej. `+02:00`).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const pad = (n: number) => String(n).padStart(2, '0');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Devuelve la fecha/hora actual en formato ISO 8601 con el offset de zona
|
|
14
|
+
* detectado automáticamente, p. ej. `2026-09-07T10:12:41+02:00`.
|
|
15
|
+
*
|
|
16
|
+
* @param date Fecha a formatear (por defecto, ahora).
|
|
17
|
+
*/
|
|
18
|
+
export function nowIsoWithOffset(date: Date = new Date()): string {
|
|
19
|
+
const off = -date.getTimezoneOffset();
|
|
20
|
+
const sign = off >= 0 ? '+' : '-';
|
|
21
|
+
const abs = Math.abs(off);
|
|
22
|
+
return (
|
|
23
|
+
`${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` +
|
|
24
|
+
`T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}` +
|
|
25
|
+
`${sign}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './date.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './validate.js';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Invoice } from '../models/invoice.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resultado de la validación de una factura.
|
|
5
|
+
*/
|
|
6
|
+
export interface ValidationResult {
|
|
7
|
+
/** `true` si la factura es válida. */
|
|
8
|
+
valid: boolean;
|
|
9
|
+
/** Lista de errores (campos obligatorios o con formato incorrecto). */
|
|
10
|
+
errors: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Valida una factura antes de enviarla a AEAT.
|
|
15
|
+
*
|
|
16
|
+
* Comprueba los campos obligatorios y el formato de las fechas (el XSD
|
|
17
|
+
* `fecha` de AEAT usa `DD-MM-YYYY`). Devuelve `{ valid, errors }`.
|
|
18
|
+
*/
|
|
19
|
+
export function validateInvoice(invoice: Invoice): ValidationResult {
|
|
20
|
+
const errors: string[] = [];
|
|
21
|
+
|
|
22
|
+
// Identificación de la factura.
|
|
23
|
+
if (!invoice.id?.numero) errors.push('id.numero es obligatorio');
|
|
24
|
+
if (!invoice.id?.fechaExpedicion) {
|
|
25
|
+
errors.push('id.fechaExpedicion es obligatorio');
|
|
26
|
+
} else if (!/^\d{2}-\d{2}-\d{4}$/.test(invoice.id.fechaExpedicion)) {
|
|
27
|
+
errors.push('id.fechaExpedicion debe tener formato DD-MM-YYYY');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Emisor.
|
|
31
|
+
if (!invoice.emisor?.nif) errors.push('emisor.nif es obligatorio');
|
|
32
|
+
if (!invoice.emisor?.nombreRazon) errors.push('emisor.nombreRazon es obligatorio');
|
|
33
|
+
|
|
34
|
+
// Tipo de factura.
|
|
35
|
+
if (!invoice.tipoFactura) errors.push('tipoFactura es obligatorio');
|
|
36
|
+
|
|
37
|
+
// Desglose.
|
|
38
|
+
if (!invoice.desglose?.detalles?.length) {
|
|
39
|
+
errors.push('desglose.detalles debe tener al menos un detalle');
|
|
40
|
+
} else {
|
|
41
|
+
invoice.desglose.detalles.forEach((d, i) => {
|
|
42
|
+
if (!d.calificacionOperacion) errors.push(`desglose.detalles[${i}].calificacionOperacion es obligatorio`);
|
|
43
|
+
if (d.baseImponible === undefined || d.baseImponible === null) {
|
|
44
|
+
errors.push(`desglose.detalles[${i}].baseImponible es obligatorio`);
|
|
45
|
+
}
|
|
46
|
+
if (d.tipoImpositivo !== undefined && !d.claveRegimen) {
|
|
47
|
+
errors.push(`desglose.detalles[${i}].claveRegimen es obligatorio si hay tipoImpositivo`);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (invoice.desglose?.cuotaTotal === undefined) errors.push('desglose.cuotaTotal es obligatorio');
|
|
52
|
+
if (invoice.desglose?.importeTotal === undefined) errors.push('desglose.importeTotal es obligatorio');
|
|
53
|
+
|
|
54
|
+
// Destinatarios obligatorios para F1/F3 (según AEAT).
|
|
55
|
+
if (['F1', 'F3'].includes(invoice.tipoFactura) && !invoice.destinatarios?.length) {
|
|
56
|
+
errors.push('destinatarios es obligatorio para tipoFactura F1/F3');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Sistema informático.
|
|
60
|
+
if (!invoice.sistemaInformatico?.nif) errors.push('sistemaInformatico.nif es obligatorio');
|
|
61
|
+
if (!invoice.sistemaInformatico?.idSistemaInformatico) {
|
|
62
|
+
errors.push('sistemaInformatico.idSistemaInformatico es obligatorio');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Fecha/hora de generación.
|
|
66
|
+
if (!invoice.fechaHoraHusoGenRegistro) {
|
|
67
|
+
errors.push('fechaHoraHusoGenRegistro es obligatorio');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { valid: errors.length === 0, errors };
|
|
71
|
+
}
|