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";
|
|
|
|
|
|
2025-11-05 17:19:33 +00:00
|
|
|
import {
|
|
|
|
|
CustomerInvoiceNumber,
|
2025-11-14 15:48:09 +00:00
|
|
|
type CustomerInvoiceSerie,
|
|
|
|
|
type ICustomerInvoiceNumberGenerator,
|
2025-11-05 17:19:33 +00:00
|
|
|
} 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}`
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|