Refactorización Issued invoice / Proformas
This commit is contained in:
parent
8d6b53e431
commit
97544b012d
@ -1,4 +1,4 @@
|
|||||||
import { Collection, Result } from "@repo/rdx-utils";
|
import type { 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
|
||||||
|
|||||||
@ -3,15 +3,11 @@ import type { IAggregateRootRepository, UniqueID } from "@repo/rdx-ddd";
|
|||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
import type { FindOptions, ModelDefined, Sequelize, Transaction } from "sequelize";
|
import type { FindOptions, ModelDefined, Sequelize, Transaction } from "sequelize";
|
||||||
|
|
||||||
import type { IMapperRegistry } from "../mappers";
|
|
||||||
|
|
||||||
export abstract class SequelizeRepository<T> implements IAggregateRootRepository<T> {
|
export abstract class SequelizeRepository<T> implements IAggregateRootRepository<T> {
|
||||||
protected readonly _database!: Sequelize;
|
protected readonly database!: Sequelize;
|
||||||
protected readonly _registry!: IMapperRegistry;
|
|
||||||
|
|
||||||
constructor(params: { mapperRegistry: IMapperRegistry; database: Sequelize }) {
|
constructor(params: { database: Sequelize }) {
|
||||||
this._registry = params.mapperRegistry;
|
this.database = params.database;
|
||||||
this._database = params.database;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected convertCriteria(criteria: Criteria): FindOptions {
|
protected convertCriteria(criteria: Criteria): FindOptions {
|
||||||
|
|||||||
@ -8,10 +8,10 @@ import { Result } from "@repo/rdx-utils";
|
|||||||
|
|
||||||
import type { CreateCustomerInvoiceRequestDTO } from "../../../common";
|
import type { CreateCustomerInvoiceRequestDTO } from "../../../common";
|
||||||
import {
|
import {
|
||||||
CustomerInvoiceItem,
|
IssuedInvoiceItem,
|
||||||
CustomerInvoiceItemDescription,
|
type IssuedInvoiceItemProps,
|
||||||
type CustomerInvoiceItemProps,
|
|
||||||
ItemAmount,
|
ItemAmount,
|
||||||
|
ItemDescription,
|
||||||
ItemDiscount,
|
ItemDiscount,
|
||||||
ItemQuantity,
|
ItemQuantity,
|
||||||
} from "../../domain";
|
} from "../../domain";
|
||||||
@ -20,17 +20,15 @@ import { hasNoUndefinedFields } from "./has-no-undefined-fields";
|
|||||||
|
|
||||||
export function mapDTOToCustomerInvoiceItemsProps(
|
export function mapDTOToCustomerInvoiceItemsProps(
|
||||||
dtoItems: Pick<CreateCustomerInvoiceRequestDTO, "items">["items"]
|
dtoItems: Pick<CreateCustomerInvoiceRequestDTO, "items">["items"]
|
||||||
): Result<CustomerInvoiceItem[], ValidationErrorCollection> {
|
): Result<IssuedInvoiceItem[], ValidationErrorCollection> {
|
||||||
const errors: ValidationErrorDetail[] = [];
|
const errors: ValidationErrorDetail[] = [];
|
||||||
const items: CustomerInvoiceItem[] = [];
|
const items: IssuedInvoiceItem[] = [];
|
||||||
|
|
||||||
dtoItems.forEach((item, index) => {
|
dtoItems.forEach((item, index) => {
|
||||||
const path = (field: string) => `items[${index}].${field}`;
|
const path = (field: string) => `items[${index}].${field}`;
|
||||||
|
|
||||||
const description = extractOrPushError(
|
const description = extractOrPushError(
|
||||||
maybeFromNullableVO(item.description, (value) =>
|
maybeFromNullableVO(item.description, (value) => ItemDescription.create(value)),
|
||||||
CustomerInvoiceItemDescription.create(value)
|
|
||||||
),
|
|
||||||
path("description"),
|
path("description"),
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
@ -54,7 +52,7 @@ export function mapDTOToCustomerInvoiceItemsProps(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (errors.length === 0) {
|
if (errors.length === 0) {
|
||||||
const itemProps: CustomerInvoiceItemProps = {
|
const itemProps: IssuedInvoiceItemProps = {
|
||||||
description: description,
|
description: description,
|
||||||
quantity: quantity,
|
quantity: quantity,
|
||||||
unitAmount: unitAmount,
|
unitAmount: unitAmount,
|
||||||
@ -66,7 +64,7 @@ export function mapDTOToCustomerInvoiceItemsProps(
|
|||||||
|
|
||||||
if (hasNoUndefinedFields(itemProps)) {
|
if (hasNoUndefinedFields(itemProps)) {
|
||||||
// Validar y crear el item de factura
|
// Validar y crear el item de factura
|
||||||
const itemOrError = CustomerInvoiceItem.create(itemProps);
|
const itemOrError = IssuedInvoiceItem.create(itemProps);
|
||||||
|
|
||||||
if (itemOrError.isSuccess) {
|
if (itemOrError.isSuccess) {
|
||||||
items.push(itemOrError.data);
|
items.push(itemOrError.data);
|
||||||
|
|||||||
@ -10,12 +10,7 @@ import {
|
|||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { CreateCustomerInvoiceRequestDTO } from "../../../common";
|
import type { CreateCustomerInvoiceRequestDTO } from "../../../common";
|
||||||
import {
|
import { type IProformaProps, InvoiceNumber, InvoiceSerie, InvoiceStatus } from "../../domain";
|
||||||
CustomerInvoiceNumber,
|
|
||||||
type CustomerInvoiceProps,
|
|
||||||
CustomerInvoiceSerie,
|
|
||||||
CustomerInvoiceStatus,
|
|
||||||
} from "../../domain";
|
|
||||||
|
|
||||||
import { mapDTOToCustomerInvoiceItemsProps } from "./map-dto-to-customer-invoice-items-props";
|
import { mapDTOToCustomerInvoiceItemsProps } from "./map-dto-to-customer-invoice-items-props";
|
||||||
|
|
||||||
@ -35,12 +30,12 @@ export function mapDTOToCustomerInvoiceProps(dto: CreateCustomerInvoiceRequestDT
|
|||||||
const invoiceId = extractOrPushError(UniqueID.create(dto.id), "id", errors);
|
const invoiceId = extractOrPushError(UniqueID.create(dto.id), "id", errors);
|
||||||
|
|
||||||
const invoiceNumber = extractOrPushError(
|
const invoiceNumber = extractOrPushError(
|
||||||
maybeFromNullableVO(dto.invoice_number, (value) => CustomerInvoiceNumber.create(value)),
|
maybeFromNullableVO(dto.invoice_number, (value) => InvoiceNumber.create(value)),
|
||||||
"invoice_number",
|
"invoice_number",
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
const invoiceSeries = extractOrPushError(
|
const invoiceSeries = extractOrPushError(
|
||||||
maybeFromNullableVO(dto.series, (value) => CustomerInvoiceSerie.create(value)),
|
maybeFromNullableVO(dto.series, (value) => InvoiceSerie.create(value)),
|
||||||
"invoice_series",
|
"invoice_series",
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
@ -71,12 +66,12 @@ export function mapDTOToCustomerInvoiceProps(dto: CreateCustomerInvoiceRequestDT
|
|||||||
return Result.fail(new ValidationErrorCollection("Invoice dto mapping failed", errors));
|
return Result.fail(new ValidationErrorCollection("Invoice dto mapping failed", errors));
|
||||||
}
|
}
|
||||||
|
|
||||||
const invoiceProps: CustomerInvoiceProps = {
|
const invoiceProps: IProformaProps = {
|
||||||
invoiceNumber: invoiceNumber!,
|
invoiceNumber: invoiceNumber!,
|
||||||
series: invoiceSeries!,
|
series: invoiceSeries!,
|
||||||
invoiceDate: invoiceDate!,
|
invoiceDate: invoiceDate!,
|
||||||
operationDate: operationDate!,
|
operationDate: operationDate!,
|
||||||
status: CustomerInvoiceStatus.createDraft(),
|
status: InvoiceStatus.createDraft(),
|
||||||
currencyCode: currencyCode!,
|
currencyCode: currencyCode!,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,2 @@
|
|||||||
//export * from "./services";
|
|
||||||
//export * from "./snapshot-builders";
|
|
||||||
|
|
||||||
export * from "./issued-invoices";
|
export * from "./issued-invoices";
|
||||||
export * from "./use-cases";
|
export * from "./proformas";
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
export * from "./issued-invoice-document.model";
|
|
||||||
export * from "./report-cache-key";
|
|
||||||
export * from "./snapshots";
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* Documento legal generado para una factura emitida.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface IIssuedInvoiceDocument {
|
|
||||||
payload: Buffer;
|
|
||||||
mimeType: string;
|
|
||||||
filename: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class IssuedInvoiceDocument implements IIssuedInvoiceDocument {
|
|
||||||
public readonly payload: Buffer;
|
|
||||||
public readonly mimeType: string;
|
|
||||||
public readonly filename: string;
|
|
||||||
|
|
||||||
constructor(params: {
|
|
||||||
payload: Buffer;
|
|
||||||
filename: string;
|
|
||||||
}) {
|
|
||||||
if (!params.payload || params.payload.length === 0) {
|
|
||||||
throw new Error("IssuedInvoiceDocument payload cannot be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!params.filename.toLowerCase().endsWith(".pdf")) {
|
|
||||||
throw new Error("IssuedInvoiceDocument filename must end with .pdf");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.payload = params.payload;
|
|
||||||
this.mimeType = "application/pdf";
|
|
||||||
this.filename = params.filename;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
import type { UniqueID } from "@repo/rdx-ddd";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clave determinista que identifica de forma única
|
|
||||||
* un documento legal generado.
|
|
||||||
*
|
|
||||||
* Encapsula la regla de idempotencia del caso de uso.
|
|
||||||
*/
|
|
||||||
export class ReportCacheKey {
|
|
||||||
private readonly value: string;
|
|
||||||
|
|
||||||
private constructor(value: string) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static forIssuedInvoice(params: {
|
|
||||||
companyId: UniqueID;
|
|
||||||
invoiceId: UniqueID;
|
|
||||||
language: string;
|
|
||||||
canonicalModelHash: string;
|
|
||||||
templateChecksum: string;
|
|
||||||
rendererVersion: string;
|
|
||||||
signingProviderVersion: string;
|
|
||||||
}): ReportCacheKey {
|
|
||||||
const {
|
|
||||||
companyId,
|
|
||||||
invoiceId,
|
|
||||||
language,
|
|
||||||
canonicalModelHash,
|
|
||||||
templateChecksum,
|
|
||||||
rendererVersion,
|
|
||||||
signingProviderVersion,
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
// Fail-fast: campos obligatorios
|
|
||||||
for (const [key, value] of Object.entries(params)) {
|
|
||||||
if (!value || String(value).trim() === "") {
|
|
||||||
throw new Error(`ReportCacheKey missing field: ${key}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ReportCacheKey(
|
|
||||||
[
|
|
||||||
"issued-invoice",
|
|
||||||
companyId,
|
|
||||||
invoiceId,
|
|
||||||
language,
|
|
||||||
canonicalModelHash,
|
|
||||||
templateChecksum,
|
|
||||||
rendererVersion,
|
|
||||||
signingProviderVersion,
|
|
||||||
].join("__")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toString(): string {
|
|
||||||
return this.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
export * from "./issued-invoice-full-snapshot";
|
|
||||||
export * from "./issued-invoice-item-full-snapshot";
|
|
||||||
export * from "./issued-invoice-recipient-full-snapshot";
|
|
||||||
export * from "./issued-invoice-verifactu-full-snapshot";
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export * from "./issued-invoice-list-item-snapshot";
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
export * from "./issued-invoice-report-item-snapshot";
|
|
||||||
export * from "./issued-invoice-report-snapshot";
|
|
||||||
export * from "./issued-invoice-report-tax-snapshot";
|
|
||||||
@ -1,3 +1,3 @@
|
|||||||
export * from "./finder.di";
|
export * from "./issued-invoice-finder.di";
|
||||||
export * from "./snapshot-builders.di";
|
export * from "./issued-invoice-snapshot-builders.di";
|
||||||
export * from "./use-cases.di";
|
export * from "./issued-invoice-use-cases.di";
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
export * from "./sign-document-command";
|
export * from "./issued-invoice-list.dto";
|
||||||
|
|||||||
@ -0,0 +1,43 @@
|
|||||||
|
import type { CurrencyCode, LanguageCode, Percentage, UniqueID, UtcDate } from "@repo/rdx-ddd";
|
||||||
|
import type { Maybe } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type {
|
||||||
|
InvoiceAmount,
|
||||||
|
InvoiceNumber,
|
||||||
|
InvoiceRecipient,
|
||||||
|
InvoiceSerie,
|
||||||
|
InvoiceStatus,
|
||||||
|
VerifactuRecord,
|
||||||
|
} from "../../../domain";
|
||||||
|
|
||||||
|
export type IssuedInvoiceListDTO = {
|
||||||
|
id: UniqueID;
|
||||||
|
companyId: UniqueID;
|
||||||
|
|
||||||
|
isProforma: boolean;
|
||||||
|
invoiceNumber: InvoiceNumber;
|
||||||
|
status: InvoiceStatus;
|
||||||
|
series: Maybe<InvoiceSerie>;
|
||||||
|
|
||||||
|
invoiceDate: UtcDate;
|
||||||
|
operationDate: Maybe<UtcDate>;
|
||||||
|
|
||||||
|
reference: Maybe<string>;
|
||||||
|
description: Maybe<string>;
|
||||||
|
|
||||||
|
customerId: UniqueID;
|
||||||
|
recipient: InvoiceRecipient;
|
||||||
|
|
||||||
|
languageCode: LanguageCode;
|
||||||
|
currencyCode: CurrencyCode;
|
||||||
|
|
||||||
|
discountPercentage: Percentage;
|
||||||
|
|
||||||
|
subtotalAmount: InvoiceAmount;
|
||||||
|
discountAmount: InvoiceAmount;
|
||||||
|
taxableAmount: InvoiceAmount;
|
||||||
|
taxesAmount: InvoiceAmount;
|
||||||
|
totalAmount: InvoiceAmount;
|
||||||
|
|
||||||
|
verifactu: Maybe<VerifactuRecord>;
|
||||||
|
};
|
||||||
@ -1,15 +0,0 @@
|
|||||||
/**
|
|
||||||
* DTO de Application para solicitar la firma de un documento.
|
|
||||||
*
|
|
||||||
* Se utiliza exclusivamente para intercambiar datos con la capa
|
|
||||||
* de infraestructura (DocumentSigningService).
|
|
||||||
*
|
|
||||||
* No contiene lógica ni validaciones de negocio.
|
|
||||||
*/
|
|
||||||
export interface SignDocumentCommand {
|
|
||||||
/** PDF sin firmar */
|
|
||||||
readonly file: Buffer;
|
|
||||||
|
|
||||||
/** Identificador estable de la empresa */
|
|
||||||
readonly companyId: string;
|
|
||||||
}
|
|
||||||
@ -1,5 +1,8 @@
|
|||||||
export * from "./application-models";
|
export * from "./application-models";
|
||||||
export * from "./di";
|
export * from "./di";
|
||||||
|
export * from "./dtos";
|
||||||
|
export * from "./mappers";
|
||||||
|
export * from "./repositories";
|
||||||
export * from "./services";
|
export * from "./services";
|
||||||
export * from "./snapshot-builders";
|
export * from "./snapshot-builders";
|
||||||
export * from "./use-cases";
|
export * from "./use-cases";
|
||||||
|
|||||||
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./issued-invoice-domain-mapper.interface";
|
||||||
|
export * from "./issued-invoice-list-mapper.interface";
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import type { MapperParamsType } from "@erp/core/api";
|
||||||
|
import type { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { IssuedInvoice } from "../../../domain";
|
||||||
|
|
||||||
|
export interface IIssuedInvoiceDomainMapper {
|
||||||
|
mapToPersistence(invoice: IssuedInvoice, params?: MapperParamsType): Result<unknown, Error>;
|
||||||
|
mapToDomain(raw: unknown, params?: MapperParamsType): Result<IssuedInvoice, Error>;
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import type { MapperParamsType } from "@erp/core/api";
|
||||||
|
import type { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { IssuedInvoiceListDTO } from "../dtos";
|
||||||
|
|
||||||
|
export interface IIssuedInvoiceListMapper {
|
||||||
|
mapToDTO(raw: unknown, params?: MapperParamsType): Result<IssuedInvoiceListDTO, Error>;
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./issued-invoice-repository.interface";
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
import type { Criteria } from "@repo/rdx-criteria/server";
|
||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import type { Collection, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { IssuedInvoice } from "../../../domain";
|
||||||
|
import type { IssuedInvoiceListDTO } from "../dtos";
|
||||||
|
|
||||||
|
export interface IIssuedInvoiceRepository {
|
||||||
|
create(invoice: IssuedInvoice, transaction?: unknown): Promise<Result<void, Error>>;
|
||||||
|
|
||||||
|
getByIdInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
id: UniqueID,
|
||||||
|
transaction: unknown
|
||||||
|
): Promise<Result<IssuedInvoice, Error>>;
|
||||||
|
|
||||||
|
findByCriteriaInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
criteria: Criteria,
|
||||||
|
transaction: unknown
|
||||||
|
): Promise<Result<Collection<IssuedInvoiceListDTO>, Error>>;
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
export * from "./issued-invoice-document-generator.interface";
|
export * from "./issued-invoice-document-generator.interface";
|
||||||
export * from "./issued-invoice-document-metadata-factory";
|
export * from "./issued-invoice-document-metadata-factory";
|
||||||
export * from "./issued-invoice-document-properties-factory";
|
export * from "./issued-invoice-document-properties-factory";
|
||||||
export * from "./issued-invoice-document-renderer.interface";
|
|
||||||
export * from "./issued-invoice-finder";
|
export * from "./issued-invoice-finder";
|
||||||
|
export * from "./proforma-to-issued-invoice-materializer";
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
import type { Result } from "@repo/rdx-utils";
|
|
||||||
|
|
||||||
import type { IIssuedInvoiceDocument, IssuedInvoiceReportSnapshot } from "../application-models";
|
|
||||||
|
|
||||||
export interface IIssuedInvoiceDocumentRenderer {
|
|
||||||
render(input: {
|
|
||||||
snapshot: IssuedInvoiceReportSnapshot;
|
|
||||||
documentId: string;
|
|
||||||
}): Promise<Result<IIssuedInvoiceDocument, Error>>;
|
|
||||||
}
|
|
||||||
@ -4,14 +4,14 @@ import type { UniqueID } from "@repo/rdx-ddd";
|
|||||||
import type { Collection, Result } from "@repo/rdx-utils";
|
import type { Collection, Result } from "@repo/rdx-utils";
|
||||||
import type { Transaction } from "sequelize";
|
import type { Transaction } from "sequelize";
|
||||||
|
|
||||||
import type { CustomerInvoice, ICustomerInvoiceRepository } from "../../../domain";
|
import type { ICustomerInvoiceRepository, Proforma } from "../../../domain";
|
||||||
|
|
||||||
export interface IIssuedInvoiceFinder {
|
export interface IIssuedInvoiceFinder {
|
||||||
findIssuedInvoiceById(
|
findIssuedInvoiceById(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
invoiceId: UniqueID,
|
invoiceId: UniqueID,
|
||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
): Promise<Result<CustomerInvoice, Error>>;
|
): Promise<Result<Proforma, Error>>;
|
||||||
|
|
||||||
issuedInvoiceExists(
|
issuedInvoiceExists(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
@ -33,18 +33,10 @@ export class IssuedInvoiceFinder implements IIssuedInvoiceFinder {
|
|||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
invoiceId: UniqueID,
|
invoiceId: UniqueID,
|
||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
): Promise<Result<CustomerInvoice, Error>> {
|
): Promise<Result<Proforma, Error>> {
|
||||||
return this.repository.getIssuedInvoiceByIdInCompany(companyId, invoiceId, transaction, {});
|
return this.repository.getIssuedInvoiceByIdInCompany(companyId, invoiceId, transaction, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
async findProformaById(
|
|
||||||
companyId: UniqueID,
|
|
||||||
proformaId: UniqueID,
|
|
||||||
transaction?: Transaction
|
|
||||||
): Promise<Result<CustomerInvoice, Error>> {
|
|
||||||
return this.repository.getProformaByIdInCompany(companyId, proformaId, transaction, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
async issuedInvoiceExists(
|
async issuedInvoiceExists(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
invoiceId: UniqueID,
|
invoiceId: UniqueID,
|
||||||
@ -55,16 +47,6 @@ export class IssuedInvoiceFinder implements IIssuedInvoiceFinder {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async proformaExists(
|
|
||||||
companyId: UniqueID,
|
|
||||||
proformaId: UniqueID,
|
|
||||||
transaction?: Transaction
|
|
||||||
): Promise<Result<boolean, Error>> {
|
|
||||||
return this.repository.existsByIdInCompany(companyId, proformaId, transaction, {
|
|
||||||
is_proforma: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async findIssuedInvoicesByCriteria(
|
async findIssuedInvoicesByCriteria(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
criteria: Criteria,
|
criteria: Criteria,
|
||||||
@ -77,12 +59,4 @@ export class IssuedInvoiceFinder implements IIssuedInvoiceFinder {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findProformasByCriteria(
|
|
||||||
companyId: UniqueID,
|
|
||||||
criteria: Criteria,
|
|
||||||
transaction?: Transaction
|
|
||||||
): Promise<Result<Collection<CustomerInvoiceListDTO>, Error>> {
|
|
||||||
return this.repository.findProformasByCriteriaInCompany(companyId, criteria, transaction, {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
import type { UniqueID } from "@repo/rdx-ddd";
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
import type { Maybe, Result } from "@repo/rdx-utils";
|
import type { Maybe, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type {
|
import type { ICustomerInvoiceNumberGenerator, InvoiceNumber, InvoiceSerie } from "../../../domain";
|
||||||
CustomerInvoiceNumber,
|
|
||||||
CustomerInvoiceSerie,
|
|
||||||
ICustomerInvoiceNumberGenerator,
|
|
||||||
} from "../../../domain";
|
|
||||||
|
|
||||||
export interface IIssuedInvoiceNumberService {
|
export interface IIssuedInvoiceNumberService {
|
||||||
/**
|
/**
|
||||||
@ -13,9 +9,9 @@ export interface IIssuedInvoiceNumberService {
|
|||||||
*/
|
*/
|
||||||
nextIssuedInvoiceNumber(
|
nextIssuedInvoiceNumber(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
series: Maybe<CustomerInvoiceSerie>,
|
series: Maybe<InvoiceSerie>,
|
||||||
transaction: unknown
|
transaction: unknown
|
||||||
): Promise<Result<CustomerInvoiceNumber, Error>>;
|
): Promise<Result<InvoiceNumber, Error>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IssuedInvoiceNumberService implements IIssuedInvoiceNumberService {
|
export class IssuedInvoiceNumberService implements IIssuedInvoiceNumberService {
|
||||||
@ -23,9 +19,9 @@ export class IssuedInvoiceNumberService implements IIssuedInvoiceNumberService {
|
|||||||
|
|
||||||
async nextIssuedInvoiceNumber(
|
async nextIssuedInvoiceNumber(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
series: Maybe<CustomerInvoiceSerie>,
|
series: Maybe<InvoiceSerie>,
|
||||||
transaction: unknown
|
transaction: unknown
|
||||||
): Promise<Result<CustomerInvoiceNumber, Error>> {
|
): Promise<Result<InvoiceNumber, Error>> {
|
||||||
return this.numberGenerator.nextForCompany(companyId, series, transaction);
|
return this.numberGenerator.nextForCompany(companyId, series, transaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,56 @@
|
|||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { Collection, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { IssuedInvoiceProps, Proforma } from "../../../domain";
|
||||||
|
|
||||||
|
export interface IProformaToIssuedInvoiceMaterializer {
|
||||||
|
materialize(proforma: Proforma, issuedInvoiceId: UniqueID): Result<IssuedInvoiceProps, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProformaToIssuedInvoiceMaterializer implements IProformaToIssuedInvoiceMaterializer {
|
||||||
|
public materialize(
|
||||||
|
proforma: Proforma,
|
||||||
|
issuedInvoiceId: UniqueID
|
||||||
|
): Result<IssuedInvoiceProps, Error> {
|
||||||
|
const amounts = proforma.calculateAllAmounts();
|
||||||
|
const taxGroups = proforma.getTaxes();
|
||||||
|
|
||||||
|
const issuedItems = proforma.items.map((item) => ({
|
||||||
|
description: item.description,
|
||||||
|
quantity: item.quantity,
|
||||||
|
unitPrice: item.unitAmount,
|
||||||
|
taxableAmount: item.getTaxableAmount(),
|
||||||
|
taxesAmount: item.getTaxesAmount(),
|
||||||
|
totalAmount: item.getTotalAmount(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const issuedTaxes = taxGroups.map((group) => ({
|
||||||
|
ivaCode: group.iva.code,
|
||||||
|
ivaPercentage: group.iva.percentage,
|
||||||
|
ivaAmount: group.calculateAmounts().ivaAmount,
|
||||||
|
recCode: group.rec?.code,
|
||||||
|
recPercentage: group.rec?.percentage,
|
||||||
|
recAmount: group.calculateAmounts().recAmount,
|
||||||
|
retentionCode: group.retention?.code,
|
||||||
|
retentionPercentage: group.retention?.percentage,
|
||||||
|
retentionAmount: group.calculateAmounts().retentionAmount,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return Result.ok({
|
||||||
|
companyId: proforma.companyId,
|
||||||
|
invoiceNumber: proforma.invoiceNumber,
|
||||||
|
invoiceDate: proforma.invoiceDate,
|
||||||
|
customerId: proforma.customerId,
|
||||||
|
languageCode: proforma.languageCode,
|
||||||
|
currencyCode: proforma.currencyCode,
|
||||||
|
paymentMethod: proforma.paymentMethod,
|
||||||
|
discountPercentage: proforma.discountPercentage,
|
||||||
|
|
||||||
|
items: new Collection(issuedItems),
|
||||||
|
taxes: new Collection(issuedTaxes),
|
||||||
|
subtotalAmount: amounts.subtotalAmount,
|
||||||
|
taxableAmount: amounts.taxableAmount,
|
||||||
|
totalAmount: amounts.totalAmount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,8 @@
|
|||||||
|
export * from "./issued-invoice-full-snapshot.interface";
|
||||||
export * from "./issued-invoice-full-snapshot-builder";
|
export * from "./issued-invoice-full-snapshot-builder";
|
||||||
|
export * from "./issued-invoice-item-full-snapshot.interface";
|
||||||
export * from "./issued-invoice-items-full-snapshot-builder";
|
export * from "./issued-invoice-items-full-snapshot-builder";
|
||||||
|
export * from "./issued-invoice-recipient-full-snapshot.interfce";
|
||||||
export * from "./issued-invoice-recipient-full-snapshot-builder";
|
export * from "./issued-invoice-recipient-full-snapshot-builder";
|
||||||
|
export * from "./issued-invoice-verifactu-full-snapshot.interface";
|
||||||
export * from "./issued-invoice-verifactu-full-snapshot-builder";
|
export * from "./issued-invoice-verifactu-full-snapshot-builder";
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
import type { ISnapshotBuilder } from "@erp/core/api";
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
import { toEmptyString } from "@repo/rdx-ddd";
|
import { toEmptyString } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
import { type CustomerInvoice, InvoiceAmount } from "../../../../domain";
|
import { InvoiceAmount, type Proforma } from "../../../../domain";
|
||||||
import type { IssuedInvoiceFullSnapshot } from "../../application-models";
|
|
||||||
|
|
||||||
|
import type { IssuedInvoiceFullSnapshot } from "./issued-invoice-full-snapshot.interface";
|
||||||
import type { IIssuedInvoiceItemsFullSnapshotBuilder } from "./issued-invoice-items-full-snapshot-builder";
|
import type { IIssuedInvoiceItemsFullSnapshotBuilder } from "./issued-invoice-items-full-snapshot-builder";
|
||||||
import type { IIssuedInvoiceRecipientFullSnapshotBuilder } from "./issued-invoice-recipient-full-snapshot-builder";
|
import type { IIssuedInvoiceRecipientFullSnapshotBuilder } from "./issued-invoice-recipient-full-snapshot-builder";
|
||||||
import type { IIssuedInvoiceVerifactuFullSnapshotBuilder } from "./issued-invoice-verifactu-full-snapshot-builder";
|
import type { IIssuedInvoiceVerifactuFullSnapshotBuilder } from "./issued-invoice-verifactu-full-snapshot-builder";
|
||||||
|
|
||||||
export interface IIssuedInvoiceFullSnapshotBuilder
|
export interface IIssuedInvoiceFullSnapshotBuilder
|
||||||
extends ISnapshotBuilder<CustomerInvoice, IssuedInvoiceFullSnapshot> {}
|
extends ISnapshotBuilder<Proforma, IssuedInvoiceFullSnapshot> {}
|
||||||
|
|
||||||
export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnapshotBuilder {
|
export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnapshotBuilder {
|
||||||
constructor(
|
constructor(
|
||||||
@ -18,7 +18,7 @@ export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnaps
|
|||||||
private readonly verifactuBuilder: IIssuedInvoiceVerifactuFullSnapshotBuilder
|
private readonly verifactuBuilder: IIssuedInvoiceVerifactuFullSnapshotBuilder
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
toOutput(invoice: CustomerInvoice): IssuedInvoiceFullSnapshot {
|
toOutput(invoice: Proforma): IssuedInvoiceFullSnapshot {
|
||||||
const items = this.itemsBuilder.toOutput(invoice.items);
|
const items = this.itemsBuilder.toOutput(invoice.items);
|
||||||
const recipient = this.recipientBuilder.toOutput(invoice);
|
const recipient = this.recipientBuilder.toOutput(invoice);
|
||||||
const verifactu = this.verifactuBuilder.toOutput(invoice);
|
const verifactu = this.verifactuBuilder.toOutput(invoice);
|
||||||
@ -129,7 +129,6 @@ export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnaps
|
|||||||
|
|
||||||
metadata: {
|
metadata: {
|
||||||
entity: "issued-invoices",
|
entity: "issued-invoices",
|
||||||
link: "",
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { IssuedInvoiceItemFullSnapshot } from "./issued-invoice-item-full-snapshot";
|
import type { IssuedInvoiceItemFullSnapshot } from "./issued-invoice-item-full-snapshot.interface";
|
||||||
import type { IssuedInvoiceRecipientFullSnapshot } from "./issued-invoice-recipient-full-snapshot";
|
import type { IssuedInvoiceRecipientFullSnapshot } from "./issued-invoice-recipient-full-snapshot.interfce";
|
||||||
import type { IssuedInvoiceVerifactuFullSnapshot } from "./issued-invoice-verifactu-full-snapshot";
|
import type { IssuedInvoiceVerifactuFullSnapshot } from "./issued-invoice-verifactu-full-snapshot.interface";
|
||||||
|
|
||||||
export interface IssuedInvoiceFullSnapshot {
|
export interface IssuedInvoiceFullSnapshot {
|
||||||
id: string;
|
id: string;
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import type { ISnapshotBuilder } from "@erp/core/api";
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
import { toEmptyString } from "@repo/rdx-ddd";
|
import { toEmptyString } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
import type { CustomerInvoiceItem, CustomerInvoiceItems } from "../../../../domain";
|
import type { CustomerInvoiceItems, IssuedInvoiceItem } from "../../../../domain";
|
||||||
import type { IssuedInvoiceItemFullSnapshot } from "../../application-models";
|
import type { IssuedInvoiceItemFullSnapshot } from "../../application-models";
|
||||||
|
|
||||||
export interface IIssuedInvoiceItemsFullSnapshotBuilder
|
export interface IIssuedInvoiceItemsFullSnapshotBuilder
|
||||||
@ -10,7 +10,7 @@ export interface IIssuedInvoiceItemsFullSnapshotBuilder
|
|||||||
export class IssuedInvoiceItemsFullSnapshotBuilder
|
export class IssuedInvoiceItemsFullSnapshotBuilder
|
||||||
implements IIssuedInvoiceItemsFullSnapshotBuilder
|
implements IIssuedInvoiceItemsFullSnapshotBuilder
|
||||||
{
|
{
|
||||||
private mapItem(invoiceItem: CustomerInvoiceItem, index: number): IssuedInvoiceItemFullSnapshot {
|
private mapItem(invoiceItem: IssuedInvoiceItem, index: number): IssuedInvoiceItemFullSnapshot {
|
||||||
const allAmounts = invoiceItem.calculateAllAmounts();
|
const allAmounts = invoiceItem.calculateAllAmounts();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import type { ISnapshotBuilder } from "@erp/core/api";
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
import { DomainValidationError, toEmptyString } from "@repo/rdx-ddd";
|
import { DomainValidationError, toEmptyString } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
import type { CustomerInvoice, InvoiceRecipient } from "../../../../domain";
|
import type { InvoiceRecipient, Proforma } from "../../../../domain";
|
||||||
import type { IssuedInvoiceRecipientFullSnapshot } from "../../application-models";
|
import type { IssuedInvoiceRecipientFullSnapshot } from "../../application-models";
|
||||||
|
|
||||||
export interface IIssuedInvoiceRecipientFullSnapshotBuilder
|
export interface IIssuedInvoiceRecipientFullSnapshotBuilder
|
||||||
extends ISnapshotBuilder<CustomerInvoice, IssuedInvoiceRecipientFullSnapshot> {}
|
extends ISnapshotBuilder<Proforma, IssuedInvoiceRecipientFullSnapshot> {}
|
||||||
|
|
||||||
export class IssuedInvoiceRecipientFullSnapshotBuilder
|
export class IssuedInvoiceRecipientFullSnapshotBuilder
|
||||||
implements IIssuedInvoiceRecipientFullSnapshotBuilder
|
implements IIssuedInvoiceRecipientFullSnapshotBuilder
|
||||||
{
|
{
|
||||||
toOutput(invoice: CustomerInvoice): IssuedInvoiceRecipientFullSnapshot {
|
toOutput(invoice: Proforma): IssuedInvoiceRecipientFullSnapshot {
|
||||||
if (!invoice.recipient) {
|
if (!invoice.recipient) {
|
||||||
throw DomainValidationError.requiredValue("recipient", {
|
throw DomainValidationError.requiredValue("recipient", {
|
||||||
cause: invoice,
|
cause: invoice,
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import type { ISnapshotBuilder } from "@erp/core/api";
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
import { DomainValidationError } from "@repo/rdx-ddd";
|
import { DomainValidationError } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
import type { CustomerInvoice } from "../../../../domain";
|
import type { Proforma } from "../../../../domain";
|
||||||
import type { IssuedInvoiceVerifactuFullSnapshot } from "../../application-models";
|
import type { IssuedInvoiceVerifactuFullSnapshot } from "../../application-models";
|
||||||
|
|
||||||
export interface IIssuedInvoiceVerifactuFullSnapshotBuilder
|
export interface IIssuedInvoiceVerifactuFullSnapshotBuilder
|
||||||
extends ISnapshotBuilder<CustomerInvoice, IssuedInvoiceVerifactuFullSnapshot> {}
|
extends ISnapshotBuilder<Proforma, IssuedInvoiceVerifactuFullSnapshot> {}
|
||||||
|
|
||||||
export class IssuedInvoiceVerifactuFullSnapshotBuilder
|
export class IssuedInvoiceVerifactuFullSnapshotBuilder
|
||||||
implements IIssuedInvoiceVerifactuFullSnapshotBuilder
|
implements IIssuedInvoiceVerifactuFullSnapshotBuilder
|
||||||
{
|
{
|
||||||
toOutput(invoice: CustomerInvoice): IssuedInvoiceVerifactuFullSnapshot {
|
toOutput(invoice: Proforma): IssuedInvoiceVerifactuFullSnapshot {
|
||||||
if (!invoice.verifactu) {
|
if (!invoice.verifactu) {
|
||||||
throw DomainValidationError.requiredValue("verifactu", {
|
throw DomainValidationError.requiredValue("verifactu", {
|
||||||
cause: invoice,
|
cause: invoice,
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
|
export * from "./issued-invoice-list-item-snapshot.interface";
|
||||||
export * from "./issued-invoice-list-item-snapshot-builder";
|
export * from "./issued-invoice-list-item-snapshot-builder";
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import type { ISnapshotBuilder } from "@erp/core/api";
|
|||||||
import { toEmptyString } from "@repo/rdx-ddd";
|
import { toEmptyString } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
import type { CustomerInvoiceListDTO } from "../../../../infrastructure";
|
import type { CustomerInvoiceListDTO } from "../../../../infrastructure";
|
||||||
import type { IssuedInvoiceListItemSnapshot } from "../../application-models/snapshots/list";
|
import type { IssuedInvoiceListItemSnapshot } from "../../application-models";
|
||||||
|
|
||||||
export interface IIssuedInvoiceListItemSnapshotBuilder
|
export interface IIssuedInvoiceListItemSnapshotBuilder
|
||||||
extends ISnapshotBuilder<CustomerInvoiceListDTO, IssuedInvoiceListItemSnapshot> {}
|
extends ISnapshotBuilder<CustomerInvoiceListDTO, IssuedInvoiceListItemSnapshot> {}
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
export * from "./issued-invoice-items-report-snapshot-builder";
|
export * from "./issued-invoice-items-report-snapshot-builder";
|
||||||
|
export * from "./issued-invoice-report-item-snapshot.interface";
|
||||||
|
export * from "./issued-invoice-report-snapshot.interface";
|
||||||
export * from "./issued-invoice-report-snapshot-builder";
|
export * from "./issued-invoice-report-snapshot-builder";
|
||||||
|
export * from "./issued-invoice-report-tax-snapshot.interface";
|
||||||
export * from "./issued-invoice-tax-report-snapshot-builder";
|
export * from "./issued-invoice-tax-report-snapshot-builder";
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type { IssuedInvoiceReportItemSnapshot } from "./issued-invoice-report-item-snapshot";
|
import type { IssuedInvoiceReportItemSnapshot } from "./issued-invoice-report-item-snapshot.interface";
|
||||||
import type { IssuedInvoiceReportTaxSnapshot } from "./issued-invoice-report-tax-snapshot";
|
import type { IssuedInvoiceReportTaxSnapshot } from "./issued-invoice-report-tax-snapshot.interface";
|
||||||
|
|
||||||
export interface IssuedInvoiceReportSnapshot {
|
export interface IssuedInvoiceReportSnapshot {
|
||||||
id: string;
|
id: string;
|
||||||
@ -1,3 +1,3 @@
|
|||||||
export * from "./get-issued-invoice-by-id.use-case";
|
export * from "./get-issued-invoice-by-id.use-case";
|
||||||
export * from "./list-issued-invoices.use-case";
|
export * from "./list-issued-invoices.use-case";
|
||||||
export * from "./report-issued-invoices";
|
export * from "./report-issued-invoice.use-case";
|
||||||
|
|||||||
@ -15,7 +15,7 @@ type ListIssuedInvoicesUseCaseInput = {
|
|||||||
export class ListIssuedInvoicesUseCase {
|
export class ListIssuedInvoicesUseCase {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly finder: IIssuedInvoiceFinder,
|
private readonly finder: IIssuedInvoiceFinder,
|
||||||
private readonly itemSnapshotBuilder: IIssuedInvoiceListItemSnapshotBuilder,
|
private readonly listItemSnapshotBuilder: IIssuedInvoiceListItemSnapshotBuilder,
|
||||||
private readonly transactionManager: ITransactionManager
|
private readonly transactionManager: ITransactionManager
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -37,9 +37,8 @@ export class ListIssuedInvoicesUseCase {
|
|||||||
const invoices = result.data;
|
const invoices = result.data;
|
||||||
const totalInvoices = invoices.total();
|
const totalInvoices = invoices.total();
|
||||||
|
|
||||||
const items = invoices.map((item) => this.itemSnapshotBuilder.toOutput(item));
|
const items = invoices.map((item) => this.listItemSnapshotBuilder.toOutput(item));
|
||||||
|
|
||||||
// ?????
|
|
||||||
const snapshot = {
|
const snapshot = {
|
||||||
page: criteria.pageNumber,
|
page: criteria.pageNumber,
|
||||||
per_page: criteria.pageSize,
|
per_page: criteria.pageSize,
|
||||||
|
|||||||
@ -2,9 +2,9 @@ import type { ITransactionManager, RendererFormat } 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 type { IIssuedInvoiceFinder, IssuedInvoiceDocumentGeneratorService } from "../../services";
|
import type { IIssuedInvoiceFinder, IssuedInvoiceDocumentGeneratorService } from "../services";
|
||||||
import type { IIssuedInvoiceFullSnapshotBuilder } from "../../snapshot-builders";
|
import type { IIssuedInvoiceFullSnapshotBuilder } from "../snapshot-builders";
|
||||||
import type { IIssuedInvoiceReportSnapshotBuilder } from "../../snapshot-builders/report";
|
import type { IIssuedInvoiceReportSnapshotBuilder } from "../snapshot-builders/report";
|
||||||
|
|
||||||
type ReportIssuedInvoiceUseCaseInput = {
|
type ReportIssuedInvoiceUseCaseInput = {
|
||||||
companyId: UniqueID;
|
companyId: UniqueID;
|
||||||
@ -23,7 +23,7 @@ export class ReportIssuedInvoiceUseCase {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async execute(params: ReportIssuedInvoiceUseCaseInput) {
|
public async execute(params: ReportIssuedInvoiceUseCaseInput) {
|
||||||
const { invoice_id, companyId, companySlug, format } = params;
|
const { invoice_id, companyId } = params;
|
||||||
|
|
||||||
const idOrError = UniqueID.create(invoice_id);
|
const idOrError = UniqueID.create(invoice_id);
|
||||||
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
//export * from "./issued-invoice-document";
|
|
||||||
export * from "./report-issued-invoice.use-case";
|
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
export * from "./proforma-creator.di";
|
||||||
|
export * from "./proforma-finder.di";
|
||||||
|
export * from "./proforma-snapshot-builders.di";
|
||||||
|
export * from "./proforma-use-cases.di";
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import type { ICustomerInvoiceRepository } from "../../../domain/repositories";
|
||||||
|
import { ProformaFactory } from "../factories";
|
||||||
|
import { type IProformaCreator, type IProformaNumberGenerator, ProformaCreator } from "../services";
|
||||||
|
|
||||||
|
export function buildProformaCreator(
|
||||||
|
numberService: IProformaNumberGenerator,
|
||||||
|
repository: ICustomerInvoiceRepository
|
||||||
|
): IProformaCreator {
|
||||||
|
return new ProformaCreator({
|
||||||
|
numberService,
|
||||||
|
factory: new ProformaFactory(),
|
||||||
|
repository,
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import type { ICustomerInvoiceRepository } from "../../../domain";
|
||||||
|
import { type IProformaFinder, ProformaFinder } from "../services";
|
||||||
|
|
||||||
|
export function buildProformaFinder(repository: ICustomerInvoiceRepository): IProformaFinder {
|
||||||
|
return new ProformaFinder(repository);
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
// application/issued-invoices/di/snapshot-builders.di.ts
|
||||||
|
|
||||||
|
import {
|
||||||
|
ProformaFullSnapshotBuilder,
|
||||||
|
ProformaItemReportSnapshotBuilder,
|
||||||
|
ProformaItemsFullSnapshotBuilder,
|
||||||
|
ProformaListItemSnapshotBuilder,
|
||||||
|
ProformaRecipientFullSnapshotBuilder,
|
||||||
|
ProformaReportSnapshotBuilder,
|
||||||
|
ProformaTaxReportSnapshotBuilder,
|
||||||
|
} from "../snapshot-builders";
|
||||||
|
|
||||||
|
export function buildProformaSnapshotBuilders() {
|
||||||
|
const itemsBuilder = new ProformaItemsFullSnapshotBuilder();
|
||||||
|
|
||||||
|
const recipientBuilder = new ProformaRecipientFullSnapshotBuilder();
|
||||||
|
|
||||||
|
const fullSnapshotBuilder = new ProformaFullSnapshotBuilder(itemsBuilder, recipientBuilder);
|
||||||
|
|
||||||
|
const listSnapshotBuilder = new ProformaListItemSnapshotBuilder();
|
||||||
|
|
||||||
|
const itemsReportBuilder = new ProformaItemReportSnapshotBuilder();
|
||||||
|
const taxesReportBuilder = new ProformaTaxReportSnapshotBuilder();
|
||||||
|
const reportSnapshotBuilder = new ProformaReportSnapshotBuilder(
|
||||||
|
itemsReportBuilder,
|
||||||
|
taxesReportBuilder
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
full: fullSnapshotBuilder,
|
||||||
|
list: listSnapshotBuilder,
|
||||||
|
report: reportSnapshotBuilder,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
import type { ITransactionManager } from "@erp/core/api";
|
||||||
|
|
||||||
|
import type { IProformaFinder, ProformaDocumentGeneratorService } from "../services";
|
||||||
|
import type {
|
||||||
|
IProformaListItemSnapshotBuilder,
|
||||||
|
IProformaReportSnapshotBuilder,
|
||||||
|
} from "../snapshot-builders";
|
||||||
|
import type { IProformaFullSnapshotBuilder } from "../snapshot-builders/full";
|
||||||
|
import { GetProformaByIdUseCase, ListProformasUseCase, ReportProformaUseCase } from "../use-cases";
|
||||||
|
|
||||||
|
export function buildGetProformaByIdUseCase(deps: {
|
||||||
|
finder: IProformaFinder;
|
||||||
|
fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||||
|
transactionManager: ITransactionManager;
|
||||||
|
}) {
|
||||||
|
return new GetProformaByIdUseCase(deps.finder, deps.fullSnapshotBuilder, deps.transactionManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildListProformasUseCase(deps: {
|
||||||
|
finder: IProformaFinder;
|
||||||
|
itemSnapshotBuilder: IProformaListItemSnapshotBuilder;
|
||||||
|
transactionManager: ITransactionManager;
|
||||||
|
}) {
|
||||||
|
return new ListProformasUseCase(deps.finder, deps.itemSnapshotBuilder, deps.transactionManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildReportProformaUseCase(deps: {
|
||||||
|
finder: IProformaFinder;
|
||||||
|
fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||||
|
reportSnapshotBuilder: IProformaReportSnapshotBuilder;
|
||||||
|
documentService: ProformaDocumentGeneratorService;
|
||||||
|
transactionManager: ITransactionManager;
|
||||||
|
}) {
|
||||||
|
return new ReportProformaUseCase(
|
||||||
|
deps.finder,
|
||||||
|
deps.fullSnapshotBuilder,
|
||||||
|
deps.reportSnapshotBuilder,
|
||||||
|
deps.documentService,
|
||||||
|
deps.transactionManager
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*export function buildCreateProformaUseCase(deps: {
|
||||||
|
creator: IProformaCreator;
|
||||||
|
fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||||
|
transactionManager: ITransactionManager;
|
||||||
|
}) {
|
||||||
|
return new CreateProformaUseCase({
|
||||||
|
mapper: new CreateProformaPropsMapper(),
|
||||||
|
creator: deps.creator,
|
||||||
|
fullSnapshotBuilder: deps.fullSnapshotBuilder,
|
||||||
|
transactionManager: deps.transactionManager,
|
||||||
|
});
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*export function buildUpdateProformaUseCase(deps: {
|
||||||
|
finder: IProformaFinder;
|
||||||
|
fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||||
|
}) {
|
||||||
|
return new UpdateProformaUseCase(deps.finder, deps.fullSnapshotBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildDeleteProformaUseCase(deps: { finder: IProformaFinder }) {
|
||||||
|
return new DeleteProformaUseCase(deps.finder);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildIssueProformaUseCase(deps: { finder: IProformaFinder }) {
|
||||||
|
return new IssueProformaUseCase(deps.finder);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildChangeStatusProformaUseCase(deps: {
|
||||||
|
finder: IProformaFinder;
|
||||||
|
transactionManager: ITransactionManager;
|
||||||
|
}) {
|
||||||
|
return new ChangeStatusProformaUseCase(deps.finder, deps.transactionManager);
|
||||||
|
}*/
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./proforma-list.dto";
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
import type { CurrencyCode, LanguageCode, Percentage, UniqueID, UtcDate } from "@repo/rdx-ddd";
|
||||||
|
import type { Maybe } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type {
|
||||||
|
InvoiceAmount,
|
||||||
|
InvoiceNumber,
|
||||||
|
InvoiceRecipient,
|
||||||
|
InvoiceSerie,
|
||||||
|
InvoiceStatus,
|
||||||
|
} from "../../../domain";
|
||||||
|
|
||||||
|
export type ProformaListDTO = {
|
||||||
|
id: UniqueID;
|
||||||
|
companyId: UniqueID;
|
||||||
|
|
||||||
|
isProforma: boolean;
|
||||||
|
invoiceNumber: InvoiceNumber;
|
||||||
|
status: InvoiceStatus;
|
||||||
|
series: Maybe<InvoiceSerie>;
|
||||||
|
|
||||||
|
invoiceDate: UtcDate;
|
||||||
|
operationDate: Maybe<UtcDate>;
|
||||||
|
|
||||||
|
reference: Maybe<string>;
|
||||||
|
description: Maybe<string>;
|
||||||
|
|
||||||
|
customerId: UniqueID;
|
||||||
|
recipient: InvoiceRecipient;
|
||||||
|
|
||||||
|
languageCode: LanguageCode;
|
||||||
|
currencyCode: CurrencyCode;
|
||||||
|
|
||||||
|
discountPercentage: Percentage;
|
||||||
|
|
||||||
|
subtotalAmount: InvoiceAmount;
|
||||||
|
discountAmount: InvoiceAmount;
|
||||||
|
taxableAmount: InvoiceAmount;
|
||||||
|
taxesAmount: InvoiceAmount;
|
||||||
|
totalAmount: InvoiceAmount;
|
||||||
|
};
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./proforma-factory";
|
||||||
|
export * from "./proforma-factory.interface";
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import type { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { IProformaProps, Proforma } from "../../../domain";
|
||||||
|
|
||||||
|
export interface IProformaFactory {
|
||||||
|
/**
|
||||||
|
* Crea una proforma válida para una empresa a partir de props ya validadas.
|
||||||
|
*
|
||||||
|
* No persiste el agregado.
|
||||||
|
*/
|
||||||
|
createProforma(
|
||||||
|
companyId: UniqueID,
|
||||||
|
props: Omit<IProformaProps, "companyId">,
|
||||||
|
proformaId?: UniqueID
|
||||||
|
): Result<Proforma, Error>;
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import type { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import { type IProformaProps, Proforma } from "../../../domain";
|
||||||
|
|
||||||
|
import type { IProformaFactory } from "./proforma-factory.interface";
|
||||||
|
|
||||||
|
export class ProformaFactory implements IProformaFactory {
|
||||||
|
createProforma(
|
||||||
|
companyId: UniqueID,
|
||||||
|
props: Omit<IProformaProps, "companyId">,
|
||||||
|
proformaId?: UniqueID
|
||||||
|
): Result<Proforma, Error> {
|
||||||
|
return Proforma.create({ ...props, companyId }, proformaId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
export * from "./application-models";
|
||||||
|
export * from "./di";
|
||||||
|
export * from "./dtos";
|
||||||
|
export * from "./mappers";
|
||||||
|
export * from "./repositories";
|
||||||
|
export * from "./services";
|
||||||
|
export * from "./snapshot-builders";
|
||||||
|
export * from "./use-cases";
|
||||||
@ -15,24 +15,25 @@ import {
|
|||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
import { Maybe, Result } from "@repo/rdx-utils";
|
import { Maybe, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { CreateProformaItemRequestDTO, CreateProformaRequestDTO } from "../../../../../common";
|
import type { CreateProformaItemRequestDTO, CreateProformaRequestDTO } from "../../../../common";
|
||||||
import {
|
import {
|
||||||
CustomerInvoiceItem,
|
|
||||||
CustomerInvoiceItemDescription,
|
|
||||||
type CustomerInvoiceItemProps,
|
|
||||||
CustomerInvoiceItems,
|
CustomerInvoiceItems,
|
||||||
CustomerInvoiceNumber,
|
type IProformaProps,
|
||||||
type CustomerInvoiceProps,
|
InvoiceNumber,
|
||||||
CustomerInvoiceSerie,
|
|
||||||
CustomerInvoiceStatus,
|
|
||||||
InvoicePaymentMethod,
|
InvoicePaymentMethod,
|
||||||
type InvoiceRecipient,
|
type InvoiceRecipient,
|
||||||
|
InvoiceSerie,
|
||||||
|
InvoiceStatus,
|
||||||
|
IssuedInvoiceItem,
|
||||||
|
type IssuedInvoiceItemProps,
|
||||||
ItemAmount,
|
ItemAmount,
|
||||||
|
ItemDescription,
|
||||||
ItemDiscount,
|
ItemDiscount,
|
||||||
ItemQuantity,
|
ItemQuantity,
|
||||||
} from "../../../../domain";
|
} from "../../../domain";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* CreateProformaPropsMapper
|
||||||
* Convierte el DTO a las props validadas (CustomerProps).
|
* Convierte el DTO a las props validadas (CustomerProps).
|
||||||
* No construye directamente el agregado.
|
* No construye directamente el agregado.
|
||||||
*
|
*
|
||||||
@ -42,7 +43,7 @@ import {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class CreateCustomerInvoicePropsMapper {
|
export class CreateProformaPropsMapper {
|
||||||
private readonly taxCatalog: JsonTaxCatalogProvider;
|
private readonly taxCatalog: JsonTaxCatalogProvider;
|
||||||
private errors: ValidationErrorDetail[] = [];
|
private errors: ValidationErrorDetail[] = [];
|
||||||
private languageCode?: LanguageCode;
|
private languageCode?: LanguageCode;
|
||||||
@ -57,7 +58,7 @@ export class CreateCustomerInvoicePropsMapper {
|
|||||||
try {
|
try {
|
||||||
this.errors = [];
|
this.errors = [];
|
||||||
|
|
||||||
const defaultStatus = CustomerInvoiceStatus.createDraft();
|
const defaultStatus = InvoiceStatus.createDraft();
|
||||||
|
|
||||||
const proformaId = extractOrPushError(UniqueID.create(dto.id), "id", this.errors);
|
const proformaId = extractOrPushError(UniqueID.create(dto.id), "id", this.errors);
|
||||||
|
|
||||||
@ -72,13 +73,13 @@ export class CreateCustomerInvoicePropsMapper {
|
|||||||
const recipient = Maybe.none<InvoiceRecipient>();
|
const recipient = Maybe.none<InvoiceRecipient>();
|
||||||
|
|
||||||
const proformaNumber = extractOrPushError(
|
const proformaNumber = extractOrPushError(
|
||||||
CustomerInvoiceNumber.create(dto.invoice_number),
|
InvoiceNumber.create(dto.invoice_number),
|
||||||
"invoice_number",
|
"invoice_number",
|
||||||
this.errors
|
this.errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const series = extractOrPushError(
|
const series = extractOrPushError(
|
||||||
maybeFromNullableVO(dto.series, (value) => CustomerInvoiceSerie.create(value)),
|
maybeFromNullableVO(dto.series, (value) => InvoiceSerie.create(value)),
|
||||||
"series",
|
"series",
|
||||||
this.errors
|
this.errors
|
||||||
);
|
);
|
||||||
@ -150,7 +151,7 @@ export class CreateCustomerInvoicePropsMapper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const proformaProps: CustomerInvoiceProps = {
|
const proformaProps: IProformaProps = {
|
||||||
companyId,
|
companyId,
|
||||||
isProforma,
|
isProforma,
|
||||||
proformaId: Maybe.none(),
|
proformaId: Maybe.none(),
|
||||||
@ -194,9 +195,7 @@ export class CreateCustomerInvoicePropsMapper {
|
|||||||
|
|
||||||
items.forEach((item, index) => {
|
items.forEach((item, index) => {
|
||||||
const description = extractOrPushError(
|
const description = extractOrPushError(
|
||||||
maybeFromNullableVO(item.description, (value) =>
|
maybeFromNullableVO(item.description, (value) => ItemDescription.create(value)),
|
||||||
CustomerInvoiceItemDescription.create(value)
|
|
||||||
),
|
|
||||||
"description",
|
"description",
|
||||||
this.errors
|
this.errors
|
||||||
);
|
);
|
||||||
@ -221,7 +220,7 @@ export class CreateCustomerInvoicePropsMapper {
|
|||||||
|
|
||||||
const taxes = this.mapTaxes(item, index);
|
const taxes = this.mapTaxes(item, index);
|
||||||
|
|
||||||
const itemProps: CustomerInvoiceItemProps = {
|
const itemProps: IssuedInvoiceItemProps = {
|
||||||
currencyCode: this.currencyCode!,
|
currencyCode: this.currencyCode!,
|
||||||
languageCode: this.languageCode!,
|
languageCode: this.languageCode!,
|
||||||
description: description!,
|
description: description!,
|
||||||
@ -231,7 +230,7 @@ export class CreateCustomerInvoicePropsMapper {
|
|||||||
taxes: taxes,
|
taxes: taxes,
|
||||||
};
|
};
|
||||||
|
|
||||||
const itemResult = CustomerInvoiceItem.create(itemProps);
|
const itemResult = IssuedInvoiceItem.create(itemProps);
|
||||||
if (itemResult.isSuccess) {
|
if (itemResult.isSuccess) {
|
||||||
invoiceItems.add(itemResult.data);
|
invoiceItems.add(itemResult.data);
|
||||||
} else {
|
} else {
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
export * from "./create-proforma-props.mapper";
|
||||||
|
export * from "./proforma-domain-mapper.interface";
|
||||||
|
export * from "./proforma-list-mapper.interface";
|
||||||
|
export * from "./update-proforma-props.mapper";
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import type { MapperParamsType } from "@erp/core/api";
|
||||||
|
import type { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { Proforma } from "../../../domain";
|
||||||
|
|
||||||
|
export interface IProformaDomainMapper {
|
||||||
|
mapToPersistence(proforma: Proforma, params?: MapperParamsType): Result<unknown, Error>;
|
||||||
|
mapToDomain(raw: unknown, params?: MapperParamsType): Result<Proforma, Error>;
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import type { MapperParamsType } from "@erp/core/api";
|
||||||
|
import type { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { ProformaListDTO } from "../dtos";
|
||||||
|
|
||||||
|
export interface IProformaListMapper {
|
||||||
|
mapToDTO(raw: unknown, params?: MapperParamsType): Result<ProformaListDTO, Error>;
|
||||||
|
}
|
||||||
@ -16,7 +16,7 @@ import type { UpdateProformaByIdRequestDTO } from "../../../../../common/dto";
|
|||||||
import { type CustomerInvoicePatchProps, CustomerInvoiceSerie } from "../../../../domain";
|
import { type CustomerInvoicePatchProps, CustomerInvoiceSerie } from "../../../../domain";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mapDTOToUpdateCustomerInvoicePatchProps
|
* UpdateProformaPropsMapper
|
||||||
* Convierte el DTO a las props validadas (CustomerInvoiceProps).
|
* Convierte el DTO a las props validadas (CustomerInvoiceProps).
|
||||||
* No construye directamente el agregado.
|
* No construye directamente el agregado.
|
||||||
* Tri-estado:
|
* Tri-estado:
|
||||||
@ -29,7 +29,7 @@ import { type CustomerInvoicePatchProps, CustomerInvoiceSerie } from "../../../.
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function mapDTOToUpdateCustomerInvoicePatchProps(dto: UpdateProformaByIdRequestDTO) {
|
export function UpdateProformaPropsMapper(dto: UpdateProformaByIdRequestDTO) {
|
||||||
try {
|
try {
|
||||||
const errors: ValidationErrorDetail[] = [];
|
const errors: ValidationErrorDetail[] = [];
|
||||||
const props: CustomerInvoicePatchProps = {};
|
const props: CustomerInvoicePatchProps = {};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./proforma-repository.interface";
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import type { Criteria } from "@repo/rdx-criteria/server";
|
||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import type { Collection, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { InvoiceStatus, Proforma } from "../../../domain";
|
||||||
|
import type { ProformaListDTO } from "../dtos";
|
||||||
|
|
||||||
|
export interface IProformaRepository {
|
||||||
|
create(proforma: Proforma, transaction?: unknown): Promise<Result<void, Error>>;
|
||||||
|
|
||||||
|
update(proforma: Proforma, transaction?: unknown): Promise<Result<void, Error>>;
|
||||||
|
|
||||||
|
existsByIdInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
id: UniqueID,
|
||||||
|
tx: unknown
|
||||||
|
): Promise<Result<boolean, Error>>;
|
||||||
|
|
||||||
|
getByIdInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
id: UniqueID,
|
||||||
|
tx: unknown
|
||||||
|
): Promise<Result<Proforma, Error>>;
|
||||||
|
|
||||||
|
findByCriteriaInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
criteria: Criteria,
|
||||||
|
tx: unknown
|
||||||
|
): Promise<Result<Collection<ProformaListDTO>, Error>>;
|
||||||
|
|
||||||
|
deleteByIdInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
id: UniqueID,
|
||||||
|
tx: unknown
|
||||||
|
): Promise<Result<boolean, Error>>;
|
||||||
|
|
||||||
|
updateStatusByIdInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
id: UniqueID,
|
||||||
|
newStatus: InvoiceStatus,
|
||||||
|
tx: unknown
|
||||||
|
): Promise<Result<boolean, Error>>;
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
export * from "./proforma-creator";
|
||||||
|
export * from "./proforma-document-generator.interface";
|
||||||
|
export * from "./proforma-document-metadata-factory";
|
||||||
|
export * from "./proforma-document-properties-factory";
|
||||||
|
export * from "./proforma-finder";
|
||||||
|
export * from "./proforma-issuer";
|
||||||
|
export * from "./proforma-number-generator.interface";
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
import type { Transaction } from "sequelize";
|
||||||
|
|
||||||
|
import type { ICustomerInvoiceRepository } from "../../../domain";
|
||||||
|
import type { CustomerInvoice, CustomerInvoiceProps } from "../../../domain/aggregates";
|
||||||
|
import type { IProformaFactory } from "../factories";
|
||||||
|
|
||||||
|
import type { IProformaNumberGenerator } from "./proforma-number-generator.interface";
|
||||||
|
|
||||||
|
export interface IProformaCreator {
|
||||||
|
create(
|
||||||
|
companyId: UniqueID,
|
||||||
|
id: UniqueID,
|
||||||
|
props: CustomerInvoiceProps,
|
||||||
|
transaction: Transaction
|
||||||
|
): Promise<Result<CustomerInvoice, Error>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProformaCreatorDeps = {
|
||||||
|
numberService: IProformaNumberGenerator;
|
||||||
|
factory: IProformaFactory;
|
||||||
|
repository: ICustomerInvoiceRepository;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ProformaCreator implements IProformaCreator {
|
||||||
|
private readonly numberService: IProformaNumberGenerator;
|
||||||
|
private readonly factory: IProformaFactory;
|
||||||
|
private readonly repository: ICustomerInvoiceRepository;
|
||||||
|
|
||||||
|
constructor(deps: ProformaCreatorDeps) {
|
||||||
|
this.numberService = deps.numberService;
|
||||||
|
this.factory = deps.factory;
|
||||||
|
this.repository = deps.repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(
|
||||||
|
companyId: UniqueID,
|
||||||
|
id: UniqueID,
|
||||||
|
props: CustomerInvoiceProps,
|
||||||
|
transaction: Transaction
|
||||||
|
): Promise<Result<CustomerInvoice, Error>> {
|
||||||
|
// 1. Obtener siguiente número
|
||||||
|
const { series } = props;
|
||||||
|
const numberResult = await this.numberService.getNextForCompany(companyId, series, transaction);
|
||||||
|
|
||||||
|
if (numberResult.isFailure) {
|
||||||
|
return Result.fail(numberResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const invoiceNumber = numberResult.data;
|
||||||
|
|
||||||
|
// 2. Crear agregado
|
||||||
|
const buildResult = this.factory.createProforma(
|
||||||
|
companyId,
|
||||||
|
{
|
||||||
|
...props,
|
||||||
|
invoiceNumber,
|
||||||
|
},
|
||||||
|
id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (buildResult.isFailure) {
|
||||||
|
return Result.fail(buildResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const proforma = buildResult.data;
|
||||||
|
|
||||||
|
// 3. Persistir
|
||||||
|
const saveResult = await this.repository.create(proforma, transaction);
|
||||||
|
|
||||||
|
if (saveResult.isFailure) {
|
||||||
|
return Result.fail(saveResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(proforma);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import type { DocumentGenerationService } from "@erp/core/api";
|
||||||
|
|
||||||
|
import type { ProformaReportSnapshot } from "../application-models";
|
||||||
|
|
||||||
|
export interface ProformaDocumentGeneratorService
|
||||||
|
extends DocumentGenerationService<ProformaReportSnapshot> {}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
import type { IDocumentMetadata, IDocumentMetadataFactory } from "@erp/core/api";
|
||||||
|
|
||||||
|
import type { ProformaReportSnapshot } from "../application-models";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construye los metadatos del documento PDF de una factura emitida.
|
||||||
|
*
|
||||||
|
* - Application-level
|
||||||
|
* - Determinista
|
||||||
|
* - Sin IO
|
||||||
|
*/
|
||||||
|
export class ProformaDocumentMetadataFactory
|
||||||
|
implements IDocumentMetadataFactory<ProformaReportSnapshot>
|
||||||
|
{
|
||||||
|
build(snapshot: ProformaReportSnapshot): IDocumentMetadata {
|
||||||
|
if (!snapshot.id) {
|
||||||
|
throw new Error("ProformaReportSnapshot.id is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!snapshot.company_id) {
|
||||||
|
throw new Error("ProformaReportSnapshot.companyId is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
documentType: "proforma",
|
||||||
|
documentId: snapshot.id,
|
||||||
|
companyId: snapshot.company_id,
|
||||||
|
companySlug: snapshot.company_slug,
|
||||||
|
format: "PDF",
|
||||||
|
languageCode: snapshot.language_code ?? "es",
|
||||||
|
filename: this.buildFilename(snapshot),
|
||||||
|
storageKey: this.buildCacheKey(snapshot),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildFilename(snapshot: ProformaReportSnapshot): string {
|
||||||
|
// Ejemplo: factura-F2024-000123-FULANITO.pdf
|
||||||
|
return `factura-${snapshot.series}${snapshot.invoice_number}-${snapshot.recipient.name}.pdf`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildCacheKey(snapshot: ProformaReportSnapshot): string {
|
||||||
|
// Versionado explícito para invalidaciones futuras
|
||||||
|
return ["proforma", snapshot.company_id, snapshot.series, snapshot.invoice_number, "v1"].join(
|
||||||
|
":"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import type { IDocumentProperties, IDocumentPropertiesFactory } from "@erp/core/api";
|
||||||
|
|
||||||
|
import type { ProformaReportSnapshot } from "../application-models";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construye los metadatos del documento PDF de una factura emitida.
|
||||||
|
*
|
||||||
|
* - Application-level
|
||||||
|
* - Determinista
|
||||||
|
* - Sin IO
|
||||||
|
*/
|
||||||
|
export class ProformaDocumentPropertiesFactory
|
||||||
|
implements IDocumentPropertiesFactory<ProformaReportSnapshot>
|
||||||
|
{
|
||||||
|
build(snapshot: ProformaReportSnapshot): IDocumentProperties {
|
||||||
|
return {
|
||||||
|
title: snapshot.reference,
|
||||||
|
subject: "proforma",
|
||||||
|
author: snapshot.company_slug,
|
||||||
|
creator: "FactuGES ERP",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
import type { CustomerInvoiceListDTO } from "@erp/customer-invoices/api/infrastructure";
|
||||||
|
import type { Criteria } from "@repo/rdx-criteria/server";
|
||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import type { Collection, Result } from "@repo/rdx-utils";
|
||||||
|
import type { Transaction } from "sequelize";
|
||||||
|
|
||||||
|
import type { ICustomerInvoiceRepository, Proforma } from "../../../domain";
|
||||||
|
|
||||||
|
export interface IProformaFinder {
|
||||||
|
findProformaById(
|
||||||
|
companyId: UniqueID,
|
||||||
|
invoiceId: UniqueID,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<Proforma, Error>>;
|
||||||
|
|
||||||
|
proformaExists(
|
||||||
|
companyId: UniqueID,
|
||||||
|
invoiceId: UniqueID,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<boolean, Error>>;
|
||||||
|
|
||||||
|
findProformasByCriteria(
|
||||||
|
companyId: UniqueID,
|
||||||
|
criteria: Criteria,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<Collection<CustomerInvoiceListDTO>, Error>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProformaFinder implements IProformaFinder {
|
||||||
|
constructor(private readonly repository: ICustomerInvoiceRepository) {}
|
||||||
|
|
||||||
|
async findProformaById(
|
||||||
|
companyId: UniqueID,
|
||||||
|
proformaId: UniqueID,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<Proforma, Error>> {
|
||||||
|
return this.repository.getProformaByIdInCompany(companyId, proformaId, transaction, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
async proformaExists(
|
||||||
|
companyId: UniqueID,
|
||||||
|
proformaId: UniqueID,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<boolean, Error>> {
|
||||||
|
return this.repository.existsByIdInCompany(companyId, proformaId, transaction, {
|
||||||
|
is_proforma: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async findProformasByCriteria(
|
||||||
|
companyId: UniqueID,
|
||||||
|
criteria: Criteria,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<Collection<CustomerInvoiceListDTO>, Error>> {
|
||||||
|
return this.repository.findProformasByCriteriaInCompany(companyId, criteria, transaction, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
import type { Transaction } from "sequelize";
|
||||||
|
|
||||||
|
import type { IProformaToIssuedInvoiceMaterializer } from "../../issued-invoices";
|
||||||
|
|
||||||
|
export class ProformaIssuer implements IProformaIssuer {
|
||||||
|
private readonly proformaRepository: IProformaRepository;
|
||||||
|
private readonly issuedInvoiceFactory: IIssuedInvoiceFactory;
|
||||||
|
private readonly issuedInvoiceRepository: IIssuedInvoiceRepository;
|
||||||
|
private readonly materializer: IProformaToIssuedInvoiceMaterializer;
|
||||||
|
|
||||||
|
constructor(deps: ProformaIssuerDeps) {
|
||||||
|
this.proformaRepository = deps.proformaRepository;
|
||||||
|
this.issuedInvoiceFactory = deps.issuedInvoiceFactory;
|
||||||
|
this.issuedInvoiceRepository = deps.issuedInvoiceRepository;
|
||||||
|
this.materializer = deps.materializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async issue(
|
||||||
|
proforma: Proforma,
|
||||||
|
issuedInvoiceId: UniqueID,
|
||||||
|
transaction: Transaction
|
||||||
|
): Promise<Result<Proforma, Error>> {
|
||||||
|
const issueResult = proforma.issue();
|
||||||
|
if (issueResult.isFailure) return Result.fail(issueResult.error);
|
||||||
|
|
||||||
|
const propsResult = this.materializer.materialize(proforma, issuedInvoiceId);
|
||||||
|
|
||||||
|
if (propsResult.isFailure) return Result.fail(propsResult.error);
|
||||||
|
|
||||||
|
const invoiceResult = this.issuedInvoiceFactory.create(propsResult.data, issuedInvoiceId);
|
||||||
|
|
||||||
|
if (invoiceResult.isFailure) {
|
||||||
|
return Result.fail(invoiceResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.issuedInvoiceRepository.save(proforma.companyId, invoiceResult.data, transaction);
|
||||||
|
|
||||||
|
await this.proformaRepository.save(proforma.companyId, proforma, transaction);
|
||||||
|
|
||||||
|
return Result.ok(proforma);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,11 @@
|
|||||||
|
import type { InvoiceNumber, InvoiceSerie } from "@erp/customer-invoices/api/domain";
|
||||||
import type { UniqueID } from "@repo/rdx-ddd";
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
import type { Maybe, Result } from "@repo/rdx-utils";
|
import type { Maybe, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { CustomerInvoiceNumber, CustomerInvoiceSerie } from "../value-objects";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Servicio de dominio que define cómo se genera el siguiente número de factura.
|
* Servicio de dominio que define cómo se genera el siguiente número de factura.
|
||||||
*/
|
*/
|
||||||
export interface ICustomerInvoiceNumberGenerator {
|
export interface IProformaNumberGenerator {
|
||||||
/**
|
/**
|
||||||
* Devuelve el siguiente número de factura disponible para una empresa dentro de una "serie" de factura.
|
* Devuelve el siguiente número de factura disponible para una empresa dentro de una "serie" de factura.
|
||||||
*
|
*
|
||||||
@ -14,9 +13,9 @@ export interface ICustomerInvoiceNumberGenerator {
|
|||||||
* @param serie - Serie por la que buscar la última factura
|
* @param serie - Serie por la que buscar la última factura
|
||||||
* @param transaction - Transacción activa
|
* @param transaction - Transacción activa
|
||||||
*/
|
*/
|
||||||
nextForCompany(
|
getNextForCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
series: Maybe<CustomerInvoiceSerie>,
|
series: Maybe<InvoiceSerie>,
|
||||||
transaction: any
|
transaction: any
|
||||||
): Promise<Result<CustomerInvoiceNumber, Error>>;
|
): Promise<Result<InvoiceNumber, Error>>;
|
||||||
}
|
}
|
||||||
@ -4,18 +4,17 @@ import type { Transaction } from "sequelize";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
CustomerInvoiceIsProformaSpecification,
|
CustomerInvoiceIsProformaSpecification,
|
||||||
type CustomerInvoiceStatus,
|
type InvoiceStatus,
|
||||||
ProformaCannotBeDeletedError,
|
ProformaCannotBeDeletedError,
|
||||||
StatusInvoiceIsDraftSpecification,
|
StatusInvoiceIsDraftSpecification,
|
||||||
} from "../../domain";
|
} from "../../../domain";
|
||||||
import type {
|
import type {
|
||||||
CustomerInvoice,
|
CustomerInvoice,
|
||||||
CustomerInvoicePatchProps,
|
CustomerInvoicePatchProps,
|
||||||
CustomerInvoiceProps,
|
CustomerInvoiceProps,
|
||||||
} from "../../domain/aggregates";
|
} from "../../../domain/aggregates";
|
||||||
import type { ICustomerInvoiceRepository } from "../../domain/repositories";
|
import type { ICustomerInvoiceRepository } from "../../../domain/repositories";
|
||||||
|
import type { IProformaFactory } from "../../services/proforma-factory";
|
||||||
import type { IProformaFactory } from "./proforma-factory";
|
|
||||||
|
|
||||||
export type IIssuedInvoiceWriteService = {};
|
export type IIssuedInvoiceWriteService = {};
|
||||||
|
|
||||||
@ -122,7 +121,7 @@ export class IssuedInvoiceWriteService implements IIssuedInvoiceWriteService {
|
|||||||
async updateProformaStatus(
|
async updateProformaStatus(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
proformaId: UniqueID,
|
proformaId: UniqueID,
|
||||||
newStatus: CustomerInvoiceStatus,
|
newStatus: InvoiceStatus,
|
||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
): Promise<Result<boolean, Error>> {
|
): Promise<Result<boolean, Error>> {
|
||||||
return this.repository.updateProformaStatusByIdInCompany(
|
return this.repository.updateProformaStatusByIdInCompany(
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
export * from "./proforma-full-snapshot.interface";
|
||||||
|
export * from "./proforma-full-snapshot-builder";
|
||||||
|
export * from "./proforma-item-full-snapshot.interface";
|
||||||
|
export * from "./proforma-items-full-snapshot-builder";
|
||||||
|
export * from "./proforma-recipient-full-snapshot.interface";
|
||||||
|
export * from "./proforma-recipient-full-snapshot-builder";
|
||||||
@ -0,0 +1,130 @@
|
|||||||
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
|
import { toEmptyString } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
|
import { InvoiceAmount, type Proforma } from "../../../../domain";
|
||||||
|
|
||||||
|
import type { IProformaFullSnapshot } from "./proforma-full-snapshot.interface";
|
||||||
|
import type { IProformaItemsFullSnapshotBuilder } from "./proforma-items-full-snapshot-builder";
|
||||||
|
import type { IProformaRecipientFullSnapshotBuilder } from "./proforma-recipient-full-snapshot-builder";
|
||||||
|
|
||||||
|
export interface IProformaFullSnapshotBuilder
|
||||||
|
extends ISnapshotBuilder<Proforma, IProformaFullSnapshot> {}
|
||||||
|
|
||||||
|
export class ProformaFullSnapshotBuilder implements IProformaFullSnapshotBuilder {
|
||||||
|
constructor(
|
||||||
|
private readonly itemsBuilder: IProformaItemsFullSnapshotBuilder,
|
||||||
|
private readonly recipientBuilder: IProformaRecipientFullSnapshotBuilder
|
||||||
|
) {}
|
||||||
|
|
||||||
|
toOutput(invoice: Proforma): IProformaFullSnapshot {
|
||||||
|
const items = this.itemsBuilder.toOutput(invoice.items);
|
||||||
|
const recipient = this.recipientBuilder.toOutput(invoice);
|
||||||
|
|
||||||
|
const allAmounts = invoice.calculateAllAmounts();
|
||||||
|
|
||||||
|
const payment = invoice.paymentMethod.match(
|
||||||
|
(payment) => {
|
||||||
|
const { id, payment_description } = payment.toObjectString();
|
||||||
|
return {
|
||||||
|
payment_id: id,
|
||||||
|
payment_description,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
() => undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
let totalIvaAmount = InvoiceAmount.zero(invoice.currencyCode.code);
|
||||||
|
let totalRecAmount = InvoiceAmount.zero(invoice.currencyCode.code);
|
||||||
|
let totalRetentionAmount = InvoiceAmount.zero(invoice.currencyCode.code);
|
||||||
|
|
||||||
|
const invoiceTaxes = invoice.getTaxes().map((taxGroup) => {
|
||||||
|
const { ivaAmount, recAmount, retentionAmount, totalAmount } = taxGroup.calculateAmounts();
|
||||||
|
|
||||||
|
totalIvaAmount = totalIvaAmount.add(ivaAmount);
|
||||||
|
totalRecAmount = totalRecAmount.add(recAmount);
|
||||||
|
totalRetentionAmount = totalRetentionAmount.add(retentionAmount);
|
||||||
|
|
||||||
|
return {
|
||||||
|
taxable_amount: taxGroup.taxableAmount.toObjectString(),
|
||||||
|
|
||||||
|
iva_code: taxGroup.iva.code,
|
||||||
|
iva_percentage: taxGroup.iva.percentage.toObjectString(),
|
||||||
|
iva_amount: ivaAmount.toObjectString(),
|
||||||
|
|
||||||
|
rec_code: taxGroup.rec.match(
|
||||||
|
(rec) => rec.code,
|
||||||
|
() => ""
|
||||||
|
),
|
||||||
|
|
||||||
|
rec_percentage: taxGroup.rec.match(
|
||||||
|
(rec) => rec.percentage.toObjectString(),
|
||||||
|
() => ({ value: "", scale: "" })
|
||||||
|
),
|
||||||
|
|
||||||
|
rec_amount: recAmount.toObjectString(),
|
||||||
|
|
||||||
|
retention_code: taxGroup.retention.match(
|
||||||
|
(retention) => retention.code,
|
||||||
|
() => ""
|
||||||
|
),
|
||||||
|
|
||||||
|
retention_percentage: taxGroup.retention.match(
|
||||||
|
(retention) => retention.percentage.toObjectString(),
|
||||||
|
() => ({ value: "", scale: "" })
|
||||||
|
),
|
||||||
|
|
||||||
|
retention_amount: retentionAmount.toObjectString(),
|
||||||
|
|
||||||
|
taxes_amount: totalAmount.toObjectString(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: invoice.id.toString(),
|
||||||
|
company_id: invoice.companyId.toString(),
|
||||||
|
|
||||||
|
is_proforma: invoice.isProforma ? "true" : "false",
|
||||||
|
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()),
|
||||||
|
|
||||||
|
reference: toEmptyString(invoice.reference, (value) => value.toString()),
|
||||||
|
description: toEmptyString(invoice.description, (value) => value.toString()),
|
||||||
|
notes: toEmptyString(invoice.notes, (value) => value.toString()),
|
||||||
|
|
||||||
|
language_code: invoice.languageCode.toString(),
|
||||||
|
currency_code: invoice.currencyCode.toString(),
|
||||||
|
|
||||||
|
customer_id: invoice.customerId.toString(),
|
||||||
|
recipient,
|
||||||
|
|
||||||
|
payment_method: payment,
|
||||||
|
|
||||||
|
subtotal_amount: allAmounts.subtotalAmount.toObjectString(),
|
||||||
|
items_discount_amount: allAmounts.itemDiscountAmount.toObjectString(),
|
||||||
|
|
||||||
|
discount_percentage: invoice.discountPercentage.toObjectString(),
|
||||||
|
discount_amount: allAmounts.globalDiscountAmount.toObjectString(),
|
||||||
|
|
||||||
|
taxable_amount: allAmounts.taxableAmount.toObjectString(),
|
||||||
|
|
||||||
|
iva_amount: totalIvaAmount.toObjectString(),
|
||||||
|
rec_amount: totalRecAmount.toObjectString(),
|
||||||
|
retention_amount: totalRetentionAmount.toObjectString(),
|
||||||
|
|
||||||
|
taxes_amount: allAmounts.taxesAmount.toObjectString(),
|
||||||
|
total_amount: allAmounts.totalAmount.toObjectString(),
|
||||||
|
|
||||||
|
taxes: invoiceTaxes,
|
||||||
|
|
||||||
|
items,
|
||||||
|
|
||||||
|
metadata: {
|
||||||
|
entity: "proformas",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
import type { IProformaItemFullSnapshot } from "./proforma-item-full-snapshot.interface";
|
||||||
|
import type { IProformaRecipientFullSnapshot } from "./proforma-recipient-full-snapshot.interface";
|
||||||
|
|
||||||
|
export interface IProformaFullSnapshot {
|
||||||
|
id: string;
|
||||||
|
company_id: string;
|
||||||
|
|
||||||
|
is_proforma: "true" | "false";
|
||||||
|
invoice_number: string;
|
||||||
|
status: string;
|
||||||
|
series: string;
|
||||||
|
|
||||||
|
invoice_date: string;
|
||||||
|
operation_date: string;
|
||||||
|
|
||||||
|
reference: string;
|
||||||
|
description: string;
|
||||||
|
notes: string;
|
||||||
|
|
||||||
|
language_code: string;
|
||||||
|
currency_code: string;
|
||||||
|
|
||||||
|
customer_id: string;
|
||||||
|
recipient: IProformaRecipientFullSnapshot;
|
||||||
|
|
||||||
|
payment_method?: {
|
||||||
|
payment_id: string;
|
||||||
|
payment_description: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
subtotal_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
items_discount_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
discount_percentage: { value: string; scale: string };
|
||||||
|
discount_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
taxable_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
iva_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
rec_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
retention_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
taxes_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
total_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
taxes: Array<{
|
||||||
|
taxable_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
iva_code: string;
|
||||||
|
iva_percentage: { value: string; scale: string };
|
||||||
|
iva_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
rec_code: string;
|
||||||
|
rec_percentage: { value: string; scale: string };
|
||||||
|
rec_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
retention_code: string;
|
||||||
|
retention_percentage: { value: string; scale: string };
|
||||||
|
retention_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
taxes_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
}>;
|
||||||
|
|
||||||
|
items: IProformaItemFullSnapshot[];
|
||||||
|
|
||||||
|
metadata?: Record<string, string>;
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
export interface IProformaItemFullSnapshot {
|
||||||
|
id: string;
|
||||||
|
is_valued: string;
|
||||||
|
position: string;
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
quantity: { value: string; scale: string };
|
||||||
|
unit_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
subtotal_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
discount_percentage: { value: string; scale: string };
|
||||||
|
discount_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
global_discount_percentage: { value: string; scale: string };
|
||||||
|
global_discount_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
taxable_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
iva_code: string;
|
||||||
|
iva_percentage: { value: string; scale: string };
|
||||||
|
iva_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
rec_code: string;
|
||||||
|
rec_percentage: { value: string; scale: string };
|
||||||
|
rec_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
retention_code: string;
|
||||||
|
retention_percentage: { value: string; scale: string };
|
||||||
|
retention_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
taxes_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
total_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
}
|
||||||
@ -0,0 +1,94 @@
|
|||||||
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
|
import { toEmptyString } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
|
import type { CustomerInvoiceItems, IssuedInvoiceItem } from "../../../../domain";
|
||||||
|
|
||||||
|
import type { IProformaItemFullSnapshot } from "./proforma-item-full-snapshot.interface";
|
||||||
|
|
||||||
|
export interface IProformaItemsFullSnapshotBuilder
|
||||||
|
extends ISnapshotBuilder<CustomerInvoiceItems, IProformaItemFullSnapshot[]> {}
|
||||||
|
|
||||||
|
export class ProformaItemsFullSnapshotBuilder implements IProformaItemsFullSnapshotBuilder {
|
||||||
|
private mapItem(invoiceItem: IssuedInvoiceItem, index: number): IProformaItemFullSnapshot {
|
||||||
|
const allAmounts = invoiceItem.calculateAllAmounts();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: invoiceItem.id.toPrimitive(),
|
||||||
|
is_valued: String(invoiceItem.isValued),
|
||||||
|
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: "" })
|
||||||
|
),
|
||||||
|
|
||||||
|
subtotal_amount: allAmounts.subtotalAmount.toObjectString(),
|
||||||
|
|
||||||
|
discount_percentage: invoiceItem.itemDiscountPercentage.match(
|
||||||
|
(discountPercentage) => discountPercentage.toObjectString(),
|
||||||
|
() => ({ value: "", scale: "" })
|
||||||
|
),
|
||||||
|
|
||||||
|
discount_amount: allAmounts.itemDiscountAmount.toObjectString(),
|
||||||
|
|
||||||
|
global_discount_percentage: invoiceItem.globalDiscountPercentage.match(
|
||||||
|
(discountPercentage) => discountPercentage.toObjectString(),
|
||||||
|
() => ({ value: "", scale: "" })
|
||||||
|
),
|
||||||
|
|
||||||
|
global_discount_amount: allAmounts.globalDiscountAmount.toObjectString(),
|
||||||
|
|
||||||
|
taxable_amount: allAmounts.taxableAmount.toObjectString(),
|
||||||
|
|
||||||
|
iva_code: invoiceItem.taxes.iva.match(
|
||||||
|
(iva) => iva.code,
|
||||||
|
() => ""
|
||||||
|
),
|
||||||
|
|
||||||
|
iva_percentage: invoiceItem.taxes.iva.match(
|
||||||
|
(iva) => iva.percentage.toObjectString(),
|
||||||
|
() => ({ value: "", scale: "" })
|
||||||
|
),
|
||||||
|
|
||||||
|
iva_amount: allAmounts.ivaAmount.toObjectString(),
|
||||||
|
|
||||||
|
rec_code: invoiceItem.taxes.rec.match(
|
||||||
|
(rec) => rec.code,
|
||||||
|
() => ""
|
||||||
|
),
|
||||||
|
|
||||||
|
rec_percentage: invoiceItem.taxes.rec.match(
|
||||||
|
(rec) => rec.percentage.toObjectString(),
|
||||||
|
() => ({ value: "", scale: "" })
|
||||||
|
),
|
||||||
|
|
||||||
|
rec_amount: allAmounts.recAmount.toObjectString(),
|
||||||
|
|
||||||
|
retention_code: invoiceItem.taxes.retention.match(
|
||||||
|
(retention) => retention.code,
|
||||||
|
() => ""
|
||||||
|
),
|
||||||
|
|
||||||
|
retention_percentage: invoiceItem.taxes.retention.match(
|
||||||
|
(retention) => retention.percentage.toObjectString(),
|
||||||
|
() => ({ value: "", scale: "" })
|
||||||
|
),
|
||||||
|
|
||||||
|
retention_amount: allAmounts.retentionAmount.toObjectString(),
|
||||||
|
|
||||||
|
taxes_amount: allAmounts.taxesAmount.toObjectString(),
|
||||||
|
|
||||||
|
total_amount: allAmounts.totalAmount.toObjectString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toOutput(invoiceItems: CustomerInvoiceItems): IProformaItemFullSnapshot[] {
|
||||||
|
return invoiceItems.map((item, index) => this.mapItem(item, index));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
|
import { DomainValidationError, toEmptyString } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
|
import type { InvoiceRecipient, Proforma } from "../../../../domain";
|
||||||
|
import type { ProformaRecipientFullSnapshot } from "../../application-models";
|
||||||
|
|
||||||
|
export interface IProformaRecipientFullSnapshotBuilder
|
||||||
|
extends ISnapshotBuilder<Proforma, ProformaRecipientFullSnapshot> {}
|
||||||
|
|
||||||
|
export class ProformaRecipientFullSnapshotBuilder implements IProformaRecipientFullSnapshotBuilder {
|
||||||
|
toOutput(invoice: Proforma): ProformaRecipientFullSnapshot {
|
||||||
|
if (!invoice.recipient) {
|
||||||
|
throw DomainValidationError.requiredValue("recipient", {
|
||||||
|
cause: invoice,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoice.recipient.match(
|
||||||
|
(recipient: InvoiceRecipient) => ({
|
||||||
|
id: invoice.customerId.toString(),
|
||||||
|
name: recipient.name.toString(),
|
||||||
|
tin: recipient.tin.toString(),
|
||||||
|
street: toEmptyString(recipient.street, (v) => v.toString()),
|
||||||
|
street2: toEmptyString(recipient.street2, (v) => v.toString()),
|
||||||
|
city: toEmptyString(recipient.city, (v) => v.toString()),
|
||||||
|
province: toEmptyString(recipient.province, (v) => v.toString()),
|
||||||
|
postal_code: toEmptyString(recipient.postalCode, (v) => v.toString()),
|
||||||
|
country: toEmptyString(recipient.country, (v) => v.toString()),
|
||||||
|
}),
|
||||||
|
() => ({
|
||||||
|
id: "",
|
||||||
|
name: "",
|
||||||
|
tin: "",
|
||||||
|
street: "",
|
||||||
|
street2: "",
|
||||||
|
city: "",
|
||||||
|
province: "",
|
||||||
|
postal_code: "",
|
||||||
|
country: "",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
export interface IProformaRecipientFullSnapshot {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
tin: string;
|
||||||
|
street: string;
|
||||||
|
street2: string;
|
||||||
|
city: string;
|
||||||
|
province: string;
|
||||||
|
postal_code: string;
|
||||||
|
country: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./proforma-list-item-snapshot.interface";
|
||||||
|
export * from "./proforma-list-item-snapshot-builder";
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
|
import { toEmptyString } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
|
import type { CustomerInvoiceListDTO } from "../../../../infrastructure";
|
||||||
|
import type { ProformaListItemSnapshot } from "../../application-models";
|
||||||
|
|
||||||
|
export interface IProformaListItemSnapshotBuilder
|
||||||
|
extends ISnapshotBuilder<CustomerInvoiceListDTO, ProformaListItemSnapshot> {}
|
||||||
|
|
||||||
|
export class ProformaListItemSnapshotBuilder implements IProformaListItemSnapshotBuilder {
|
||||||
|
toOutput(proforma: CustomerInvoiceListDTO): ProformaListItemSnapshot {
|
||||||
|
const recipient = proforma.recipient.toObjectString();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: proforma.id.toString(),
|
||||||
|
company_id: proforma.companyId.toString(),
|
||||||
|
is_proforma: proforma.isProforma,
|
||||||
|
customer_id: proforma.customerId.toString(),
|
||||||
|
|
||||||
|
invoice_number: proforma.invoiceNumber.toString(),
|
||||||
|
status: proforma.status.toPrimitive(),
|
||||||
|
series: toEmptyString(proforma.series, (value) => value.toString()),
|
||||||
|
|
||||||
|
invoice_date: proforma.invoiceDate.toDateString(),
|
||||||
|
operation_date: toEmptyString(proforma.operationDate, (value) => value.toDateString()),
|
||||||
|
reference: toEmptyString(proforma.reference, (value) => value.toString()),
|
||||||
|
description: toEmptyString(proforma.description, (value) => value.toString()),
|
||||||
|
|
||||||
|
recipient,
|
||||||
|
|
||||||
|
language_code: proforma.languageCode.code,
|
||||||
|
currency_code: proforma.currencyCode.code,
|
||||||
|
|
||||||
|
subtotal_amount: proforma.subtotalAmount.toObjectString(),
|
||||||
|
discount_percentage: proforma.discountPercentage.toObjectString(),
|
||||||
|
discount_amount: proforma.discountAmount.toObjectString(),
|
||||||
|
taxable_amount: proforma.taxableAmount.toObjectString(),
|
||||||
|
taxes_amount: proforma.taxesAmount.toObjectString(),
|
||||||
|
total_amount: proforma.totalAmount.toObjectString(),
|
||||||
|
|
||||||
|
metadata: {
|
||||||
|
entity: "proforma",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
export interface ProformaListItemSnapshot {
|
||||||
|
id: string;
|
||||||
|
company_id: string;
|
||||||
|
is_proforma: boolean;
|
||||||
|
|
||||||
|
customer_id: string;
|
||||||
|
|
||||||
|
invoice_number: string;
|
||||||
|
status: string;
|
||||||
|
series: string;
|
||||||
|
|
||||||
|
invoice_date: string;
|
||||||
|
operation_date: string;
|
||||||
|
|
||||||
|
language_code: string;
|
||||||
|
currency_code: string;
|
||||||
|
|
||||||
|
reference: string;
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
recipient: {
|
||||||
|
tin: string;
|
||||||
|
name: string;
|
||||||
|
street: string;
|
||||||
|
street2: string;
|
||||||
|
city: string;
|
||||||
|
postal_code: string;
|
||||||
|
province: string;
|
||||||
|
country: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
subtotal_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
discount_percentage: { value: string; scale: string };
|
||||||
|
discount_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
taxable_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
taxes_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
total_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
metadata?: Record<string, string>;
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
export * from "./proforma-items-report-snapshot-builder";
|
||||||
|
export * from "./proforma-report-item-snapshot.interface";
|
||||||
|
export * from "./proforma-report-snapshot.interface";
|
||||||
|
export * from "./proforma-report-snapshot-builder";
|
||||||
|
export * from "./proforma-report-tax-snapshot.interface";
|
||||||
|
export * from "./proforma-tax-report-snapshot-builder";
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { MoneyDTOHelper, PercentageDTOHelper, QuantityDTOHelper } from "@erp/core";
|
||||||
|
import type { ISnapshotBuilder, ISnapshotBuilderParams } from "@erp/core/api";
|
||||||
|
|
||||||
|
import type { ProformaFullSnapshot, ProformaReportItemSnapshot } from "../../application-models";
|
||||||
|
|
||||||
|
export interface IProformaItemReportSnapshotBuilder
|
||||||
|
extends ISnapshotBuilder<ProformaFullSnapshot["items"], ProformaReportItemSnapshot[]> {}
|
||||||
|
|
||||||
|
export class ProformaItemReportSnapshotBuilder implements IProformaItemReportSnapshotBuilder {
|
||||||
|
toOutput(
|
||||||
|
items: ProformaFullSnapshot["items"],
|
||||||
|
params?: ISnapshotBuilderParams
|
||||||
|
): ProformaReportItemSnapshot[] {
|
||||||
|
const locale = params?.locale as string;
|
||||||
|
|
||||||
|
const moneyOptions = {
|
||||||
|
hideZeros: true,
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
return items.map((item) => ({
|
||||||
|
description: item.description,
|
||||||
|
quantity: QuantityDTOHelper.format(item.quantity, locale, { minimumFractionDigits: 0 }),
|
||||||
|
unit_amount: MoneyDTOHelper.format(item.unit_amount, locale, moneyOptions),
|
||||||
|
subtotal_amount: MoneyDTOHelper.format(item.subtotal_amount, locale, moneyOptions),
|
||||||
|
discount_percentage: PercentageDTOHelper.format(item.discount_percentage, locale, {
|
||||||
|
minimumFractionDigits: 0,
|
||||||
|
}),
|
||||||
|
discount_amount: MoneyDTOHelper.format(item.discount_amount, locale, moneyOptions),
|
||||||
|
taxable_amount: MoneyDTOHelper.format(item.taxable_amount, locale, moneyOptions),
|
||||||
|
taxes_amount: MoneyDTOHelper.format(item.taxes_amount, locale, moneyOptions),
|
||||||
|
total_amount: MoneyDTOHelper.format(item.total_amount, locale, moneyOptions),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
export interface ProformaReportItemSnapshot {
|
||||||
|
description: string;
|
||||||
|
quantity: string;
|
||||||
|
unit_amount: string;
|
||||||
|
subtotal_amount: string;
|
||||||
|
discount_percentage: string;
|
||||||
|
discount_amount: string;
|
||||||
|
taxable_amount: string;
|
||||||
|
taxes_amount: string;
|
||||||
|
total_amount: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
import { DateHelper, MoneyDTOHelper, PercentageDTOHelper } from "@erp/core";
|
||||||
|
import type { ISnapshotBuilder, ISnapshotBuilderParams } from "@erp/core/api";
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ProformaFullSnapshot,
|
||||||
|
ProformaReportItemSnapshot,
|
||||||
|
ProformaReportSnapshot,
|
||||||
|
ProformaReportTaxSnapshot,
|
||||||
|
} from "../../application-models";
|
||||||
|
|
||||||
|
export interface IProformaReportSnapshotBuilder
|
||||||
|
extends ISnapshotBuilder<ProformaFullSnapshot, ProformaReportSnapshot> {}
|
||||||
|
|
||||||
|
export class ProformaReportSnapshotBuilder implements IProformaReportSnapshotBuilder {
|
||||||
|
constructor(
|
||||||
|
private readonly itemsBuilder: ISnapshotBuilder<
|
||||||
|
ProformaFullSnapshot["items"],
|
||||||
|
ProformaReportItemSnapshot[]
|
||||||
|
>,
|
||||||
|
private readonly taxesBuilder: ISnapshotBuilder<
|
||||||
|
ProformaFullSnapshot["taxes"],
|
||||||
|
ProformaReportTaxSnapshot[]
|
||||||
|
>
|
||||||
|
) {}
|
||||||
|
|
||||||
|
toOutput(
|
||||||
|
snapshot: ProformaFullSnapshot,
|
||||||
|
params?: ISnapshotBuilderParams
|
||||||
|
): ProformaReportSnapshot {
|
||||||
|
const locale = params?.locale as string;
|
||||||
|
|
||||||
|
const moneyOptions = {
|
||||||
|
hideZeros: true,
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: snapshot.id,
|
||||||
|
company_id: snapshot.company_id,
|
||||||
|
company_slug: "rodax",
|
||||||
|
invoice_number: snapshot.invoice_number,
|
||||||
|
series: snapshot.series,
|
||||||
|
status: snapshot.status,
|
||||||
|
reference: snapshot.reference,
|
||||||
|
|
||||||
|
language_code: snapshot.language_code,
|
||||||
|
currency_code: snapshot.currency_code,
|
||||||
|
|
||||||
|
invoice_date: DateHelper.format(snapshot.invoice_date, locale),
|
||||||
|
|
||||||
|
payment_method: snapshot.payment_method?.payment_description ?? "",
|
||||||
|
notes: snapshot.notes,
|
||||||
|
|
||||||
|
recipient: {
|
||||||
|
name: snapshot.recipient.name,
|
||||||
|
tin: snapshot.recipient.tin,
|
||||||
|
format_address: this.formatAddress(snapshot.recipient),
|
||||||
|
},
|
||||||
|
|
||||||
|
items: this.itemsBuilder.toOutput(snapshot.items, { locale }),
|
||||||
|
taxes: this.taxesBuilder.toOutput(snapshot.taxes, { locale }),
|
||||||
|
|
||||||
|
subtotal_amount: MoneyDTOHelper.format(snapshot.subtotal_amount, locale, moneyOptions),
|
||||||
|
discount_percentage: PercentageDTOHelper.format(snapshot.discount_percentage, locale, {
|
||||||
|
hideZeros: true,
|
||||||
|
}),
|
||||||
|
discount_amount: MoneyDTOHelper.format(snapshot.discount_amount, locale, moneyOptions),
|
||||||
|
taxable_amount: MoneyDTOHelper.format(snapshot.taxable_amount, locale, moneyOptions),
|
||||||
|
taxes_amount: MoneyDTOHelper.format(snapshot.taxes_amount, locale, moneyOptions),
|
||||||
|
total_amount: MoneyDTOHelper.format(snapshot.total_amount, locale, moneyOptions),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatAddress(recipient: ProformaFullSnapshot["recipient"]): string {
|
||||||
|
const lines: string[] = [];
|
||||||
|
|
||||||
|
if (recipient.street) lines.push(recipient.street);
|
||||||
|
if (recipient.street2) lines.push(recipient.street2);
|
||||||
|
|
||||||
|
const cityLine = [recipient.postal_code, recipient.city].filter(Boolean).join(" ");
|
||||||
|
|
||||||
|
if (cityLine) lines.push(cityLine);
|
||||||
|
if (recipient.province && recipient.province !== recipient.city) {
|
||||||
|
lines.push(recipient.province);
|
||||||
|
}
|
||||||
|
if (recipient.country && recipient.country !== "es") {
|
||||||
|
lines.push(recipient.country);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines.join("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import type { ProformaReportItemSnapshot } from "./proforma-report-item-snapshot.interface";
|
||||||
|
import type { ProformaReportTaxSnapshot } from "./proforma-report-tax-snapshot.interface";
|
||||||
|
|
||||||
|
export interface ProformaReportSnapshot {
|
||||||
|
id: string;
|
||||||
|
company_id: string;
|
||||||
|
company_slug: string;
|
||||||
|
invoice_number: string;
|
||||||
|
series: string;
|
||||||
|
status: string;
|
||||||
|
reference: string;
|
||||||
|
|
||||||
|
language_code: string;
|
||||||
|
currency_code: string;
|
||||||
|
|
||||||
|
invoice_date: string;
|
||||||
|
payment_method: string;
|
||||||
|
notes: string;
|
||||||
|
|
||||||
|
recipient: {
|
||||||
|
name: string;
|
||||||
|
tin: string;
|
||||||
|
format_address: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
items: ProformaReportItemSnapshot[];
|
||||||
|
taxes: ProformaReportTaxSnapshot[];
|
||||||
|
|
||||||
|
subtotal_amount: string;
|
||||||
|
discount_percentage: string;
|
||||||
|
discount_amount: string;
|
||||||
|
taxable_amount: string;
|
||||||
|
taxes_amount: string;
|
||||||
|
total_amount: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
export interface ProformaReportTaxSnapshot {
|
||||||
|
taxable_amount: string;
|
||||||
|
|
||||||
|
iva_code: string;
|
||||||
|
iva_percentage: string;
|
||||||
|
iva_amount: string;
|
||||||
|
|
||||||
|
rec_code: string;
|
||||||
|
rec_percentage: string;
|
||||||
|
rec_amount: string;
|
||||||
|
|
||||||
|
retention_code: string;
|
||||||
|
retention_percentage: string;
|
||||||
|
retention_amount: string;
|
||||||
|
|
||||||
|
taxes_amount: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
import { MoneyDTOHelper, PercentageDTOHelper } from "@erp/core";
|
||||||
|
import type { ISnapshotBuilder, ISnapshotBuilderParams } from "@erp/core/api";
|
||||||
|
|
||||||
|
import type { ProformaFullSnapshot, ProformaReportTaxSnapshot } from "../../application-models";
|
||||||
|
|
||||||
|
export interface IProformaTaxReportSnapshotBuilder
|
||||||
|
extends ISnapshotBuilder<ProformaFullSnapshot["taxes"], ProformaReportTaxSnapshot[]> {}
|
||||||
|
|
||||||
|
export class ProformaTaxReportSnapshotBuilder implements IProformaTaxReportSnapshotBuilder {
|
||||||
|
toOutput(
|
||||||
|
taxes: ProformaFullSnapshot["taxes"],
|
||||||
|
params?: ISnapshotBuilderParams
|
||||||
|
): ProformaReportTaxSnapshot[] {
|
||||||
|
const locale = params?.locale as string;
|
||||||
|
|
||||||
|
const moneyOptions = {
|
||||||
|
hideZeros: true,
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
return taxes.map((tax) => ({
|
||||||
|
taxable_amount: MoneyDTOHelper.format(tax.taxable_amount, locale, moneyOptions),
|
||||||
|
|
||||||
|
iva_code: tax.iva_code,
|
||||||
|
iva_percentage: PercentageDTOHelper.format(tax.iva_percentage, locale),
|
||||||
|
iva_amount: MoneyDTOHelper.format(tax.iva_amount, locale, moneyOptions),
|
||||||
|
|
||||||
|
rec_code: tax.rec_code,
|
||||||
|
rec_percentage: PercentageDTOHelper.format(tax.rec_percentage, locale),
|
||||||
|
rec_amount: MoneyDTOHelper.format(tax.rec_amount, locale, moneyOptions),
|
||||||
|
|
||||||
|
retention_code: tax.retention_code,
|
||||||
|
retention_percentage: PercentageDTOHelper.format(tax.retention_percentage, locale),
|
||||||
|
retention_amount: MoneyDTOHelper.format(tax.retention_amount, locale, moneyOptions),
|
||||||
|
|
||||||
|
taxes_amount: MoneyDTOHelper.format(tax.taxes_amount, locale, moneyOptions),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
import type { ITransactionManager } from "@erp/core/api";
|
||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { CreateProformaRequestDTO } from "../../../../../common";
|
||||||
|
import type { CreateProformaPropsMapper } from "../../mappers";
|
||||||
|
import type { IProformaCreator } from "../../services";
|
||||||
|
import type { IProformaFullSnapshotBuilder } from "../../snapshot-builders";
|
||||||
|
|
||||||
|
type CreateProformaUseCaseInput = {
|
||||||
|
companyId: UniqueID;
|
||||||
|
dto: CreateProformaRequestDTO;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CreateProformaUseCaseDeps = {
|
||||||
|
mapper: CreateProformaPropsMapper;
|
||||||
|
creator: IProformaCreator;
|
||||||
|
fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||||
|
transactionManager: ITransactionManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class CreateProformaUseCase {
|
||||||
|
private readonly mapper: CreateProformaPropsMapper;
|
||||||
|
private readonly creator: IProformaCreator;
|
||||||
|
private readonly fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||||
|
private readonly transactionManager: ITransactionManager;
|
||||||
|
|
||||||
|
constructor(deps: CreateProformaUseCaseDeps) {
|
||||||
|
this.mapper = deps.mapper;
|
||||||
|
this.creator = deps.creator;
|
||||||
|
this.fullSnapshotBuilder = deps.fullSnapshotBuilder;
|
||||||
|
this.transactionManager = deps.transactionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async execute(params: CreateProformaUseCaseInput) {
|
||||||
|
const { dto, companyId } = params;
|
||||||
|
|
||||||
|
// 1) Mapear DTO → props de dominio
|
||||||
|
const mappedResult = this.mapper.map(dto, companyId);
|
||||||
|
if (mappedResult.isFailure) {
|
||||||
|
return Result.fail(mappedResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { props, id } = mappedResult.data;
|
||||||
|
|
||||||
|
return this.transactionManager.complete(async (transaction) => {
|
||||||
|
try {
|
||||||
|
const createResult = await this.creator.create(companyId, id, props, transaction);
|
||||||
|
|
||||||
|
if (createResult.isFailure) {
|
||||||
|
return Result.fail(createResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const snapshot = this.fullSnapshotBuilder.toOutput(createResult.data);
|
||||||
|
|
||||||
|
return Result.ok(snapshot);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return Result.fail(error as Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import type { ITransactionManager } from "@erp/core/api";
|
||||||
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { IProformaFinder } from "../services";
|
||||||
|
import type { IProformaFullSnapshotBuilder } from "../snapshot-builders";
|
||||||
|
|
||||||
|
type GetProformaUseCaseInput = {
|
||||||
|
companyId: UniqueID;
|
||||||
|
proforma_id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class GetProformaByIdUseCase {
|
||||||
|
constructor(
|
||||||
|
private readonly finder: IProformaFinder,
|
||||||
|
private readonly fullSnapshotBuilder: IProformaFullSnapshotBuilder,
|
||||||
|
private readonly transactionManager: ITransactionManager
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public execute(params: GetProformaUseCaseInput) {
|
||||||
|
const { proforma_id, companyId } = params;
|
||||||
|
|
||||||
|
const idOrError = UniqueID.create(proforma_id);
|
||||||
|
if (idOrError.isFailure) {
|
||||||
|
return Result.fail(idOrError.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const proformaId = idOrError.data;
|
||||||
|
|
||||||
|
return this.transactionManager.complete(async (transaction) => {
|
||||||
|
try {
|
||||||
|
const proformaResult = await this.finder.findProformaById(
|
||||||
|
companyId,
|
||||||
|
proformaId,
|
||||||
|
transaction
|
||||||
|
);
|
||||||
|
if (proformaResult.isFailure) {
|
||||||
|
return Result.fail(proformaResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullSnapshot = this.fullSnapshotBuilder.toOutput(proformaResult.data);
|
||||||
|
|
||||||
|
return Result.ok(fullSnapshot);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return Result.fail(error as Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user