Uecko_ERP/modules/customer-invoices/src/api/infrastructure/services/sequelize-invoice-number-generator.ts

70 lines
2.0 KiB
TypeScript
Raw Normal View History

2025-11-14 15:48:09 +00:00
import type { UniqueID } from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils";
import { type Transaction, type WhereOptions, literal } from "sequelize";
import {
CustomerInvoiceNumber,
2025-11-14 15:48:09 +00:00
type CustomerInvoiceSerie,
type ICustomerInvoiceNumberGenerator,
} from "../../domain";
import { CustomerInvoiceModel } from "../sequelize";
/**
* Generador de números de factura
*/
export class SequelizeInvoiceNumberGenerator implements ICustomerInvoiceNumberGenerator {
public async nextForCompany(
companyId: UniqueID,
series: Maybe<CustomerInvoiceSerie>,
transaction: Transaction
): Promise<Result<CustomerInvoiceNumber, Error>> {
const where: WhereOptions = {
company_id: companyId.toString(),
is_proforma: false,
};
series.match(
(serieVO) => {
where.series = serieVO.toString();
},
() => {
where.series = null;
}
);
try {
const lastInvoice = await CustomerInvoiceModel.findOne({
attributes: ["invoice_number"],
where,
// Orden numérico real: CAST(... AS UNSIGNED)
order: [literal("CAST(invoice_number AS UNSIGNED) DESC")],
transaction,
raw: true,
// Bloqueo opcional para evitar carreras si estás dentro de una TX
lock: transaction.LOCK.UPDATE, // requiere InnoDB y TX abierta
});
let nextValue = "00001"; // valor inicial por defecto
if (lastInvoice) {
const current = Number(lastInvoice.invoice_number);
const next = Number.isFinite(current) && current > 0 ? current + 1 : 1;
nextValue = String(next).padStart(5, "0");
}
const numberResult = CustomerInvoiceNumber.create(nextValue);
if (numberResult.isFailure) {
return Result.fail(numberResult.error);
}
return Result.ok(numberResult.data);
} catch (error) {
return Result.fail(
new Error(
`Error generating invoice number for company ${companyId}: ${(error as Error).message}`
)
);
}
}
}