Facturas de cliente
This commit is contained in:
parent
56b37c4256
commit
817dcff8c5
@ -19,7 +19,7 @@ export const currentState = {
|
|||||||
host: ENV.HOST,
|
host: ENV.HOST,
|
||||||
port: ENV.PORT,
|
port: ENV.PORT,
|
||||||
environment: ENV.NODE_ENV,
|
environment: ENV.NODE_ENV,
|
||||||
connections: {} as Record<string, any>,
|
connections: {} as Record<string, unknown>,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { logger } from "../logger";
|
|||||||
*/
|
*/
|
||||||
type SequelizeLike = {
|
type SequelizeLike = {
|
||||||
sync: (options?: any) => Promise<void>;
|
sync: (options?: any) => Promise<void>;
|
||||||
models: Record<string, any>;
|
models: Record<string, unknown>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ModelStatic = {
|
type ModelStatic = {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
const services: Record<string, any> = {};
|
const services: Record<string, unknown> = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registra un objeto de servicio (API) bajo un nombre.
|
* Registra un objeto de servicio (API) bajo un nombre.
|
||||||
|
|||||||
@ -7,14 +7,11 @@ import { IInvoiceRepository, Invoice } from "../domain";
|
|||||||
import { IInvoiceMapper } from "./mappers";
|
import { IInvoiceMapper } from "./mappers";
|
||||||
|
|
||||||
export type QueryParams = {
|
export type QueryParams = {
|
||||||
pagination: Record<string, any>;
|
pagination: Record<string, unknown>;
|
||||||
filters: Record<string, any>;
|
filters: Record<string, unknown>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class InvoiceRepository
|
export class InvoiceRepository extends SequelizeRepository<Invoice> implements IInvoiceRepository {
|
||||||
extends SequelizeRepository<Invoice>
|
|
||||||
implements IInvoiceRepository
|
|
||||||
{
|
|
||||||
protected mapper: IInvoiceMapper;
|
protected mapper: IInvoiceMapper;
|
||||||
|
|
||||||
public constructor(props: {
|
public constructor(props: {
|
||||||
@ -33,10 +30,7 @@ export class InvoiceRepository
|
|||||||
{ association: "items" },
|
{ association: "items" },
|
||||||
{
|
{
|
||||||
association: "participants",
|
association: "participants",
|
||||||
include: [
|
include: [{ association: "shippingAddress" }, { association: "billingAddress" }],
|
||||||
{ association: "shippingAddress" },
|
|
||||||
{ association: "billingAddress" },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -48,28 +42,21 @@ export class InvoiceRepository
|
|||||||
return this.mapper.mapToDomain(rawContact);
|
return this.mapper.mapToDomain(rawContact);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findAll(
|
public async findAll(queryCriteria?: IQueryCriteria): Promise<ICollection<Invoice>> {
|
||||||
queryCriteria?: IQueryCriteria
|
const { rows, count } = await this._findAll("Invoice_Model", queryCriteria, {
|
||||||
): Promise<ICollection<Invoice>> {
|
include: [
|
||||||
const { rows, count } = await this._findAll(
|
{
|
||||||
"Invoice_Model",
|
association: "participants",
|
||||||
queryCriteria,
|
separate: true,
|
||||||
{
|
},
|
||||||
include: [
|
],
|
||||||
{
|
});
|
||||||
association: "participants",
|
|
||||||
separate: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.mapper.mapArrayAndCountToDomain(rows, count);
|
return this.mapper.mapArrayAndCountToDomain(rows, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async save(invoice: Invoice): Promise<void> {
|
public async save(invoice: Invoice): Promise<void> {
|
||||||
const { items, participants, ...invoiceData } =
|
const { items, participants, ...invoiceData } = this.mapper.mapToPersistence(invoice);
|
||||||
this.mapper.mapToPersistence(invoice);
|
|
||||||
|
|
||||||
await this.adapter
|
await this.adapter
|
||||||
.getModel("Invoice_Model")
|
.getModel("Invoice_Model")
|
||||||
@ -85,10 +72,9 @@ export class InvoiceRepository
|
|||||||
|
|
||||||
await this.adapter
|
await this.adapter
|
||||||
.getModel("InvoiceParticipantAddress_Model")
|
.getModel("InvoiceParticipantAddress_Model")
|
||||||
.bulkCreate(
|
.bulkCreate([participants[0].billingAddress, participants[0].shippingAddress], {
|
||||||
[participants[0].billingAddress, participants[0].shippingAddress],
|
transaction: this.transaction,
|
||||||
{ transaction: this.transaction }
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeById(id: UniqueID): Promise<void> {
|
public removeById(id: UniqueID): Promise<void> {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
export * from "./presenter";
|
||||||
export * from "./presenter-registry";
|
export * from "./presenter-registry";
|
||||||
export * from "./presenter-registry.interface";
|
export * from "./presenter-registry.interface";
|
||||||
export * from "./presenter.interface";
|
export * from "./presenter.interface";
|
||||||
|
|||||||
@ -4,8 +4,9 @@ import { IPresenter } from "./presenter.interface";
|
|||||||
* 🔑 Claves de proyección comunes para seleccionar presenters
|
* 🔑 Claves de proyección comunes para seleccionar presenters
|
||||||
*/
|
*/
|
||||||
export type PresenterKey = {
|
export type PresenterKey = {
|
||||||
|
resource: string;
|
||||||
projection: "FULL" | "LIST" | "REPORT" | (string & {});
|
projection: "FULL" | "LIST" | "REPORT" | (string & {});
|
||||||
format?: "JSON" | "PDF" | "CSV" | (string & {});
|
format?: "JSON" | "HTML" | "PDF" | "CSV" | (string & {});
|
||||||
version?: number; // 1 | 2
|
version?: number; // 1 | 2
|
||||||
locale?: string; // es | en | fr
|
locale?: string; // es | en | fr
|
||||||
};
|
};
|
||||||
|
|||||||
@ -49,10 +49,12 @@ export class InMemoryPresenterRegistry implements IPresenterRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.registry.has(exactKey)) {
|
if (!this.registry.has(exactKey)) {
|
||||||
throw new ApplicationError(`Error. Presenter ${key} not registred!`);
|
throw new ApplicationError(
|
||||||
|
`Error. Presenter ${key.resource} ${key.projection} not registred!`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ApplicationError(`Error. Presenter ${key} not registred!`);
|
throw new ApplicationError(`Error. Presenter ${key.resource} ${key.projection} not registred!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerPresenter<TSource, TOutput>(
|
registerPresenter<TSource, TOutput>(
|
||||||
@ -76,8 +78,9 @@ export class InMemoryPresenterRegistry implements IPresenterRegistry {
|
|||||||
* 🔹 Construye la clave única para el registro.
|
* 🔹 Construye la clave única para el registro.
|
||||||
*/
|
*/
|
||||||
private _buildKey(key: PresenterKey): string {
|
private _buildKey(key: PresenterKey): string {
|
||||||
const { projection, format, version, locale } = key;
|
const { resource, projection, format, version, locale } = key;
|
||||||
return [
|
return [
|
||||||
|
resource.toLowerCase(),
|
||||||
projection.toLowerCase(),
|
projection.toLowerCase(),
|
||||||
format!.toLowerCase(),
|
format!.toLowerCase(),
|
||||||
version ?? "latest",
|
version ?? "latest",
|
||||||
|
|||||||
11
modules/core/src/api/application/presenters/presenter.ts
Normal file
11
modules/core/src/api/application/presenters/presenter.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { IPresenterRegistry } from "./presenter-registry.interface";
|
||||||
|
import { IPresenter } from "./presenter.interface";
|
||||||
|
|
||||||
|
export type IPresenterParams = {
|
||||||
|
presenterRegistry: IPresenterRegistry;
|
||||||
|
} & Record<string, unknown>;
|
||||||
|
|
||||||
|
export abstract class Presenter implements IPresenter {
|
||||||
|
constructor(protected presenterRegistry: IPresenterRegistry) {}
|
||||||
|
abstract toOutput(source: unknown): unknown;
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ import { Collection, Result } from "@repo/rdx-utils";
|
|||||||
* Tipo para los parámetros que reciben los métodos de los mappers
|
* Tipo para los parámetros que reciben los métodos de los mappers
|
||||||
* Es un objeto que puede contener cualquier cosa.
|
* Es un objeto que puede contener cualquier cosa.
|
||||||
*/
|
*/
|
||||||
export type MapperParamsType = Record<string, any>;
|
export type MapperParamsType = Record<string, unknown>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🧭 Mapper de Dominio (Persistencia ↔ Dominio/Agregado)
|
* 🧭 Mapper de Dominio (Persistencia ↔ Dominio/Agregado)
|
||||||
|
|||||||
@ -111,6 +111,24 @@ export abstract class ExpressController {
|
|||||||
return this.res.sendStatus(httpStatus.NO_CONTENT);
|
return this.res.sendStatus(httpStatus.NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sendFile(filepath: string) {
|
||||||
|
return this.res.sendFile(filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public downloadFile(filepath: string, filename: string, done?: any) {
|
||||||
|
return this.res.download(filepath, filename, done);
|
||||||
|
}
|
||||||
|
|
||||||
|
public downloadPDF(pdfBuffer: Buffer, filename: string) {
|
||||||
|
this.res.set({
|
||||||
|
"Content-Type": "application/pdf",
|
||||||
|
"Content-Disposition": `attachment; filename=${filename}`,
|
||||||
|
//"Content-Length": buffer.length,
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.res.send(pdfBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
protected clientError(message: string, errors?: any[] | any) {
|
protected clientError(message: string, errors?: any[] | any) {
|
||||||
return this.handleApiError(
|
return this.handleApiError(
|
||||||
new ValidationApiError(message, Array.isArray(errors) ? errors : [errors])
|
new ValidationApiError(message, Array.isArray(errors) ? errors : [errors])
|
||||||
|
|||||||
@ -6,7 +6,7 @@ export class InMemoryMapperRegistry implements IMapperRegistry {
|
|||||||
private readModelMappers: Map<MapperProjectionKey, any> = new Map();
|
private readModelMappers: Map<MapperProjectionKey, any> = new Map();
|
||||||
|
|
||||||
getDomainMapper<T>(key: MapperProjectionKey): T {
|
getDomainMapper<T>(key: MapperProjectionKey): T {
|
||||||
if (!this.readModelMappers.has(key)) {
|
if (!this.domainMappers.has(key)) {
|
||||||
throw new InfrastructureError(`Error. Domain model mapper ${key} not registred!`);
|
throw new InfrastructureError(`Error. Domain model mapper ${key} not registred!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Collection, Result } from "@repo/rdx-utils";
|
import { Collection, Result } from "@repo/rdx-utils";
|
||||||
import { Model } from "sequelize";
|
import { Model } from "sequelize";
|
||||||
|
|
||||||
export type MapperParamsType = Record<string, any>;
|
export type MapperParamsType = Record<string, unknown>;
|
||||||
|
|
||||||
interface IDomainMapper<TModel extends Model, TEntity> {
|
interface IDomainMapper<TModel extends Model, TEntity> {
|
||||||
mapToDomain(source: TModel, params?: MapperParamsType): Result<TEntity, Error>;
|
mapToDomain(source: TModel, params?: MapperParamsType): Result<TEntity, Error>;
|
||||||
|
|||||||
@ -10,11 +10,11 @@ export interface IErrorResponseDTO {
|
|||||||
|
|
||||||
export interface IErrorContextResponseDTO {
|
export interface IErrorContextResponseDTO {
|
||||||
user?: unknown;
|
user?: unknown;
|
||||||
params?: Record<string, any>;
|
params?: Record<string, unknown>;
|
||||||
query?: Record<string, any>;
|
query?: Record<string, unknown>;
|
||||||
body?: Record<string, any>;
|
body?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IErrorExtraResponseDTO {
|
export interface IErrorExtraResponseDTO {
|
||||||
errors: Record<string, any>[];
|
errors: Record<string, unknown>[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export const createAxiosDataSource = (client: AxiosInstance): IDataSource => {
|
|||||||
return {
|
return {
|
||||||
getBaseUrl: () => (client as AxiosInstance).getUri(),
|
getBaseUrl: () => (client as AxiosInstance).getUri(),
|
||||||
|
|
||||||
getList: async <T, R>(resource: string, params?: Record<string, any>): Promise<R> => {
|
getList: async <T, R>(resource: string, params?: Record<string, unknown>): Promise<R> => {
|
||||||
const { pagination } = params as any;
|
const { pagination } = params as any;
|
||||||
|
|
||||||
const res = await (client as AxiosInstance).get<T[]>(resource, {
|
const res = await (client as AxiosInstance).get<T[]>(resource, {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export interface ICustomParams {
|
|||||||
|
|
||||||
export interface IDataSource {
|
export interface IDataSource {
|
||||||
getBaseUrl(): string;
|
getBaseUrl(): string;
|
||||||
getList<T, R>(resource: string, params?: Record<string, any>): Promise<R>;
|
getList<T, R>(resource: string, params?: Record<string, unknown>): Promise<R>;
|
||||||
getOne<T>(resource: string, id: string | number): Promise<T>;
|
getOne<T>(resource: string, id: string | number): Promise<T>;
|
||||||
getMany<T, R>(resource: string, ids: Array<string | number>): Promise<R>;
|
getMany<T, R>(resource: string, ids: Array<string | number>): Promise<R>;
|
||||||
createOne<T>(resource: string, data: Partial<T>): Promise<T>;
|
createOne<T>(resource: string, data: Partial<T>): Promise<T>;
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { ITransactionManager } from "@erp/core/api";
|
import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
|
||||||
import { UniqueID } from "@repo/rdx-ddd";
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
import { CustomerInvoiceService } from "../../domain";
|
import { CustomerInvoiceService } from "../../domain";
|
||||||
import { GetCustomerInvoiceAssembler } from "./assembler";
|
import { CustomerInvoiceFullPresenter } from "../presenters/full-domain";
|
||||||
|
|
||||||
type GetCustomerInvoiceUseCaseInput = {
|
type GetCustomerInvoiceUseCaseInput = {
|
||||||
companyId: UniqueID;
|
companyId: UniqueID;
|
||||||
@ -13,19 +13,22 @@ export class GetCustomerInvoiceUseCase {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly service: CustomerInvoiceService,
|
private readonly service: CustomerInvoiceService,
|
||||||
private readonly transactionManager: ITransactionManager,
|
private readonly transactionManager: ITransactionManager,
|
||||||
private readonly assembler: GetCustomerInvoiceAssembler,
|
private readonly presenterRegistry: IPresenterRegistry
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public execute(params: GetCustomerInvoiceUseCaseInput) {
|
public execute(params: GetCustomerInvoiceUseCaseInput) {
|
||||||
const { invoice_id, companyId } = params;
|
const { invoice_id, companyId } = params;
|
||||||
|
|
||||||
const idOrError = UniqueID.create(invoice_id);
|
const idOrError = UniqueID.create(invoice_id);
|
||||||
|
|
||||||
if (idOrError.isFailure) {
|
if (idOrError.isFailure) {
|
||||||
return Result.fail(idOrError.error);
|
return Result.fail(idOrError.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const invoiceId = idOrError.data;
|
const invoiceId = idOrError.data;
|
||||||
|
const presenter = this.presenterRegistry.getPresenter({
|
||||||
|
resource: "customer-invoice",
|
||||||
|
projection: "FULL",
|
||||||
|
}) as CustomerInvoiceFullPresenter;
|
||||||
|
|
||||||
return this.transactionManager.complete(async (transaction) => {
|
return this.transactionManager.complete(async (transaction) => {
|
||||||
try {
|
try {
|
||||||
@ -38,8 +41,10 @@ export class GetCustomerInvoiceUseCase {
|
|||||||
return Result.fail(invoiceOrError.error);
|
return Result.fail(invoiceOrError.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getDTO = this.assembler.toDTO(invoiceOrError.data);
|
const customerInvoice = invoiceOrError.data;
|
||||||
return Result.ok(getDTO);
|
const dto = presenter.toOutput(customerInvoice);
|
||||||
|
|
||||||
|
return Result.ok(dto);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
return Result.fail(error as Error);
|
return Result.fail(error as Error);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,23 @@
|
|||||||
import { UpdateCustomerInvoiceByIdResponseDTO } from "@erp/customer-invoices/common/dto";
|
import { IPresenter, IPresenterRegistry } from "@erp/core/api";
|
||||||
import { toEmptyString } from "@repo/rdx-ddd";
|
import { toEmptyString } from "@repo/rdx-ddd";
|
||||||
|
import { GetCustomerInvoiceByIdResponseDTO } from "../../../../common/dto";
|
||||||
import { CustomerInvoice } from "../../../domain";
|
import { CustomerInvoice } from "../../../domain";
|
||||||
import { GetCustomerInvoiceItemsPresenter } from "./get-invoice-items.presenter";
|
import { GetCustomerInvoiceItemsPresenter } from "./get-invoice-items.presenter";
|
||||||
|
|
||||||
export class GetCustomerInvoicePresenter {
|
export class GetCustomerInvoicePresentwer implements IPresenter {
|
||||||
private _itemsPresenter!: GetCustomerInvoiceItemsPresenter;
|
private _itemsPresenter!: GetCustomerInvoiceItemsPresenter;
|
||||||
|
|
||||||
constructor() {
|
constructor(private presenterRegistry: IPresenterRegistry) {
|
||||||
this._itemsPresenter = new GetCustomerInvoiceItemsPresenter();
|
this._itemsPresenter = this.presenterRegistry.getPresenter({
|
||||||
|
resource: "customer-invoice-item",
|
||||||
|
projection: "FULL",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public toDTO(invoice: CustomerInvoice): UpdateCustomerInvoiceByIdResponseDTO {
|
toOutput(params: {
|
||||||
|
invoice: CustomerInvoice;
|
||||||
|
}): GetCustomerInvoiceByIdResponseDTO {
|
||||||
|
const { invoice } = params;
|
||||||
const items = this._itemsPresenter.toDTO(invoice);
|
const items = this._itemsPresenter.toDTO(invoice);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
* @returns true si el objeto no tiene campos undefined, false en caso contrario.
|
* @returns true si el objeto no tiene campos undefined, false en caso contrario.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function hasNoUndefinedFields<T extends Record<string, any>>(
|
export function hasNoUndefinedFields<T extends Record<string, unknown>>(
|
||||||
obj: T
|
obj: T
|
||||||
): obj is { [K in keyof T]-?: Exclude<T[K], undefined> } {
|
): obj is { [K in keyof T]-?: Exclude<T[K], undefined> } {
|
||||||
return Object.values(obj).every((value) => value !== undefined);
|
return Object.values(obj).every((value) => value !== undefined);
|
||||||
@ -43,7 +43,7 @@ export function hasNoUndefinedFields<T extends Record<string, any>>(
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function hasUndefinedFields<T extends Record<string, any>>(
|
export function hasUndefinedFields<T extends Record<string, unknown>>(
|
||||||
obj: T
|
obj: T
|
||||||
): obj is { [K in keyof T]-?: Exclude<T[K], undefined> } {
|
): obj is { [K in keyof T]-?: Exclude<T[K], undefined> } {
|
||||||
return !hasNoUndefinedFields(obj);
|
return !hasNoUndefinedFields(obj);
|
||||||
|
|||||||
@ -4,3 +4,4 @@ export * from "./get-customer-invoice";
|
|||||||
export * from "./list-customer-invoices";
|
export * from "./list-customer-invoices";
|
||||||
export * from "./report-customer-invoice";
|
export * from "./report-customer-invoice";
|
||||||
//export * from "./update-customer-invoice";
|
//export * from "./update-customer-invoice";
|
||||||
|
export * from "./presenters";
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { ITransactionManager } from "@erp/core/api";
|
import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
|
||||||
import { Criteria } from "@repo/rdx-criteria/server";
|
import { Criteria } from "@repo/rdx-criteria/server";
|
||||||
import { UniqueID } from "@repo/rdx-ddd";
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
import { Transaction } from "sequelize";
|
import { Transaction } from "sequelize";
|
||||||
import { CustomerInvoiceListResponseDTO } from "../../../common/dto";
|
import { CustomerInvoiceListResponseDTO } from "../../../common/dto";
|
||||||
import { CustomerInvoiceService } from "../../domain";
|
import { CustomerInvoiceService } from "../../domain";
|
||||||
import { ListCustomerInvoicesAssembler } from "./assembler";
|
import { CustomerInvoicesListPresenter } from "../presenters/list";
|
||||||
|
|
||||||
type ListCustomerInvoicesUseCaseInput = {
|
type ListCustomerInvoicesUseCaseInput = {
|
||||||
companyId: UniqueID;
|
companyId: UniqueID;
|
||||||
@ -16,13 +16,17 @@ export class ListCustomerInvoicesUseCase {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly service: CustomerInvoiceService,
|
private readonly service: CustomerInvoiceService,
|
||||||
private readonly transactionManager: ITransactionManager,
|
private readonly transactionManager: ITransactionManager,
|
||||||
private readonly assembler: ListCustomerInvoicesAssembler
|
private readonly presenterRegistry: IPresenterRegistry
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public execute(
|
public execute(
|
||||||
params: ListCustomerInvoicesUseCaseInput
|
params: ListCustomerInvoicesUseCaseInput
|
||||||
): Promise<Result<CustomerInvoiceListResponseDTO, Error>> {
|
): Promise<Result<CustomerInvoiceListResponseDTO, Error>> {
|
||||||
const { criteria, companyId } = params;
|
const { criteria, companyId } = params;
|
||||||
|
const presenter = this.presenterRegistry.getPresenter({
|
||||||
|
resource: "customer-invoice",
|
||||||
|
projection: "LIST",
|
||||||
|
}) as CustomerInvoicesListPresenter;
|
||||||
|
|
||||||
return this.transactionManager.complete(async (transaction: Transaction) => {
|
return this.transactionManager.complete(async (transaction: Transaction) => {
|
||||||
try {
|
try {
|
||||||
@ -36,7 +40,12 @@ export class ListCustomerInvoicesUseCase {
|
|||||||
return Result.fail(result.error);
|
return Result.fail(result.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dto = this.assembler.toDTO(result.data, criteria);
|
const customerInvoices = result.data;
|
||||||
|
const dto = presenter.toOutput({
|
||||||
|
customerInvoices,
|
||||||
|
criteria,
|
||||||
|
});
|
||||||
|
|
||||||
return Result.ok(dto);
|
return Result.ok(dto);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
return Result.fail(error as Error);
|
return Result.fail(error as Error);
|
||||||
|
|||||||
@ -1,56 +1,57 @@
|
|||||||
import { IPresenter } from "@erp/core/api";
|
import { Presenter } from "@erp/core/api";
|
||||||
import { CustomerInvoiceListDTO } from "@erp/customer-invoices/api/infrastructure";
|
import { CustomerInvoiceListDTO } from "@erp/customer-invoices/api/infrastructure";
|
||||||
import { Criteria } from "@repo/rdx-criteria/server";
|
import { Criteria } from "@repo/rdx-criteria/server";
|
||||||
import { toEmptyString } from "@repo/rdx-ddd";
|
import { toEmptyString } from "@repo/rdx-ddd";
|
||||||
import { ArrayElement, Collection } from "@repo/rdx-utils";
|
import { ArrayElement, Collection } from "@repo/rdx-utils";
|
||||||
import { CustomerInvoiceListResponseDTO } from "../../../../common/dto";
|
import { CustomerInvoiceListResponseDTO } from "../../../../common/dto";
|
||||||
|
|
||||||
export class ListCustomerInvoicesPresenter implements IPresenter {
|
export class ListCustomerInvoicesPresenter extends Presenter {
|
||||||
|
protected _mapInvoice(invoice: CustomerInvoiceListDTO) {
|
||||||
|
const recipientDTO = invoice.recipient.toObjectString();
|
||||||
|
|
||||||
|
const invoiceDTO: ArrayElement<CustomerInvoiceListResponseDTO["items"]> = {
|
||||||
|
id: invoice.id.toString(),
|
||||||
|
company_id: invoice.companyId.toString(),
|
||||||
|
customer_id: invoice.customerId.toString(),
|
||||||
|
|
||||||
|
invoice_number: invoice.invoiceNumber.toString(),
|
||||||
|
status: invoice.status.toPrimitive(),
|
||||||
|
series: toEmptyString(invoice.series, (value) => value.toString()),
|
||||||
|
|
||||||
|
invoice_date: invoice.invoiceDate.toDateString(),
|
||||||
|
operation_date: toEmptyString(invoice.operationDate, (value) => value.toDateString()),
|
||||||
|
|
||||||
|
recipient: {
|
||||||
|
customer_id: invoice.customerId.toString(),
|
||||||
|
...recipientDTO,
|
||||||
|
},
|
||||||
|
|
||||||
|
language_code: invoice.languageCode.code,
|
||||||
|
currency_code: invoice.currencyCode.code,
|
||||||
|
|
||||||
|
taxes: invoice.taxes,
|
||||||
|
|
||||||
|
subtotal_amount: invoice.subtotalAmount.toObjectString(),
|
||||||
|
discount_amount: invoice.discountAmount.toObjectString(),
|
||||||
|
taxable_amount: invoice.taxableAmount.toObjectString(),
|
||||||
|
taxes_amount: invoice.taxesAmount.toObjectString(),
|
||||||
|
total_amount: invoice.totalAmount.toObjectString(),
|
||||||
|
|
||||||
|
metadata: {
|
||||||
|
entity: "customer-invoice",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return invoiceDTO;
|
||||||
|
}
|
||||||
|
|
||||||
toOutput(params: {
|
toOutput(params: {
|
||||||
customerInvoices: Collection<CustomerInvoiceListDTO>;
|
customerInvoices: Collection<CustomerInvoiceListDTO>;
|
||||||
criteria: Criteria;
|
criteria: Criteria;
|
||||||
}): CustomerInvoiceListResponseDTO {
|
}): CustomerInvoiceListResponseDTO {
|
||||||
const { customerInvoices, criteria } = params;
|
const { customerInvoices, criteria } = params;
|
||||||
|
|
||||||
const invoices = customerInvoices.map((invoice) => {
|
const invoices = customerInvoices.map((invoice) => this._mapInvoice(invoice));
|
||||||
const recipientDTO = invoice.recipient.toObjectString();
|
|
||||||
|
|
||||||
const invoiceDTO: ArrayElement<CustomerInvoiceListResponseDTO["items"]> = {
|
|
||||||
id: invoice.id.toString(),
|
|
||||||
company_id: invoice.companyId.toString(),
|
|
||||||
customer_id: invoice.customerId.toString(),
|
|
||||||
|
|
||||||
invoice_number: invoice.invoiceNumber.toString(),
|
|
||||||
status: invoice.status.toPrimitive(),
|
|
||||||
series: toEmptyString(invoice.series, (value) => value.toString()),
|
|
||||||
|
|
||||||
invoice_date: invoice.invoiceDate.toDateString(),
|
|
||||||
operation_date: toEmptyString(invoice.operationDate, (value) => value.toDateString()),
|
|
||||||
|
|
||||||
recipient: {
|
|
||||||
customer_id: invoice.customerId.toString(),
|
|
||||||
...recipientDTO,
|
|
||||||
},
|
|
||||||
|
|
||||||
language_code: invoice.languageCode.code,
|
|
||||||
currency_code: invoice.currencyCode.code,
|
|
||||||
|
|
||||||
taxes: invoice.taxes,
|
|
||||||
|
|
||||||
subtotal_amount: invoice.subtotalAmount.toObjectString(),
|
|
||||||
discount_amount: invoice.discountAmount.toObjectString(),
|
|
||||||
taxable_amount: invoice.taxableAmount.toObjectString(),
|
|
||||||
taxes_amount: invoice.taxesAmount.toObjectString(),
|
|
||||||
total_amount: invoice.totalAmount.toObjectString(),
|
|
||||||
|
|
||||||
metadata: {
|
|
||||||
entity: "customer-invoice",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return invoiceDTO;
|
|
||||||
});
|
|
||||||
|
|
||||||
const totalItems = customerInvoices.total();
|
const totalItems = customerInvoices.total();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -0,0 +1,52 @@
|
|||||||
|
import { Presenter } from "@erp/core/api";
|
||||||
|
import { toEmptyString } from "@repo/rdx-ddd";
|
||||||
|
import { ArrayElement } from "@repo/rdx-utils";
|
||||||
|
import { GetCustomerInvoiceByIdResponseDTO } from "../../../../common/dto";
|
||||||
|
import { CustomerInvoiceItem, CustomerInvoiceItems } from "../../../domain";
|
||||||
|
|
||||||
|
type GetCustomerInvoiceItemByInvoiceIdResponseDTO = ArrayElement<
|
||||||
|
GetCustomerInvoiceByIdResponseDTO["items"]
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class CustomerInvoiceItemsFullPresenter extends Presenter {
|
||||||
|
protected _map(
|
||||||
|
invoiceItem: CustomerInvoiceItem,
|
||||||
|
index: number
|
||||||
|
): GetCustomerInvoiceItemByInvoiceIdResponseDTO {
|
||||||
|
const allAmounts = invoiceItem.getAllAmounts();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: invoiceItem.id.toPrimitive(),
|
||||||
|
position: String(index),
|
||||||
|
description: toEmptyString(invoiceItem.description, (value) => value.toPrimitive()),
|
||||||
|
|
||||||
|
quantity: invoiceItem.quantity.match(
|
||||||
|
(quantity) => quantity.toObjectString(),
|
||||||
|
() => ({ value: "", scale: "" })
|
||||||
|
),
|
||||||
|
|
||||||
|
unit_amount: invoiceItem.unitAmount.match(
|
||||||
|
(unitAmount) => unitAmount.toObjectString(),
|
||||||
|
() => ({ value: "", scale: "", currency_code: "" })
|
||||||
|
),
|
||||||
|
|
||||||
|
taxes: invoiceItem.taxes.getCodesToString(),
|
||||||
|
|
||||||
|
subtotal_amount: allAmounts.subtotalAmount.toObjectString(),
|
||||||
|
|
||||||
|
discount_percentage: invoiceItem.discountPercentage.match(
|
||||||
|
(discountPercentage) => discountPercentage.toObjectString(),
|
||||||
|
() => ({ value: "", scale: "" })
|
||||||
|
),
|
||||||
|
|
||||||
|
discount_amount: allAmounts.discountAmount.toObjectString(),
|
||||||
|
taxable_amount: allAmounts.taxableAmount.toObjectString(),
|
||||||
|
taxes_amount: allAmounts.taxesAmount.toObjectString(),
|
||||||
|
total_amount: allAmounts.totalAmount.toObjectString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toOutput(invoiceItems: CustomerInvoiceItems): GetCustomerInvoiceByIdResponseDTO["items"] {
|
||||||
|
return invoiceItems.map(this._map);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
import { Presenter } from "@erp/core/api";
|
||||||
|
import { toEmptyString } from "@repo/rdx-ddd";
|
||||||
|
import { GetCustomerInvoiceByIdResponseDTO } from "../../../../common/dto";
|
||||||
|
import { CustomerInvoice } from "../../../domain";
|
||||||
|
import { CustomerInvoiceItemsFullPresenter } from "./customer-invoice-items.full.presenter";
|
||||||
|
|
||||||
|
export class CustomerInvoiceFullPresenter extends Presenter {
|
||||||
|
toOutput(invoice: CustomerInvoice): GetCustomerInvoiceByIdResponseDTO {
|
||||||
|
const itemsPresenter = this.presenterRegistry.getPresenter({
|
||||||
|
resource: "customer-invoice-items",
|
||||||
|
projection: "FULL",
|
||||||
|
}) as CustomerInvoiceItemsFullPresenter;
|
||||||
|
|
||||||
|
const items = itemsPresenter.toOutput(invoice.items);
|
||||||
|
const allAmounts = invoice.getAllAmounts();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: invoice.id.toString(),
|
||||||
|
company_id: invoice.companyId.toString(),
|
||||||
|
|
||||||
|
invoice_number: invoice.invoiceNumber.toString(),
|
||||||
|
status: invoice.status.toPrimitive(),
|
||||||
|
series: toEmptyString(invoice.series, (value) => value.toString()),
|
||||||
|
|
||||||
|
invoice_date: invoice.invoiceDate.toDateString(),
|
||||||
|
operation_date: toEmptyString(invoice.operationDate, (value) => value.toDateString()),
|
||||||
|
|
||||||
|
notes: toEmptyString(invoice.notes, (value) => value.toString()),
|
||||||
|
|
||||||
|
language_code: invoice.languageCode.toString(),
|
||||||
|
currency_code: invoice.currencyCode.toString(),
|
||||||
|
|
||||||
|
taxes: invoice.taxes.getCodesToString(),
|
||||||
|
|
||||||
|
subtotal_amount: allAmounts.subtotalAmount.toObjectString(),
|
||||||
|
|
||||||
|
discount_percentage: invoice.discountPercentage.toObjectString(),
|
||||||
|
|
||||||
|
discount_amount: allAmounts.discountAmount.toObjectString(),
|
||||||
|
taxable_amount: allAmounts.taxableAmount.toObjectString(),
|
||||||
|
taxes_amount: allAmounts.taxesAmount.toObjectString(),
|
||||||
|
total_amount: allAmounts.totalAmount.toObjectString(),
|
||||||
|
|
||||||
|
items,
|
||||||
|
|
||||||
|
metadata: {
|
||||||
|
entity: "customer-invoices",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./customer-invoice-items.full.presenter";
|
||||||
|
export * from "./customer-invoice.full.presenter";
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./full-domain";
|
||||||
|
export * from "./list";
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
import { Presenter } from "@erp/core/api";
|
||||||
|
import { CustomerInvoiceListDTO } from "@erp/customer-invoices/api/infrastructure";
|
||||||
|
import { Criteria } from "@repo/rdx-criteria/server";
|
||||||
|
import { toEmptyString } from "@repo/rdx-ddd";
|
||||||
|
import { ArrayElement, Collection } from "@repo/rdx-utils";
|
||||||
|
import { CustomerInvoiceListResponseDTO } from "../../../../common/dto";
|
||||||
|
|
||||||
|
export class CustomerInvoicesListPresenter extends Presenter {
|
||||||
|
protected _map(invoice: CustomerInvoiceListDTO) {
|
||||||
|
const recipientDTO = invoice.recipient.toObjectString();
|
||||||
|
|
||||||
|
const invoiceDTO: ArrayElement<CustomerInvoiceListResponseDTO["items"]> = {
|
||||||
|
id: invoice.id.toString(),
|
||||||
|
company_id: invoice.companyId.toString(),
|
||||||
|
customer_id: invoice.customerId.toString(),
|
||||||
|
|
||||||
|
invoice_number: invoice.invoiceNumber.toString(),
|
||||||
|
status: invoice.status.toPrimitive(),
|
||||||
|
series: toEmptyString(invoice.series, (value) => value.toString()),
|
||||||
|
|
||||||
|
invoice_date: invoice.invoiceDate.toDateString(),
|
||||||
|
operation_date: toEmptyString(invoice.operationDate, (value) => value.toDateString()),
|
||||||
|
|
||||||
|
recipient: {
|
||||||
|
customer_id: invoice.customerId.toString(),
|
||||||
|
...recipientDTO,
|
||||||
|
},
|
||||||
|
|
||||||
|
language_code: invoice.languageCode.code,
|
||||||
|
currency_code: invoice.currencyCode.code,
|
||||||
|
|
||||||
|
taxes: invoice.taxes,
|
||||||
|
|
||||||
|
subtotal_amount: invoice.subtotalAmount.toObjectString(),
|
||||||
|
discount_amount: invoice.discountAmount.toObjectString(),
|
||||||
|
taxable_amount: invoice.taxableAmount.toObjectString(),
|
||||||
|
taxes_amount: invoice.taxesAmount.toObjectString(),
|
||||||
|
total_amount: invoice.totalAmount.toObjectString(),
|
||||||
|
|
||||||
|
metadata: {
|
||||||
|
entity: "customer-invoice",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return invoiceDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
toOutput(params: {
|
||||||
|
customerInvoices: Collection<CustomerInvoiceListDTO>;
|
||||||
|
criteria: Criteria;
|
||||||
|
}): CustomerInvoiceListResponseDTO {
|
||||||
|
const { customerInvoices, criteria } = params;
|
||||||
|
|
||||||
|
const invoices = customerInvoices.map((invoice) => this._map(invoice));
|
||||||
|
const totalItems = customerInvoices.total();
|
||||||
|
|
||||||
|
return {
|
||||||
|
page: criteria.pageNumber,
|
||||||
|
per_page: criteria.pageSize,
|
||||||
|
total_pages: Math.ceil(totalItems / criteria.pageSize),
|
||||||
|
total_items: totalItems,
|
||||||
|
items: invoices,
|
||||||
|
metadata: {
|
||||||
|
entity: "customer-invoices",
|
||||||
|
criteria: criteria.toJSON(),
|
||||||
|
//links: {
|
||||||
|
// self: `/api/customer-invoices?page=${criteria.pageNumber}&per_page=${criteria.pageSize}`,
|
||||||
|
// first: `/api/customer-invoices?page=1&per_page=${criteria.pageSize}`,
|
||||||
|
// last: `/api/customer-invoices?page=${Math.ceil(totalItems / criteria.pageSize)}&per_page=${criteria.pageSize}`,
|
||||||
|
//},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./customer-invoices.list.presenter";
|
||||||
@ -1 +1,2 @@
|
|||||||
export * from "./report-customer-invoice.use-case";
|
export * from "./report-customer-invoice.use-case";
|
||||||
|
export * from "./reporter";
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import { ITransactionManager } from "@erp/core/api";
|
import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
|
||||||
import { UniqueID } from "@repo/rdx-ddd";
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
import { CustomerInvoiceService } from "../../domain";
|
import { CustomerInvoiceService } from "../../domain";
|
||||||
import { ReportCustomerInvoiceAssembler } from "./assembler";
|
|
||||||
|
|
||||||
type ReportCustomerInvoiceUseCaseInput = {
|
type ReportCustomerInvoiceUseCaseInput = {
|
||||||
companyId: UniqueID;
|
companyId: UniqueID;
|
||||||
@ -13,7 +12,7 @@ export class ReportCustomerInvoiceUseCase {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly service: CustomerInvoiceService,
|
private readonly service: CustomerInvoiceService,
|
||||||
private readonly transactionManager: ITransactionManager,
|
private readonly transactionManager: ITransactionManager,
|
||||||
private readonly assembler: ReportCustomerInvoiceAssembler
|
private readonly presenterRegistry: IPresenterRegistry
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public execute(params: ReportCustomerInvoiceUseCaseInput) {
|
public execute(params: ReportCustomerInvoiceUseCaseInput) {
|
||||||
@ -26,6 +25,11 @@ export class ReportCustomerInvoiceUseCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const invoiceId = idOrError.data;
|
const invoiceId = idOrError.data;
|
||||||
|
const pdfPresenter = this.presenterRegistry.getPresenter({
|
||||||
|
resource: "customer-invoice",
|
||||||
|
projection: "REPORT",
|
||||||
|
format: "PDF",
|
||||||
|
});
|
||||||
|
|
||||||
return this.transactionManager.complete(async (transaction) => {
|
return this.transactionManager.complete(async (transaction) => {
|
||||||
try {
|
try {
|
||||||
@ -38,10 +42,12 @@ export class ReportCustomerInvoiceUseCase {
|
|||||||
return Result.fail(invoiceOrError.error);
|
return Result.fail(invoiceOrError.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const invoiceDto = this.registry.getPresenter("").toDTO(invoideOIrError.data);
|
const invoice = invoiceOrError.data;
|
||||||
|
const pdfData = pdfPresenter.toOutput(invoiceOrError.data);
|
||||||
const pdfData = this.assembler.toPDF(invoiceDto);
|
return Result.ok({
|
||||||
return Result.ok(pdfData);
|
data: pdfData,
|
||||||
|
filename: `invoice-${invoice.invoiceNumber}.pdf`,
|
||||||
|
});
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
return Result.fail(error as Error);
|
return Result.fail(error as Error);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { Presenter } from "@erp/core/api";
|
||||||
|
import * as handlebars from "handlebars";
|
||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import { CustomerInvoice } from "../../../domain";
|
||||||
|
|
||||||
|
export class CustomerInvoiceReportHTMLPresenter extends Presenter {
|
||||||
|
toOutput(customerInvoice: CustomerInvoice): string {
|
||||||
|
const dtoPresenter = this.presenterRegistry.getPresenter({
|
||||||
|
resource: "customer-invoice",
|
||||||
|
projection: "FULL",
|
||||||
|
});
|
||||||
|
|
||||||
|
const invoiceDTO = dtoPresenter.toOutput(customerInvoice);
|
||||||
|
|
||||||
|
// Obtener y compilar la plantilla HTML
|
||||||
|
const templateHtml = readFileSync(
|
||||||
|
path.join(__dirname, "./templates/quote/template.hbs")
|
||||||
|
).toString();
|
||||||
|
const template = handlebars.compile(templateHtml, {});
|
||||||
|
return template(invoiceDTO);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
import { Presenter } from "@erp/core/api";
|
||||||
|
import puppeteer from "puppeteer";
|
||||||
|
import report from "puppeteer-report";
|
||||||
|
import { CustomerInvoice } from "../../../domain";
|
||||||
|
import { CustomerInvoiceReportHTMLPresenter } from "./customer-invoice.report.html";
|
||||||
|
|
||||||
|
export interface ICustomerInvoiceReporter {
|
||||||
|
toHTML: (invoice: CustomerInvoice) => Promise<string>;
|
||||||
|
toPDF: (invoice: CustomerInvoice) => Promise<Buffer>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://plnkr.co/edit/lWk6Yd?preview
|
||||||
|
|
||||||
|
export class CustomerInvoiceReportPDFPresenter extends Presenter {
|
||||||
|
async toOutput(customerInvoice: CustomerInvoice): Promise<Buffer> {
|
||||||
|
const htmlPresenter = this.presenterRegistry.getPresenter({
|
||||||
|
resource: "customer-invoice",
|
||||||
|
projection: "REPORT",
|
||||||
|
format: "HTML",
|
||||||
|
}) as CustomerInvoiceReportHTMLPresenter;
|
||||||
|
|
||||||
|
const htmlData = htmlPresenter.toOutput(customerInvoice);
|
||||||
|
|
||||||
|
// Generar el PDF con Puppeteer
|
||||||
|
const browser = await puppeteer.launch({
|
||||||
|
args: [
|
||||||
|
"--disable-extensions",
|
||||||
|
"--no-sandbox",
|
||||||
|
"--disable-setuid-sandbox",
|
||||||
|
"--disable-dev-shm-usage",
|
||||||
|
"--disable-gpu",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const page = await browser.newPage();
|
||||||
|
const navigationPromise = page.waitForNavigation();
|
||||||
|
await page.setContent(htmlData, { waitUntil: "networkidle2" });
|
||||||
|
|
||||||
|
await navigationPromise;
|
||||||
|
const reportPDF = await report.pdfPage(page, {
|
||||||
|
format: "A4",
|
||||||
|
margin: {
|
||||||
|
bottom: "10mm",
|
||||||
|
left: "10mm",
|
||||||
|
right: "10mm",
|
||||||
|
top: "10mm",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
return Buffer.from(reportPDF);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,97 +0,0 @@
|
|||||||
import { toEmptyString } from "@repo/rdx-ddd";
|
|
||||||
import * as handlebars from "handlebars";
|
|
||||||
import { readFileSync } from "node:fs";
|
|
||||||
import path from "node:path";
|
|
||||||
import puppeteer from "puppeteer";
|
|
||||||
import report from "puppeteer-report";
|
|
||||||
import { CustomerInvoice } from "../../../domain";
|
|
||||||
|
|
||||||
export interface ICustomerInvoiceReporter {
|
|
||||||
toHTML: (invoice: CustomerInvoice) => Promise<string>;
|
|
||||||
toPDF: (invoice: CustomerInvoice) => Promise<Buffer>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://plnkr.co/edit/lWk6Yd?preview
|
|
||||||
|
|
||||||
export const CustomerInvoiceReporter: ICustomerInvoiceReporter = {
|
|
||||||
toHTML: async (invoice: CustomerInvoice): Promise<string> => {
|
|
||||||
const quote_dto = await map(quote, context);
|
|
||||||
|
|
||||||
// Obtener y compilar la plantilla HTML
|
|
||||||
const templateHtml = readFileSync(
|
|
||||||
path.join(__dirname, "./templates/quote/template.hbs")
|
|
||||||
).toString();
|
|
||||||
const template = handlebars.compile(templateHtml, {});
|
|
||||||
return template(quote_dto);
|
|
||||||
},
|
|
||||||
|
|
||||||
toPDF: async (quote: CustomerInvoice, context: ISalesContext): Promise<Buffer> => {
|
|
||||||
const html = await CustomerInvoiceReporter.toHTML(quote, context);
|
|
||||||
|
|
||||||
// Generar el PDF con Puppeteer
|
|
||||||
const browser = await puppeteer.launch({
|
|
||||||
args: [
|
|
||||||
"--disable-extensions",
|
|
||||||
"--no-sandbox",
|
|
||||||
"--disable-setuid-sandbox",
|
|
||||||
"--disable-dev-shm-usage",
|
|
||||||
"--disable-gpu",
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
const page = await browser.newPage();
|
|
||||||
const navigationPromise = page.waitForNavigation();
|
|
||||||
await page.setContent(html, { waitUntil: "networkidle2" });
|
|
||||||
|
|
||||||
await navigationPromise;
|
|
||||||
const reportPDF = await report.pdfPage(page, {
|
|
||||||
format: "A4",
|
|
||||||
margin: {
|
|
||||||
bottom: "10mm",
|
|
||||||
left: "10mm",
|
|
||||||
right: "10mm",
|
|
||||||
top: "10mm",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await browser.close();
|
|
||||||
return Buffer.from(reportPDF);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const map = async (invoice: CustomerInvoice) => {
|
|
||||||
return {
|
|
||||||
id: invoice.id.toString(),
|
|
||||||
company_id: invoice.companyId.toString(),
|
|
||||||
|
|
||||||
invoice_number: invoice.invoiceNumber.toString(),
|
|
||||||
status: invoice.status.toPrimitive(),
|
|
||||||
series: toEmptyString(invoice.series, (value) => value.toString()),
|
|
||||||
|
|
||||||
invoice_date: invoice.invoiceDate.toDateString(),
|
|
||||||
operation_date: toEmptyString(invoice.operationDate, (value) => value.toDateString()),
|
|
||||||
|
|
||||||
notes: toEmptyString(invoice.notes, (value) => value.toString()),
|
|
||||||
|
|
||||||
language_code: invoice.languageCode.toString(),
|
|
||||||
currency_code: invoice.currencyCode.toString(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const quoteItemPresenter = (
|
|
||||||
items: ICollection<CustomerInvoiceItem>,
|
|
||||||
context: ISalesContext
|
|
||||||
): any[] =>
|
|
||||||
items.totalCount > 0
|
|
||||||
? items.items.map((item: CustomerInvoiceItem) => ({
|
|
||||||
id_article: item.idArticle.toString(),
|
|
||||||
description: item.description.toString(),
|
|
||||||
quantity: item.quantity.toFormat(),
|
|
||||||
unit_price: item.unitPrice.toFormat(),
|
|
||||||
subtotal_price: item.subtotalPrice.toFormat(),
|
|
||||||
discount: item.discount.toFormat(),
|
|
||||||
total_price: item.totalPrice.toFormat(),
|
|
||||||
}))
|
|
||||||
: [];
|
|
||||||
2;
|
|
||||||
@ -1 +1,2 @@
|
|||||||
export * from "./customer-invoice.reporter";
|
export * from "./customer-invoice.report.html";
|
||||||
|
export * from "./customer-invoice.report.pdf";
|
||||||
|
|||||||
@ -22,4 +22,10 @@ export class InvoiceTaxes extends Collection<InvoiceTax> {
|
|||||||
InvoiceAmount.zero(taxableAmount.currencyCode)
|
InvoiceAmount.zero(taxableAmount.currencyCode)
|
||||||
) as InvoiceAmount;
|
) as InvoiceAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getCodesToString(): string {
|
||||||
|
return this.getAll()
|
||||||
|
.map((taxItem) => taxItem.tax.code)
|
||||||
|
.join(", ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,4 +22,10 @@ export class ItemTaxes extends Collection<ItemTax> {
|
|||||||
ItemAmount.zero(taxableAmount.currencyCode)
|
ItemAmount.zero(taxableAmount.currencyCode)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getCodesToString(): string {
|
||||||
|
return this.getAll()
|
||||||
|
.map((taxItem) => taxItem.tax.code)
|
||||||
|
.join(", ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,14 @@ export class ItemAmount extends MoneyValue {
|
|||||||
return ItemAmount.create(props).data;
|
return ItemAmount.create(props).data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toObjectString() {
|
||||||
|
return {
|
||||||
|
value: String(this.value),
|
||||||
|
scale: String(this.scale),
|
||||||
|
currency_code: this.currencyCode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure fluent operations keep the subclass type
|
// Ensure fluent operations keep the subclass type
|
||||||
convertScale(newScale: number) {
|
convertScale(newScale: number) {
|
||||||
const mv = super.convertScale(newScale);
|
const mv = super.convertScale(newScale);
|
||||||
|
|||||||
@ -7,13 +7,17 @@ import {
|
|||||||
} from "@erp/core/api";
|
} from "@erp/core/api";
|
||||||
import {
|
import {
|
||||||
CreateCustomerInvoiceUseCase,
|
CreateCustomerInvoiceUseCase,
|
||||||
|
CustomerInvoiceFullPresenter,
|
||||||
|
CustomerInvoiceItemsFullPresenter,
|
||||||
|
CustomerInvoiceReportHTMLPresenter,
|
||||||
|
CustomerInvoiceReportPDFPresenter,
|
||||||
DeleteCustomerInvoiceUseCase,
|
DeleteCustomerInvoiceUseCase,
|
||||||
GetCustomerInvoiceUseCase,
|
GetCustomerInvoiceUseCase,
|
||||||
ListCustomerInvoicesPresenter,
|
ListCustomerInvoicesPresenter,
|
||||||
ListCustomerInvoicesUseCase,
|
ListCustomerInvoicesUseCase,
|
||||||
ReportCustomerInvoiceUseCase,
|
ReportCustomerInvoiceUseCase,
|
||||||
UpdateCustomerInvoiceUseCase,
|
|
||||||
} from "../application";
|
} from "../application";
|
||||||
|
|
||||||
import { CustomerInvoiceService } from "../domain";
|
import { CustomerInvoiceService } from "../domain";
|
||||||
import { CustomerInvoiceFullMapper, CustomerInvoiceListMapper } from "./mappers";
|
import { CustomerInvoiceFullMapper, CustomerInvoiceListMapper } from "./mappers";
|
||||||
import { CustomerInvoiceRepository } from "./sequelize";
|
import { CustomerInvoiceRepository } from "./sequelize";
|
||||||
@ -31,7 +35,7 @@ type InvoiceDeps = {
|
|||||||
list: () => ListCustomerInvoicesUseCase;
|
list: () => ListCustomerInvoicesUseCase;
|
||||||
get: () => GetCustomerInvoiceUseCase;
|
get: () => GetCustomerInvoiceUseCase;
|
||||||
create: () => CreateCustomerInvoiceUseCase;
|
create: () => CreateCustomerInvoiceUseCase;
|
||||||
update: () => UpdateCustomerInvoiceUseCase;
|
//update: () => UpdateCustomerInvoiceUseCase;
|
||||||
delete: () => DeleteCustomerInvoiceUseCase;
|
delete: () => DeleteCustomerInvoiceUseCase;
|
||||||
report: () => ReportCustomerInvoiceUseCase;
|
report: () => ReportCustomerInvoiceUseCase;
|
||||||
};
|
};
|
||||||
@ -64,12 +68,48 @@ export function getInvoiceDependencies(params: ModuleParams): InvoiceDeps {
|
|||||||
|
|
||||||
if (!_presenterRegistry) {
|
if (!_presenterRegistry) {
|
||||||
_presenterRegistry = new InMemoryPresenterRegistry();
|
_presenterRegistry = new InMemoryPresenterRegistry();
|
||||||
_presenterRegistry.registerPresenter(
|
_presenterRegistry.registerPresenters([
|
||||||
{
|
{
|
||||||
projection: "LIST",
|
key: {
|
||||||
|
resource: "customer-invoice-items",
|
||||||
|
projection: "FULL",
|
||||||
|
},
|
||||||
|
presenter: new CustomerInvoiceItemsFullPresenter(_presenterRegistry),
|
||||||
},
|
},
|
||||||
new ListCustomerInvoicesPresenter()
|
{
|
||||||
);
|
key: {
|
||||||
|
resource: "customer-invoice",
|
||||||
|
projection: "FULL",
|
||||||
|
},
|
||||||
|
presenter: new CustomerInvoiceFullPresenter(_presenterRegistry),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
key: {
|
||||||
|
resource: "customer-invoice",
|
||||||
|
projection: "LIST",
|
||||||
|
},
|
||||||
|
presenter: new ListCustomerInvoicesPresenter(_presenterRegistry),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
key: {
|
||||||
|
resource: "customer-invoice",
|
||||||
|
projection: "REPORT",
|
||||||
|
format: "HTML",
|
||||||
|
},
|
||||||
|
presenter: new CustomerInvoiceReportHTMLPresenter(_presenterRegistry),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
key: {
|
||||||
|
resource: "customer-invoice",
|
||||||
|
projection: "REPORT",
|
||||||
|
format: "PDF",
|
||||||
|
},
|
||||||
|
presenter: new CustomerInvoiceReportPDFPresenter(_presenterRegistry),
|
||||||
|
},
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -82,38 +122,25 @@ export function getInvoiceDependencies(params: ModuleParams): InvoiceDeps {
|
|||||||
|
|
||||||
build: {
|
build: {
|
||||||
list: () =>
|
list: () =>
|
||||||
new ListCustomerInvoicesUseCase(
|
new ListCustomerInvoicesUseCase(_service!, transactionManager!, _presenterRegistry!),
|
||||||
_service!,
|
get: () => new GetCustomerInvoiceUseCase(_service!, transactionManager!, _presenterRegistry!),
|
||||||
transactionManager!,
|
|
||||||
_presenterRegistry?.getPresenter({ projection: "LIST" })
|
|
||||||
),
|
|
||||||
get: () =>
|
|
||||||
new GetCustomerInvoiceUseCase(
|
|
||||||
_service!,
|
|
||||||
transactionManager!,
|
|
||||||
_presenterRegistry?.getPresenter({ projection: "FULL" })
|
|
||||||
),
|
|
||||||
create: () =>
|
create: () =>
|
||||||
new CreateCustomerInvoiceUseCase(
|
new CreateCustomerInvoiceUseCase(
|
||||||
_service!,
|
_service!,
|
||||||
transactionManager!,
|
transactionManager!,
|
||||||
_presenterRegistry?.getPresenter({ projection: "FULL" }),
|
_presenterRegistry!,
|
||||||
_catalogs!.taxes
|
_catalogs!.taxes
|
||||||
),
|
),
|
||||||
update: () =>
|
/*update: () =>
|
||||||
new UpdateCustomerInvoiceUseCase(
|
new UpdateCustomerInvoiceUseCase(
|
||||||
_service!,
|
_service!,
|
||||||
transactionManager!,
|
transactionManager!,
|
||||||
_presenterRegistry?.getPresenter({ projection: "FULL" }),
|
_presenterRegistry!,
|
||||||
_catalogs!.taxes
|
_catalogs!.taxes
|
||||||
),
|
),*/
|
||||||
delete: () => new DeleteCustomerInvoiceUseCase(_service!, transactionManager!),
|
delete: () => new DeleteCustomerInvoiceUseCase(_service!, transactionManager!),
|
||||||
report: () =>
|
report: () =>
|
||||||
new ReportCustomerInvoiceUseCase(
|
new ReportCustomerInvoiceUseCase(_service!, transactionManager!, _presenterRegistry!),
|
||||||
_service!,
|
|
||||||
transactionManager!,
|
|
||||||
_presenterRegistry?.getPresenter({ projection: "REPORT" })
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,3 +3,4 @@ export * from "./delete-customer-invoice.controller";
|
|||||||
export * from "./get-customer-invoice.controller";
|
export * from "./get-customer-invoice.controller";
|
||||||
export * from "./list-customer-invoices.controller";
|
export * from "./list-customer-invoices.controller";
|
||||||
///export * from "./update-customer-invoice.controller";
|
///export * from "./update-customer-invoice.controller";
|
||||||
|
export * from "./report-customer-invoice.controller";
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api";
|
import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api";
|
||||||
import { GetCustomerInvoiceUseCase, GetCustomerInvoiceUseCase as ReportCustomerInvoiceUseCase } from "../../../application";
|
import { GetCustomerInvoiceUseCase as ReportCustomerInvoiceUseCase } from "../../../application";
|
||||||
|
|
||||||
export class ReportCustomerInvoiceController extends ExpressController {
|
export class ReportCustomerInvoiceController extends ExpressController {
|
||||||
public constructor(private readonly useCase: ReportCustomerInvoiceUseCase) {
|
public constructor(private readonly useCase: ReportCustomerInvoiceUseCase) {
|
||||||
@ -12,13 +12,10 @@ export class ReportCustomerInvoiceController extends ExpressController {
|
|||||||
const companyId = this.getTenantId()!; // garantizado por tenantGuard
|
const companyId = this.getTenantId()!; // garantizado por tenantGuard
|
||||||
const { invoice_id } = this.req.params;
|
const { invoice_id } = this.req.params;
|
||||||
|
|
||||||
const getUseCase = getUsecaasdasd;
|
const result = await this.useCase.execute({ invoice_id, companyId });
|
||||||
const invoiceDto = await
|
|
||||||
|
|
||||||
const result = await this.useCase.execute({ invoice_id, companyId, });
|
|
||||||
|
|
||||||
return result.match(
|
return result.match(
|
||||||
(data) => this.downloadPDF(result.data),
|
({ pdfData, filename }) => this.downloadPDF(pdfData, filename),
|
||||||
(err) => this.handleError(err)
|
(err) => this.handleError(err)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import {
|
|||||||
DeleteCustomerInvoiceController,
|
DeleteCustomerInvoiceController,
|
||||||
GetCustomerInvoiceController,
|
GetCustomerInvoiceController,
|
||||||
ListCustomerInvoicesController,
|
ListCustomerInvoicesController,
|
||||||
|
ReportCustomerInvoiceController,
|
||||||
} from "./controllers";
|
} from "./controllers";
|
||||||
|
|
||||||
export const customerInvoicesRouter = (params: ModuleParams) => {
|
export const customerInvoicesRouter = (params: ModuleParams) => {
|
||||||
|
|||||||
@ -134,7 +134,7 @@ export class CustomerInvoiceRepository
|
|||||||
transaction: Transaction
|
transaction: Transaction
|
||||||
): Promise<Result<CustomerInvoice, Error>> {
|
): Promise<Result<CustomerInvoice, Error>> {
|
||||||
try {
|
try {
|
||||||
const mapper: ICustomerInvoiceFullMapper = this._registry.getReadModelMapper("FULL");
|
const mapper: ICustomerInvoiceFullMapper = this._registry.getDomainMapper("FULL");
|
||||||
const { CustomerModel } = this._database.models;
|
const { CustomerModel } = this._database.models;
|
||||||
|
|
||||||
const row = await CustomerInvoiceModel.findOne({
|
const row = await CustomerInvoiceModel.findOne({
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
import { AmountSchema, MetadataSchema, PercentageSchema, QuantitySchema } from "@erp/core";
|
import {
|
||||||
|
AmountSchema,
|
||||||
|
MetadataSchema,
|
||||||
|
MoneySchema,
|
||||||
|
PercentageSchema,
|
||||||
|
QuantitySchema,
|
||||||
|
} from "@erp/core";
|
||||||
import * as z from "zod/v4";
|
import * as z from "zod/v4";
|
||||||
|
|
||||||
export const CreateCustomerInvoiceResponseSchema = z.object({
|
export const CreateCustomerInvoiceResponseSchema = z.object({
|
||||||
@ -17,12 +23,12 @@ export const CreateCustomerInvoiceResponseSchema = z.object({
|
|||||||
language_code: z.string(),
|
language_code: z.string(),
|
||||||
currency_code: z.string(),
|
currency_code: z.string(),
|
||||||
|
|
||||||
subtotal_amount: AmountSchema,
|
subtotal_amount: MoneySchema,
|
||||||
discount_percentage: PercentageSchema,
|
discount_percentage: PercentageSchema,
|
||||||
discount_amount: AmountSchema,
|
discount_amount: MoneySchema,
|
||||||
taxable_amount: AmountSchema,
|
taxable_amount: MoneySchema,
|
||||||
tax_amount: AmountSchema,
|
taxes_amount: MoneySchema,
|
||||||
total_amount: AmountSchema,
|
total_amount: MoneySchema,
|
||||||
|
|
||||||
items: z.array(
|
items: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
@ -31,8 +37,15 @@ export const CreateCustomerInvoiceResponseSchema = z.object({
|
|||||||
description: z.string(),
|
description: z.string(),
|
||||||
quantity: QuantitySchema,
|
quantity: QuantitySchema,
|
||||||
unit_amount: AmountSchema,
|
unit_amount: AmountSchema,
|
||||||
|
|
||||||
|
taxes: z.string(),
|
||||||
|
|
||||||
|
subtotal_amount: MoneySchema,
|
||||||
discount_percentage: PercentageSchema,
|
discount_percentage: PercentageSchema,
|
||||||
total_amount: AmountSchema,
|
discount_amount: MoneySchema,
|
||||||
|
taxable_amount: MoneySchema,
|
||||||
|
taxes_amount: MoneySchema,
|
||||||
|
total_amount: MoneySchema,
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { AmountSchema, MetadataSchema, PercentageSchema, QuantitySchema } from "@erp/core";
|
import { MetadataSchema, MoneySchema, PercentageSchema, QuantitySchema } from "@erp/core";
|
||||||
import * as z from "zod/v4";
|
import * as z from "zod/v4";
|
||||||
|
|
||||||
export const GetCustomerInvoiceByIdResponseSchema = z.object({
|
export const GetCustomerInvoiceByIdResponseSchema = z.object({
|
||||||
@ -19,11 +19,12 @@ export const GetCustomerInvoiceByIdResponseSchema = z.object({
|
|||||||
|
|
||||||
taxes: z.string(),
|
taxes: z.string(),
|
||||||
|
|
||||||
subtotal_amount: AmountSchema,
|
subtotal_amount: MoneySchema,
|
||||||
discount_percentage: PercentageSchema,
|
discount_percentage: PercentageSchema,
|
||||||
discount_amount: AmountSchema,
|
discount_amount: MoneySchema,
|
||||||
taxable_amount: AmountSchema,
|
taxable_amount: MoneySchema,
|
||||||
total_amount: AmountSchema,
|
taxes_amount: MoneySchema,
|
||||||
|
total_amount: MoneySchema,
|
||||||
|
|
||||||
items: z.array(
|
items: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
@ -31,10 +32,16 @@ export const GetCustomerInvoiceByIdResponseSchema = z.object({
|
|||||||
position: z.string(),
|
position: z.string(),
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
quantity: QuantitySchema,
|
quantity: QuantitySchema,
|
||||||
unit_amount: AmountSchema,
|
unit_amount: MoneySchema,
|
||||||
discount_percentage: PercentageSchema,
|
|
||||||
taxes: z.string(),
|
taxes: z.string(),
|
||||||
total_amount: AmountSchema,
|
|
||||||
|
subtotal_amount: MoneySchema,
|
||||||
|
discount_percentage: PercentageSchema,
|
||||||
|
discount_amount: MoneySchema,
|
||||||
|
taxable_amount: MoneySchema,
|
||||||
|
taxes_amount: MoneySchema,
|
||||||
|
total_amount: MoneySchema,
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { AmountSchema, MetadataSchema, createListViewResponseSchema } from "@erp/core";
|
import { MetadataSchema, MoneySchema, createListViewResponseSchema } from "@erp/core";
|
||||||
import * as z from "zod/v4";
|
import * as z from "zod/v4";
|
||||||
|
|
||||||
export const ListCustomerInvoiceResponseSchema = createListViewResponseSchema(
|
export const ListCustomerInvoiceResponseSchema = createListViewResponseSchema(
|
||||||
@ -30,11 +30,11 @@ export const ListCustomerInvoiceResponseSchema = createListViewResponseSchema(
|
|||||||
|
|
||||||
taxes: z.string(),
|
taxes: z.string(),
|
||||||
|
|
||||||
subtotal_amount: AmountSchema,
|
subtotal_amount: MoneySchema,
|
||||||
discount_amount: AmountSchema,
|
discount_amount: MoneySchema,
|
||||||
taxable_amount: AmountSchema,
|
taxable_amount: MoneySchema,
|
||||||
taxes_amount: AmountSchema,
|
taxes_amount: MoneySchema,
|
||||||
total_amount: AmountSchema,
|
total_amount: MoneySchema,
|
||||||
|
|
||||||
metadata: MetadataSchema.optional(),
|
metadata: MetadataSchema.optional(),
|
||||||
})
|
})
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { CSSProperties, PropsWithChildren, createContext, useMemo } from "react"
|
|||||||
import { CustomerInvoiceItemsSortableProps } from "./customer-invoice-items-sortable-datatable";
|
import { CustomerInvoiceItemsSortableProps } from "./customer-invoice-items-sortable-datatable";
|
||||||
|
|
||||||
interface Context {
|
interface Context {
|
||||||
attributes: Record<string, any>;
|
attributes: Record<string, unknown>;
|
||||||
listeners: DraggableSyntheticListeners;
|
listeners: DraggableSyntheticListeners;
|
||||||
ref(node: HTMLElement | null): void;
|
ref(node: HTMLElement | null): void;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,10 @@
|
|||||||
import type { IMapperRegistry, IPresenterRegistry, ModuleParams } from "@erp/core/api";
|
import type { IMapperRegistry, IPresenterRegistry, ModuleParams } from "@erp/core/api";
|
||||||
import {
|
import { InMemoryMapperRegistry, SequelizeTransactionManager } from "@erp/core/api";
|
||||||
InMemoryMapperRegistry,
|
|
||||||
InMemoryPresenterRegistry,
|
|
||||||
SequelizeTransactionManager,
|
|
||||||
} from "@erp/core/api";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CreateCustomerUseCase,
|
CreateCustomerUseCase,
|
||||||
DeleteCustomerUseCase,
|
DeleteCustomerUseCase,
|
||||||
GetCustomerAssembler,
|
|
||||||
GetCustomerUseCase,
|
GetCustomerUseCase,
|
||||||
ListCustomersAssembler,
|
|
||||||
ListCustomersUseCase,
|
ListCustomersUseCase,
|
||||||
UpdateCustomerUseCase,
|
UpdateCustomerUseCase,
|
||||||
} from "../application";
|
} from "../application";
|
||||||
@ -33,7 +27,7 @@ type CustomerDeps = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let _presenterRegistry: IPresenterRegistry | null = null;
|
const _presenterRegistry: IPresenterRegistry | null = null;
|
||||||
let _mapperRegistry: IMapperRegistry | null = null;
|
let _mapperRegistry: IMapperRegistry | null = null;
|
||||||
|
|
||||||
let _repo: CustomerRepository | null = null;
|
let _repo: CustomerRepository | null = null;
|
||||||
@ -51,42 +45,34 @@ export function getCustomerDependencies(params: ModuleParams): CustomerDeps {
|
|||||||
if (!_repo) _repo = new CustomerRepository({ mapperRegistry: _mapperRegistry });
|
if (!_repo) _repo = new CustomerRepository({ mapperRegistry: _mapperRegistry });
|
||||||
if (!_service) _service = new CustomerService(_repo);
|
if (!_service) _service = new CustomerService(_repo);
|
||||||
|
|
||||||
if (!_presenterRegistry) {
|
/*if (!_presenterRegistry) {
|
||||||
_presenterRegistry = new InMemoryPresenterRegistry();
|
_presenterRegistry = new InMemoryPresenterRegistry();
|
||||||
|
|
||||||
_presenterRegistry.registerPresenters([
|
_presenterRegistry.registerPresenters([
|
||||||
{
|
{
|
||||||
key: { projection: "FULL" },
|
key: { resource: "customer", projection: "FULL" },
|
||||||
presenter: new ListCustomersAssembler(),
|
presenter: new ListCustomersAssembler(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: { projection: "LIST" },
|
key: { resource: "customer", projection: "LIST" },
|
||||||
presenter: new GetCustomerAssembler(),
|
presenter: new GetCustomerAssembler(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
}*/
|
||||||
/*if (!_assemblers) {
|
|
||||||
_assemblers = {
|
|
||||||
list: new ListCustomersAssembler(), // transforma domain → ListDTO
|
|
||||||
get: new GetCustomerAssembler(), // transforma domain → DetailDTO
|
|
||||||
create: new CreateCustomersAssembler(), // transforma domain → CreatedDTO
|
|
||||||
update: new UpdateCustomerAssembler(), // transforma domain -> UpdateDTO
|
|
||||||
};*/
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
transactionManager,
|
transactionManager,
|
||||||
repo: _repo,
|
repo: _repo,
|
||||||
mapperRegistry: _mapperRegistry,
|
mapperRegistry: _mapperRegistry,
|
||||||
presenterRegistry: _presenterRegistry,
|
//presenterRegistry: _presenterRegistry,
|
||||||
service: _service,
|
service: _service,
|
||||||
|
|
||||||
build: {
|
build: {
|
||||||
list: () => new ListCustomersUseCase(_service!, transactionManager!, _assemblers!.list),
|
/*list: () => new ListCustomersUseCase(_service!, transactionManager!, presenterRegistry!),
|
||||||
get: () => new GetCustomerUseCase(_service!, transactionManager!, _assemblers!.get),
|
get: () => new GetCustomerUseCase(_service!, transactionManager!, presenterRegistry!),
|
||||||
create: () => new CreateCustomerUseCase(_service!, transactionManager!, _assemblers!.create),
|
create: () => new CreateCustomerUseCase(_service!, transactionManager!, presenterRegistry!),
|
||||||
update: () => new UpdateCustomerUseCase(_service!, transactionManager!, _assemblers!.update),
|
update: () => new UpdateCustomerUseCase(_service!, transactionManager!, presenterRegistry!),
|
||||||
delete: () => new DeleteCustomerUseCase(_service!, transactionManager!),
|
delete: () => new DeleteCustomerUseCase(_service!, transactionManager!),*/
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -81,4 +81,11 @@ export class Percentage extends ValueObject<PercentageProps> {
|
|||||||
toString(): string {
|
toString(): string {
|
||||||
return `${this.toNumber().toFixed(this.scale)}%`;
|
return `${this.toNumber().toFixed(this.scale)}%`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toObjectString() {
|
||||||
|
return {
|
||||||
|
value: String(this.value),
|
||||||
|
scale: String(this.scale),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,6 +62,13 @@ export class Quantity extends ValueObject<QuantityProps> {
|
|||||||
return this.toNumber().toFixed(this.scale);
|
return this.toNumber().toFixed(this.scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toObjectString() {
|
||||||
|
return {
|
||||||
|
value: String(this.value),
|
||||||
|
scale: String(this.scale),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
isZero(): boolean {
|
isZero(): boolean {
|
||||||
return this.value === 0;
|
return this.value === 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user