import { UniqueID } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; import { literal, Transaction, WhereOptions } from "sequelize"; import { CustomerInvoiceNumber, CustomerInvoiceSerie, 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, transaction: Transaction ): Promise> { 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}` ) ); } } }