Facturas de cliente
This commit is contained in:
parent
d17a22dc9f
commit
2e4bb56458
@ -1,3 +1,5 @@
|
||||
export * from "./spain-tax-catalog-provider";
|
||||
export * from "./tax-catalog-provider";
|
||||
export * from "./json-tax-catalog.provider";
|
||||
export * from "./spain-tax-catalog.provider";
|
||||
export * from "./tax-catalog-types";
|
||||
export * from "./tax-catalog.provider";
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// --- Adaptador que carga el catálogo JSON en memoria e indexa por code ---
|
||||
|
||||
import { Maybe } from "@repo/rdx-utils";
|
||||
import { TaxCatalogProvider } from "./tax-catalog-provider";
|
||||
import { TaxCatalogType, TaxItemType } from "./tax-catalog-types";
|
||||
import { TaxCatalogProvider } from "./tax-catalog.provider";
|
||||
|
||||
// Si quieres habilitar la carga desde fichero en Node:
|
||||
// import * as fs from "node:fs";
|
||||
@ -1,4 +1,4 @@
|
||||
import { JsonTaxCatalogProvider } from "./json-tax-catalog-provider";
|
||||
import { JsonTaxCatalogProvider } from "./json-tax-catalog.provider";
|
||||
import spainTaxCatalog from "./spain-tax-catalog.json";
|
||||
|
||||
export const spainTaxCatalogProvider = new JsonTaxCatalogProvider(spainTaxCatalog);
|
||||
@ -1,3 +1,4 @@
|
||||
import { JsonTaxCatalogProvider } from "@erp/core";
|
||||
import { DuplicateEntityError, ITransactionManager } from "@erp/core/api";
|
||||
import { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
@ -5,7 +6,7 @@ import { Transaction } from "sequelize";
|
||||
import { CreateCustomerInvoiceRequestDTO } from "../../../common/dto";
|
||||
import { CustomerInvoiceService } from "../../domain";
|
||||
import { CreateCustomerInvoiceAssembler } from "./assembler";
|
||||
import { mapDTOToCreateCustomerInvoiceProps } from "./map-dto-to-create-customer-invoice-props";
|
||||
import { CreateCustomerInvoicePropsMapper } from "./map-dto-to-create-customer-invoice-props";
|
||||
|
||||
type CreateCustomerInvoiceUseCaseInput = {
|
||||
companyId: UniqueID;
|
||||
@ -16,14 +17,17 @@ export class CreateCustomerInvoiceUseCase {
|
||||
constructor(
|
||||
private readonly service: CustomerInvoiceService,
|
||||
private readonly transactionManager: ITransactionManager,
|
||||
private readonly assembler: CreateCustomerInvoiceAssembler
|
||||
private readonly assembler: CreateCustomerInvoiceAssembler,
|
||||
private readonly taxCatalog: JsonTaxCatalogProvider,
|
||||
|
||||
) {}
|
||||
|
||||
public execute(params: CreateCustomerInvoiceUseCaseInput) {
|
||||
const { dto, companyId } = params;
|
||||
const dtoMapper = new CreateCustomerInvoicePropsMapper({ taxCatalog: this.taxCatalog });
|
||||
|
||||
// 1) Mapear DTO → props de dominio
|
||||
const dtoResult = mapDTOToCreateCustomerInvoiceProps(dto);
|
||||
const dtoResult = dtoMapper.map(dto);
|
||||
if (dtoResult.isFailure) {
|
||||
return Result.fail(dtoResult.error);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { spainTaxCatalogProvider } from "@erp/core";
|
||||
import { JsonTaxCatalogProvider } from "@erp/core";
|
||||
import {
|
||||
DomainError,
|
||||
Tax,
|
||||
@ -45,181 +45,189 @@ import {
|
||||
*
|
||||
*/
|
||||
|
||||
export function mapDTOToCreateCustomerInvoiceProps(dto: CreateCustomerInvoiceRequestDTO) {
|
||||
try {
|
||||
const errors: ValidationErrorDetail[] = [];
|
||||
export class CreateCustomerInvoicePropsMapper {
|
||||
private readonly taxCatalog: JsonTaxCatalogProvider;
|
||||
private errors: ValidationErrorDetail[] = [];
|
||||
private languageCode?: LanguageCode;
|
||||
private currencyCode?: CurrencyCode;
|
||||
|
||||
const customerId = extractOrPushError(UniqueID.create(dto.id), "id", errors);
|
||||
const companyId = extractOrPushError(UniqueID.create(dto.company_id), "company_id", errors);
|
||||
constructor(params: {taxCatalog: JsonTaxCatalogProvider}) {
|
||||
this.taxCatalog = params.taxCatalog;
|
||||
this.errors = [];
|
||||
}
|
||||
|
||||
const invoiceNumber = extractOrPushError(
|
||||
CustomerInvoiceNumber.create(dto.invoice_number),
|
||||
"invoice_number",
|
||||
errors
|
||||
);
|
||||
const status = extractOrPushError(CustomerInvoiceStatus.create(dto.status), "status", errors);
|
||||
public map(dto: CreateCustomerInvoiceRequestDTO) {
|
||||
try {
|
||||
this.errors = [];
|
||||
|
||||
const series = extractOrPushError(
|
||||
maybeFromNullableVO(dto.series, (value) => CustomerInvoiceSerie.create(value)),
|
||||
"series",
|
||||
errors
|
||||
);
|
||||
const defaultStatus = CustomerInvoiceStatus.createDraft();
|
||||
|
||||
const invoiceDate = extractOrPushError(
|
||||
UtcDate.createFromISO(dto.invoice_date),
|
||||
"invoice_date",
|
||||
errors
|
||||
);
|
||||
const invoiceId = extractOrPushError(UniqueID.create(dto.id), "id", this.errors);
|
||||
const companyId = extractOrPushError(UniqueID.create(dto.company_id), "company_id", this.errors);
|
||||
const customerId = extractOrPushError(UniqueID.create(dto.customer_id), "customer_id", this.errors);
|
||||
|
||||
const operationDate = extractOrPushError(
|
||||
maybeFromNullableVO(dto.operation_date, (value) => UtcDate.createFromISO(value)),
|
||||
"operation_date",
|
||||
errors
|
||||
);
|
||||
|
||||
const notes = extractOrPushError(
|
||||
maybeFromNullableVO(dto.notes, (value) => TextValue.create(value)),
|
||||
"notes",
|
||||
errors
|
||||
);
|
||||
|
||||
const languageCode = extractOrPushError(
|
||||
LanguageCode.create(dto.language_code),
|
||||
"language_code",
|
||||
errors
|
||||
);
|
||||
|
||||
const currencyCode = extractOrPushError(
|
||||
CurrencyCode.create(dto.currency_code),
|
||||
"currency_code",
|
||||
errors
|
||||
);
|
||||
|
||||
const discountPercentage = extractOrPushError(
|
||||
Percentage.create({
|
||||
value: Number(dto.discount_percentage.value),
|
||||
scale: Number(dto.discount_percentage.scale),
|
||||
}),
|
||||
"discount_percentage",
|
||||
errors
|
||||
);
|
||||
|
||||
const items = _mapDTOtoInvoiceItems(dto.items, languageCode!, currencyCode!, errors);
|
||||
|
||||
if (errors.length > 0) {
|
||||
return Result.fail(
|
||||
new ValidationErrorCollection("Customer invoice props mapping failed", errors)
|
||||
const invoiceNumber = extractOrPushError(
|
||||
CustomerInvoiceNumber.create(dto.invoice_number),
|
||||
"invoice_number",
|
||||
this.errors
|
||||
);
|
||||
|
||||
const series = extractOrPushError(
|
||||
maybeFromNullableVO(dto.series, (value) => CustomerInvoiceSerie.create(value)),
|
||||
"series",
|
||||
this.errors
|
||||
);
|
||||
|
||||
const invoiceDate = extractOrPushError(
|
||||
UtcDate.createFromISO(dto.invoice_date),
|
||||
"invoice_date",
|
||||
this.errors
|
||||
);
|
||||
|
||||
const operationDate = extractOrPushError(
|
||||
maybeFromNullableVO(dto.operation_date, (value) => UtcDate.createFromISO(value)),
|
||||
"operation_date",
|
||||
this.errors
|
||||
);
|
||||
|
||||
const notes = extractOrPushError(
|
||||
maybeFromNullableVO(dto.notes, (value) => TextValue.create(value)),
|
||||
"notes",
|
||||
this.errors
|
||||
);
|
||||
|
||||
this.languageCode = extractOrPushError(
|
||||
LanguageCode.create(dto.language_code),
|
||||
"language_code",
|
||||
this.errors
|
||||
);
|
||||
|
||||
this.currencyCode = extractOrPushError(
|
||||
CurrencyCode.create(dto.currency_code),
|
||||
"currency_code",
|
||||
this.errors
|
||||
);
|
||||
|
||||
const discountPercentage = extractOrPushError(
|
||||
Percentage.create({
|
||||
value: Number(dto.discount_percentage.value),
|
||||
scale: Number(dto.discount_percentage.scale),
|
||||
}),
|
||||
"discount_percentage",
|
||||
this.errors
|
||||
);
|
||||
|
||||
const items = this.mapItems(dto.items);
|
||||
|
||||
if (this.errors.length > 0) {
|
||||
return Result.fail(
|
||||
new ValidationErrorCollection("Customer invoice props mapping failed", this.errors)
|
||||
);
|
||||
}
|
||||
|
||||
const invoiceProps: CustomerInvoiceProps = {
|
||||
companyId: companyId!,
|
||||
status: defaultStatus!,
|
||||
|
||||
invoiceNumber: invoiceNumber!,
|
||||
invoiceDate: invoiceDate!,
|
||||
|
||||
operationDate: operationDate!,
|
||||
series: series!,
|
||||
|
||||
notes: notes!,
|
||||
|
||||
customerId: customerId!,
|
||||
|
||||
languageCode: this.languageCode!,
|
||||
currencyCode: this.currencyCode!,
|
||||
|
||||
discountPercentage: discountPercentage!,
|
||||
|
||||
taxes: Taxes.create({ items: [] }),
|
||||
items: items,
|
||||
};
|
||||
|
||||
return Result.ok({ id: invoiceId!, props: invoiceProps });
|
||||
} catch (err: unknown) {
|
||||
return Result.fail(new DomainError("Customer invoice props mapping failed", { cause: err }));
|
||||
}
|
||||
}
|
||||
|
||||
const invoiceProps: CustomerInvoiceProps = {
|
||||
companyId: companyId!,
|
||||
status: status!,
|
||||
private mapItems(items: CreateCustomerInvoiceItemRequestDTO[]) {
|
||||
const invoiceItems = CustomerInvoiceItems.create({
|
||||
currencyCode: this.currencyCode!,
|
||||
languageCode: this.languageCode!,
|
||||
items: [],
|
||||
});
|
||||
|
||||
invoiceNumber: invoiceNumber!,
|
||||
invoiceDate: invoiceDate!,
|
||||
items.forEach((item, index) => {
|
||||
const description = extractOrPushError(
|
||||
maybeFromNullableVO(item.description, (value) =>
|
||||
CustomerInvoiceItemDescription.create(value)
|
||||
),
|
||||
"description",
|
||||
this.errors
|
||||
);
|
||||
|
||||
operationDate: operationDate!,
|
||||
series: series!,
|
||||
const quantity = extractOrPushError(
|
||||
maybeFromNullableVO(item.quantity, (value) => ItemQuantity.create(value)),
|
||||
"quantity",
|
||||
this.errors
|
||||
);
|
||||
|
||||
notes: notes!,
|
||||
const unitAmount = extractOrPushError(
|
||||
maybeFromNullableVO(item.unit_amount, (value) => ItemAmount.create(value)),
|
||||
"unit_amount",
|
||||
this.errors
|
||||
);
|
||||
|
||||
languageCode: languageCode!,
|
||||
currencyCode: currencyCode!,
|
||||
const discountPercentage = extractOrPushError(
|
||||
maybeFromNullableVO(item.discount_percentage, (value) => ItemDiscount.create(value)),
|
||||
"discount_percentage",
|
||||
this.errors
|
||||
);
|
||||
|
||||
discountPercentage: discountPercentage!,
|
||||
const taxes = this.mapTaxes(item, index);
|
||||
|
||||
taxes: Taxes.create({ items: [] }),
|
||||
items: items,
|
||||
};
|
||||
const itemProps: CustomerInvoiceItemProps = {
|
||||
currencyCode: this.currencyCode!,
|
||||
languageCode: this.languageCode!,
|
||||
description: description!,
|
||||
quantity: quantity!,
|
||||
unitAmount: unitAmount!,
|
||||
discountPercentage: discountPercentage!,
|
||||
taxes,
|
||||
};
|
||||
|
||||
return Result.ok({ id: customerId!, props: invoiceProps });
|
||||
} catch (err: unknown) {
|
||||
return Result.fail(new DomainError("Customer invoice props mapping failed", { cause: err }));
|
||||
const itemResult = CustomerInvoiceItem.create(itemProps);
|
||||
if (itemResult.isSuccess) {
|
||||
invoiceItems.add(itemResult.data);
|
||||
} else {
|
||||
this.errors.push({
|
||||
path: `items[${index}]`,
|
||||
message: itemResult.error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
return invoiceItems;
|
||||
}
|
||||
|
||||
private mapTaxes(item: CreateCustomerInvoiceItemRequestDTO, itemIndex: number) {
|
||||
const taxes = Taxes.create({ items: [] });
|
||||
|
||||
item.taxes.split(",").every((tax_code, taxIndex) => {
|
||||
const taxResult = Tax.createFromCode(tax_code, this.taxCatalog);
|
||||
if (taxResult.isSuccess) {
|
||||
taxes.add(taxResult.data);
|
||||
} else {
|
||||
this.errors.push({
|
||||
path: `items[${itemIndex}].taxes[${taxIndex}]`,
|
||||
message: taxResult.error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
return taxes;
|
||||
}
|
||||
}
|
||||
|
||||
function _mapDTOtoInvoiceItems(
|
||||
items: CreateCustomerInvoiceItemRequestDTO[],
|
||||
languageCode: LanguageCode,
|
||||
currencyCode: CurrencyCode,
|
||||
errors: ValidationErrorDetail[]
|
||||
) {
|
||||
const invoiceItems = CustomerInvoiceItems.create({
|
||||
currencyCode,
|
||||
languageCode,
|
||||
items: [],
|
||||
});
|
||||
|
||||
items.forEach((item, index) => {
|
||||
const description = extractOrPushError(
|
||||
maybeFromNullableVO(item.description, (value) =>
|
||||
CustomerInvoiceItemDescription.create(value)
|
||||
),
|
||||
"description",
|
||||
errors
|
||||
);
|
||||
|
||||
const quantity = extractOrPushError(
|
||||
maybeFromNullableVO(item.quantity, (value) => ItemQuantity.create(value)),
|
||||
"quantity",
|
||||
errors
|
||||
);
|
||||
|
||||
const unitAmount = extractOrPushError(
|
||||
maybeFromNullableVO(item.unit_amount, (value) => ItemAmount.create(value)),
|
||||
"unit_amount",
|
||||
errors
|
||||
);
|
||||
|
||||
const discountPercentage = extractOrPushError(
|
||||
maybeFromNullableVO(item.discount_percentage, (value) => ItemDiscount.create(value)),
|
||||
"discount_percentage",
|
||||
errors
|
||||
);
|
||||
|
||||
const taxes = _mapDTOtoTaxes(item, index, errors);
|
||||
|
||||
const itemProps: CustomerInvoiceItemProps = {
|
||||
currencyCode: currencyCode!,
|
||||
languageCode: languageCode!,
|
||||
description: description!,
|
||||
quantity: quantity!,
|
||||
unitAmount: unitAmount!,
|
||||
discountPercentage: discountPercentage!,
|
||||
taxes,
|
||||
};
|
||||
|
||||
const itemResult = CustomerInvoiceItem.create(itemProps);
|
||||
if (itemResult.isSuccess) {
|
||||
invoiceItems.add(itemResult.data);
|
||||
} else {
|
||||
errors.push({
|
||||
path: `items[${index}]`,
|
||||
message: itemResult.error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
return invoiceItems;
|
||||
}
|
||||
|
||||
function _mapDTOtoTaxes(
|
||||
item: CreateCustomerInvoiceItemRequestDTO,
|
||||
itemIndex: number,
|
||||
errors: ValidationErrorDetail[]
|
||||
) {
|
||||
const taxes = Taxes.create({ items: [] });
|
||||
|
||||
item.taxes.split(",").every((tax_code, taxIndex) => {
|
||||
const taxResult = Tax.createFromCode(tax_code, spainTaxCatalogProvider);
|
||||
if (taxResult.isSuccess) {
|
||||
taxes.add(taxResult.data);
|
||||
} else {
|
||||
errors.push({
|
||||
path: `items[${itemIndex}].taxes[${taxIndex}]`,
|
||||
message: taxResult.error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
return taxes;
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ export class GetCustomerInvoiceUseCase {
|
||||
constructor(
|
||||
private readonly service: CustomerInvoiceService,
|
||||
private readonly transactionManager: ITransactionManager,
|
||||
private readonly assembler: GetCustomerInvoiceAssembler
|
||||
private readonly assembler: GetCustomerInvoiceAssembler,
|
||||
) {}
|
||||
|
||||
public execute(params: GetCustomerInvoiceUseCaseInput) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Taxes } from "@erp/core/api";
|
||||
import { DomainValidationError, Taxes } from "@erp/core/api";
|
||||
import {
|
||||
AggregateRoot,
|
||||
CurrencyCode,
|
||||
@ -10,7 +10,7 @@ import {
|
||||
UtcDate,
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Maybe, Result } from "@repo/rdx-utils";
|
||||
import { CustomerInvoiceItems } from "../entities";
|
||||
import { CustomerInvoiceItems, InvoiceRecipient } from "../entities";
|
||||
import {
|
||||
CustomerInvoiceNumber,
|
||||
CustomerInvoiceSerie,
|
||||
@ -27,6 +27,10 @@ export interface CustomerInvoiceProps {
|
||||
invoiceDate: UtcDate;
|
||||
operationDate: Maybe<UtcDate>;
|
||||
|
||||
customerId: UniqueID;
|
||||
|
||||
recipient: Maybe<InvoiceRecipient>;
|
||||
|
||||
notes: Maybe<TextValue>;
|
||||
|
||||
//dueDate: UtcDate; // ? --> depende de la forma de pago
|
||||
@ -52,7 +56,7 @@ export interface CustomerInvoiceProps {
|
||||
|
||||
//totalAmount: MoneyValue;
|
||||
|
||||
//customer?: CustomerInvoiceCustomer;
|
||||
|
||||
items: CustomerInvoiceItems;
|
||||
}
|
||||
|
||||
@ -75,9 +79,17 @@ export class CustomerInvoice extends AggregateRoot<CustomerInvoiceProps> {
|
||||
const customerInvoice = new CustomerInvoice(props, id);
|
||||
|
||||
// Reglas de negocio / validaciones
|
||||
// ...
|
||||
// ...
|
||||
|
||||
|
||||
if (!customerInvoice.isDraft() && !customerInvoice.hasRecipient()) {
|
||||
return Result.fail(
|
||||
new DomainValidationError(
|
||||
"MISSING_CUSTOMER_DATA",
|
||||
"customerData",
|
||||
"Customer data must be provided for non-draft invoices"
|
||||
)
|
||||
);
|
||||
}
|
||||
// 🔹 Disparar evento de dominio "CustomerInvoiceAuthenticatedEvent"
|
||||
//const { customerInvoice } = props;
|
||||
//user.addDomainEvent(new CustomerInvoiceAuthenticatedEvent(id, customerInvoice.toString()));
|
||||
@ -93,6 +105,10 @@ export class CustomerInvoice extends AggregateRoot<CustomerInvoiceProps> {
|
||||
return this.props.companyId;
|
||||
}
|
||||
|
||||
public get customerId(): UniqueID {
|
||||
return this.props.customerId;
|
||||
}
|
||||
|
||||
public get status(): CustomerInvoiceStatus {
|
||||
return this.props.status;
|
||||
}
|
||||
@ -117,6 +133,10 @@ export class CustomerInvoice extends AggregateRoot<CustomerInvoiceProps> {
|
||||
return this.props.notes;
|
||||
}
|
||||
|
||||
public get recipient(): Maybe<InvoiceRecipient> {
|
||||
return this.props.recipient;
|
||||
}
|
||||
|
||||
public get languageCode(): LanguageCode {
|
||||
return this.props.languageCode;
|
||||
}
|
||||
@ -154,6 +174,14 @@ export class CustomerInvoice extends AggregateRoot<CustomerInvoiceProps> {
|
||||
return this._items;
|
||||
}
|
||||
|
||||
public isDraft() {
|
||||
return this.status.isDraft();
|
||||
}
|
||||
|
||||
public hasRecipient() {
|
||||
return this.recipient.isSome()
|
||||
}
|
||||
|
||||
/*get senderId(): UniqueID {
|
||||
return this.props.senderId;
|
||||
}*/
|
||||
|
||||
@ -1,2 +1,4 @@
|
||||
export * from "./customer-invoice-items";
|
||||
export * from "./invoice-customer";
|
||||
export * from "./invoice-recipient";
|
||||
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
export * from "./invoice-recipient";
|
||||
|
||||
|
||||
@ -0,0 +1,103 @@
|
||||
import {
|
||||
City,
|
||||
Country,
|
||||
Name,
|
||||
PostalCode,
|
||||
Province,
|
||||
Street,
|
||||
TINNumber,
|
||||
ValueObject
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Maybe, Result } from "@repo/rdx-utils";
|
||||
import * as z from "zod/v4";
|
||||
|
||||
export interface InvoiceRecipientProps {
|
||||
name: Name;
|
||||
tin: TINNumber;
|
||||
street: Maybe<Street>;
|
||||
street2: Maybe<Street>;
|
||||
city: Maybe<City>;
|
||||
postalCode: Maybe<PostalCode>;
|
||||
province: Maybe<Province>;
|
||||
country: Maybe<Country>;
|
||||
}
|
||||
|
||||
export class InvoiceRecipient extends ValueObject<InvoiceRecipientProps> {
|
||||
protected static validate(values: InvoiceRecipientProps) {
|
||||
const schema = z.object({
|
||||
name: z.string(),
|
||||
tin: z.string(),
|
||||
street: z.string().optional(),
|
||||
street2: z.string().optional(),
|
||||
city: z.string().optional(),
|
||||
postalCode: z.string().optional(),
|
||||
province: z.string().optional(),
|
||||
country: z.string().optional(),
|
||||
})
|
||||
|
||||
return schema.safeParse(values);
|
||||
}
|
||||
|
||||
static create(values: InvoiceRecipientProps): Result<InvoiceRecipient, Error> {
|
||||
const valueIsValid = InvoiceRecipient.validate(values)
|
||||
|
||||
if (!valueIsValid.success) {
|
||||
return Result.fail(new Error(valueIsValid.error.issues[0].message));
|
||||
}
|
||||
|
||||
return Result.ok(new InvoiceRecipient(values));
|
||||
}
|
||||
|
||||
public update(partial: Partial<InvoiceRecipientProps>): Result<InvoiceRecipient, Error> {
|
||||
const updatedProps = {
|
||||
...this.props,
|
||||
...partial,
|
||||
} as InvoiceRecipientProps;
|
||||
|
||||
return InvoiceRecipient.create(updatedProps);
|
||||
}
|
||||
|
||||
|
||||
public get name(): Name {
|
||||
return this.props.name;
|
||||
}
|
||||
|
||||
public get tin(): TINNumber {
|
||||
return this.props.tin;
|
||||
}
|
||||
|
||||
get street(): Maybe<Street> {
|
||||
return this.props.street;
|
||||
}
|
||||
|
||||
get street2(): Maybe<Street> {
|
||||
return this.props.street2;
|
||||
}
|
||||
|
||||
get city(): Maybe<City> {
|
||||
return this.props.city;
|
||||
}
|
||||
|
||||
get postalCode(): Maybe<PostalCode> {
|
||||
return this.props.postalCode;
|
||||
}
|
||||
|
||||
get province(): Maybe<Province> {
|
||||
return this.props.province;
|
||||
}
|
||||
|
||||
get country(): Maybe<Country> {
|
||||
return this.props.country;
|
||||
}
|
||||
|
||||
getProps(): InvoiceRecipientProps {
|
||||
return this.props;
|
||||
}
|
||||
|
||||
toPrimitive() {
|
||||
return this.getProps();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -71,6 +71,10 @@ export class CustomerInvoiceStatus extends ValueObject<ICustomerInvoiceStatusPro
|
||||
return new CustomerInvoiceStatus({ value: INVOICE_STATUS.REJECTED });
|
||||
}
|
||||
|
||||
isDraft(): boolean {
|
||||
return this.props.value === INVOICE_STATUS.DRAFT
|
||||
}
|
||||
|
||||
getProps(): string {
|
||||
return this.props.value;
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { JsonTaxCatalogProvider, spainTaxCatalogProvider } from "@erp/core";
|
||||
import type { ModuleParams } from "@erp/core/api";
|
||||
import { SequelizeTransactionManager } from "@erp/core/api";
|
||||
import {
|
||||
@ -9,6 +10,7 @@ import {
|
||||
ListCustomerInvoicesAssembler,
|
||||
ListCustomerInvoicesUseCase,
|
||||
UpdateCustomerInvoiceAssembler,
|
||||
UpdateCustomerInvoiceUseCase,
|
||||
} from "../application";
|
||||
import { CustomerInvoiceService } from "../domain";
|
||||
import { CustomerInvoiceMapper } from "./mappers";
|
||||
@ -19,6 +21,9 @@ type InvoiceDeps = {
|
||||
repo: CustomerInvoiceRepository;
|
||||
mapper: CustomerInvoiceMapper;
|
||||
service: CustomerInvoiceService;
|
||||
catalogs: {
|
||||
taxes: JsonTaxCatalogProvider;
|
||||
};
|
||||
assemblers: {
|
||||
list: ListCustomerInvoicesAssembler;
|
||||
get: GetCustomerInvoiceAssembler;
|
||||
@ -41,6 +46,7 @@ let _repo: CustomerInvoiceRepository | null = null;
|
||||
let _mapper: CustomerInvoiceMapper | null = null;
|
||||
let _service: CustomerInvoiceService | null = null;
|
||||
let _assemblers: InvoiceDeps["assemblers"] | null = null;
|
||||
let _catalogs: InvoiceDeps["catalogs"] | null = null;
|
||||
|
||||
export function getInvoiceDependencies(params: ModuleParams): InvoiceDeps {
|
||||
const { database } = params;
|
||||
@ -49,6 +55,7 @@ export function getInvoiceDependencies(params: ModuleParams): InvoiceDeps {
|
||||
if (!_mapper) _mapper = new CustomerInvoiceMapper();
|
||||
if (!_repo) _repo = new CustomerInvoiceRepository(_mapper);
|
||||
if (!_service) _service = new CustomerInvoiceService(_repo);
|
||||
if (!_catalogs) _catalogs = { taxes: spainTaxCatalogProvider };
|
||||
|
||||
if (!_assemblers) {
|
||||
_assemblers = {
|
||||
@ -65,12 +72,18 @@ export function getInvoiceDependencies(params: ModuleParams): InvoiceDeps {
|
||||
mapper: _mapper,
|
||||
service: _service,
|
||||
assemblers: _assemblers,
|
||||
catalogs: _catalogs,
|
||||
build: {
|
||||
list: () =>
|
||||
new ListCustomerInvoicesUseCase(_service!, transactionManager!, _assemblers!.list),
|
||||
get: () => new GetCustomerInvoiceUseCase(_service!, transactionManager!, _assemblers!.get),
|
||||
create: () =>
|
||||
new CreateCustomerInvoiceUseCase(_service!, transactionManager!, _assemblers!.create),
|
||||
new CreateCustomerInvoiceUseCase(
|
||||
_service!,
|
||||
transactionManager!,
|
||||
_assemblers!.create,
|
||||
_catalogs!.taxes
|
||||
),
|
||||
update: () =>
|
||||
new UpdateCustomerInvoiceUseCase(_service!, transactionManager!, _assemblers!.update),
|
||||
delete: () => new DeleteCustomerInvoiceUseCase(_service!, transactionManager!),
|
||||
|
||||
@ -7,9 +7,16 @@ import {
|
||||
extractOrPushError,
|
||||
} from "@erp/core/api";
|
||||
import {
|
||||
City,
|
||||
Country,
|
||||
CurrencyCode,
|
||||
LanguageCode,
|
||||
Name,
|
||||
Percentage,
|
||||
PostalCode,
|
||||
Province,
|
||||
Street,
|
||||
TINNumber,
|
||||
TextValue,
|
||||
UniqueID,
|
||||
UtcDate,
|
||||
@ -117,11 +124,60 @@ export class CustomerInvoiceMapper
|
||||
errors
|
||||
);
|
||||
|
||||
if (errors.length > 0) {
|
||||
return Result.fail(
|
||||
new ValidationErrorCollection("Customer invoice props mapping failed", errors)
|
||||
);
|
||||
}
|
||||
// Customer
|
||||
const customerId = extractOrPushError(
|
||||
UniqueID.create(source.customer_id),
|
||||
"customer_id",
|
||||
errors
|
||||
);
|
||||
|
||||
const customerName = extractOrPushError(
|
||||
maybeFromNullableVO(source.customer_name, (value) => Name.create(value)),
|
||||
"customer_name",
|
||||
errors
|
||||
);
|
||||
|
||||
const customerTin = extractOrPushError(
|
||||
maybeFromNullableVO(source.customer_tin, (value) => TINNumber.create(value)),
|
||||
"customer_tin",
|
||||
errors
|
||||
);
|
||||
|
||||
const customerStreet = extractOrPushError(
|
||||
maybeFromNullableVO(source.customer_street, (value) => Street.create(value)),
|
||||
"customer_street",
|
||||
errors
|
||||
);
|
||||
|
||||
const customerStreet2 = extractOrPushError(
|
||||
maybeFromNullableVO(source.customer_street2, (value) => Street.create(value)),
|
||||
"customer_street2",
|
||||
errors
|
||||
);
|
||||
|
||||
const customerCity = extractOrPushError(
|
||||
maybeFromNullableVO(source.customer_city, (value) => City.create(value)),
|
||||
"customer_city",
|
||||
errors
|
||||
);
|
||||
|
||||
const customerProvince = extractOrPushError(
|
||||
maybeFromNullableVO(source.customer_province, (value) => Province.create(value)),
|
||||
"customer_province",
|
||||
errors
|
||||
);
|
||||
|
||||
const customerPostalCode = extractOrPushError(
|
||||
maybeFromNullableVO(source.customer_postal_code, (value) => PostalCode.create(value)),
|
||||
"customer_postal_code",
|
||||
errors
|
||||
);
|
||||
|
||||
const customerCountry = extractOrPushError(
|
||||
maybeFromNullableVO(source.customer_country, (value) => Country.create(value)),
|
||||
"customer_country",
|
||||
errors
|
||||
);
|
||||
|
||||
// Mapear los items de la factura
|
||||
const items = this._itemsMapper.mapArrayToDomain(source.items, {
|
||||
@ -130,6 +186,8 @@ export class CustomerInvoiceMapper
|
||||
...params,
|
||||
});
|
||||
|
||||
// Mapear los impuestos
|
||||
|
||||
if (errors.length > 0) {
|
||||
return Result.fail(
|
||||
new ValidationErrorCollection("Customer invoice item props mapping failed", errors)
|
||||
@ -144,6 +202,8 @@ export class CustomerInvoiceMapper
|
||||
invoiceDate: invoiceDate!,
|
||||
operationDate: operationDate!,
|
||||
|
||||
customerId: customerId!,
|
||||
|
||||
notes: notes!,
|
||||
|
||||
languageCode: languageCode!,
|
||||
@ -167,11 +227,30 @@ export class CustomerInvoiceMapper
|
||||
source: CustomerInvoice,
|
||||
params?: MapperParamsType
|
||||
): CustomerInvoiceCreationAttributes {
|
||||
//const subtotal = source.calculateSubtotal();
|
||||
//const total = source.calculateTotal();
|
||||
|
||||
const items = this._itemsMapper.mapCollectionToPersistence(source.items, params);
|
||||
|
||||
const customer = source.recipient.match((recipient) => ({
|
||||
customer_id: recipient.id.toPrimitive(),
|
||||
customer_tin: recipient.tin.toPrimitive(),
|
||||
customer_name: recipient.name.toPrimitive(),
|
||||
customer_street: toNullable(recipient.address.street, (street) => street.toPrimitive()),
|
||||
customer_street2: toNullable(recipient.address.street2, (street2) => street2.toPrimitive()),
|
||||
customer_city: toNullable(recipient.address.city, (city) => city.toPrimitive()),
|
||||
customer_province: toNullable(recipient.address.province, (province) => province.toPrimitive()),
|
||||
customer_postal_code: toNullable(recipient.address.postalCode, (postalCode) => postalCode.toPrimitive()),
|
||||
customer_country: toNullable(recipient.address.country, (country) => country.toPrimitive()),
|
||||
}) as any, () => ({
|
||||
customer_id: source.customerId.toPrimitive(),
|
||||
customer_tin: null,
|
||||
customer_name: null,
|
||||
customer_street: null,
|
||||
customer_street2: null,
|
||||
customer_city: null,
|
||||
customer_province: null,
|
||||
customer_postal_code: null,
|
||||
customer_country: null,
|
||||
})) as any
|
||||
|
||||
return {
|
||||
id: source.id.toPrimitive(),
|
||||
company_id: source.companyId.toPrimitive(),
|
||||
@ -194,16 +273,23 @@ export class CustomerInvoiceMapper
|
||||
subtotal_amount_value: 0, //subtotal.amount,
|
||||
subtotal_amount_scale: 2, //subtotal.scale,
|
||||
|
||||
/*discount_percentage_value: source.discountPercentage.value,
|
||||
discount_percentage_value: source.discountPercentage.value,
|
||||
discount_percentage_scale: source.discountPercentage.scale,
|
||||
|
||||
discount_amount_value: source.discountAmount.value,
|
||||
discount_amount_scale: source.discountAmount.scale,*/
|
||||
discount_amount_scale: source.discountAmount.scale,
|
||||
|
||||
taxable_amount_value: source.taxableAmount.value,
|
||||
taxable_amount_scale: source.taxableAmount.value,
|
||||
tax_amount_value: source.taxAmount.value,
|
||||
tax_amount_scale: source.taxAmount.value,
|
||||
|
||||
total_amount_value: 0, //total.amount,
|
||||
total_amount_scale: 2, //total.scale,
|
||||
|
||||
items,
|
||||
...customer,
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,6 +59,17 @@ export class CustomerInvoiceModel extends Model<
|
||||
declare total_amount_value: number;
|
||||
declare total_amount_scale: number;
|
||||
|
||||
// Customer
|
||||
declare customer_id: string;
|
||||
declare customer_tin: string;
|
||||
declare customer_name: string;
|
||||
declare customer_street: string;
|
||||
declare customer_street2: string;
|
||||
declare customer_city: string;
|
||||
declare customer_province: string;
|
||||
declare customer_postal_code: string;
|
||||
declare customer_country: string;
|
||||
|
||||
// Relaciones
|
||||
declare items: NonAttribute<CustomerInvoiceItemModel[]>;
|
||||
//declare customer: NonAttribute<CustomerInvoiceParticipant_Model[]>;
|
||||
@ -216,6 +227,51 @@ export default (database: Sequelize) => {
|
||||
allowNull: false,
|
||||
defaultValue: 2,
|
||||
},
|
||||
|
||||
customer_id: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
},
|
||||
customer_tin: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: null,
|
||||
},
|
||||
customer_name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: null,
|
||||
},
|
||||
customer_street: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: null,
|
||||
},
|
||||
customer_street2: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: null,
|
||||
},
|
||||
customer_city: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: null,
|
||||
},
|
||||
customer_province: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: null,
|
||||
},
|
||||
customer_postal_code: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: null,
|
||||
},
|
||||
customer_country: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize: database,
|
||||
|
||||
@ -16,12 +16,13 @@ export const CreateCustomerInvoiceRequestSchema = z.object({
|
||||
company_id: z.uuid(),
|
||||
|
||||
invoice_number: z.string(),
|
||||
status: z.string().default("draft"),
|
||||
series: z.string().default(""),
|
||||
|
||||
invoice_date: z.string(),
|
||||
operation_date: z.string().default(""),
|
||||
|
||||
customer_id: z.uuid(),
|
||||
|
||||
notes: z.string().default(""),
|
||||
|
||||
language_code: z.string().toLowerCase().default("es"),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user