Facturas de cliente
This commit is contained in:
parent
67c76c3185
commit
5879220fe9
@ -11,10 +11,10 @@ const DEFAULT_MIN_SCALE = 0;
|
|||||||
const DEFAULT_MAX_SCALE = 4;
|
const DEFAULT_MAX_SCALE = 4;
|
||||||
|
|
||||||
export interface TaxProps {
|
export interface TaxProps {
|
||||||
value: number;
|
code: string; // iva_21
|
||||||
scale: number;
|
name: string; // 21% IVA
|
||||||
name: string;
|
value: number; // 2100
|
||||||
code: string;
|
scale: number; // 2
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Tax extends ValueObject<TaxProps> {
|
export class Tax extends ValueObject<TaxProps> {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { JsonTaxCatalogProvider } from "@erp/core";
|
||||||
import { ITransactionManager } from "@erp/core/api";
|
import { ITransactionManager } from "@erp/core/api";
|
||||||
import { Criteria } from "@repo/rdx-criteria/server";
|
import { Criteria } from "@repo/rdx-criteria/server";
|
||||||
import { UniqueID } from "@repo/rdx-ddd";
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
@ -16,7 +17,8 @@ export class ListCustomerInvoicesUseCase {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly service: CustomerInvoiceService,
|
private readonly service: CustomerInvoiceService,
|
||||||
private readonly transactionManager: ITransactionManager,
|
private readonly transactionManager: ITransactionManager,
|
||||||
private readonly assembler: ListCustomerInvoicesAssembler
|
private readonly assembler: ListCustomerInvoicesAssembler,
|
||||||
|
private readonly taxCatalog: JsonTaxCatalogProvider
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public execute(
|
public execute(
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { DomainValidationError, Taxes } from "@erp/core/api";
|
import { DomainValidationError } from "@erp/core/api";
|
||||||
import {
|
import {
|
||||||
AggregateRoot,
|
AggregateRoot,
|
||||||
CurrencyCode,
|
CurrencyCode,
|
||||||
@ -11,6 +11,7 @@ import {
|
|||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
import { Maybe, Result } from "@repo/rdx-utils";
|
import { Maybe, Result } from "@repo/rdx-utils";
|
||||||
import { CustomerInvoiceItems, InvoiceRecipient } from "../entities";
|
import { CustomerInvoiceItems, InvoiceRecipient } from "../entities";
|
||||||
|
import { InvoiceTaxes } from "../entities/invoice-taxes";
|
||||||
import {
|
import {
|
||||||
CustomerInvoiceNumber,
|
CustomerInvoiceNumber,
|
||||||
CustomerInvoiceSerie,
|
CustomerInvoiceSerie,
|
||||||
@ -53,7 +54,7 @@ export interface CustomerInvoiceProps {
|
|||||||
discountPercentage: Percentage;
|
discountPercentage: Percentage;
|
||||||
//discountAmount: MoneyValue;
|
//discountAmount: MoneyValue;
|
||||||
|
|
||||||
taxes: Taxes;
|
taxes: InvoiceTaxes;
|
||||||
|
|
||||||
//totalAmount: MoneyValue;
|
//totalAmount: MoneyValue;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
export * from "./customer-invoice-items";
|
export * from "./customer-invoice-items";
|
||||||
export * from "./invoice-customer";
|
|
||||||
export * from "./invoice-recipient";
|
export * from "./invoice-recipient";
|
||||||
|
export * from "./invoice-taxes";
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
export * from "./invoice-customer";
|
|
||||||
@ -1,81 +0,0 @@
|
|||||||
import { EmailAddress, Name, PostalAddress, ValueObject } from "@repo/rdx-ddd";
|
|
||||||
import { Result } from "@repo/rdx-utils";
|
|
||||||
import { PhoneNumber } from "libphonenumber-js";
|
|
||||||
import { CustomerInvoiceAddressType } from "../../value-objects";
|
|
||||||
|
|
||||||
export interface ICustomerInvoiceAddressProps {
|
|
||||||
type: CustomerInvoiceAddressType;
|
|
||||||
title: Name;
|
|
||||||
address: PostalAddress;
|
|
||||||
email: EmailAddress;
|
|
||||||
phone: PhoneNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ICustomerInvoiceAddress {
|
|
||||||
type: CustomerInvoiceAddressType;
|
|
||||||
title: Name;
|
|
||||||
address: PostalAddress;
|
|
||||||
email: EmailAddress;
|
|
||||||
phone: PhoneNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CustomerInvoiceAddress
|
|
||||||
extends ValueObject<ICustomerInvoiceAddressProps>
|
|
||||||
implements ICustomerInvoiceAddress
|
|
||||||
{
|
|
||||||
public static create(props: ICustomerInvoiceAddressProps) {
|
|
||||||
return Result.ok(new CustomerInvoiceAddress(props));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static createShippingAddress(props: ICustomerInvoiceAddressProps) {
|
|
||||||
return Result.ok(
|
|
||||||
new CustomerInvoiceAddress({
|
|
||||||
...props,
|
|
||||||
type: CustomerInvoiceAddressType.create("shipping").data,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static createBillingAddress(props: ICustomerInvoiceAddressProps) {
|
|
||||||
return Result.ok(
|
|
||||||
new CustomerInvoiceAddress({
|
|
||||||
...props,
|
|
||||||
type: CustomerInvoiceAddressType.create("billing").data,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get title(): Name {
|
|
||||||
return this.props.title;
|
|
||||||
}
|
|
||||||
|
|
||||||
get address(): PostalAddress {
|
|
||||||
return this.props.address;
|
|
||||||
}
|
|
||||||
|
|
||||||
get email(): EmailAddress {
|
|
||||||
return this.props.email;
|
|
||||||
}
|
|
||||||
|
|
||||||
get phone(): PhoneNumber {
|
|
||||||
return this.props.phone;
|
|
||||||
}
|
|
||||||
|
|
||||||
get type(): CustomerInvoiceAddressType {
|
|
||||||
return this.props.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
getProps(): ICustomerInvoiceAddressProps {
|
|
||||||
return this.props;
|
|
||||||
}
|
|
||||||
|
|
||||||
toPrimitive() {
|
|
||||||
return {
|
|
||||||
type: this.type.toString(),
|
|
||||||
title: this.title.toString(),
|
|
||||||
address: this.address.toString(),
|
|
||||||
email: this.email.toString(),
|
|
||||||
phone: this.phone.toString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
import { DomainEntity, Name, TINNumber, UniqueID } from "@repo/rdx-ddd";
|
|
||||||
import { Result } from "@repo/rdx-utils";
|
|
||||||
import { CustomerInvoiceAddress } from "./customer-invoice-address";
|
|
||||||
|
|
||||||
export interface ICustomerInvoiceCustomerProps {
|
|
||||||
tin: TINNumber;
|
|
||||||
companyName: Name;
|
|
||||||
firstName: Name;
|
|
||||||
lastName: Name;
|
|
||||||
|
|
||||||
billingAddress?: CustomerInvoiceAddress;
|
|
||||||
shippingAddress?: CustomerInvoiceAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ICustomerInvoiceCustomer {
|
|
||||||
id: UniqueID;
|
|
||||||
tin: TINNumber;
|
|
||||||
companyName: Name;
|
|
||||||
firstName: Name;
|
|
||||||
lastName: Name;
|
|
||||||
|
|
||||||
billingAddress?: CustomerInvoiceAddress;
|
|
||||||
shippingAddress?: CustomerInvoiceAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CustomerInvoiceCustomer
|
|
||||||
extends DomainEntity<ICustomerInvoiceCustomerProps>
|
|
||||||
implements ICustomerInvoiceCustomer
|
|
||||||
{
|
|
||||||
public static create(
|
|
||||||
props: ICustomerInvoiceCustomerProps,
|
|
||||||
id?: UniqueID
|
|
||||||
): Result<CustomerInvoiceCustomer, Error> {
|
|
||||||
const participant = new CustomerInvoiceCustomer(props, id);
|
|
||||||
return Result.ok<CustomerInvoiceCustomer>(participant);
|
|
||||||
}
|
|
||||||
|
|
||||||
get tin(): TINNumber {
|
|
||||||
return this.props.tin;
|
|
||||||
}
|
|
||||||
|
|
||||||
get companyName(): Name {
|
|
||||||
return this.props.companyName;
|
|
||||||
}
|
|
||||||
|
|
||||||
get firstName(): Name {
|
|
||||||
return this.props.firstName;
|
|
||||||
}
|
|
||||||
|
|
||||||
get lastName(): Name {
|
|
||||||
return this.props.lastName;
|
|
||||||
}
|
|
||||||
|
|
||||||
get billingAddress() {
|
|
||||||
return this.props.billingAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
get shippingAddress() {
|
|
||||||
return this.props.shippingAddress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./invoice-tax";
|
||||||
|
export * from "./invoice-taxes";
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
import { Tax } from "@erp/core/api";
|
||||||
|
import { ValueObject } from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
import { InvoiceAmount } from "../../value-objects/invoice-amount";
|
||||||
|
|
||||||
|
export interface InvoiceTaxProps {
|
||||||
|
tax: Tax;
|
||||||
|
taxableAmount: InvoiceAmount;
|
||||||
|
taxesAmount: InvoiceAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvoiceTax extends ValueObject<InvoiceTaxProps> {
|
||||||
|
protected static validate(values: InvoiceTaxProps) {
|
||||||
|
return Result.ok(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(values: InvoiceTaxProps): Result<InvoiceTax, Error> {
|
||||||
|
const valueIsValid = InvoiceTax.validate(values);
|
||||||
|
|
||||||
|
if (valueIsValid.isFailure) {
|
||||||
|
return Result.fail(valueIsValid.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(new InvoiceTax(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(partial: Partial<InvoiceTaxProps>): Result<InvoiceTax, Error> {
|
||||||
|
const updatedProps = {
|
||||||
|
...this.props,
|
||||||
|
...partial,
|
||||||
|
} as InvoiceTaxProps;
|
||||||
|
|
||||||
|
return InvoiceTax.create(updatedProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get tax(): Tax {
|
||||||
|
return this.props.tax;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get taxableAmount(): InvoiceAmount {
|
||||||
|
return this.props.taxableAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get taxesAmount(): InvoiceAmount {
|
||||||
|
return this.props.taxesAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
getProps(): InvoiceTaxProps {
|
||||||
|
return this.props;
|
||||||
|
}
|
||||||
|
|
||||||
|
toPrimitive() {
|
||||||
|
return this.getProps();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import { Collection } from "@repo/rdx-utils";
|
||||||
|
import { InvoiceTax } from "./invoice-tax";
|
||||||
|
|
||||||
|
export interface InvoiceTaxesProps {
|
||||||
|
items?: InvoiceTax[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvoiceTaxes extends Collection<InvoiceTax> {
|
||||||
|
constructor(props: InvoiceTaxesProps) {
|
||||||
|
const { items = [] } = props;
|
||||||
|
super(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static create(props: InvoiceTaxesProps): InvoiceTaxes {
|
||||||
|
return new InvoiceTaxes(props);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ export * from "./customer-invoice-item-description";
|
|||||||
export * from "./customer-invoice-number";
|
export * from "./customer-invoice-number";
|
||||||
export * from "./customer-invoice-serie";
|
export * from "./customer-invoice-serie";
|
||||||
export * from "./customer-invoice-status";
|
export * from "./customer-invoice-status";
|
||||||
|
export * from "./invoice-amount";
|
||||||
export * from "./item-amount";
|
export * from "./item-amount";
|
||||||
export * from "./item-discount";
|
export * from "./item-discount";
|
||||||
export * from "./item-quantity";
|
export * from "./item-quantity";
|
||||||
|
|||||||
@ -0,0 +1,24 @@
|
|||||||
|
import { MoneyValue, MoneyValueProps } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
|
type InvoiceAmountProps = Pick<MoneyValueProps, "value" | "currency_code">;
|
||||||
|
|
||||||
|
export class InvoiceAmount extends MoneyValue {
|
||||||
|
public static DEFAULT_SCALE = 2;
|
||||||
|
|
||||||
|
static create({ value, currency_code }: InvoiceAmountProps) {
|
||||||
|
const props = {
|
||||||
|
value: Number(value),
|
||||||
|
scale: InvoiceAmount.DEFAULT_SCALE,
|
||||||
|
currency_code,
|
||||||
|
};
|
||||||
|
return MoneyValue.create(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
static zero(currency_code: string) {
|
||||||
|
const props = {
|
||||||
|
value: 0,
|
||||||
|
currency_code,
|
||||||
|
};
|
||||||
|
return InvoiceAmount.create(props);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -52,10 +52,13 @@ export function getInvoiceDependencies(params: ModuleParams): InvoiceDeps {
|
|||||||
const { database } = params;
|
const { database } = params;
|
||||||
const transactionManager = new SequelizeTransactionManager(database);
|
const transactionManager = new SequelizeTransactionManager(database);
|
||||||
|
|
||||||
if (!_mapper) _mapper = new CustomerInvoiceMapper();
|
if (!_catalogs) _catalogs = { taxes: spainTaxCatalogProvider };
|
||||||
|
if (!_mapper)
|
||||||
|
_mapper = new CustomerInvoiceMapper({
|
||||||
|
taxCatalog: _catalogs!.taxes,
|
||||||
|
});
|
||||||
if (!_repo) _repo = new CustomerInvoiceRepository({ mapper: _mapper, database });
|
if (!_repo) _repo = new CustomerInvoiceRepository({ mapper: _mapper, database });
|
||||||
if (!_service) _service = new CustomerInvoiceService(_repo);
|
if (!_service) _service = new CustomerInvoiceService(_repo);
|
||||||
if (!_catalogs) _catalogs = { taxes: spainTaxCatalogProvider };
|
|
||||||
|
|
||||||
if (!_assemblers) {
|
if (!_assemblers) {
|
||||||
_assemblers = {
|
_assemblers = {
|
||||||
@ -75,8 +78,19 @@ export function getInvoiceDependencies(params: ModuleParams): InvoiceDeps {
|
|||||||
catalogs: _catalogs,
|
catalogs: _catalogs,
|
||||||
build: {
|
build: {
|
||||||
list: () =>
|
list: () =>
|
||||||
new ListCustomerInvoicesUseCase(_service!, transactionManager!, _assemblers!.list),
|
new ListCustomerInvoicesUseCase(
|
||||||
get: () => new GetCustomerInvoiceUseCase(_service!, transactionManager!, _assemblers!.get),
|
_service!,
|
||||||
|
transactionManager!,
|
||||||
|
_assemblers!.list,
|
||||||
|
_catalogs!.taxes
|
||||||
|
),
|
||||||
|
get: () =>
|
||||||
|
new GetCustomerInvoiceUseCase(
|
||||||
|
_service!,
|
||||||
|
transactionManager!,
|
||||||
|
_assemblers!.get,
|
||||||
|
_catalogs!.taxes
|
||||||
|
),
|
||||||
create: () =>
|
create: () =>
|
||||||
new CreateCustomerInvoiceUseCase(
|
new CreateCustomerInvoiceUseCase(
|
||||||
_service!,
|
_service!,
|
||||||
@ -85,7 +99,12 @@ export function getInvoiceDependencies(params: ModuleParams): InvoiceDeps {
|
|||||||
_catalogs!.taxes
|
_catalogs!.taxes
|
||||||
),
|
),
|
||||||
update: () =>
|
update: () =>
|
||||||
new UpdateCustomerInvoiceUseCase(_service!, transactionManager!, _assemblers!.update),
|
new UpdateCustomerInvoiceUseCase(
|
||||||
|
_service!,
|
||||||
|
transactionManager!,
|
||||||
|
_assemblers!.update,
|
||||||
|
_catalogs!.taxes
|
||||||
|
),
|
||||||
delete: () => new DeleteCustomerInvoiceUseCase(_service!, transactionManager!),
|
delete: () => new DeleteCustomerInvoiceUseCase(_service!, transactionManager!),
|
||||||
},
|
},
|
||||||
presenters: {
|
presenters: {
|
||||||
|
|||||||
@ -2,31 +2,23 @@ import {
|
|||||||
ISequelizeMapper,
|
ISequelizeMapper,
|
||||||
MapperParamsType,
|
MapperParamsType,
|
||||||
SequelizeMapper,
|
SequelizeMapper,
|
||||||
|
ValidationErrorCollection,
|
||||||
ValidationErrorDetail,
|
ValidationErrorDetail,
|
||||||
extractOrPushError,
|
extractOrPushError,
|
||||||
} from "@erp/core/api";
|
} from "@erp/core/api";
|
||||||
import {
|
import { UniqueID, maybeFromNullableVO, toNullable } from "@repo/rdx-ddd";
|
||||||
CurrencyCode,
|
|
||||||
LanguageCode,
|
|
||||||
UniqueID,
|
|
||||||
maybeFromNullableVO,
|
|
||||||
toNullable,
|
|
||||||
} from "@repo/rdx-ddd";
|
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
import { InferCreationAttributes } from "sequelize";
|
import { InferCreationAttributes } from "sequelize";
|
||||||
import {
|
import {
|
||||||
CustomerInvoice,
|
CustomerInvoice,
|
||||||
CustomerInvoiceItem,
|
CustomerInvoiceItem,
|
||||||
CustomerInvoiceItemDescription,
|
CustomerInvoiceItemDescription,
|
||||||
|
CustomerInvoiceProps,
|
||||||
ItemAmount,
|
ItemAmount,
|
||||||
ItemDiscount,
|
ItemDiscount,
|
||||||
ItemQuantity,
|
ItemQuantity,
|
||||||
} from "../../domain";
|
} from "../../domain";
|
||||||
import {
|
import { CustomerInvoiceItemCreationAttributes, CustomerInvoiceItemModel } from "../sequelize";
|
||||||
CustomerInvoiceItemCreationAttributes,
|
|
||||||
CustomerInvoiceItemModel,
|
|
||||||
CustomerInvoiceModel,
|
|
||||||
} from "../sequelize";
|
|
||||||
|
|
||||||
export interface ICustomerInvoiceItemMapper
|
export interface ICustomerInvoiceItemMapper
|
||||||
extends ISequelizeMapper<
|
extends ISequelizeMapper<
|
||||||
@ -43,26 +35,16 @@ export class CustomerInvoiceItemMapper
|
|||||||
>
|
>
|
||||||
implements ICustomerInvoiceItemMapper
|
implements ICustomerInvoiceItemMapper
|
||||||
{
|
{
|
||||||
public mapToDomain(
|
private mapAttributesToDomain(source: CustomerInvoiceItemModel, params?: MapperParamsType) {
|
||||||
source: CustomerInvoiceItemModel,
|
const { errors, index, attributes } = params as {
|
||||||
params?: MapperParamsType
|
index: number;
|
||||||
): Result<CustomerInvoiceItem, Error> {
|
|
||||||
const { sourceParent, errors } = params as {
|
|
||||||
sourceParent: CustomerInvoiceModel;
|
|
||||||
errors: ValidationErrorDetail[];
|
errors: ValidationErrorDetail[];
|
||||||
|
attributes: Partial<CustomerInvoiceProps>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const itemId = extractOrPushError(UniqueID.create(source.item_id), "item_id", errors);
|
const itemId = extractOrPushError(
|
||||||
|
UniqueID.create(source.item_id),
|
||||||
const languageCode = extractOrPushError(
|
`items[${index}].item_id`,
|
||||||
LanguageCode.create(sourceParent.language_code),
|
|
||||||
"language_code",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const currencyCode = extractOrPushError(
|
|
||||||
CurrencyCode.create(sourceParent.currency_code),
|
|
||||||
"currency_code",
|
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -70,51 +52,87 @@ export class CustomerInvoiceItemMapper
|
|||||||
maybeFromNullableVO(source.description, (value) =>
|
maybeFromNullableVO(source.description, (value) =>
|
||||||
CustomerInvoiceItemDescription.create(value)
|
CustomerInvoiceItemDescription.create(value)
|
||||||
),
|
),
|
||||||
"description",
|
`items[${index}].description`,
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const quantity = extractOrPushError(
|
const quantity = extractOrPushError(
|
||||||
maybeFromNullableVO(source.quantity_value, (value) => ItemQuantity.create({ value })),
|
maybeFromNullableVO(source.quantity_value, (value) => ItemQuantity.create({ value })),
|
||||||
"discount_percentage",
|
`items[${index}].discount_percentage`,
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const unitAmount = extractOrPushError(
|
const unitAmount = extractOrPushError(
|
||||||
maybeFromNullableVO(source.unit_amount_value, (value) =>
|
maybeFromNullableVO(source.unit_amount_value, (value) =>
|
||||||
ItemAmount.create({ value, currency_code: currencyCode!.code })
|
ItemAmount.create({ value, currency_code: attributes.currencyCode!.code })
|
||||||
),
|
),
|
||||||
"unit_amount",
|
`items[${index}].unit_amount`,
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
itemId,
|
||||||
|
languageCode: attributes.languageCode,
|
||||||
|
currencyCode: attributes.currencyCode,
|
||||||
|
description,
|
||||||
|
quantity,
|
||||||
|
|
||||||
|
unitAmount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public mapToDomain(
|
||||||
|
source: CustomerInvoiceItemModel,
|
||||||
|
params?: MapperParamsType
|
||||||
|
): Result<CustomerInvoiceItem, Error> {
|
||||||
|
const { errors, index, requireIncludes } = params as {
|
||||||
|
index: number;
|
||||||
|
requireIncludes: boolean;
|
||||||
|
errors: ValidationErrorDetail[];
|
||||||
|
attributes: Partial<CustomerInvoiceProps>;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (requireIncludes) {
|
||||||
|
if (!source.taxes) {
|
||||||
|
errors.push({
|
||||||
|
path: `items[${index}].taxes`,
|
||||||
|
message: "Taxes not included in query (requireIncludes=true)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const attributes = this.mapAttributesToDomain(source, params);
|
||||||
|
|
||||||
const discountPercentage = extractOrPushError(
|
const discountPercentage = extractOrPushError(
|
||||||
maybeFromNullableVO(source.discount_percentage_value, (value) =>
|
maybeFromNullableVO(source.discount_percentage_value, (value) =>
|
||||||
ItemDiscount.create({ value })
|
ItemDiscount.create({ value })
|
||||||
),
|
),
|
||||||
"discount_percentage",
|
`items[${index}].discount_percentage`,
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
// Creación del objeto de dominio
|
// Creación del objeto de dominio
|
||||||
const itemOrError = CustomerInvoiceItem.create(
|
const createResult = CustomerInvoiceItem.create(
|
||||||
{
|
{
|
||||||
languageCode: languageCode!,
|
languageCode: attributes.languageCode!,
|
||||||
currencyCode: currencyCode!,
|
currencyCode: attributes.currencyCode!,
|
||||||
description: description!,
|
description: attributes.description!,
|
||||||
quantity: quantity!,
|
quantity: attributes.quantity!,
|
||||||
unitAmount: unitAmount!,
|
unitAmount: attributes.unitAmount!,
|
||||||
discountPercentage: discountPercentage!,
|
discountPercentage: discountPercentage!,
|
||||||
taxes: "",
|
|
||||||
},
|
},
|
||||||
itemId
|
attributes.itemId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (itemOrError.isFailure) {
|
if (createResult.isFailure) {
|
||||||
errors.push({ path: "item", message: itemOrError.error.message });
|
return Result.fail(
|
||||||
|
new ValidationErrorCollection("Invoice item entity creation failed", [
|
||||||
|
{ path: `items[${index}]`, message: createResult.error.message },
|
||||||
|
])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return itemOrError;
|
return createResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public mapToPersistence(
|
public mapToPersistence(
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { JsonTaxCatalogProvider } from "@erp/core";
|
||||||
import {
|
import {
|
||||||
ISequelizeMapper,
|
ISequelizeMapper,
|
||||||
MapperParamsType,
|
MapperParamsType,
|
||||||
@ -25,6 +26,7 @@ import {
|
|||||||
CustomerInvoiceSerie,
|
CustomerInvoiceSerie,
|
||||||
CustomerInvoiceStatus,
|
CustomerInvoiceStatus,
|
||||||
} from "../../domain";
|
} from "../../domain";
|
||||||
|
import { InvoiceTaxes } from "../../domain/entities/invoice-taxes";
|
||||||
import { CustomerInvoiceCreationAttributes, CustomerInvoiceModel } from "../sequelize";
|
import { CustomerInvoiceCreationAttributes, CustomerInvoiceModel } from "../sequelize";
|
||||||
import { CustomerInvoiceItemMapper } from "./customer-invoice-item.mapper";
|
import { CustomerInvoiceItemMapper } from "./customer-invoice-item.mapper";
|
||||||
import { InvoiceRecipientMapper } from "./invoice-recipient.mapper";
|
import { InvoiceRecipientMapper } from "./invoice-recipient.mapper";
|
||||||
@ -45,11 +47,103 @@ export class CustomerInvoiceMapper
|
|||||||
private _recipientMapper: InvoiceRecipientMapper;
|
private _recipientMapper: InvoiceRecipientMapper;
|
||||||
private _taxesMapper: TaxesMapper;
|
private _taxesMapper: TaxesMapper;
|
||||||
|
|
||||||
constructor() {
|
constructor(params: {
|
||||||
|
taxCatalog: JsonTaxCatalogProvider;
|
||||||
|
}) {
|
||||||
super();
|
super();
|
||||||
this._itemsMapper = new CustomerInvoiceItemMapper(); // Instanciar el mapper de items
|
this._itemsMapper = new CustomerInvoiceItemMapper(); // Instanciar el mapper de items
|
||||||
this._recipientMapper = new InvoiceRecipientMapper();
|
this._recipientMapper = new InvoiceRecipientMapper();
|
||||||
this._taxesMapper = new TaxesMapper();
|
this._taxesMapper = new TaxesMapper(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapAttributesToDomain(source: CustomerInvoiceModel, params?: MapperParamsType) {
|
||||||
|
const { errors } = params as {
|
||||||
|
errors: ValidationErrorDetail[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const invoiceId = extractOrPushError(UniqueID.create(source.id), "id", errors);
|
||||||
|
const companyId = extractOrPushError(UniqueID.create(source.company_id), "company_id", errors);
|
||||||
|
|
||||||
|
const customerId = extractOrPushError(
|
||||||
|
UniqueID.create(source.customer_id),
|
||||||
|
"customer_id",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const isProforma = Boolean(source.is_proforma);
|
||||||
|
|
||||||
|
const status = extractOrPushError(
|
||||||
|
CustomerInvoiceStatus.create(source.status),
|
||||||
|
"status",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const series = extractOrPushError(
|
||||||
|
maybeFromNullableVO(source.series, (value) => CustomerInvoiceSerie.create(value)),
|
||||||
|
"serie",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const invoiceNumber = extractOrPushError(
|
||||||
|
CustomerInvoiceNumber.create(source.invoice_number),
|
||||||
|
"invoice_number",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const invoiceDate = extractOrPushError(
|
||||||
|
UtcDate.createFromISO(source.invoice_date),
|
||||||
|
"invoice_date",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const operationDate = extractOrPushError(
|
||||||
|
maybeFromNullableVO(source.operation_date, (value) => UtcDate.createFromISO(value)),
|
||||||
|
"operation_date",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const notes = extractOrPushError(
|
||||||
|
maybeFromNullableVO(source.notes, (value) => TextValue.create(value)),
|
||||||
|
"notes",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const languageCode = extractOrPushError(
|
||||||
|
LanguageCode.create(source.language_code),
|
||||||
|
"language_code",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const currencyCode = extractOrPushError(
|
||||||
|
CurrencyCode.create(source.currency_code),
|
||||||
|
"currency_code",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const discountPercentage = extractOrPushError(
|
||||||
|
Percentage.create({
|
||||||
|
value: source.discount_percentage_value,
|
||||||
|
scale: source.discount_percentage_scale,
|
||||||
|
}),
|
||||||
|
"discount_percentage",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
invoiceId,
|
||||||
|
companyId,
|
||||||
|
customerId,
|
||||||
|
isProforma,
|
||||||
|
status,
|
||||||
|
series,
|
||||||
|
invoiceNumber,
|
||||||
|
invoiceDate,
|
||||||
|
operationDate,
|
||||||
|
notes,
|
||||||
|
languageCode,
|
||||||
|
currencyCode,
|
||||||
|
discountPercentage,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public mapToDomain(
|
public mapToDomain(
|
||||||
@ -59,135 +153,130 @@ export class CustomerInvoiceMapper
|
|||||||
try {
|
try {
|
||||||
const errors: ValidationErrorDetail[] = [];
|
const errors: ValidationErrorDetail[] = [];
|
||||||
|
|
||||||
const invoiceId = extractOrPushError(UniqueID.create(source.id), "id", errors);
|
const attributes = this.mapAttributesToDomain(source, { errors, ...params });
|
||||||
const companyId = extractOrPushError(
|
|
||||||
UniqueID.create(source.company_id),
|
|
||||||
"company_id",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const isProforma = Boolean(source.is_proforma);
|
const requireIncludes = Boolean(params?.requireIncludes);
|
||||||
|
if (requireIncludes) {
|
||||||
|
if (!source.items) {
|
||||||
|
errors.push({
|
||||||
|
path: "items",
|
||||||
|
message: "Items not included in query (requireIncludes=true)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!source.taxes) {
|
||||||
|
errors.push({
|
||||||
|
path: "taxes",
|
||||||
|
message: "Taxes not included in query (requireIncludes=true)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const status = extractOrPushError(
|
if (attributes.isProforma && !source.current_customer) {
|
||||||
CustomerInvoiceStatus.create(source.status),
|
errors.push({
|
||||||
"status",
|
path: "current_customer",
|
||||||
errors
|
message: "Current customer not included in query (requireIncludes=true)",
|
||||||
);
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const series = extractOrPushError(
|
// 3) Recipient (snapshot en la factura o include)
|
||||||
maybeFromNullableVO(source.series, (value) => CustomerInvoiceSerie.create(value)),
|
const recipientResult = this._recipientMapper.mapToDomain(source, {
|
||||||
"serie",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const invoiceNumber = extractOrPushError(
|
|
||||||
CustomerInvoiceNumber.create(source.invoice_number),
|
|
||||||
"invoice_number",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const invoiceDate = extractOrPushError(
|
|
||||||
UtcDate.createFromISO(source.invoice_date),
|
|
||||||
"invoice_date",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const operationDate = extractOrPushError(
|
|
||||||
maybeFromNullableVO(source.operation_date, (value) => UtcDate.createFromISO(value)),
|
|
||||||
"operation_date",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const notes = extractOrPushError(
|
|
||||||
maybeFromNullableVO(source.notes, (value) => TextValue.create(value)),
|
|
||||||
"notes",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const languageCode = extractOrPushError(
|
|
||||||
LanguageCode.create(source.language_code),
|
|
||||||
"language_code",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const currencyCode = extractOrPushError(
|
|
||||||
CurrencyCode.create(source.currency_code),
|
|
||||||
"currency_code",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const discountPercentage = extractOrPushError(
|
|
||||||
Percentage.create({
|
|
||||||
value: source.discount_percentage_value,
|
|
||||||
scale: source.discount_percentage_scale,
|
|
||||||
}),
|
|
||||||
"discount_percentage",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
// Customer
|
|
||||||
const customerId = extractOrPushError(
|
|
||||||
UniqueID.create(source.customer_id),
|
|
||||||
"customer_id",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
// Recipient (customer data) (snapshot)
|
|
||||||
const recipient = this._recipientMapper.mapToDomain(source, {
|
|
||||||
errors,
|
errors,
|
||||||
|
attributes,
|
||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mapear los items de la factura
|
if (recipientResult.isFailure) {
|
||||||
const itemsOrResult = this._itemsMapper.mapArrayToDomain(source.items, {
|
errors.push({
|
||||||
parent: source,
|
path: "recipient",
|
||||||
|
message: recipientResult.error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) Items (colección)
|
||||||
|
const itemsResults = this._itemsMapper.mapArrayToDomain(source.items, {
|
||||||
|
requireIncludes,
|
||||||
errors,
|
errors,
|
||||||
|
attributes,
|
||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mapear los impuestos
|
if (itemsResults.isFailure) {
|
||||||
const taxesOrResult = this._taxesMapper.mapArrayToDomain(source.taxes, {
|
errors.push({
|
||||||
parent: source,
|
path: "items",
|
||||||
|
message: recipientResult.error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5) Taxes (colección a nivel factura)
|
||||||
|
const taxesResults = this._taxesMapper.mapArrayToDomain(source.taxes, {
|
||||||
errors,
|
errors,
|
||||||
|
attributes,
|
||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (taxesResults.isFailure) {
|
||||||
|
errors.push({
|
||||||
|
path: "taxes",
|
||||||
|
message: recipientResult.error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6) Si hubo errores de mapeo, devolvemos colección de validación
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
return Result.fail(
|
return Result.fail(
|
||||||
new ValidationErrorCollection("Customer invoice item props mapping failed", errors)
|
new ValidationErrorCollection("Customer invoice mapping failed", errors)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 7) Construcción del agregado (Dominio)
|
||||||
|
|
||||||
|
const recipient = recipientResult.data;
|
||||||
|
|
||||||
|
const taxes = InvoiceTaxes.create({
|
||||||
|
items: taxesResults.data.getAll(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = CustomerInvoiceItems.create({
|
||||||
|
languageCode: attributes.languageCode!,
|
||||||
|
currencyCode: attributes.currencyCode!,
|
||||||
|
items: itemsResults.data.getAll(),
|
||||||
|
});
|
||||||
|
|
||||||
const invoiceProps: CustomerInvoiceProps = {
|
const invoiceProps: CustomerInvoiceProps = {
|
||||||
companyId: companyId!,
|
companyId: attributes.companyId!,
|
||||||
|
|
||||||
isProforma: isProforma,
|
isProforma: attributes.isProforma,
|
||||||
status: status!,
|
status: attributes.status!,
|
||||||
series: series!,
|
series: attributes.series!,
|
||||||
invoiceNumber: invoiceNumber!,
|
invoiceNumber: attributes.invoiceNumber!,
|
||||||
invoiceDate: invoiceDate!,
|
invoiceDate: attributes.invoiceDate!,
|
||||||
operationDate: operationDate!,
|
operationDate: attributes.operationDate!,
|
||||||
|
|
||||||
customerId: customerId!,
|
customerId: attributes.customerId!,
|
||||||
recipient: recipient,
|
recipient: recipient,
|
||||||
|
|
||||||
notes: notes!,
|
notes: attributes.notes!,
|
||||||
|
|
||||||
languageCode: languageCode!,
|
languageCode: attributes.languageCode!,
|
||||||
currencyCode: currencyCode!,
|
currencyCode: attributes.currencyCode!,
|
||||||
|
|
||||||
discountPercentage: discountPercentage!,
|
discountPercentage: attributes.discountPercentage!,
|
||||||
|
|
||||||
taxes: taxesOrResult,
|
taxes,
|
||||||
|
items,
|
||||||
items: CustomerInvoiceItems.create({
|
|
||||||
languageCode: languageCode!,
|
|
||||||
currencyCode: currencyCode!,
|
|
||||||
items: itemsOrResult.isSuccess ? itemsOrResult.data.getAll() : [],
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return CustomerInvoice.create(invoiceProps, invoiceId);
|
const createResult = CustomerInvoice.create(invoiceProps, attributes.invoiceId);
|
||||||
|
|
||||||
|
if (createResult.isFailure) {
|
||||||
|
return Result.fail(
|
||||||
|
new ValidationErrorCollection("Customer invoice entity creation failed", [
|
||||||
|
{ path: "invoice", message: createResult.error.message },
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(createResult.data);
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
return Result.fail(err as Error);
|
return Result.fail(err as Error);
|
||||||
}
|
}
|
||||||
@ -262,9 +351,10 @@ export class CustomerInvoiceMapper
|
|||||||
discount_amount_scale: source.discountAmount.scale,
|
discount_amount_scale: source.discountAmount.scale,
|
||||||
|
|
||||||
taxable_amount_value: source.taxableAmount.value,
|
taxable_amount_value: source.taxableAmount.value,
|
||||||
taxable_amount_scale: source.taxableAmount.value,
|
taxable_amount_scale: source.taxableAmount.scale,
|
||||||
tax_amount_value: source.taxAmount.value,
|
|
||||||
tax_amount_scale: source.taxAmount.value,
|
taxes_amount_value: source.taxAmount.value,
|
||||||
|
taxes_amount_scale: source.taxAmount.scale,
|
||||||
|
|
||||||
total_amount_value: 0, //total.amount,
|
total_amount_value: 0, //total.amount,
|
||||||
total_amount_scale: 2, //total.scale,
|
total_amount_scale: 2, //total.scale,
|
||||||
|
|||||||
@ -9,69 +9,82 @@ import {
|
|||||||
maybeFromNullableVO,
|
maybeFromNullableVO,
|
||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
|
|
||||||
import { MapperParamsType, ValidationErrorDetail, extractOrPushError } from "@erp/core/api";
|
import {
|
||||||
import { Maybe, isNullishOrEmpty } from "@repo/rdx-utils";
|
MapperParamsType,
|
||||||
|
ValidationErrorCollection,
|
||||||
|
ValidationErrorDetail,
|
||||||
|
extractOrPushError,
|
||||||
|
} from "@erp/core/api";
|
||||||
|
import { Maybe, Result } from "@repo/rdx-utils";
|
||||||
import { InferCreationAttributes } from "sequelize";
|
import { InferCreationAttributes } from "sequelize";
|
||||||
import { CustomerInvoice, InvoiceRecipient } from "../../domain";
|
import { CustomerInvoice, CustomerInvoiceProps, InvoiceRecipient } from "../../domain";
|
||||||
import { CustomerInvoiceModel } from "../sequelize";
|
import { CustomerInvoiceModel } from "../sequelize";
|
||||||
|
|
||||||
export class InvoiceRecipientMapper {
|
export class InvoiceRecipientMapper {
|
||||||
public mapToDomain(source: CustomerInvoiceModel, params?: MapperParamsType) {
|
public mapToDomain(
|
||||||
const { errors } = params as {
|
source: CustomerInvoiceModel,
|
||||||
|
params?: MapperParamsType
|
||||||
|
): Result<Maybe<InvoiceRecipient>, Error> {
|
||||||
|
const { errors, attributes } = params as {
|
||||||
errors: ValidationErrorDetail[];
|
errors: ValidationErrorDetail[];
|
||||||
|
attributes: Partial<CustomerInvoiceProps>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { isProforma } = attributes;
|
||||||
|
|
||||||
|
const _name = isProforma ? source.current_customer.name : source.customer_name;
|
||||||
|
const _tin = isProforma ? source.current_customer.tin : source.customer_tin;
|
||||||
|
const _street = isProforma ? source.current_customer.street : source.customer_street;
|
||||||
|
const _street2 = isProforma ? source.current_customer.street2 : source.customer_street2;
|
||||||
|
const _city = isProforma ? source.current_customer.city : source.customer_city;
|
||||||
|
const _postal_code = isProforma
|
||||||
|
? source.current_customer.postal_code
|
||||||
|
: source.customer_postal_code;
|
||||||
|
const _province = isProforma ? source.current_customer.province : source.customer_province;
|
||||||
|
const _country = isProforma ? source.current_customer.country : source.customer_country;
|
||||||
|
|
||||||
// Customer (snapshot)
|
// Customer (snapshot)
|
||||||
|
const customerName = extractOrPushError(Name.create(_name), "customer_name", errors);
|
||||||
|
|
||||||
const customerName = extractOrPushError(
|
const customerTin = extractOrPushError(TINNumber.create(_tin), "customer_tin", errors);
|
||||||
Name.create(source.customer_name),
|
|
||||||
"customer_name",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const customerTin = extractOrPushError(
|
|
||||||
TINNumber.create(source.customer_tin),
|
|
||||||
"customer_tin",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const customerStreet = extractOrPushError(
|
const customerStreet = extractOrPushError(
|
||||||
maybeFromNullableVO(source.customer_street, (value) => Street.create(value)),
|
maybeFromNullableVO(_street, (value) => Street.create(value)),
|
||||||
"customer_street",
|
"customer_street",
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const customerStreet2 = extractOrPushError(
|
const customerStreet2 = extractOrPushError(
|
||||||
maybeFromNullableVO(source.customer_street2, (value) => Street.create(value)),
|
maybeFromNullableVO(_street2, (value) => Street.create(value)),
|
||||||
"customer_street2",
|
"customer_street2",
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const customerCity = extractOrPushError(
|
const customerCity = extractOrPushError(
|
||||||
maybeFromNullableVO(source.customer_city, (value) => City.create(value)),
|
maybeFromNullableVO(_city, (value) => City.create(value)),
|
||||||
"customer_city",
|
"customer_city",
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const customerProvince = extractOrPushError(
|
const customerProvince = extractOrPushError(
|
||||||
maybeFromNullableVO(source.customer_province, (value) => Province.create(value)),
|
maybeFromNullableVO(_province, (value) => Province.create(value)),
|
||||||
"customer_province",
|
"customer_province",
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const customerPostalCode = extractOrPushError(
|
const customerPostalCode = extractOrPushError(
|
||||||
maybeFromNullableVO(source.customer_postal_code, (value) => PostalCode.create(value)),
|
maybeFromNullableVO(_postal_code, (value) => PostalCode.create(value)),
|
||||||
"customer_postal_code",
|
"customer_postal_code",
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const customerCountry = extractOrPushError(
|
const customerCountry = extractOrPushError(
|
||||||
maybeFromNullableVO(source.customer_country, (value) => Country.create(value)),
|
maybeFromNullableVO(_country, (value) => Country.create(value)),
|
||||||
"customer_country",
|
"customer_country",
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const recipientOrError = InvoiceRecipient.create({
|
const createResult = InvoiceRecipient.create({
|
||||||
name: customerName!,
|
name: customerName!,
|
||||||
tin: customerTin!,
|
tin: customerTin!,
|
||||||
street: customerStreet!,
|
street: customerStreet!,
|
||||||
@ -82,16 +95,21 @@ export class InvoiceRecipientMapper {
|
|||||||
country: customerCountry!,
|
country: customerCountry!,
|
||||||
});
|
});
|
||||||
|
|
||||||
return isNullishOrEmpty(recipientOrError)
|
if (createResult.isFailure) {
|
||||||
? Maybe.none<InvoiceRecipient>()
|
return Result.fail(
|
||||||
: Maybe.some(recipientOrError.data);
|
new ValidationErrorCollection("Invoice recipient entity creation failed", [
|
||||||
|
{ path: "recipient", message: createResult.error.message },
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(Maybe.some(createResult.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public mapToPersistence(
|
public mapToPersistence(
|
||||||
source: InvoiceRecipient,
|
source: InvoiceRecipient,
|
||||||
params?: MapperParamsType
|
params?: MapperParamsType
|
||||||
): Partial<InferCreationAttributes<CustomerInvoiceModel, {}>> {
|
): Partial<InferCreationAttributes<CustomerInvoiceModel, {}>> {
|
||||||
1;
|
|
||||||
const { index, sourceParent } = params as {
|
const { index, sourceParent } = params as {
|
||||||
index: number;
|
index: number;
|
||||||
sourceParent: CustomerInvoice;
|
sourceParent: CustomerInvoice;
|
||||||
|
|||||||
@ -1,9 +1,104 @@
|
|||||||
import { MapperParamsType, Taxes } from "@erp/core/api";
|
import { JsonTaxCatalogProvider } from "@erp/core";
|
||||||
|
import {
|
||||||
|
MapperParamsType,
|
||||||
|
SequelizeMapper,
|
||||||
|
Tax,
|
||||||
|
Taxes,
|
||||||
|
ValidationErrorCollection,
|
||||||
|
ValidationErrorDetail,
|
||||||
|
extractOrPushError,
|
||||||
|
} from "@erp/core/api";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
import { InferCreationAttributes } from "sequelize";
|
import { InferCreationAttributes } from "sequelize";
|
||||||
import { CustomerInvoiceItemTaxModel, CustomerInvoiceTaxModel } from "../sequelize";
|
import { CustomerInvoiceProps, InvoiceAmount } from "../../domain";
|
||||||
|
import { InvoiceTax } from "../../domain/entities/invoice-taxes";
|
||||||
|
import {
|
||||||
|
CustomerInvoiceItemTaxModel,
|
||||||
|
CustomerInvoiceTaxCreationAttributes,
|
||||||
|
CustomerInvoiceTaxModel,
|
||||||
|
} from "../sequelize";
|
||||||
|
|
||||||
export class TaxesMapper {
|
export class TaxesMapper extends SequelizeMapper<
|
||||||
public mapArrayToDomain(taxes: CustomerInvoiceTaxModel[], params?: MapperParamsType) {}
|
CustomerInvoiceTaxModel,
|
||||||
|
CustomerInvoiceTaxCreationAttributes,
|
||||||
|
InvoiceTax
|
||||||
|
> {
|
||||||
|
private _taxCatalog: JsonTaxCatalogProvider;
|
||||||
|
|
||||||
|
constructor(params: {
|
||||||
|
taxCatalog: JsonTaxCatalogProvider;
|
||||||
|
}) {
|
||||||
|
super();
|
||||||
|
const { taxCatalog } = params;
|
||||||
|
this._taxCatalog = taxCatalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public mapToDomain(
|
||||||
|
source: CustomerInvoiceTaxModel,
|
||||||
|
params?: MapperParamsType
|
||||||
|
): Result<InvoiceTax, Error> {
|
||||||
|
const { errors, index, attributes } = params as {
|
||||||
|
index: number;
|
||||||
|
requireIncludes: boolean;
|
||||||
|
errors: ValidationErrorDetail[];
|
||||||
|
attributes: Partial<CustomerInvoiceProps>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tax = extractOrPushError(
|
||||||
|
Tax.createFromCode(source.tax_code, this._taxCatalog),
|
||||||
|
`taxes[${index}].tax_code`,
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const taxableAmount = extractOrPushError(
|
||||||
|
InvoiceAmount.create({
|
||||||
|
value: source.taxable_amount_value,
|
||||||
|
currency_code: attributes.currencyCode?.code,
|
||||||
|
}),
|
||||||
|
`taxes[${index}].taxable_amount_value`,
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
if (source.taxable_amount_scale !== InvoiceAmount.DEFAULT_SCALE) {
|
||||||
|
errors.push({
|
||||||
|
path: `taxes[${index}].taxable_amount_scale`,
|
||||||
|
message: "Invalid taxable amount scale",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const taxesAmount = extractOrPushError(
|
||||||
|
InvoiceAmount.create({
|
||||||
|
value: source.taxes_amount_value,
|
||||||
|
currency_code: attributes.currencyCode?.code,
|
||||||
|
}),
|
||||||
|
`taxes[${index}].taxes_amount_value`,
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
if (source.taxes_amount_scale !== InvoiceAmount.DEFAULT_SCALE) {
|
||||||
|
errors.push({
|
||||||
|
path: `taxes[${index}].taxes_amount_scale`,
|
||||||
|
message: "Invalid taxes amount scale",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creación del objeto de dominio
|
||||||
|
const createResult = InvoiceTax.create({
|
||||||
|
tax: tax!,
|
||||||
|
taxableAmount: taxableAmount!,
|
||||||
|
taxesAmount: taxesAmount!,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (createResult.isFailure) {
|
||||||
|
return Result.fail(
|
||||||
|
new ValidationErrorCollection("Invoice taxes creation failed", [
|
||||||
|
{ path: `taxes[${index}]`, message: createResult.error.message },
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return createResult;
|
||||||
|
}
|
||||||
|
|
||||||
public mapToPersistence(
|
public mapToPersistence(
|
||||||
source: Taxes,
|
source: Taxes,
|
||||||
|
|||||||
@ -27,8 +27,8 @@ export class CustomerInvoiceItemTaxModel extends Model<
|
|||||||
declare taxable_amount_scale: number;
|
declare taxable_amount_scale: number;
|
||||||
|
|
||||||
// Total tax amount / taxes total // 21,00 €
|
// Total tax amount / taxes total // 21,00 €
|
||||||
declare tax_amount_value: number;
|
declare taxes_amount_value: number;
|
||||||
declare tax_amount_scale: number;
|
declare taxes_amount_scale: number;
|
||||||
|
|
||||||
// Relaciones
|
// Relaciones
|
||||||
declare item: NonAttribute<CustomerInvoiceItem>;
|
declare item: NonAttribute<CustomerInvoiceItem>;
|
||||||
@ -71,18 +71,20 @@ export default (database: Sequelize) => {
|
|||||||
allowNull: true,
|
allowNull: true,
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
taxable_amount_scale: {
|
taxable_amount_scale: {
|
||||||
type: new DataTypes.SMALLINT(),
|
type: new DataTypes.SMALLINT(),
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
defaultValue: 2,
|
defaultValue: 2,
|
||||||
},
|
},
|
||||||
|
|
||||||
tax_amount_value: {
|
taxes_amount_value: {
|
||||||
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
|
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
tax_amount_scale: {
|
|
||||||
|
taxes_amount_scale: {
|
||||||
type: new DataTypes.SMALLINT(),
|
type: new DataTypes.SMALLINT(),
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
defaultValue: 2,
|
defaultValue: 2,
|
||||||
|
|||||||
@ -27,8 +27,8 @@ export class CustomerInvoiceTaxModel extends Model<
|
|||||||
declare taxable_amount_scale: number;
|
declare taxable_amount_scale: number;
|
||||||
|
|
||||||
// Total tax amount / taxes total // 21,00 €
|
// Total tax amount / taxes total // 21,00 €
|
||||||
declare tax_amount_value: number;
|
declare taxes_amount_value: number;
|
||||||
declare tax_amount_scale: number;
|
declare taxes_amount_scale: number;
|
||||||
|
|
||||||
// Relaciones
|
// Relaciones
|
||||||
declare invoice: NonAttribute<CustomerInvoice>;
|
declare invoice: NonAttribute<CustomerInvoice>;
|
||||||
@ -70,18 +70,20 @@ export default (database: Sequelize) => {
|
|||||||
allowNull: true,
|
allowNull: true,
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
taxable_amount_scale: {
|
taxable_amount_scale: {
|
||||||
type: new DataTypes.SMALLINT(),
|
type: new DataTypes.SMALLINT(),
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
defaultValue: 2,
|
defaultValue: 2,
|
||||||
},
|
},
|
||||||
|
|
||||||
tax_amount_value: {
|
taxes_amount_value: {
|
||||||
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
|
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
tax_amount_scale: {
|
|
||||||
|
taxes_amount_scale: {
|
||||||
type: new DataTypes.SMALLINT(),
|
type: new DataTypes.SMALLINT(),
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
defaultValue: 2,
|
defaultValue: 2,
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import {
|
|||||||
|
|
||||||
export type CustomerInvoiceCreationAttributes = InferCreationAttributes<
|
export type CustomerInvoiceCreationAttributes = InferCreationAttributes<
|
||||||
CustomerInvoiceModel,
|
CustomerInvoiceModel,
|
||||||
{ omit: "items" | "taxes" | "currentCustomer" }
|
{ omit: "items" | "taxes" | "current_customer" }
|
||||||
> & {
|
> & {
|
||||||
items?: CustomerInvoiceItemCreationAttributes[];
|
items?: CustomerInvoiceItemCreationAttributes[];
|
||||||
taxes?: CustomerInvoiceTaxCreationAttributes[];
|
taxes?: CustomerInvoiceTaxCreationAttributes[];
|
||||||
@ -81,7 +81,7 @@ export class CustomerInvoiceModel extends Model<
|
|||||||
// Relaciones
|
// Relaciones
|
||||||
declare items: NonAttribute<CustomerInvoiceItemModel[]>;
|
declare items: NonAttribute<CustomerInvoiceItemModel[]>;
|
||||||
declare taxes: NonAttribute<CustomerInvoiceTaxModel[]>;
|
declare taxes: NonAttribute<CustomerInvoiceTaxModel[]>;
|
||||||
declare currentCustomer: NonAttribute<CustomerModel>;
|
declare current_customer: NonAttribute<CustomerModel>;
|
||||||
|
|
||||||
static associate(database: Sequelize) {
|
static associate(database: Sequelize) {
|
||||||
const {
|
const {
|
||||||
@ -92,7 +92,7 @@ export class CustomerInvoiceModel extends Model<
|
|||||||
} = database.models;
|
} = database.models;
|
||||||
|
|
||||||
CustomerInvoiceModel.belongsTo(CustomerModel, {
|
CustomerInvoiceModel.belongsTo(CustomerModel, {
|
||||||
as: "currentCustomer",
|
as: "current_customer",
|
||||||
foreignKey: "customer_id",
|
foreignKey: "customer_id",
|
||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -159,7 +159,7 @@ export class CustomerInvoiceRepository
|
|||||||
query.include = [
|
query.include = [
|
||||||
{
|
{
|
||||||
model: CustomerModel,
|
model: CustomerModel,
|
||||||
as: "currentCustomer",
|
as: "current_customer",
|
||||||
required: false, // false => LEFT JOIN
|
required: false, // false => LEFT JOIN
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user