2025-09-03 10:41:12 +00:00
|
|
|
import { EntityNotFoundError, SequelizeRepository, translateSequelizeError } from "@erp/core/api";
|
2025-06-26 11:32:55 +00:00
|
|
|
import { Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server";
|
2025-06-11 15:13:44 +00:00
|
|
|
import { UniqueID } from "@repo/rdx-ddd";
|
|
|
|
|
import { Collection, Result } from "@repo/rdx-utils";
|
2025-09-09 15:48:12 +00:00
|
|
|
import { Sequelize, Transaction } from "sequelize";
|
2025-06-12 06:55:17 +00:00
|
|
|
import { CustomerInvoice, ICustomerInvoiceRepository } from "../../domain";
|
|
|
|
|
import { ICustomerInvoiceMapper } from "../mappers/customer-invoice.mapper";
|
2025-09-10 16:06:29 +00:00
|
|
|
import { CustomerInvoiceItemTaxModel } from "./customer-invoice-item-tax.model";
|
|
|
|
|
import { CustomerInvoiceItemModel } from "./customer-invoice-item.model";
|
|
|
|
|
import { CustomerInvoiceTaxModel } from "./customer-invoice-tax.model";
|
2025-06-12 06:55:17 +00:00
|
|
|
import { CustomerInvoiceModel } from "./customer-invoice.model";
|
2025-06-11 15:13:44 +00:00
|
|
|
|
2025-06-12 06:55:17 +00:00
|
|
|
export class CustomerInvoiceRepository
|
|
|
|
|
extends SequelizeRepository<CustomerInvoice>
|
|
|
|
|
implements ICustomerInvoiceRepository
|
|
|
|
|
{
|
2025-09-09 15:48:12 +00:00
|
|
|
private readonly _database!: Sequelize;
|
|
|
|
|
private readonly _mapper!: ICustomerInvoiceMapper;
|
2025-06-11 15:13:44 +00:00
|
|
|
|
2025-09-09 15:48:12 +00:00
|
|
|
constructor(params: { mapper: ICustomerInvoiceMapper; database: Sequelize }) {
|
2025-08-14 14:58:13 +00:00
|
|
|
super();
|
2025-09-09 15:48:12 +00:00
|
|
|
this._mapper = params.mapper;
|
|
|
|
|
this._database = params.database;
|
2025-06-11 15:13:44 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-23 11:57:48 +00:00
|
|
|
// Listado por tenant con criteria saneada
|
|
|
|
|
/* async searchInCompany(criteria: any, companyId: string): Promise<{
|
|
|
|
|
rows: InvoiceListRow[];
|
|
|
|
|
total: number;
|
|
|
|
|
limit: number;
|
|
|
|
|
offset: number;
|
|
|
|
|
}> {
|
|
|
|
|
const { where, order, limit, offset, attributes } = sanitizeListCriteria(criteria);
|
|
|
|
|
|
|
|
|
|
// WHERE con scope de company
|
|
|
|
|
const scopedWhere = { ...where, company_id: companyId };
|
|
|
|
|
|
|
|
|
|
const options: FindAndCountOptions = {
|
|
|
|
|
where: scopedWhere,
|
|
|
|
|
order,
|
|
|
|
|
limit,
|
|
|
|
|
offset,
|
|
|
|
|
attributes,
|
|
|
|
|
raw: true, // devolvemos objetos planos -> más rápido
|
|
|
|
|
nest: false,
|
|
|
|
|
distinct: true // por si en el futuro añadimos includes no duplicar count
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const { rows, count } = await CustomerInvoiceModel.findAndCountAll(options);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
rows: rows as unknown as InvoiceListRow[],
|
|
|
|
|
total: typeof count === "number" ? count : (count as any[]).length,
|
|
|
|
|
limit,
|
|
|
|
|
offset,
|
|
|
|
|
};
|
|
|
|
|
} */
|
|
|
|
|
|
2025-06-26 11:32:55 +00:00
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Persiste una nueva factura o actualiza una existente.
|
|
|
|
|
*
|
|
|
|
|
* @param invoice - El agregado a guardar.
|
|
|
|
|
* @param transaction - Transacción activa para la operación.
|
|
|
|
|
* @returns Result<CustomerInvoice, Error>
|
|
|
|
|
*/
|
|
|
|
|
async save(
|
|
|
|
|
invoice: CustomerInvoice,
|
|
|
|
|
transaction: Transaction
|
|
|
|
|
): Promise<Result<CustomerInvoice, Error>> {
|
|
|
|
|
try {
|
2025-09-09 15:48:12 +00:00
|
|
|
const data = this._mapper.mapToPersistence(invoice);
|
2025-09-03 10:41:12 +00:00
|
|
|
const [instance] = await CustomerInvoiceModel.upsert(data, { transaction, returning: true });
|
2025-09-09 15:48:12 +00:00
|
|
|
const savedInvoice = this._mapper.mapToDomain(instance);
|
2025-09-03 10:41:12 +00:00
|
|
|
return savedInvoice;
|
2025-06-26 11:32:55 +00:00
|
|
|
} catch (err: unknown) {
|
2025-08-26 18:55:59 +00:00
|
|
|
return Result.fail(translateSequelizeError(err));
|
2025-06-26 11:32:55 +00:00
|
|
|
}
|
2025-06-11 15:13:44 +00:00
|
|
|
}
|
|
|
|
|
|
2025-09-03 10:41:12 +00:00
|
|
|
/**
|
|
|
|
|
* Comprueba si existe una factura con un `id` dentro de una `company`.
|
|
|
|
|
*
|
|
|
|
|
* @param companyId - Identificador UUID de la empresa a la que pertenece la factura.
|
|
|
|
|
* @param id - Identificador UUID de la factura.
|
|
|
|
|
* @param transaction - Transacción activa para la operación.
|
|
|
|
|
* @returns Result<boolean, Error>
|
|
|
|
|
*/
|
|
|
|
|
async existsByIdInCompany(
|
|
|
|
|
companyId: UniqueID,
|
|
|
|
|
id: UniqueID,
|
|
|
|
|
transaction?: Transaction
|
|
|
|
|
): Promise<Result<boolean, Error>> {
|
|
|
|
|
try {
|
|
|
|
|
const count = await CustomerInvoiceModel.count({
|
|
|
|
|
where: { id: id.toString(), company_id: companyId.toString() },
|
|
|
|
|
transaction,
|
|
|
|
|
});
|
|
|
|
|
return Result.ok(Boolean(count > 0));
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
return Result.fail(translateSequelizeError(error));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-26 11:32:55 +00:00
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Busca una factura por su identificador único.
|
2025-09-03 10:41:12 +00:00
|
|
|
*
|
|
|
|
|
* @param companyId - Identificador UUID de la empresa a la que pertenece la factura.
|
2025-06-26 11:32:55 +00:00
|
|
|
* @param id - UUID de la factura.
|
|
|
|
|
* @param transaction - Transacción activa para la operación.
|
|
|
|
|
* @returns Result<CustomerInvoice, Error>
|
|
|
|
|
*/
|
2025-09-03 10:41:12 +00:00
|
|
|
async getByIdInCompany(
|
|
|
|
|
companyId: UniqueID,
|
|
|
|
|
id: UniqueID,
|
|
|
|
|
transaction: Transaction
|
|
|
|
|
): Promise<Result<CustomerInvoice, Error>> {
|
2025-06-11 15:13:44 +00:00
|
|
|
try {
|
2025-09-10 10:27:07 +00:00
|
|
|
const { CustomerModel } = this._database.models;
|
|
|
|
|
|
2025-09-03 10:41:12 +00:00
|
|
|
const row = await CustomerInvoiceModel.findOne({
|
|
|
|
|
where: { id: id.toString(), company_id: companyId.toString() },
|
2025-09-10 10:27:07 +00:00
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: CustomerModel,
|
|
|
|
|
as: "current_customer",
|
|
|
|
|
required: false, // false => LEFT JOIN
|
|
|
|
|
},
|
2025-09-10 16:06:29 +00:00
|
|
|
{
|
|
|
|
|
model: CustomerInvoiceItemModel,
|
|
|
|
|
as: "items",
|
|
|
|
|
required: false,
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: CustomerInvoiceItemTaxModel,
|
|
|
|
|
as: "taxes",
|
|
|
|
|
required: false,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
model: CustomerInvoiceTaxModel,
|
|
|
|
|
as: "taxes",
|
|
|
|
|
required: false,
|
|
|
|
|
},
|
2025-09-10 10:27:07 +00:00
|
|
|
],
|
2025-09-03 10:41:12 +00:00
|
|
|
transaction,
|
|
|
|
|
});
|
2025-06-26 11:32:55 +00:00
|
|
|
|
2025-09-03 10:41:12 +00:00
|
|
|
if (!row) {
|
|
|
|
|
return Result.fail(new EntityNotFoundError("CustomerInvoice", "id", id.toString()));
|
2025-06-26 11:32:55 +00:00
|
|
|
}
|
2025-06-11 15:13:44 +00:00
|
|
|
|
2025-09-09 15:48:12 +00:00
|
|
|
const customer = this._mapper.mapToDomain(row);
|
2025-09-03 10:41:12 +00:00
|
|
|
return customer;
|
2025-06-26 11:32:55 +00:00
|
|
|
} catch (err: unknown) {
|
2025-08-26 18:55:59 +00:00
|
|
|
return Result.fail(translateSequelizeError(err));
|
2025-06-11 15:13:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-26 11:32:55 +00:00
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Consulta facturas usando un objeto Criteria (filtros, orden, paginación).
|
2025-09-03 10:41:12 +00:00
|
|
|
*
|
|
|
|
|
* @param companyId - Identificador UUID de la empresa a la que pertenece el cliente.
|
2025-06-26 11:32:55 +00:00
|
|
|
* @param criteria - Criterios de búsqueda.
|
|
|
|
|
* @param transaction - Transacción activa para la operación.
|
|
|
|
|
* @returns Result<CustomerInvoice[], Error>
|
|
|
|
|
*
|
|
|
|
|
* @see Criteria
|
|
|
|
|
*/
|
2025-09-03 10:41:12 +00:00
|
|
|
public async findByCriteriaInCompany(
|
|
|
|
|
companyId: UniqueID,
|
2025-06-11 15:13:44 +00:00
|
|
|
criteria: Criteria,
|
2025-06-26 11:32:55 +00:00
|
|
|
transaction: Transaction
|
2025-06-11 15:13:44 +00:00
|
|
|
): Promise<Result<Collection<CustomerInvoice>, Error>> {
|
|
|
|
|
try {
|
2025-09-09 15:48:12 +00:00
|
|
|
const { CustomerModel } = this._database.models;
|
2025-06-26 11:32:55 +00:00
|
|
|
const converter = new CriteriaToSequelizeConverter();
|
|
|
|
|
const query = converter.convert(criteria);
|
|
|
|
|
|
2025-09-03 10:41:12 +00:00
|
|
|
query.where = {
|
|
|
|
|
...query.where,
|
|
|
|
|
company_id: companyId.toString(),
|
|
|
|
|
};
|
|
|
|
|
|
2025-09-09 15:48:12 +00:00
|
|
|
query.include = [
|
|
|
|
|
{
|
|
|
|
|
model: CustomerModel,
|
2025-09-09 18:13:54 +00:00
|
|
|
as: "current_customer",
|
2025-09-09 15:48:12 +00:00
|
|
|
required: false, // false => LEFT JOIN
|
|
|
|
|
},
|
2025-09-10 18:14:19 +00:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
model: CustomerInvoiceTaxModel,
|
|
|
|
|
as: "taxes",
|
|
|
|
|
required: false,
|
|
|
|
|
},
|
2025-09-09 15:48:12 +00:00
|
|
|
];
|
|
|
|
|
|
2025-06-26 18:05:33 +00:00
|
|
|
const instances = await CustomerInvoiceModel.findAll({
|
2025-06-26 11:32:55 +00:00
|
|
|
...query,
|
2025-06-11 15:13:44 +00:00
|
|
|
transaction,
|
|
|
|
|
});
|
|
|
|
|
|
2025-09-09 15:48:12 +00:00
|
|
|
return this._mapper.mapArrayToDomain(instances);
|
2025-06-26 11:32:55 +00:00
|
|
|
} catch (err: unknown) {
|
2025-08-26 18:55:59 +00:00
|
|
|
return Result.fail(translateSequelizeError(err));
|
2025-06-11 15:13:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-26 11:32:55 +00:00
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Elimina o marca como eliminada una factura.
|
2025-09-03 10:41:12 +00:00
|
|
|
*
|
|
|
|
|
* @param companyId - Identificador UUID de la empresa a la que pertenece el cliente.
|
2025-06-26 11:32:55 +00:00
|
|
|
* @param id - UUID de la factura a eliminar.
|
|
|
|
|
* @param transaction - Transacción activa para la operación.
|
|
|
|
|
* @returns Result<void, Error>
|
|
|
|
|
*/
|
2025-09-03 10:41:12 +00:00
|
|
|
async deleteByIdInCompany(
|
|
|
|
|
companyId: UniqueID,
|
|
|
|
|
id: UniqueID,
|
|
|
|
|
transaction: any
|
|
|
|
|
): Promise<Result<void, Error>> {
|
2025-06-11 15:13:44 +00:00
|
|
|
try {
|
2025-09-03 10:41:12 +00:00
|
|
|
const deleted = await CustomerInvoiceModel.destroy({
|
|
|
|
|
where: { id: id.toString(), company_id: companyId.toString() },
|
|
|
|
|
transaction,
|
|
|
|
|
});
|
|
|
|
|
|
2025-06-26 11:32:55 +00:00
|
|
|
return Result.ok<void>();
|
|
|
|
|
} catch (err: unknown) {
|
2025-08-26 18:55:59 +00:00
|
|
|
return Result.fail(translateSequelizeError(err));
|
2025-06-11 15:13:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|