Document Numering

This commit is contained in:
David Arranz 2025-09-26 20:42:47 +02:00
parent d06c4932ed
commit bf960bc3ac

View File

@ -2,6 +2,48 @@ import { AggregateRoot, UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { DocType } from "../value-objects";
/**
*
*
* const num1 = DocumentNumber.create(7, "2025", "{series}/{number:0003}");
* console.log(num1.isSuccess, num1.data.getFormatted()); // true, "2025/007"
*
* const num2 = DocumentNumber.create(7, undefined, "{series}/{number:0003}");
* console.log(num2.isFailure, num2.error.message); // true, error porque falta {series}
*
*. Número simple sin padding
* DocumentNumber.create(123, undefined, "{number}")
* // → "123"
*
* 2. Número con padding
* DocumentNumber.create(7, undefined, "{number:0005}")
* // → "00007"
*
* 3. Serie + número con padding
* DocumentNumber.create(45, "2025", "{series}/{number:0003}")
* // → "2025/045"
*
* 4. Serie + número sin padding
* DocumentNumber.create(987, "Sucursal-01", "{series}-{number}")
* // → "Sucursal-01-987"
*
* 5. Año + mes + día + número
*
* (Suponiendo fecha 2025-09-26 y rawValue=12)
*
* DocumentNumber.create(12, "2025", "{year}/{month}/{day}-{number:0002}")
* // → "2025/09/26-12"
*
* 6. Escapes de llaves
* DocumentNumber.create(33, "2025", "Factura N° {{{number}}}")
* // → "Factura N° {33}"
*
* 7. Patrón sin número (no muy común, pero válido)
* DocumentNumber.create(77, "2025", "SERIE-{series}")
* // → "SERIE-2025"
*
*/
export interface DocNumberProps {
companyId: UniqueID;
year: number;
@ -22,8 +64,8 @@ export class DocNumber extends AggregateRoot<DocNumberProps> {
// ...
// 🔹 Disparar evento de dominio "CustomerAuthenticatedEvent"
//const { contact } = props;
//user.addDomainEvent(new CustomerAuthenticatedEvent(id, contact.toString()));
// ...
// ...
return Result.ok(docNumber);
}
@ -35,8 +77,59 @@ export class DocNumber extends AggregateRoot<DocNumberProps> {
}
private applyFormat(): string {
// Sustituye {series}, {number:000000}, {year}, etc.
return "";
// 🔹 Preprocesar escapes de llaves
const pattern = this.props.formatPattern
.replace(/{{/g, "__LBRACE__")
.replace(/}}/g, "__RBRACE__");
const date = new Date();
// 🔹 Expresión regular para tokens {token} o {token:0000}
const tokenRegex = /{([a-zA-Z]+)(?::([0]+))?}/g;
const formatted = pattern.replace(tokenRegex, (_match, token, pad) => {
const name = token.toLowerCase();
switch (name) {
case "number": {
const raw = String(this.props.currentValue);
if (pad) {
// validar que el especificador son sólo ceros
if (!/^0+$/.test(pad)) {
throw new Error(
`DocumentNumber: especificador de padding inválido en patrón '${pad}'`
);
}
return raw.padStart(pad.length, "0");
}
return raw;
}
case "series": {
if (!this.props.series) {
throw new Error(
"DocumentNumber: el patrón requiere {series} pero no se proporcionó serie"
);
}
return this.props.series;
}
case "year":
return String(date.getFullYear());
case "month":
return String(date.getMonth() + 1).padStart(2, "0");
case "day":
return String(date.getDate()).padStart(2, "0");
default:
throw new Error(`DocumentNumber: token desconocido {${token}}`);
}
});
// 🔹 Restaurar escapes
return formatted.replace(/LBRACE/g, "{").replace(/RBRACE/g, "}");
}
getFormatted(): string {