This commit is contained in:
David Arranz 2026-02-17 19:13:59 +01:00
parent 7f77b18d52
commit 302f3293c6
67 changed files with 1288 additions and 844 deletions

View File

@ -2,7 +2,7 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
@ -12,7 +12,7 @@ import {
type IssuedInvoiceItemProps,
ItemAmount,
ItemDescription,
ItemDiscount,
ItemDiscountPercentage,
ItemQuantity,
} from "../../domain";
@ -28,25 +28,27 @@ export function mapDTOToCustomerInvoiceItemsProps(
const path = (field: string) => `items[${index}].${field}`;
const description = extractOrPushError(
maybeFromNullableVO(item.description, (value) => ItemDescription.create(value)),
maybeFromNullableResult(item.description, (value) => ItemDescription.create(value)),
path("description"),
errors
);
const quantity = extractOrPushError(
maybeFromNullableVO(item.quantity, (value) => ItemQuantity.create({ value })),
maybeFromNullableResult(item.quantity, (value) => ItemQuantity.create({ value })),
path("quantity"),
errors
);
const unitAmount = extractOrPushError(
maybeFromNullableVO(item.unit_amount, (value) => ItemAmount.create({ value })),
maybeFromNullableResult(item.unit_amount, (value) => ItemAmount.create({ value })),
path("unit_amount"),
errors
);
const discountPercentage = extractOrPushError(
maybeFromNullableVO(item.discount_percentage, (value) => ItemDiscount.create({ value })),
maybeFromNullableResult(item.discount_percentage, (value) =>
ItemDiscountPercentage.create({ value })
),
path("discount_percentage"),
errors
);

View File

@ -5,7 +5,7 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
@ -30,12 +30,12 @@ export function mapDTOToCustomerInvoiceProps(dto: CreateCustomerInvoiceRequestDT
const invoiceId = extractOrPushError(UniqueID.create(dto.id), "id", errors);
const invoiceNumber = extractOrPushError(
maybeFromNullableVO(dto.invoice_number, (value) => InvoiceNumber.create(value)),
maybeFromNullableResult(dto.invoice_number, (value) => InvoiceNumber.create(value)),
"invoice_number",
errors
);
const invoiceSeries = extractOrPushError(
maybeFromNullableVO(dto.series, (value) => InvoiceSerie.create(value)),
maybeFromNullableResult(dto.series, (value) => InvoiceSerie.create(value)),
"invoice_series",
errors
);
@ -45,7 +45,7 @@ export function mapDTOToCustomerInvoiceProps(dto: CreateCustomerInvoiceRequestDT
errors
);
const operationDate = extractOrPushError(
maybeFromNullableVO(dto.operation_date, (value) => UtcDate.createFromISO(value)),
maybeFromNullableResult(dto.operation_date, (value) => UtcDate.createFromISO(value)),
"operation_date",
errors
);

View File

@ -1,5 +1,5 @@
import type { ISnapshotBuilder } from "@erp/core/api";
import { toEmptyString } from "@repo/rdx-ddd";
import { maybeToEmptyString } from "@repo/rdx-ddd";
import { InvoiceAmount, type IssuedInvoice } from "../../../../domain";
@ -87,14 +87,14 @@ export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnaps
is_proforma: invoice.isProforma ? "true" : "false",
invoice_number: invoice.invoiceNumber.toString(),
status: invoice.status.toPrimitive(),
series: toEmptyString(invoice.series, (value) => value.toString()),
series: maybeToEmptyString(invoice.series, (value) => value.toString()),
invoice_date: invoice.invoiceDate.toDateString(),
operation_date: toEmptyString(invoice.operationDate, (value) => value.toDateString()),
operation_date: maybeToEmptyString(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()),
reference: maybeToEmptyString(invoice.reference, (value) => value.toString()),
description: maybeToEmptyString(invoice.description, (value) => value.toString()),
notes: maybeToEmptyString(invoice.notes, (value) => value.toString()),
language_code: invoice.languageCode.toString(),
currency_code: invoice.currencyCode.toString(),

View File

@ -1,5 +1,5 @@
import type { ISnapshotBuilder } from "@erp/core/api";
import { toEmptyString } from "@repo/rdx-ddd";
import { maybeToEmptyString } from "@repo/rdx-ddd";
import type { IssuedInvoiceItem, IssuedInvoiceItems } from "../../../../domain";
@ -16,7 +16,7 @@ export class IssuedInvoiceItemsFullSnapshotBuilder
id: invoiceItem.id.toPrimitive(),
is_valued: String(invoiceItem.isValued),
position: String(index),
description: toEmptyString(invoiceItem.description, (value) => value.toPrimitive()),
description: maybeToEmptyString(invoiceItem.description, (value) => value.toPrimitive()),
quantity: invoiceItem.quantity.match(
(quantity) => quantity.toObjectString(),

View File

@ -1,5 +1,5 @@
import type { ISnapshotBuilder } from "@erp/core/api";
import { DomainValidationError, toEmptyString } from "@repo/rdx-ddd";
import { DomainValidationError, maybeToEmptyString } from "@repo/rdx-ddd";
import type { InvoiceRecipient, IssuedInvoice } from "../../../../domain";
@ -23,12 +23,12 @@ export class IssuedInvoiceRecipientFullSnapshotBuilder
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()),
street: maybeToEmptyString(recipient.street, (v) => v.toString()),
street2: maybeToEmptyString(recipient.street2, (v) => v.toString()),
city: maybeToEmptyString(recipient.city, (v) => v.toString()),
province: maybeToEmptyString(recipient.province, (v) => v.toString()),
postal_code: maybeToEmptyString(recipient.postalCode, (v) => v.toString()),
country: maybeToEmptyString(recipient.country, (v) => v.toString()),
}),
() => ({
id: "",

View File

@ -1,5 +1,5 @@
import type { ISnapshotBuilder } from "@erp/core/api";
import { toEmptyString } from "@repo/rdx-ddd";
import { maybeToEmptyString } from "@repo/rdx-ddd";
import type { IssuedInvoiceListDTO } from "../../dtos";
@ -30,12 +30,12 @@ export class IssuedInvoiceListItemSnapshotBuilder implements IIssuedInvoiceListI
invoice_number: invoice.invoiceNumber.toString(),
status: invoice.status.toPrimitive(),
series: toEmptyString(invoice.series, (v) => v.toString()),
series: maybeToEmptyString(invoice.series, (v) => v.toString()),
invoice_date: invoice.invoiceDate.toDateString(),
operation_date: toEmptyString(invoice.operationDate, (v) => v.toDateString()),
reference: toEmptyString(invoice.reference, (v) => v.toString()),
description: toEmptyString(invoice.description, (v) => v.toString()),
operation_date: maybeToEmptyString(invoice.operationDate, (v) => v.toDateString()),
reference: maybeToEmptyString(invoice.reference, (v) => v.toString()),
description: maybeToEmptyString(invoice.description, (v) => v.toString()),
recipient,

View File

@ -11,7 +11,7 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
@ -28,7 +28,7 @@ import {
type IssuedInvoiceItemProps,
ItemAmount,
ItemDescription,
ItemDiscount,
ItemDiscountPercentage,
ItemQuantity,
} from "../../../domain";
@ -79,7 +79,7 @@ export class CreateProformaPropsMapper {
);
const series = extractOrPushError(
maybeFromNullableVO(dto.series, (value) => InvoiceSerie.create(value)),
maybeFromNullableResult(dto.series, (value) => InvoiceSerie.create(value)),
"series",
this.errors
);
@ -91,25 +91,25 @@ export class CreateProformaPropsMapper {
);
const operationDate = extractOrPushError(
maybeFromNullableVO(dto.operation_date, (value) => UtcDate.createFromISO(value)),
maybeFromNullableResult(dto.operation_date, (value) => UtcDate.createFromISO(value)),
"operation_date",
this.errors
);
const reference = extractOrPushError(
maybeFromNullableVO(dto.reference, (value) => Result.ok(String(value))),
maybeFromNullableResult(dto.reference, (value) => Result.ok(String(value))),
"reference",
this.errors
);
const description = extractOrPushError(
maybeFromNullableVO(dto.reference, (value) => Result.ok(String(value))),
maybeFromNullableResult(dto.reference, (value) => Result.ok(String(value))),
"description",
this.errors
);
const notes = extractOrPushError(
maybeFromNullableVO(dto.notes, (value) => TextValue.create(value)),
maybeFromNullableResult(dto.notes, (value) => TextValue.create(value)),
"notes",
this.errors
);
@ -127,7 +127,7 @@ export class CreateProformaPropsMapper {
);
const paymentMethod = extractOrPushError(
maybeFromNullableVO(dto.payment_method, (value) =>
maybeFromNullableResult(dto.payment_method, (value) =>
InvoicePaymentMethod.create({ paymentDescription: value })
),
"payment_method",
@ -195,25 +195,27 @@ export class CreateProformaPropsMapper {
items.forEach((item, index) => {
const description = extractOrPushError(
maybeFromNullableVO(item.description, (value) => ItemDescription.create(value)),
maybeFromNullableResult(item.description, (value) => ItemDescription.create(value)),
"description",
this.errors
);
const quantity = extractOrPushError(
maybeFromNullableVO(item.quantity, (value) => ItemQuantity.create(value)),
maybeFromNullableResult(item.quantity, (value) => ItemQuantity.create(value)),
"quantity",
this.errors
);
const unitAmount = extractOrPushError(
maybeFromNullableVO(item.unit_amount, (value) => ItemAmount.create(value)),
maybeFromNullableResult(item.unit_amount, (value) => ItemAmount.create(value)),
"unit_amount",
this.errors
);
const discountPercentage = extractOrPushError(
maybeFromNullableVO(item.discount_percentage, (value) => ItemDiscount.create(value)),
maybeFromNullableResult(item.discount_percentage, (value) =>
ItemDiscountPercentage.create(value)
),
"discount_percentage",
this.errors
);

View File

@ -8,7 +8,7 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Result, isNullishOrEmpty, toPatchField } from "@repo/rdx-utils";
@ -36,7 +36,7 @@ export function UpdateProformaPropsMapper(dto: UpdateProformaByIdRequestDTO) {
toPatchField(dto.series).ifSet((series) => {
props.series = extractOrPushError(
maybeFromNullableVO(series, (value) => CustomerInvoiceSerie.create(value)),
maybeFromNullableResult(series, (value) => CustomerInvoiceSerie.create(value)),
"reference",
errors
);
@ -56,7 +56,7 @@ export function UpdateProformaPropsMapper(dto: UpdateProformaByIdRequestDTO) {
toPatchField(dto.operation_date).ifSet((operation_date) => {
props.operationDate = extractOrPushError(
maybeFromNullableVO(operation_date, (value) => UtcDate.createFromISO(value)),
maybeFromNullableResult(operation_date, (value) => UtcDate.createFromISO(value)),
"operation_date",
errors
);
@ -72,7 +72,7 @@ export function UpdateProformaPropsMapper(dto: UpdateProformaByIdRequestDTO) {
toPatchField(dto.reference).ifSet((reference) => {
props.reference = extractOrPushError(
maybeFromNullableVO(reference, (value) => Result.ok(String(value))),
maybeFromNullableResult(reference, (value) => Result.ok(String(value))),
"reference",
errors
);
@ -80,7 +80,7 @@ export function UpdateProformaPropsMapper(dto: UpdateProformaByIdRequestDTO) {
toPatchField(dto.description).ifSet((description) => {
props.description = extractOrPushError(
maybeFromNullableVO(description, (value) => Result.ok(String(value))),
maybeFromNullableResult(description, (value) => Result.ok(String(value))),
"description",
errors
);
@ -88,7 +88,7 @@ export function UpdateProformaPropsMapper(dto: UpdateProformaByIdRequestDTO) {
toPatchField(dto.notes).ifSet((notes) => {
props.notes = extractOrPushError(
maybeFromNullableVO(notes, (value) => TextValue.create(value)),
maybeFromNullableResult(notes, (value) => TextValue.create(value)),
"notes",
errors
);

View File

@ -1,5 +1,5 @@
import type { ISnapshotBuilder } from "@erp/core/api";
import { toEmptyString } from "@repo/rdx-ddd";
import { maybeToEmptyString } from "@repo/rdx-ddd";
import { InvoiceAmount, type Proforma } from "../../../../domain";
@ -86,14 +86,14 @@ export class ProformaFullSnapshotBuilder implements IProformaFullSnapshotBuilder
is_proforma: invoice.isProforma ? "true" : "false",
invoice_number: invoice.invoiceNumber.toString(),
status: invoice.status.toPrimitive(),
series: toEmptyString(invoice.series, (value) => value.toString()),
series: maybeToEmptyString(invoice.series, (value) => value.toString()),
invoice_date: invoice.invoiceDate.toDateString(),
operation_date: toEmptyString(invoice.operationDate, (value) => value.toDateString()),
operation_date: maybeToEmptyString(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()),
reference: maybeToEmptyString(invoice.reference, (value) => value.toString()),
description: maybeToEmptyString(invoice.description, (value) => value.toString()),
notes: maybeToEmptyString(invoice.notes, (value) => value.toString()),
language_code: invoice.languageCode.toString(),
currency_code: invoice.currencyCode.toString(),

View File

@ -1,5 +1,5 @@
import type { ISnapshotBuilder } from "@erp/core/api";
import { toEmptyString } from "@repo/rdx-ddd";
import { maybeToEmptyString } from "@repo/rdx-ddd";
import type { CustomerInvoiceItems, IssuedInvoiceItem } from "../../../../domain";
@ -16,7 +16,7 @@ export class ProformaItemsFullSnapshotBuilder implements IProformaItemsFullSnaps
id: invoiceItem.id.toPrimitive(),
is_valued: String(invoiceItem.isValued),
position: String(index),
description: toEmptyString(invoiceItem.description, (value) => value.toPrimitive()),
description: maybeToEmptyString(invoiceItem.description, (value) => value.toPrimitive()),
quantity: invoiceItem.quantity.match(
(quantity) => quantity.toObjectString(),

View File

@ -1,5 +1,5 @@
import type { ISnapshotBuilder } from "@erp/core/api";
import { DomainValidationError, toEmptyString } from "@repo/rdx-ddd";
import { DomainValidationError, maybeToEmptyString } from "@repo/rdx-ddd";
import type { InvoiceRecipient, Proforma } from "../../../../domain";
import type { ProformaRecipientFullSnapshot } from "../../application-models";
@ -20,12 +20,12 @@ export class ProformaRecipientFullSnapshotBuilder implements IProformaRecipientF
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()),
street: maybeToEmptyString(recipient.street, (v) => v.toString()),
street2: maybeToEmptyString(recipient.street2, (v) => v.toString()),
city: maybeToEmptyString(recipient.city, (v) => v.toString()),
province: maybeToEmptyString(recipient.province, (v) => v.toString()),
postal_code: maybeToEmptyString(recipient.postalCode, (v) => v.toString()),
country: maybeToEmptyString(recipient.country, (v) => v.toString()),
}),
() => ({
id: "",

View File

@ -1,5 +1,5 @@
import type { ISnapshotBuilder } from "@erp/core/api";
import { toEmptyString } from "@repo/rdx-ddd";
import { maybeToEmptyString } from "@repo/rdx-ddd";
import type { CustomerInvoiceListDTO } from "../../../../infrastructure";
import type { ProformaListItemSnapshot } from "../../application-models";
@ -19,12 +19,12 @@ export class ProformaListItemSnapshotBuilder implements IProformaListItemSnapsho
invoice_number: proforma.invoiceNumber.toString(),
status: proforma.status.toPrimitive(),
series: toEmptyString(proforma.series, (value) => value.toString()),
series: maybeToEmptyString(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()),
operation_date: maybeToEmptyString(proforma.operationDate, (value) => value.toDateString()),
reference: maybeToEmptyString(proforma.reference, (value) => value.toString()),
description: maybeToEmptyString(proforma.description, (value) => value.toString()),
recipient,

View File

@ -1,6 +1,6 @@
import { SnapshotBuilder } from "@erp/core/api";
import type { GetProformaByIdResponseDTO } from "@erp/customer-invoices/common";
import { toEmptyString } from "@repo/rdx-ddd";
import { maybeToEmptyString } from "@repo/rdx-ddd";
import type { ArrayElement } from "@repo/rdx-utils";
import type { CustomerInvoiceItems, IssuedInvoiceItem } from "../../../../domain";
@ -15,7 +15,7 @@ export class ProformaItemsFullPresenter extends SnapshotBuilder {
id: proformaItem.id.toPrimitive(),
is_valued: String(proformaItem.isValued),
position: String(index),
description: toEmptyString(proformaItem.description, (value) => value.toPrimitive()),
description: maybeToEmptyString(proformaItem.description, (value) => value.toPrimitive()),
quantity: proformaItem.quantity.match(
(quantity) => quantity.toObjectString(),

View File

@ -1,5 +1,5 @@
import { SnapshotBuilder } from "@erp/core/api";
import { DomainValidationError, toEmptyString } from "@repo/rdx-ddd";
import { DomainValidationError, maybeToEmptyString } from "@repo/rdx-ddd";
import type { GetIssuedInvoiceByIdResponseDTO as GetProformaByIdResponseDTO } from "../../../../../common/dto";
import type { InvoiceRecipient, Proforma } from "../../../../domain";
@ -20,12 +20,12 @@ export class ProformaRecipientFullPresenter extends SnapshotBuilder {
id: proforma.customerId.toString(),
name: recipient.name.toString(),
tin: recipient.tin.toString(),
street: toEmptyString(recipient.street, (value) => value.toString()),
street2: toEmptyString(recipient.street2, (value) => value.toString()),
city: toEmptyString(recipient.city, (value) => value.toString()),
province: toEmptyString(recipient.province, (value) => value.toString()),
postal_code: toEmptyString(recipient.postalCode, (value) => value.toString()),
country: toEmptyString(recipient.country, (value) => value.toString()),
street: maybeToEmptyString(recipient.street, (value) => value.toString()),
street2: maybeToEmptyString(recipient.street2, (value) => value.toString()),
city: maybeToEmptyString(recipient.city, (value) => value.toString()),
province: maybeToEmptyString(recipient.province, (value) => value.toString()),
postal_code: maybeToEmptyString(recipient.postalCode, (value) => value.toString()),
country: maybeToEmptyString(recipient.country, (value) => value.toString()),
};
},
() => {

View File

@ -1,5 +1,5 @@
import { Presenter } from "@erp/core/api";
import { toEmptyString } from "@repo/rdx-ddd";
import { maybeToEmptyString } from "@repo/rdx-ddd";
import type { GetProformaByIdResponseDTO } from "../../../../../common/dto";
import { InvoiceAmount, type Proforma } from "../../../../domain";
@ -87,14 +87,14 @@ export class ProformaFullPresenter extends Presenter<Proforma, GetProformaByIdRe
is_proforma: proforma.isProforma ? "true" : "false",
invoice_number: proforma.invoiceNumber.toString(),
status: proforma.status.toPrimitive(),
series: toEmptyString(proforma.series, (value) => value.toString()),
series: maybeToEmptyString(proforma.series, (value) => value.toString()),
invoice_date: proforma.invoiceDate.toDateString(),
operation_date: toEmptyString(proforma.operationDate, (value) => value.toDateString()),
operation_date: maybeToEmptyString(proforma.operationDate, (value) => value.toDateString()),
reference: toEmptyString(proforma.reference, (value) => value.toString()),
description: toEmptyString(proforma.description, (value) => value.toString()),
notes: toEmptyString(proforma.notes, (value) => value.toString()),
reference: maybeToEmptyString(proforma.reference, (value) => value.toString()),
description: maybeToEmptyString(proforma.description, (value) => value.toString()),
notes: maybeToEmptyString(proforma.notes, (value) => value.toString()),
language_code: proforma.languageCode.toString(),
currency_code: proforma.currencyCode.toString(),

View File

@ -1,5 +1,5 @@
import type { Criteria } from "@repo/rdx-criteria/server";
import { toEmptyString } from "@repo/rdx-ddd";
import { maybeToEmptyString } from "@repo/rdx-ddd";
import type { ArrayElement, Collection } from "@repo/rdx-utils";
import type { ListIssuedInvoicesResponseDTO } from "../../../../../common/dto";
@ -26,12 +26,12 @@ export class IssuedInvoiceListPresenter extends Presenter {
invoice_number: invoice.invoiceNumber.toString(),
status: invoice.status.toPrimitive(),
series: toEmptyString(invoice.series, (value) => value.toString()),
series: maybeToEmptyString(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()),
operation_date: maybeToEmptyString(invoice.operationDate, (value) => value.toDateString()),
reference: maybeToEmptyString(invoice.reference, (value) => value.toString()),
description: maybeToEmptyString(invoice.description, (value) => value.toString()),
recipient: recipientDTO,

View File

@ -1,6 +1,6 @@
import { Presenter } from "@erp/core/api";
import type { Criteria } from "@repo/rdx-criteria/server";
import { toEmptyString } from "@repo/rdx-ddd";
import { maybeToEmptyString } from "@repo/rdx-ddd";
import type { ArrayElement, Collection } from "@repo/rdx-utils";
import type { ListProformasResponseDTO } from "../../../../../common/dto";
@ -18,12 +18,12 @@ export class ProformaListPresenter extends Presenter {
invoice_number: proforma.invoiceNumber.toString(),
status: proforma.status.toPrimitive(),
series: toEmptyString(proforma.series, (value) => value.toString()),
series: maybeToEmptyString(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()),
operation_date: maybeToEmptyString(proforma.operationDate, (value) => value.toDateString()),
reference: maybeToEmptyString(proforma.reference, (value) => value.toString()),
description: maybeToEmptyString(proforma.description, (value) => value.toString()),
recipient: recipientDTO,

View File

@ -1,12 +1,15 @@
export * from "./invoice-address-type.vo";
export * from "./invoice-amount.vo";
export * from "./invoice-discount-percentage.vo";
export * from "./invoice-number.vo";
export * from "./invoice-recipient";
export * from "./invoice-serie.vo";
export * from "./invoice-status.vo";
export * from "./invoice-tax-group.vo";
export * from "./invoice-tax-percentage.vo";
export * from "./item-amount.vo";
export * from "./item-description.vo";
export * from "./item-discount.vo";
export * from "./item-discount-percentage.vo";
export * from "./item-quantity.vo";
export * from "./item-tax-group.vo";
export * from "./item-tax-percentage.vo";

View File

@ -0,0 +1,19 @@
import { Percentage, type PercentageProps } from "@repo/rdx-ddd";
import type { Result } from "@repo/rdx-utils";
type InvoiceDiscountPercentageProps = Pick<PercentageProps, "value">;
export class InvoiceDiscountPercentage extends Percentage {
static DEFAULT_SCALE = 2;
static create({ value }: InvoiceDiscountPercentageProps): Result<Percentage> {
return Percentage.create({
value,
scale: InvoiceDiscountPercentage.DEFAULT_SCALE,
});
}
static zero() {
return InvoiceDiscountPercentage.create({ value: 0 }).data;
}
}

View File

@ -7,7 +7,7 @@ import {
type Street,
type TINNumber,
ValueObject,
toEmptyString,
maybeToEmptyString,
} from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils";
@ -90,12 +90,12 @@ export class InvoiceRecipient extends ValueObject<InvoiceRecipientProps> {
return {
tin: this.tin.toString(),
name: this.name.toString(),
street: toEmptyString(this.street, (value) => value.toString()),
street2: toEmptyString(this.street2, (value) => value.toString()),
city: toEmptyString(this.city, (value) => value.toString()),
postal_code: toEmptyString(this.postalCode, (value) => value.toString()),
province: toEmptyString(this.province, (value) => value.toString()),
country: toEmptyString(this.country, (value) => value.toString()),
street: maybeToEmptyString(this.street, (value) => value.toString()),
street2: maybeToEmptyString(this.street2, (value) => value.toString()),
city: maybeToEmptyString(this.city, (value) => value.toString()),
postal_code: maybeToEmptyString(this.postalCode, (value) => value.toString()),
province: maybeToEmptyString(this.province, (value) => value.toString()),
country: maybeToEmptyString(this.country, (value) => value.toString()),
};
}
}

View File

@ -0,0 +1,19 @@
import { Percentage, type PercentageProps } from "@repo/rdx-ddd";
import type { Result } from "@repo/rdx-utils";
type InvoiceTaxPercentageProps = Pick<PercentageProps, "value">;
export class InvoiceTaxPercentage extends Percentage {
static DEFAULT_SCALE = 2;
static create({ value }: InvoiceTaxPercentageProps): Result<Percentage> {
return Percentage.create({
value,
scale: InvoiceTaxPercentage.DEFAULT_SCALE,
});
}
static zero() {
return InvoiceTaxPercentage.create({ value: 0 }).data;
}
}

View File

@ -0,0 +1,19 @@
import { Percentage, type PercentageProps } from "@repo/rdx-ddd";
import type { Result } from "@repo/rdx-utils";
type ItemDiscountPercentageProps = Pick<PercentageProps, "value">;
export class ItemDiscountPercentage extends Percentage {
static DEFAULT_SCALE = 2;
static create({ value }: ItemDiscountPercentageProps): Result<Percentage> {
return Percentage.create({
value,
scale: ItemDiscountPercentage.DEFAULT_SCALE,
});
}
static zero() {
return ItemDiscountPercentage.create({ value: 0 }).data;
}
}

View File

@ -1,19 +0,0 @@
import { Percentage, type PercentageProps } from "@repo/rdx-ddd";
import type { Result } from "@repo/rdx-utils";
type ItemDiscountProps = Pick<PercentageProps, "value">;
export class ItemDiscount extends Percentage {
static DEFAULT_SCALE = 2;
static create({ value }: ItemDiscountProps): Result<Percentage> {
return Percentage.create({
value,
scale: ItemDiscount.DEFAULT_SCALE,
});
}
static zero() {
return ItemDiscount.create({ value: 0 }).data;
}
}

View File

@ -0,0 +1,19 @@
import { Percentage, type PercentageProps } from "@repo/rdx-ddd";
import type { Result } from "@repo/rdx-utils";
type ItemTaxPercentageProps = Pick<PercentageProps, "value">;
export class ItemTaxPercentage extends Percentage {
static DEFAULT_SCALE = 2;
static create({ value }: ItemTaxPercentageProps): Result<Percentage> {
return Percentage.create({
value,
scale: ItemTaxPercentage.DEFAULT_SCALE,
});
}
static zero() {
return ItemTaxPercentage.create({ value: 0 }).data;
}
}

View File

@ -49,17 +49,12 @@ export type IssuedInvoiceProps = {
subtotalAmount: InvoiceAmount;
itemDiscountAmount: InvoiceAmount;
itemsDiscountAmount: InvoiceAmount;
globalDiscountPercentage: Percentage;
globalDiscountAmount: InvoiceAmount;
totalDiscountAmount: InvoiceAmount;
taxableAmount: InvoiceAmount;
ivaAmount: InvoiceAmount;
recAmount: InvoiceAmount;
retentionAmount: InvoiceAmount;
taxesAmount: InvoiceAmount;
totalAmount: InvoiceAmount;
@ -173,8 +168,8 @@ export class IssuedInvoice extends AggregateRoot<IssuedInvoiceProps> {
return this.props.subtotalAmount;
}
public get itemDiscountAmount(): InvoiceAmount {
return this.props.itemDiscountAmount;
public get itemsDiscountAmount(): InvoiceAmount {
return this.props.itemsDiscountAmount;
}
public get globalDiscountPercentage(): Percentage {

View File

@ -1,13 +1,18 @@
import { type CurrencyCode, DomainEntity, type LanguageCode, type UniqueID } from "@repo/rdx-ddd";
import {
type CurrencyCode,
DomainEntity,
type LanguageCode,
type Percentage,
type UniqueID,
} from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils";
import type {
InvoiceAmount,
ItemAmount,
ItemDescription,
ItemDiscount,
ItemDiscountPercentage,
ItemQuantity,
ItemTaxGroup,
} from "../../../common";
/**
@ -26,26 +31,32 @@ export type IssuedInvoiceItemProps = {
quantity: Maybe<ItemQuantity>;
unitAmount: Maybe<ItemAmount>;
subtotalAmount: InvoiceAmount;
subtotalAmount: Maybe<InvoiceAmount>;
itemDiscountPercentage: Maybe<ItemDiscount>;
itemDiscountAmount: InvoiceAmount;
itemDiscountPercentage: Maybe<ItemDiscountPercentage>;
itemDiscountAmount: Maybe<InvoiceAmount>;
globalDiscountPercentage: Maybe<ItemDiscount>;
globalDiscountAmount: InvoiceAmount;
globalDiscountPercentage: Maybe<ItemDiscountPercentage>;
globalDiscountAmount: Maybe<InvoiceAmount>;
totalDiscountAmount: InvoiceAmount;
totalDiscountAmount: Maybe<InvoiceAmount>;
taxableAmount: InvoiceAmount;
taxableAmount: Maybe<InvoiceAmount>;
ivaAmount: InvoiceAmount;
recAmount: InvoiceAmount;
retentionAmount: InvoiceAmount;
ivaCode: Maybe<string>;
ivaPercentage: Maybe<ItemDiscountPercentage>;
ivaAmount: Maybe<InvoiceAmount>;
taxesAmount: InvoiceAmount;
totalAmount: InvoiceAmount;
recCode: Maybe<string>;
recPercentage: Maybe<ItemDiscountPercentage>;
recAmount: Maybe<InvoiceAmount>;
taxes: ItemTaxGroup;
retentionCode: Maybe<string>;
retentionPercentage: Maybe<ItemDiscountPercentage>;
retentionAmount: Maybe<InvoiceAmount>;
taxesAmount: Maybe<InvoiceAmount>;
totalAmount: Maybe<InvoiceAmount>;
languageCode: LanguageCode;
currencyCode: CurrencyCode;
@ -107,10 +118,6 @@ export class IssuedInvoiceItem extends DomainEntity<IssuedInvoiceItemProps> {
return this.props.globalDiscountAmount;
}
get taxes() {
return this.props.taxes;
}
get languageCode() {
return this.props.languageCode;
}
@ -119,35 +126,53 @@ export class IssuedInvoiceItem extends DomainEntity<IssuedInvoiceItemProps> {
return this.props.currencyCode;
}
public get subtotalAmount(): ItemAmount {
public get subtotalAmount() {
return this.props.subtotalAmount;
}
public get totalDiscountAmount(): ItemAmount {
public get totalDiscountAmount() {
return this.props.totalDiscountAmount;
}
public get taxableAmount(): ItemAmount {
public get taxableAmount() {
return this.props.taxableAmount;
}
public get ivaAmount(): InvoiceAmount {
public get ivaCode(): Maybe<string> {
return this.props.ivaCode;
}
public get ivaPercentage(): Maybe<Percentage> {
return this.props.ivaPercentage;
}
public get ivaAmount(): Maybe<InvoiceAmount> {
return this.props.ivaAmount;
}
public get recAmount(): InvoiceAmount {
public get recCode(): Maybe<string> {
return this.props.recCode;
}
public get recPercentage(): Maybe<Percentage> {
return this.props.recPercentage;
}
public get recAmount(): Maybe<InvoiceAmount> {
return this.props.recAmount;
}
public get retentionAmount(): InvoiceAmount {
public get retentionCode(): Maybe<string> {
return this.props.retentionCode;
}
public get retentionPercentage(): Maybe<Percentage> {
return this.props.retentionPercentage;
}
public get retentionAmount(): Maybe<InvoiceAmount> {
return this.props.retentionAmount;
}
public get taxesAmount(): ItemAmount {
public get taxesAmount() {
return this.props.taxesAmount;
}
public get totalAmount(): ItemAmount {
public get totalAmount() {
return this.props.totalAmount;
}

View File

@ -1,7 +1,7 @@
import type { CurrencyCode, LanguageCode, Percentage } from "@repo/rdx-ddd";
import { Collection } from "@repo/rdx-utils";
import { InvoiceAmount, ItemDiscount } from "../../../common";
import { InvoiceAmount, ItemDiscountPercentage } from "../../../common";
import type { IssuedInvoiceItem } from "./issued-invoice-item.entity";
@ -47,7 +47,7 @@ export class IssuedInvoiceItems extends Collection<IssuedInvoiceItem> {
this._globalDiscountPercentage.equals(
item.globalDiscountPercentage.match(
(v) => v,
() => ItemDiscount.zero()
() => ItemDiscountPercentage.zero()
)
)
)

View File

@ -12,11 +12,11 @@ export type IssuedInvoiceTaxProps = {
recCode: Maybe<string>;
recPercentage: Maybe<Percentage>;
recAmount: InvoiceAmount;
recAmount: Maybe<InvoiceAmount>;
retentionCode: Maybe<string>;
retentionPercentage: Maybe<Percentage>;
retentionAmount: InvoiceAmount;
retentionAmount: Maybe<InvoiceAmount>;
taxesAmount: InvoiceAmount;
};
@ -49,7 +49,7 @@ export class IssuedInvoiceTax extends DomainEntity<IssuedInvoiceTaxProps> {
public get recPercentage(): Maybe<Percentage> {
return this.props.recPercentage;
}
public get recAmount(): InvoiceAmount {
public get recAmount(): Maybe<InvoiceAmount> {
return this.props.recAmount;
}
@ -59,7 +59,7 @@ export class IssuedInvoiceTax extends DomainEntity<IssuedInvoiceTaxProps> {
public get retentionPercentage(): Maybe<Percentage> {
return this.props.retentionPercentage;
}
public get retentionAmount(): InvoiceAmount {
public get retentionAmount(): Maybe<InvoiceAmount> {
return this.props.retentionAmount;
}

View File

@ -1,4 +1,4 @@
import { DomainEntity, type URLAddress, type UniqueID, toEmptyString } from "@repo/rdx-ddd";
import { DomainEntity, type URLAddress, type UniqueID, maybeToEmptyString } from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils";
import type { VerifactuRecordEstado } from "../value-objects/verifactu-status.vo";
@ -53,10 +53,10 @@ export class VerifactuRecord extends DomainEntity<VerifactuRecordProps> {
toObjectString() {
return {
status: this.estado.toString(),
url: toEmptyString(this.url, (value) => value.toString()),
qr_code: toEmptyString(this.qrCode, (value) => value.toString()),
uuid: toEmptyString(this.uuid, (value) => value.toString()),
operacion: toEmptyString(this.operacion, (value) => value.toString()),
url: maybeToEmptyString(this.url, (value) => value.toString()),
qr_code: maybeToEmptyString(this.qrCode, (value) => value.toString()),
uuid: maybeToEmptyString(this.uuid, (value) => value.toString()),
operacion: maybeToEmptyString(this.operacion, (value) => value.toString()),
};
}
}

View File

@ -1,4 +1,3 @@
export * from "./aggregates";
export * from "./entities";
export * from "./errors";
export * from "./value-objects";

View File

@ -1 +1,2 @@
export * from "./issued-invoice-tax-group.vo";
export * from "./verifactu-status.vo";

View File

@ -1,117 +0,0 @@
import { type Percentage, ValueObject } from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils";
import type { InvoiceAmount } from "../../common";
export type IssuedInvoiceTaxGroupProps = {
ivaCode: string;
ivaPercentage: Percentage;
ivaAmount: InvoiceAmount;
recCode: Maybe<string>;
recPercentage: Maybe<Percentage>;
recAmount: InvoiceAmount;
retentionCode: Maybe<string>;
retentionPercentage: Maybe<Percentage>;
retentionAmount: InvoiceAmount;
taxableAmount: InvoiceAmount;
totalAmount: InvoiceAmount;
};
export class IssuedInvoiceTaxGroup extends ValueObject<IssuedInvoiceTaxGroupProps> {
static create(props: IssuedInvoiceTaxGroupProps) {
return Result.ok(new IssuedInvoiceTaxGroup(props));
}
// IVA
get ivaCode(): string {
return this.props.ivaCode;
}
get ivaPercentage(): Percentage {
return this.props.ivaPercentage;
}
get ivaAmount(): InvoiceAmount {
return this.props.ivaAmount;
}
// Recargo de equivalencia (rec)
get recCode(): Maybe<string> {
return this.props.recCode;
}
get recPercentage(): Maybe<Percentage> {
return this.props.recPercentage;
}
get recAmount(): Maybe<InvoiceAmount> {
return this.props.recAmount;
}
// Retención (ret)
get retentionCode(): Maybe<string> {
return this.props.retentionCode;
}
get retentionPercentage(): Maybe<Percentage> {
return this.props.retentionPercentage;
}
get retentionAmount(): Maybe<InvoiceAmount> {
return this.props.retentionAmount;
}
//
get taxableAmount(): InvoiceAmount {
return this.props.taxableAmount;
}
get totalAmount(): InvoiceAmount {
return this.props.totalAmount;
}
/**
* Devuelve únicamente los códigos existentes: ["iva_21", "rec_5_2"]
*/
public getCodesArray(): string[] {
const codes: string[] = [];
// IVA
codes.push(this.props.ivaCode);
this.props.rec.match(
(t) => codes.push(t.code),
() => {
//
}
);
this.props.retention.match(
(t) => codes.push(t.code),
() => {
//
}
);
return codes;
}
/**
* Devuelve una cadena tipo: "iva_21, rec_5_2"
*/
public getCodesToString(): string {
return this.getCodesArray().join(", ");
}
getProps() {
return this.props;
}
toPrimitive() {
return this.getProps();
}
}

View File

@ -0,0 +1,66 @@
import type { Tax } from "@erp/core/api";
import { ValueObject } from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils";
import type { InvoiceAmount } from "../../common";
export type IssuedInvoiceTaxGroupProps = {
taxableAmount: InvoiceAmount;
iva: Tax;
ivaAmount: InvoiceAmount;
rec: Maybe<Tax>; // si existe
recAmount: Maybe<InvoiceAmount>;
retention: Maybe<Tax>; // si existe
retentionAmount: Maybe<InvoiceAmount>;
taxesAmount: InvoiceAmount;
};
export class IssuedInvoiceTaxGroup extends ValueObject<IssuedInvoiceTaxGroupProps> {
static create(props: IssuedInvoiceTaxGroupProps) {
return Result.ok(new IssuedInvoiceTaxGroup(props));
}
get taxableAmount(): InvoiceAmount {
return this.props.taxableAmount;
}
get iva(): Tax {
return this.props.iva;
}
get ivaAmount(): InvoiceAmount {
return this.props.ivaAmount;
}
get rec(): Maybe<Tax> {
return this.props.rec;
}
get recAmount(): Maybe<InvoiceAmount> {
return this.props.recAmount;
}
get retention(): Maybe<Tax> {
return this.props.retention;
}
get retentionAmount(): Maybe<InvoiceAmount> {
return this.props.retentionAmount;
}
get taxesAmount(): InvoiceAmount {
return this.props.taxesAmount;
}
getProps() {
return this.props;
}
toPrimitive() {
return this.getProps();
}
}

View File

@ -4,7 +4,7 @@ import { type Maybe, Result } from "@repo/rdx-utils";
import {
ItemAmount,
type ItemDescription,
ItemDiscount,
ItemDiscountPercentage,
ItemQuantity,
type ItemTaxGroup,
} from "../../../common";
@ -31,8 +31,8 @@ export type ProformaItemProps = {
quantity: Maybe<ItemQuantity>; // Cantidad de unidades
unitAmount: Maybe<ItemAmount>; // Precio unitario en la moneda de la factura
itemDiscountPercentage: Maybe<ItemDiscount>; // % descuento
globalDiscountPercentage: Maybe<ItemDiscount>; // % descuento de la cabecera
itemDiscountPercentage: Maybe<ItemDiscountPercentage>; // % descuento
globalDiscountPercentage: Maybe<ItemDiscountPercentage>; // % descuento de la cabecera
taxes: ItemTaxGroup;
@ -128,7 +128,7 @@ export class ProformaItem extends DomainEntity<ProformaItemProps> {
private _calculateItemDiscountAmount(subtotal: ItemAmount): ItemAmount {
const discountPercentage = this.props.itemDiscountPercentage.match(
(discount) => discount,
() => ItemDiscount.zero()
() => ItemDiscountPercentage.zero()
);
return subtotal.percentage(discountPercentage);
@ -145,7 +145,7 @@ export class ProformaItem extends DomainEntity<ProformaItemProps> {
const globalDiscount = this.props.globalDiscountPercentage.match(
(discount) => discount,
() => ItemDiscount.zero()
() => ItemDiscountPercentage.zero()
);
return amountAfterLineDiscount.percentage(globalDiscount);

View File

@ -1,7 +1,7 @@
import type { CurrencyCode, LanguageCode, Percentage } from "@repo/rdx-ddd";
import { Collection } from "@repo/rdx-utils";
import { ItemAmount, ItemDiscount, type ItemTaxGroup } from "../../../common";
import { ItemAmount, ItemDiscountPercentage, type ItemTaxGroup } from "../../../common";
import type { ProformaItem } from "./proforma-item.entity";
@ -76,7 +76,7 @@ export class ProformaItems extends Collection<ProformaItem> {
this.globalDiscountPercentage.equals(
item.globalDiscountPercentage.match(
(v) => v,
() => ItemDiscount.zero()
() => ItemDiscountPercentage.zero()
)
);

View File

@ -10,8 +10,8 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
toNullable,
maybeFromNullableResult,
maybeToNullable,
} from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
@ -21,7 +21,7 @@ import {
type IssuedInvoiceItemProps,
ItemAmount,
ItemDescription,
ItemDiscount,
ItemDiscountPercentage,
ItemQuantity,
ItemTaxGroup,
type Proforma,
@ -78,19 +78,19 @@ export class CustomerInvoiceItemDomainMapper
);
const description = extractOrPushError(
maybeFromNullableVO(source.description, (v) => ItemDescription.create(v)),
maybeFromNullableResult(source.description, (v) => ItemDescription.create(v)),
`items[${index}].description`,
errors
);
const quantity = extractOrPushError(
maybeFromNullableVO(source.quantity_value, (v) => ItemQuantity.create({ value: v })),
maybeFromNullableResult(source.quantity_value, (v) => ItemQuantity.create({ value: v })),
`items[${index}].quantity`,
errors
);
const unitAmount = extractOrPushError(
maybeFromNullableVO(source.unit_amount_value, (value) =>
maybeFromNullableResult(source.unit_amount_value, (value) =>
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`items[${index}].unit_amount`,
@ -98,35 +98,39 @@ export class CustomerInvoiceItemDomainMapper
);
const discountPercentage = extractOrPushError(
maybeFromNullableVO(source.discount_percentage_value, (v) =>
ItemDiscount.create({ value: v })
maybeFromNullableResult(source.discount_percentage_value, (v) =>
ItemDiscountPercentage.create({ value: v })
),
`items[${index}].discount_percentage`,
errors
);
const globalDiscountPercentage = extractOrPushError(
maybeFromNullableVO(source.global_discount_percentage_value, (v) =>
ItemDiscount.create({ value: v })
maybeFromNullableResult(source.global_discount_percentage_value, (v) =>
ItemDiscountPercentage.create({ value: v })
),
`items[${index}].discount_percentage`,
errors
);
const iva = extractOrPushError(
maybeFromNullableVO(source.iva_code, (code) => Tax.createFromCode(code, this._taxCatalog)),
maybeFromNullableResult(source.iva_code, (code) =>
Tax.createFromCode(code, this._taxCatalog)
),
`items[${index}].iva_code`,
errors
);
const rec = extractOrPushError(
maybeFromNullableVO(source.rec_code, (code) => Tax.createFromCode(code, this._taxCatalog)),
maybeFromNullableResult(source.rec_code, (code) =>
Tax.createFromCode(code, this._taxCatalog)
),
`items[${index}].rec_code`,
errors
);
const retention = extractOrPushError(
maybeFromNullableVO(source.retention_code, (code) =>
maybeFromNullableResult(source.retention_code, (code) =>
Tax.createFromCode(code, this._taxCatalog)
),
`items[${index}].retention_code`,
@ -216,40 +220,42 @@ export class CustomerInvoiceItemDomainMapper
invoice_id: parent.id.toPrimitive(),
position: index,
description: toNullable(source.description, (v) => v.toPrimitive()),
description: maybeToNullable(source.description, (v) => v.toPrimitive()),
quantity_value: toNullable(source.quantity, (v) => v.toPrimitive().value),
quantity_value: maybeToNullable(source.quantity, (v) => v.toPrimitive().value),
quantity_scale:
toNullable(source.quantity, (v) => v.toPrimitive().scale) ?? ItemQuantity.DEFAULT_SCALE,
maybeToNullable(source.quantity, (v) => v.toPrimitive().scale) ??
ItemQuantity.DEFAULT_SCALE,
unit_amount_value: toNullable(source.unitAmount, (v) => v.toPrimitive().value),
unit_amount_value: maybeToNullable(source.unitAmount, (v) => v.toPrimitive().value),
unit_amount_scale:
toNullable(source.unitAmount, (v) => v.toPrimitive().scale) ?? ItemAmount.DEFAULT_SCALE,
maybeToNullable(source.unitAmount, (v) => v.toPrimitive().scale) ??
ItemAmount.DEFAULT_SCALE,
subtotal_amount_value: allAmounts.subtotalAmount.value,
subtotal_amount_scale: allAmounts.subtotalAmount.scale,
//
discount_percentage_value: toNullable(
discount_percentage_value: maybeToNullable(
source.itemDiscountPercentage,
(v) => v.toPrimitive().value
),
discount_percentage_scale:
toNullable(source.itemDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscount.DEFAULT_SCALE,
maybeToNullable(source.itemDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscountPercentage.DEFAULT_SCALE,
discount_amount_value: allAmounts.itemDiscountAmount.value,
discount_amount_scale: allAmounts.itemDiscountAmount.scale,
//
global_discount_percentage_value: toNullable(
global_discount_percentage_value: maybeToNullable(
source.globalDiscountPercentage,
(v) => v.toPrimitive().value
),
global_discount_percentage_scale:
toNullable(source.globalDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscount.DEFAULT_SCALE,
maybeToNullable(source.globalDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscountPercentage.DEFAULT_SCALE,
global_discount_amount_value: allAmounts.globalDiscountAmount.value,
global_discount_amount_scale: allAmounts.globalDiscountAmount.scale,
@ -263,29 +269,32 @@ export class CustomerInvoiceItemDomainMapper
taxable_amount_scale: allAmounts.taxableAmount.scale,
// IVA
iva_code: toNullable(source.taxes.iva, (v) => v.code),
iva_code: maybeToNullable(source.taxes.iva, (v) => v.code),
iva_percentage_value: toNullable(source.taxes.iva, (v) => v.percentage.value),
iva_percentage_scale: toNullable(source.taxes.iva, (v) => v.percentage.scale) ?? 2,
iva_percentage_value: maybeToNullable(source.taxes.iva, (v) => v.percentage.value),
iva_percentage_scale: maybeToNullable(source.taxes.iva, (v) => v.percentage.scale) ?? 2,
iva_amount_value: taxesAmounts.ivaAmount.value,
iva_amount_scale: taxesAmounts.ivaAmount.scale,
// REC
rec_code: toNullable(source.taxes.rec, (v) => v.code),
rec_code: maybeToNullable(source.taxes.rec, (v) => v.code),
rec_percentage_value: toNullable(source.taxes.rec, (v) => v.percentage.value),
rec_percentage_scale: toNullable(source.taxes.rec, (v) => v.percentage.scale) ?? 2,
rec_percentage_value: maybeToNullable(source.taxes.rec, (v) => v.percentage.value),
rec_percentage_scale: maybeToNullable(source.taxes.rec, (v) => v.percentage.scale) ?? 2,
rec_amount_value: taxesAmounts.recAmount.value,
rec_amount_scale: taxesAmounts.recAmount.scale,
// RET
retention_code: toNullable(source.taxes.retention, (v) => v.code),
retention_code: maybeToNullable(source.taxes.retention, (v) => v.code),
retention_percentage_value: toNullable(source.taxes.retention, (v) => v.percentage.value),
retention_percentage_value: maybeToNullable(
source.taxes.retention,
(v) => v.percentage.value
),
retention_percentage_scale:
toNullable(source.taxes.retention, (v) => v.percentage.scale) ?? 2,
maybeToNullable(source.taxes.retention, (v) => v.percentage.scale) ?? 2,
retention_amount_value: taxesAmounts.retentionAmount.value,
retention_amount_scale: taxesAmounts.retentionAmount.scale,

View File

@ -1,6 +1,6 @@
import type { JsonTaxCatalogProvider } from "@erp/core";
import { type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
import { UniqueID, type ValidationErrorDetail, toNullable } from "@repo/rdx-ddd";
import { UniqueID, type ValidationErrorDetail, maybeToNullable } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import type { InvoiceTaxGroup, Proforma } from "../../../../../../domain";
@ -120,19 +120,20 @@ export class CustomerInvoiceTaxesDomainMapper extends SequelizeDomainMapper<
iva_amount_scale: ivaAmount.scale,
// REC
rec_code: toNullable(source.rec, (v) => v.code),
rec_code: maybeToNullable(source.rec, (v) => v.code),
rec_percentage_value: toNullable(source.rec, (v) => v.percentage.value),
rec_percentage_scale: toNullable(source.rec, (v) => v.percentage.scale) ?? 2,
rec_percentage_value: maybeToNullable(source.rec, (v) => v.percentage.value),
rec_percentage_scale: maybeToNullable(source.rec, (v) => v.percentage.scale) ?? 2,
rec_amount_value: recAmount.value,
rec_amount_scale: recAmount.scale,
// RET
retention_code: toNullable(source.retention, (v) => v.code),
retention_code: maybeToNullable(source.retention, (v) => v.code),
retention_percentage_value: toNullable(source.retention, (v) => v.percentage.value),
retention_percentage_scale: toNullable(source.retention, (v) => v.percentage.scale) ?? 2,
retention_percentage_value: maybeToNullable(source.retention, (v) => v.percentage.value),
retention_percentage_scale:
maybeToNullable(source.retention, (v) => v.percentage.scale) ?? 2,
retention_amount_value: retentionAmount.value,
retention_amount_scale: retentionAmount.scale,

View File

@ -13,8 +13,8 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
toNullable,
maybeFromNullableResult,
maybeToNullable,
} from "@repo/rdx-ddd";
import { Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils";
@ -79,7 +79,7 @@ export class CustomerInvoiceDomainMapper
const isProforma = Boolean(source.is_proforma);
const proformaId = extractOrPushError(
maybeFromNullableVO(source.proforma_id, (v) => UniqueID.create(v)),
maybeFromNullableResult(source.proforma_id, (v) => UniqueID.create(v)),
"proforma_id",
errors
);
@ -87,7 +87,7 @@ export class CustomerInvoiceDomainMapper
const status = extractOrPushError(InvoiceStatus.create(source.status), "status", errors);
const series = extractOrPushError(
maybeFromNullableVO(source.series, (v) => InvoiceSerie.create(v)),
maybeFromNullableResult(source.series, (v) => InvoiceSerie.create(v)),
"series",
errors
);
@ -106,7 +106,7 @@ export class CustomerInvoiceDomainMapper
);
const operationDate = extractOrPushError(
maybeFromNullableVO(source.operation_date, (v) => UtcDate.createFromISO(v)),
maybeFromNullableResult(source.operation_date, (v) => UtcDate.createFromISO(v)),
"operation_date",
errors
);
@ -126,19 +126,19 @@ export class CustomerInvoiceDomainMapper
// Textos opcionales
const reference = extractOrPushError(
maybeFromNullableVO(source.reference, (value) => Result.ok(String(value))),
maybeFromNullableResult(source.reference, (value) => Result.ok(String(value))),
"reference",
errors
);
const description = extractOrPushError(
maybeFromNullableVO(source.description, (value) => Result.ok(String(value))),
maybeFromNullableResult(source.description, (value) => Result.ok(String(value))),
"description",
errors
);
const notes = extractOrPushError(
maybeFromNullableVO(source.notes, (value) => TextValue.create(value)),
maybeFromNullableResult(source.notes, (value) => TextValue.create(value)),
"notes",
errors
);
@ -362,19 +362,19 @@ export class CustomerInvoiceDomainMapper
// Flags / estado / serie / número
is_proforma: source.isProforma,
proforma_id: toNullable(source.proformaId, (v) => v.toPrimitive()),
proforma_id: maybeToNullable(source.proformaId, (v) => v.toPrimitive()),
status: source.status.toPrimitive(),
series: toNullable(source.series, (v) => v.toPrimitive()),
series: maybeToNullable(source.series, (v) => v.toPrimitive()),
invoice_number: source.invoiceNumber.toPrimitive(),
invoice_date: source.invoiceDate.toPrimitive(),
operation_date: toNullable(source.operationDate, (v) => v.toPrimitive()),
operation_date: maybeToNullable(source.operationDate, (v) => v.toPrimitive()),
language_code: source.languageCode.toPrimitive(),
currency_code: source.currencyCode.toPrimitive(),
reference: toNullable(source.reference, (reference) => reference),
description: toNullable(source.description, (description) => description),
notes: toNullable(source.notes, (v) => v.toPrimitive()),
reference: maybeToNullable(source.reference, (reference) => reference),
description: maybeToNullable(source.description, (description) => description),
notes: maybeToNullable(source.notes, (v) => v.toPrimitive()),
subtotal_amount_value: allAmounts.subtotalAmount.value,
subtotal_amount_scale: allAmounts.subtotalAmount.scale,
@ -394,8 +394,11 @@ export class CustomerInvoiceDomainMapper
total_amount_value: allAmounts.totalAmount.value,
total_amount_scale: allAmounts.totalAmount.scale,
payment_method_id: toNullable(source.paymentMethod, (payment) => payment.toObjectString().id),
payment_method_description: toNullable(
payment_method_id: maybeToNullable(
source.paymentMethod,
(payment) => payment.toObjectString().id
),
payment_method_description: maybeToNullable(
source.paymentMethod,
(payment) => payment.toObjectString().payment_description
),

View File

@ -10,8 +10,8 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
toNullable,
maybeFromNullableResult,
maybeToNullable,
} from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
@ -59,37 +59,37 @@ export class InvoiceRecipientDomainMapper {
const customerTin = extractOrPushError(TINNumber.create(_tin!), "customer_tin", errors);
const customerStreet = extractOrPushError(
maybeFromNullableVO(_street, (value) => Street.create(value)),
maybeFromNullableResult(_street, (value) => Street.create(value)),
"customer_street",
errors
);
const customerStreet2 = extractOrPushError(
maybeFromNullableVO(_street2, (value) => Street.create(value)),
maybeFromNullableResult(_street2, (value) => Street.create(value)),
"customer_street2",
errors
);
const customerCity = extractOrPushError(
maybeFromNullableVO(_city, (value) => City.create(value)),
maybeFromNullableResult(_city, (value) => City.create(value)),
"customer_city",
errors
);
const customerProvince = extractOrPushError(
maybeFromNullableVO(_province, (value) => Province.create(value)),
maybeFromNullableResult(_province, (value) => Province.create(value)),
"customer_province",
errors
);
const customerPostalCode = extractOrPushError(
maybeFromNullableVO(_postal_code, (value) => PostalCode.create(value)),
maybeFromNullableResult(_postal_code, (value) => PostalCode.create(value)),
"customer_postal_code",
errors
);
const customerCountry = extractOrPushError(
maybeFromNullableVO(_country, (value) => Country.create(value)),
maybeFromNullableResult(_country, (value) => Country.create(value)),
"customer_country",
errors
);
@ -167,12 +167,12 @@ export class InvoiceRecipientDomainMapper {
return {
customer_tin: recipient.tin.toPrimitive(),
customer_name: recipient.name.toPrimitive(),
customer_street: toNullable(recipient.street, (v) => v.toPrimitive()),
customer_street2: toNullable(recipient.street2, (v) => v.toPrimitive()),
customer_city: toNullable(recipient.city, (v) => v.toPrimitive()),
customer_province: toNullable(recipient.province, (v) => v.toPrimitive()),
customer_postal_code: toNullable(recipient.postalCode, (v) => v.toPrimitive()),
customer_country: toNullable(recipient.country, (v) => v.toPrimitive()),
customer_street: maybeToNullable(recipient.street, (v) => v.toPrimitive()),
customer_street2: maybeToNullable(recipient.street2, (v) => v.toPrimitive()),
customer_city: maybeToNullable(recipient.city, (v) => v.toPrimitive()),
customer_province: maybeToNullable(recipient.province, (v) => v.toPrimitive()),
customer_postal_code: maybeToNullable(recipient.postalCode, (v) => v.toPrimitive()),
customer_country: maybeToNullable(recipient.country, (v) => v.toPrimitive()),
};
}
}

View File

@ -6,8 +6,8 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
toEmptyString,
maybeFromNullableResult,
maybeToEmptyString,
} from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
@ -58,25 +58,25 @@ export class CustomerInvoiceVerifactuDomainMapper
);
const qr = extractOrPushError(
maybeFromNullableVO(source.qr, (value) => Result.ok(String(value))),
maybeFromNullableResult(source.qr, (value) => Result.ok(String(value))),
"qr",
errors
);
const url = extractOrPushError(
maybeFromNullableVO(source.url, (value) => URLAddress.create(value)),
maybeFromNullableResult(source.url, (value) => URLAddress.create(value)),
"url",
errors
);
const uuid = extractOrPushError(
maybeFromNullableVO(source.uuid, (value) => Result.ok(String(value))),
maybeFromNullableResult(source.uuid, (value) => Result.ok(String(value))),
"uuid",
errors
);
const operacion = extractOrPushError(
maybeFromNullableVO(source.operacion, (value) => Result.ok(String(value))),
maybeFromNullableResult(source.operacion, (value) => Result.ok(String(value))),
"operacion",
errors
);
@ -136,10 +136,10 @@ export class CustomerInvoiceVerifactuDomainMapper
id: verifactu.id.toPrimitive(),
invoice_id: parent.id.toPrimitive(),
estado: verifactu.estado.toPrimitive(),
qr: toEmptyString(verifactu.qrCode, (v) => v),
url: toEmptyString(verifactu.url, (v) => v.toPrimitive()),
uuid: toEmptyString(verifactu.uuid, (v) => v),
operacion: toEmptyString(verifactu.operacion, (v) => v),
qr: maybeToEmptyString(verifactu.qrCode, (v) => v),
url: maybeToEmptyString(verifactu.url, (v) => v.toPrimitive()),
uuid: maybeToEmptyString(verifactu.uuid, (v) => v),
operacion: maybeToEmptyString(verifactu.operacion, (v) => v),
});
}
}

View File

@ -12,7 +12,7 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
@ -167,7 +167,7 @@ export class CustomerInvoiceListMapper
const status = extractOrPushError(InvoiceStatus.create(raw.status), "status", errors);
const series = extractOrPushError(
maybeFromNullableVO(raw.series, (value) => InvoiceSerie.create(value)),
maybeFromNullableResult(raw.series, (value) => InvoiceSerie.create(value)),
"serie",
errors
);
@ -185,19 +185,19 @@ export class CustomerInvoiceListMapper
);
const operationDate = extractOrPushError(
maybeFromNullableVO(raw.operation_date, (value) => UtcDate.createFromISO(value)),
maybeFromNullableResult(raw.operation_date, (value) => UtcDate.createFromISO(value)),
"operation_date",
errors
);
const reference = extractOrPushError(
maybeFromNullableVO(raw.reference, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.reference, (value) => Result.ok(String(value))),
"description",
errors
);
const description = extractOrPushError(
maybeFromNullableVO(raw.description, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.description, (value) => Result.ok(String(value))),
"description",
errors
);

View File

@ -13,7 +13,7 @@ import {
TINNumber,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import type { Result } from "@repo/rdx-utils";
@ -67,37 +67,37 @@ export class InvoiceRecipientListMapper
const customerTin = extractOrPushError(TINNumber.create(_tin), "customer_tin", errors);
const customerStreet = extractOrPushError(
maybeFromNullableVO(_street, (value) => Street.create(value)),
maybeFromNullableResult(_street, (value) => Street.create(value)),
"customer_street",
errors
);
const customerStreet2 = extractOrPushError(
maybeFromNullableVO(_street2, (value) => Street.create(value)),
maybeFromNullableResult(_street2, (value) => Street.create(value)),
"customer_street2",
errors
);
const customerCity = extractOrPushError(
maybeFromNullableVO(_city, (value) => City.create(value)),
maybeFromNullableResult(_city, (value) => City.create(value)),
"customer_city",
errors
);
const customerProvince = extractOrPushError(
maybeFromNullableVO(_province, (value) => Province.create(value)),
maybeFromNullableResult(_province, (value) => Province.create(value)),
"customer_province",
errors
);
const customerPostalCode = extractOrPushError(
maybeFromNullableVO(_postal_code, (value) => PostalCode.create(value)),
maybeFromNullableResult(_postal_code, (value) => PostalCode.create(value)),
"customer_postal_code",
errors
);
const customerCountry = extractOrPushError(
maybeFromNullableVO(_country, (value) => Country.create(value)),
maybeFromNullableResult(_country, (value) => Country.create(value)),
"customer_country",
errors
);

View File

@ -9,7 +9,7 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
@ -35,25 +35,25 @@ export class VerifactuRecordListMapper
const estado = extractOrPushError(VerifactuRecordEstado.create(raw.estado), "estado", errors);
const qr = extractOrPushError(
maybeFromNullableVO(raw.qr, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.qr, (value) => Result.ok(String(value))),
"qr",
errors
);
const url = extractOrPushError(
maybeFromNullableVO(raw.url, (value) => URLAddress.create(value)),
maybeFromNullableResult(raw.url, (value) => URLAddress.create(value)),
"url",
errors
);
const uuid = extractOrPushError(
maybeFromNullableVO(raw.uuid, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.uuid, (value) => Result.ok(String(value))),
"uuid",
errors
);
const operacion = extractOrPushError(
maybeFromNullableVO(raw.operacion, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.operacion, (value) => Result.ok(String(value))),
"operacion",
errors
);

View File

@ -23,20 +23,20 @@ export class CustomerInvoiceTaxModel extends Model<
declare invoice_id: string;
// Taxable amount (base imponible)
declare taxable_amount_value: CreationOptional<number | null>;
declare taxable_amount_value: number;
declare taxable_amount_scale: number;
// Código de impuestos
// IVA percentage
declare iva_code: CreationOptional<string | null>;
declare iva_code: string;
declare iva_percentage_value: CreationOptional<number | null>;
declare iva_percentage_value: number;
declare iva_percentage_scale: number;
// IVA amount
declare iva_amount_value: CreationOptional<number | null>;
declare iva_amount_value: number;
declare iva_amount_scale: number;
// Recargo de equivalencia percentage
@ -60,7 +60,7 @@ export class CustomerInvoiceTaxModel extends Model<
declare retention_amount_scale: number;
// Total taxes amount / taxes total
declare taxes_amount_value: CreationOptional<number | null>;
declare taxes_amount_value: number;
declare taxes_amount_scale: number;
// Relaciones
@ -108,8 +108,8 @@ export default (database: Sequelize) => {
taxable_amount_value: {
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
allowNull: true,
defaultValue: null,
allowNull: false,
defaultValue: 0,
},
taxable_amount_scale: {
@ -207,8 +207,8 @@ export default (database: Sequelize) => {
taxes_amount_value: {
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
allowNull: true,
defaultValue: null,
allowNull: false,
defaultValue: 0,
},
taxes_amount_scale: {

View File

@ -9,13 +9,14 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
toNullable,
maybeFromNullableResult,
maybeToNullable,
} from "@repo/rdx-ddd";
import { Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils";
import type { IIssuedInvoiceDomainMapper } from "../../../../../../application";
import {
InvoiceAmount,
InvoiceNumber,
InvoicePaymentMethod,
InvoiceSerie,
@ -23,6 +24,7 @@ import {
IssuedInvoice,
IssuedInvoiceItems,
type IssuedInvoiceProps,
IssuedInvoiceTaxes,
} from "../../../../../../domain";
import type {
CustomerInvoiceCreationAttributes,
@ -52,87 +54,83 @@ export class SequelizeIssuedInvoiceDomainMapper
this._itemsMapper = new SequelizeIssuedInvoiceItemDomainMapper(params); // Instanciar el mapper de items
this._recipientMapper = new SequelizeIssuedInvoiceRecipientDomainMapper();
this._taxesMapper = new SequelizeIssuedInvoiceTaxesDomainMapper();
this._taxesMapper = new SequelizeIssuedInvoiceTaxesDomainMapper(params);
this._verifactuMapper = new SequelizeIssuedInvoiceVerifactuDomainMapper();
}
private _mapAttributesToDomain(source: CustomerInvoiceModel, params?: MapperParamsType) {
private _mapAttributesToDomain(raw: 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 invoiceId = extractOrPushError(UniqueID.create(raw.id), "id", errors);
const companyId = extractOrPushError(UniqueID.create(raw.company_id), "company_id", errors);
const customerId = extractOrPushError(
UniqueID.create(source.customer_id),
"customer_id",
errors
);
const customerId = extractOrPushError(UniqueID.create(raw.customer_id), "customer_id", errors);
const isIssuedInvoice = Boolean(source.is_proforma);
const isIssuedInvoice = Boolean(raw.is_proforma);
const proformaId = extractOrPushError(
maybeFromNullableVO(source.proforma_id, (v) => UniqueID.create(v)),
maybeFromNullableResult(raw.proforma_id, (v) => UniqueID.create(v)),
"proforma_id",
errors
);
const status = extractOrPushError(InvoiceStatus.create(source.status), "status", errors);
const status = extractOrPushError(InvoiceStatus.create(raw.status), "status", errors);
const series = extractOrPushError(
maybeFromNullableVO(source.series, (v) => InvoiceSerie.create(v)),
maybeFromNullableResult(raw.series, (v) => InvoiceSerie.create(v)),
"series",
errors
);
const invoiceNumber = extractOrPushError(
InvoiceNumber.create(source.invoice_number),
InvoiceNumber.create(raw.invoice_number),
"invoice_number",
errors
);
// Fechas
const invoiceDate = extractOrPushError(
UtcDate.createFromISO(source.invoice_date),
UtcDate.createFromISO(raw.invoice_date),
"invoice_date",
errors
);
const operationDate = extractOrPushError(
maybeFromNullableVO(source.operation_date, (v) => UtcDate.createFromISO(v)),
maybeFromNullableResult(raw.operation_date, (v) => UtcDate.createFromISO(v)),
"operation_date",
errors
);
// Idioma / divisa
const languageCode = extractOrPushError(
LanguageCode.create(source.language_code),
LanguageCode.create(raw.language_code),
"language_code",
errors
);
const currencyCode = extractOrPushError(
CurrencyCode.create(source.currency_code),
CurrencyCode.create(raw.currency_code),
"currency_code",
errors
);
// Textos opcionales
const reference = extractOrPushError(
maybeFromNullableVO(source.reference, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.reference, (value) => Result.ok(String(value))),
"reference",
errors
);
const description = extractOrPushError(
maybeFromNullableVO(source.description, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.description, (value) => Result.ok(String(value))),
"description",
errors
);
const notes = extractOrPushError(
maybeFromNullableVO(source.notes, (value) => TextValue.create(value)),
maybeFromNullableResult(raw.notes, (value) => TextValue.create(value)),
"notes",
errors
);
@ -140,16 +138,16 @@ export class SequelizeIssuedInvoiceDomainMapper
// Método de pago (VO opcional con id + descripción)
let paymentMethod = Maybe.none<InvoicePaymentMethod>();
if (!isNullishOrEmpty(source.payment_method_id)) {
if (!isNullishOrEmpty(raw.payment_method_id)) {
const paymentId = extractOrPushError(
UniqueID.create(String(source.payment_method_id)),
UniqueID.create(String(raw.payment_method_id)),
"paymentMethod.id",
errors
);
const paymentVO = extractOrPushError(
InvoicePaymentMethod.create(
{ paymentDescription: String(source.payment_method_description ?? "") },
{ paymentDescription: String(raw.payment_method_description ?? "") },
paymentId ?? undefined
),
"payment_method_description",
@ -161,13 +159,78 @@ export class SequelizeIssuedInvoiceDomainMapper
}
}
// % descuento (VO)
const discountPercentage = extractOrPushError(
Percentage.create({
value: Number(source.discount_percentage_value ?? 0),
scale: Number(source.discount_percentage_scale ?? 2),
const subtotalAmount = extractOrPushError(
InvoiceAmount.create({
value: raw.subtotal_amount_value,
currency_code: currencyCode?.code,
}),
"discount_percentage_value",
"subtotal_amount_value",
errors
);
// Total descuento de líneas
const itemsDiscountAmount = extractOrPushError(
InvoiceAmount.create({
value: Number(raw.items_discount_amount_value ?? 0),
currency_code: currencyCode?.code,
}),
"items_discount_amount_value",
errors
);
// % descuento global (VO)
const globalDiscountPercentage = extractOrPushError(
Percentage.create({
value: Number(raw.global_discount_percentage_value ?? 0),
scale: Number(raw.global_discount_percentage_scale ?? 2),
}),
"global_discount_percentage_value",
errors
);
const globalDiscountAmount = extractOrPushError(
InvoiceAmount.create({
value: Number(raw.global_discount_amount_value ?? 0),
currency_code: currencyCode?.code,
}),
"global_discount_amount_value",
errors
);
const totalDiscountAmount = extractOrPushError(
InvoiceAmount.create({
value: raw.total_discount_amount_value,
currency_code: currencyCode?.code,
}),
"total_discount_amount_value",
errors
);
const taxableAmount = extractOrPushError(
InvoiceAmount.create({
value: raw.taxable_amount_value,
currency_code: currencyCode?.code,
}),
"taxable_amount_value",
errors
);
const taxesAmount = extractOrPushError(
InvoiceAmount.create({
value: raw.taxes_amount_value,
currency_code: currencyCode?.code,
}),
"taxes_amount_value",
errors
);
const totalAmount = extractOrPushError(
InvoiceAmount.create({
value: raw.total_amount_value,
currency_code: currencyCode?.code,
}),
"total_amount_value",
errors
);
@ -187,8 +250,16 @@ export class SequelizeIssuedInvoiceDomainMapper
notes,
languageCode,
currencyCode,
discountPercentage,
paymentMethod,
subtotalAmount,
itemsDiscountAmount,
globalDiscountPercentage,
globalDiscountAmount,
totalDiscountAmount,
taxableAmount,
taxesAmount,
totalAmount,
};
}
@ -227,7 +298,18 @@ export class SequelizeIssuedInvoiceDomainMapper
}
);
// 5) Si hubo errores de mapeo, devolvemos colección de validación
// 5) Taxes (colección)
const taxesResults = this._taxesMapper.mapToDomainCollection(
source.taxes,
source.taxes.length,
{
errors,
attributes,
...params,
}
);
// 6) Si hubo errores de mapeo, devolvemos colección de validación
if (errors.length > 0) {
return Result.fail(
new ValidationErrorCollection("Customer invoice mapping failed [mapToDomain]", errors)
@ -239,10 +321,17 @@ export class SequelizeIssuedInvoiceDomainMapper
const items = IssuedInvoiceItems.create({
languageCode: attributes.languageCode!,
currencyCode: attributes.currencyCode!,
globalDiscountPercentage: attributes.discountPercentage!,
globalDiscountPercentage: attributes.globalDiscountPercentage!,
items: itemsResults.data.getAll(),
});
const taxes = IssuedInvoiceTaxes.create({
languageCode: attributes.languageCode!,
currencyCode: attributes.currencyCode!,
globalDiscountPercentage: attributes.globalDiscountPercentage!,
taxes: taxesResults.data.getAll(),
});
const invoiceProps: IssuedInvoiceProps = {
companyId: attributes.companyId!,
@ -263,11 +352,21 @@ export class SequelizeIssuedInvoiceDomainMapper
languageCode: attributes.languageCode!,
currencyCode: attributes.currencyCode!,
globalDiscountPercentage: attributes.discountPercentage!,
subtotalAmount: attributes.subtotalAmount!,
itemsDiscountAmount: attributes.itemsDiscountAmount!,
globalDiscountPercentage: attributes.globalDiscountPercentage!,
globalDiscountAmount: attributes.globalDiscountAmount!,
totalDiscountAmount: attributes.totalDiscountAmount!,
taxableAmount: attributes.taxableAmount!,
taxesAmount: attributes.taxesAmount!,
totalAmount: attributes.totalAmount!,
paymentMethod: attributes.paymentMethod!,
items,
taxes: taxesResults.data,
verifactu: verifactuResult.data,
};
@ -354,22 +453,25 @@ export class SequelizeIssuedInvoiceDomainMapper
// Flags / estado / serie / número
is_proforma: false,
status: source.status.toPrimitive(),
proforma_id: toNullable(source.proformaId, (v) => v.toPrimitive()),
proforma_id: maybeToNullable(source.proformaId, (v) => v.toPrimitive()),
series: toNullable(source.series, (v) => v.toPrimitive()),
series: maybeToNullable(source.series, (v) => v.toPrimitive()),
invoice_number: source.invoiceNumber.toPrimitive(),
invoice_date: source.invoiceDate.toPrimitive(),
operation_date: toNullable(source.operationDate, (v) => v.toPrimitive()),
operation_date: maybeToNullable(source.operationDate, (v) => v.toPrimitive()),
language_code: source.languageCode.toPrimitive(),
currency_code: source.currencyCode.toPrimitive(),
reference: toNullable(source.reference, (reference) => reference),
description: toNullable(source.description, (description) => description),
reference: maybeToNullable(source.reference, (reference) => reference),
description: maybeToNullable(source.description, (description) => description),
notes: toNullable(source.notes, (v) => v.toPrimitive()),
notes: maybeToNullable(source.notes, (v) => v.toPrimitive()),
payment_method_id: toNullable(source.paymentMethod, (payment) => payment.toObjectString().id),
payment_method_description: toNullable(
payment_method_id: maybeToNullable(
source.paymentMethod,
(payment) => payment.toObjectString().id
),
payment_method_description: maybeToNullable(
source.paymentMethod,
(payment) => payment.toObjectString().payment_description
),

View File

@ -1,12 +1,14 @@
import type { JsonTaxCatalogProvider } from "@erp/core";
import { type MapperParamsType, SequelizeDomainMapper, Tax } from "@erp/core/api";
import { type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
import {
UniqueID,
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
toNullable,
maybeFromNullableOrEmptyString,
maybeFromNullableResult,
maybeToNullable,
maybeToNullableString,
} from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
@ -17,9 +19,9 @@ import {
type IssuedInvoiceProps,
ItemAmount,
ItemDescription,
ItemDiscount,
ItemDiscountPercentage,
ItemQuantity,
ItemTaxGroup,
ItemTaxPercentage,
} from "../../../../../../domain";
import type {
CustomerInvoiceItemCreationAttributes,
@ -31,7 +33,7 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
CustomerInvoiceItemCreationAttributes,
IssuedInvoiceItem
> {
private _taxCatalog!: JsonTaxCatalogProvider;
private taxCatalog!: JsonTaxCatalogProvider;
constructor(params: MapperParamsType) {
super();
@ -40,14 +42,14 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
};
if (!taxCatalog) {
throw new Error('taxCatalog not defined ("CustomerInvoiceItemDomainMapper")');
throw new Error('taxCatalog not defined ("SequelizeIssuedInvoiceItemDomainMapper")');
}
this._taxCatalog = taxCatalog;
this.taxCatalog = taxCatalog;
}
private mapAttributesToDomain(
source: CustomerInvoiceItemModel,
raw: CustomerInvoiceItemModel,
params?: MapperParamsType
): Partial<IssuedInvoiceItemProps> & { itemId?: UniqueID } {
const { errors, index, attributes } = params as {
@ -57,83 +59,179 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
};
const itemId = extractOrPushError(
UniqueID.create(source.item_id),
UniqueID.create(raw.item_id),
`items[${index}].item_id`,
errors
);
const description = extractOrPushError(
maybeFromNullableVO(source.description, (v) => ItemDescription.create(v)),
maybeFromNullableResult(raw.description, (v) => ItemDescription.create(v)),
`items[${index}].description`,
errors
);
const quantity = extractOrPushError(
maybeFromNullableVO(source.quantity_value, (v) => ItemQuantity.create({ value: v })),
`items[${index}].quantity`,
maybeFromNullableResult(raw.quantity_value, (v) => ItemQuantity.create({ value: v })),
`items[${index}].quantity_value`,
errors
);
const unitAmount = extractOrPushError(
maybeFromNullableVO(source.unit_amount_value, (value) =>
maybeFromNullableResult(raw.unit_amount_value, (value) =>
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`items[${index}].unit_amount`,
`items[${index}].unit_amount_value`,
errors
);
const discountPercentage = extractOrPushError(
maybeFromNullableVO(source.discount_percentage_value, (v) =>
ItemDiscount.create({ value: v })
const subtotalAmount = extractOrPushError(
maybeFromNullableResult(raw.subtotal_amount_value, (value) =>
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`items[${index}].discount_percentage`,
`items[${index}].subtotal_amount_value`,
errors
);
const itemDiscountPercentage = extractOrPushError(
maybeFromNullableResult(raw.discount_percentage_value, (v) =>
ItemDiscountPercentage.create({ value: v })
),
`items[${index}].discount_percentage_value`,
errors
);
const itemDiscountAmount = extractOrPushError(
maybeFromNullableResult(raw.discount_amount_value, (value) =>
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`items[${index}].discount_amount_value`,
errors
);
const globalDiscountPercentage = extractOrPushError(
maybeFromNullableVO(source.global_discount_percentage_value, (v) =>
ItemDiscount.create({ value: v })
maybeFromNullableResult(raw.global_discount_percentage_value, (v) =>
ItemDiscountPercentage.create({ value: v })
),
`items[${index}].discount_percentage`,
`items[${index}].global_discount_percentage_value`,
errors
);
const iva = extractOrPushError(
maybeFromNullableVO(source.iva_code, (code) => Tax.createFromCode(code, this._taxCatalog)),
`items[${index}].iva_code`,
errors
);
const rec = extractOrPushError(
maybeFromNullableVO(source.rec_code, (code) => Tax.createFromCode(code, this._taxCatalog)),
`items[${index}].rec_code`,
errors
);
const retention = extractOrPushError(
maybeFromNullableVO(source.retention_code, (code) =>
Tax.createFromCode(code, this._taxCatalog)
const globalDiscountAmount = extractOrPushError(
maybeFromNullableResult(raw.global_discount_amount_value, (value) =>
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`items[${index}].retention_code`,
`items[${index}].global_discount_amount_value`,
errors
);
const totalDiscountAmount = extractOrPushError(
maybeFromNullableResult(raw.total_discount_amount_value, (value) =>
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`items[${index}].total_discount_amount_value`,
errors
);
const ivaCode = maybeFromNullableOrEmptyString(raw.iva_code);
const ivaPercentage = extractOrPushError(
maybeFromNullableResult(raw.iva_percentage_value, (value) =>
ItemTaxPercentage.create({ value })
),
`items[${index}].iva_percentage_value`,
errors
);
const ivaAmount = extractOrPushError(
maybeFromNullableResult(raw.iva_amount_value, (value) =>
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`items[${index}].iva_amount_value`,
errors
);
const recCode = maybeFromNullableOrEmptyString(raw.rec_code);
const recPercentage = extractOrPushError(
maybeFromNullableResult(raw.rec_percentage_value, (value) =>
ItemTaxPercentage.create({ value })
),
`items[${index}].rec_percentage_value`,
errors
);
const recAmount = extractOrPushError(
maybeFromNullableResult(raw.rec_amount_value, (value) =>
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`items[${index}].rec_amount_value`,
errors
);
const retentionCode = maybeFromNullableOrEmptyString(raw.retention_code);
const retentionPercentage = extractOrPushError(
maybeFromNullableResult(raw.retention_percentage_value, (value) =>
ItemTaxPercentage.create({ value })
),
`items[${index}].retention_percentage_value`,
errors
);
const retentionAmount = extractOrPushError(
maybeFromNullableResult(raw.retention_amount_value, (value) =>
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`items[${index}].retention_amount_value`,
errors
);
const taxesAmount = extractOrPushError(
maybeFromNullableResult(raw.taxes_amount_value, (value) =>
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`items[${index}].taxes_amount_value`,
errors
);
const totalAmount = extractOrPushError(
maybeFromNullableResult(raw.total_amount_value, (value) =>
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`items[${index}].total_amount_value`,
errors
);
return {
itemId,
languageCode: attributes.languageCode,
currencyCode: attributes.currencyCode,
description,
quantity,
unitAmount,
itemDiscountPercentage: discountPercentage,
globalDiscountPercentage,
subtotalAmount,
taxes: ItemTaxGroup.create({
iva: iva!,
rec: rec!,
retention: retention!,
}).data,
itemDiscountPercentage,
itemDiscountAmount,
globalDiscountPercentage,
globalDiscountAmount,
totalDiscountAmount,
ivaCode,
ivaPercentage,
ivaAmount,
recCode,
recPercentage,
recAmount,
retentionCode,
retentionPercentage,
retentionAmount,
taxesAmount,
totalAmount,
};
}
@ -177,15 +275,21 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
taxableAmount: attributes.taxableAmount!,
ivaCode: attributes.ivaCode!,
ivaPercentage: attributes.ivaPercentage!,
ivaAmount: attributes.ivaAmount!,
recCode: attributes.recCode!,
recPercentage: attributes.recPercentage!,
recAmount: attributes.recAmount!,
retentionCode: attributes.retentionCode!,
retentionPercentage: attributes.retentionPercentage!,
retentionAmount: attributes.retentionAmount!,
taxesAmount: attributes.taxesAmount!,
totalAmount: attributes.totalAmount!,
taxes: attributes.taxes!,
languageCode: attributes.languageCode!,
currencyCode: attributes.currencyCode!,
},
@ -213,47 +317,52 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
errors: ValidationErrorDetail[];
};
const taxesAmounts = source.taxes;
return Result.ok({
item_id: source.id.toPrimitive(),
invoice_id: parent.id.toPrimitive(),
position: index,
description: toNullable(source.description, (v) => v.toPrimitive()),
description: maybeToNullable(source.description, (v) => v.toPrimitive()),
quantity_value: toNullable(source.quantity, (v) => v.toPrimitive().value),
quantity_value: maybeToNullable(source.quantity, (v) => v.toPrimitive().value),
quantity_scale:
toNullable(source.quantity, (v) => v.toPrimitive().scale) ?? ItemQuantity.DEFAULT_SCALE,
maybeToNullable(source.quantity, (v) => v.toPrimitive().scale) ??
ItemQuantity.DEFAULT_SCALE,
unit_amount_value: toNullable(source.unitAmount, (v) => v.toPrimitive().value),
unit_amount_value: maybeToNullable(source.unitAmount, (v) => v.toPrimitive().value),
unit_amount_scale:
toNullable(source.unitAmount, (v) => v.toPrimitive().scale) ?? ItemAmount.DEFAULT_SCALE,
maybeToNullable(source.unitAmount, (v) => v.toPrimitive().scale) ??
ItemAmount.DEFAULT_SCALE,
subtotal_amount_value: source.subtotalAmount.value,
subtotal_amount_scale: source.subtotalAmount.scale,
subtotal_amount_value: maybeToNullable(source.subtotalAmount, (v) => v.toPrimitive().value),
subtotal_amount_scale:
maybeToNullable(source.subtotalAmount, (v) => v.toPrimitive().scale) ??
ItemAmount.DEFAULT_SCALE,
// Te has quedado aquí --- IGNORE ---
// !!!!!!!!!!!!!!!!!!!
//
discount_percentage_value: toNullable(
discount_percentage_value: maybeToNullable(
source.itemDiscountPercentage,
(v) => v.toPrimitive().value
),
discount_percentage_scale:
toNullable(source.itemDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscount.DEFAULT_SCALE,
maybeToNullable(source.itemDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscountPercentage.DEFAULT_SCALE,
discount_amount_value: source.itemDiscountAmount.value,
discount_amount_scale: source.itemDiscountAmount.scale,
//
global_discount_percentage_value: toNullable(
global_discount_percentage_value: maybeToNullable(
source.globalDiscountPercentage,
(v) => v.toPrimitive().value
),
global_discount_percentage_scale:
toNullable(source.globalDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscount.DEFAULT_SCALE,
maybeToNullable(source.globalDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscountPercentage.DEFAULT_SCALE,
global_discount_amount_value: source.globalDiscountAmount.value,
global_discount_amount_scale: source.globalDiscountAmount.scale,
@ -267,32 +376,38 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
taxable_amount_scale: source.taxableAmount.scale,
// IVA
iva_code: toNullable(source.taxes.iva, (v) => v.code),
iva_code: maybeToNullableString(source.ivaCode),
iva_percentage_value: toNullable(source.taxes.iva, (v) => v.percentage.value),
iva_percentage_scale: toNullable(source.taxes.iva, (v) => v.percentage.scale) ?? 2,
iva_percentage_value: maybeToNullable(source.ivaPercentage, (v) => v.toPrimitive().value),
iva_percentage_scale:
maybeToNullable(source.ivaPercentage, (v) => v.toPrimitive().scale) ?? 2,
iva_amount_value: taxesAmounts.ivaAmount.value,
iva_amount_scale: taxesAmounts.ivaAmount.scale,
iva_amount_value: maybeToNullable(source.ivaAmount, (v) => v.toPrimitive().value),
iva_amount_scale: maybeToNullable(source.ivaAmount, (v) => v.toPrimitive().scale) ?? 4,
// REC
rec_code: toNullable(source.taxes.rec, (v) => v.code),
rec_code: maybeToNullableString(source.recCode),
rec_percentage_value: toNullable(source.taxes.rec, (v) => v.percentage.value),
rec_percentage_scale: toNullable(source.taxes.rec, (v) => v.percentage.scale) ?? 2,
rec_percentage_value: maybeToNullable(source.recPercentage, (v) => v.toPrimitive().value),
rec_percentage_scale:
maybeToNullable(source.recPercentage, (v) => v.toPrimitive().scale) ?? 2,
rec_amount_value: taxesAmounts.recAmount.value,
rec_amount_scale: taxesAmounts.recAmount.scale,
rec_amount_value: maybeToNullable(source.recAmount, (v) => v.toPrimitive().value),
rec_amount_scale: maybeToNullable(source.recAmount, (v) => v.toPrimitive().scale) ?? 4,
// RET
retention_code: toNullable(source.taxes.retention, (v) => v.code),
retention_code: maybeToNullableString(source.retentionCode),
retention_percentage_value: toNullable(source.taxes.retention, (v) => v.percentage.value),
retention_percentage_value: maybeToNullable(
source.retentionPercentage,
(v) => v.toPrimitive().value
),
retention_percentage_scale:
toNullable(source.taxes.retention, (v) => v.percentage.scale) ?? 2,
maybeToNullable(source.retentionPercentage, (v) => v.toPrimitive().scale) ?? 2,
retention_amount_value: taxesAmounts.retentionAmount.value,
retention_amount_scale: taxesAmounts.retentionAmount.scale,
retention_amount_value: maybeToNullable(source.retentionAmount, (v) => v.toPrimitive().value),
retention_amount_scale:
maybeToNullable(source.retentionAmount, (v) => v.toPrimitive().scale) ?? 4,
//
taxes_amount_value: source.taxesAmount.value,

View File

@ -10,8 +10,8 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
toNullable,
maybeFromNullableResult,
maybeToNullable,
} from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
@ -51,37 +51,37 @@ export class SequelizeIssuedInvoiceRecipientDomainMapper {
const customerTin = extractOrPushError(TINNumber.create(_tin!), "customer_tin", errors);
const customerStreet = extractOrPushError(
maybeFromNullableVO(_street, (value) => Street.create(value)),
maybeFromNullableResult(_street, (value) => Street.create(value)),
"customer_street",
errors
);
const customerStreet2 = extractOrPushError(
maybeFromNullableVO(_street2, (value) => Street.create(value)),
maybeFromNullableResult(_street2, (value) => Street.create(value)),
"customer_street2",
errors
);
const customerCity = extractOrPushError(
maybeFromNullableVO(_city, (value) => City.create(value)),
maybeFromNullableResult(_city, (value) => City.create(value)),
"customer_city",
errors
);
const customerProvince = extractOrPushError(
maybeFromNullableVO(_province, (value) => Province.create(value)),
maybeFromNullableResult(_province, (value) => Province.create(value)),
"customer_province",
errors
);
const customerPostalCode = extractOrPushError(
maybeFromNullableVO(_postal_code, (value) => PostalCode.create(value)),
maybeFromNullableResult(_postal_code, (value) => PostalCode.create(value)),
"customer_postal_code",
errors
);
const customerCountry = extractOrPushError(
maybeFromNullableVO(_country, (value) => Country.create(value)),
maybeFromNullableResult(_country, (value) => Country.create(value)),
"customer_country",
errors
);
@ -145,12 +145,12 @@ export class SequelizeIssuedInvoiceRecipientDomainMapper {
return {
customer_tin: recipient.tin.toPrimitive(),
customer_name: recipient.name.toPrimitive(),
customer_street: toNullable(recipient.street, (v) => v.toPrimitive()),
customer_street2: toNullable(recipient.street2, (v) => v.toPrimitive()),
customer_city: toNullable(recipient.city, (v) => v.toPrimitive()),
customer_province: toNullable(recipient.province, (v) => v.toPrimitive()),
customer_postal_code: toNullable(recipient.postalCode, (v) => v.toPrimitive()),
customer_country: toNullable(recipient.country, (v) => v.toPrimitive()),
customer_street: maybeToNullable(recipient.street, (v) => v.toPrimitive()),
customer_street2: maybeToNullable(recipient.street2, (v) => v.toPrimitive()),
customer_city: maybeToNullable(recipient.city, (v) => v.toPrimitive()),
customer_province: maybeToNullable(recipient.province, (v) => v.toPrimitive()),
customer_postal_code: maybeToNullable(recipient.postalCode, (v) => v.toPrimitive()),
customer_country: maybeToNullable(recipient.country, (v) => v.toPrimitive()),
};
}
}

View File

@ -1,8 +1,24 @@
import type { JsonTaxCatalogProvider } from "@erp/core";
import { type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
import { UniqueID, type ValidationErrorDetail, toNullable } from "@repo/rdx-ddd";
import {
UniqueID,
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableOrEmptyString,
maybeFromNullableResult,
maybeToNullable,
maybeToNullableString,
} from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import type { InvoiceTaxGroup, IssuedInvoice } from "../../../../../../domain";
import {
InvoiceAmount,
InvoiceTaxPercentage,
type IssuedInvoice,
type IssuedInvoiceProps,
IssuedInvoiceTax,
} from "../../../../../../domain";
import type {
CustomerInvoiceTaxCreationAttributes,
CustomerInvoiceTaxModel,
@ -23,17 +39,145 @@ import type {
export class SequelizeIssuedInvoiceTaxesDomainMapper extends SequelizeDomainMapper<
CustomerInvoiceTaxModel,
CustomerInvoiceTaxCreationAttributes,
InvoiceTaxGroup
IssuedInvoiceTax
> {
private taxCatalog!: JsonTaxCatalogProvider;
constructor(params: MapperParamsType) {
super();
const { taxCatalog } = params as {
taxCatalog: JsonTaxCatalogProvider;
};
if (!taxCatalog) {
throw new Error('taxCatalog not defined ("SequelizeIssuedInvoiceTaxesDomainMapper")');
}
this.taxCatalog = taxCatalog;
}
public mapToDomain(
source: CustomerInvoiceTaxModel,
params?: MapperParamsType
): Result<InvoiceTaxGroup, Error> {
throw new Error("Se calcula a partir de las líneas de detalle");
): Result<IssuedInvoiceTax, Error> {
const { errors, index, attributes } = params as {
index: number;
errors: ValidationErrorDetail[];
attributes: Partial<IssuedInvoiceProps>;
};
const taxableAmount = extractOrPushError(
InvoiceAmount.create({
value: source.taxable_amount_value,
currency_code: attributes.currencyCode?.code,
}),
`taxes[${index}].taxable_amount_value`,
errors
);
const ivaCode = source.iva_code;
const ivaPercentage = extractOrPushError(
InvoiceTaxPercentage.create({
value: source.iva_percentage_value,
}),
`taxes[${index}].iva_percentage_value`,
errors
);
const ivaAmount = extractOrPushError(
InvoiceAmount.create({
value: source.iva_amount_value,
currency_code: attributes.currencyCode?.code,
}),
`taxes[${index}].iva_amount_value`,
errors
);
const recCode = maybeFromNullableOrEmptyString(source.rec_code);
const recPercentage = extractOrPushError(
maybeFromNullableResult(source.rec_percentage_value, (value) =>
InvoiceTaxPercentage.create({ value })
),
`taxes[${index}].rec_percentage_value`,
errors
);
const recAmount = extractOrPushError(
maybeFromNullableResult(source.rec_amount_value, (value) =>
InvoiceAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`taxes[${index}].rec_amount_value`,
errors
);
const retentionCode = maybeFromNullableOrEmptyString(source.retention_code);
const retentionPercentage = extractOrPushError(
maybeFromNullableResult(source.retention_percentage_value, (value) =>
InvoiceTaxPercentage.create({ value })
),
`taxes[${index}].retention_percentage_value`,
errors
);
const retentionAmount = extractOrPushError(
maybeFromNullableResult(source.retention_amount_value, (value) =>
InvoiceAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`taxes[${index}].retention_amount_value`,
errors
);
const taxesAmount = extractOrPushError(
InvoiceAmount.create({
value: source.taxes_amount_value,
currency_code: attributes.currencyCode?.code,
}),
`taxes[${index}].taxes_amount_value`,
errors
);
// Si hubo errores de mapeo, devolvemos colección de validación
if (errors.length > 0) {
return Result.fail(
new ValidationErrorCollection("Customer invoice tax mapping failed [mapToDomain]", errors)
);
}
// 2) Construcción del elemento de dominio
const createResult = IssuedInvoiceTax.create({
taxableAmount: taxableAmount!,
ivaCode: ivaCode!,
ivaPercentage: ivaPercentage!,
ivaAmount: ivaAmount!,
recCode: recCode,
recPercentage: recPercentage!,
recAmount: recAmount!,
retentionCode: retentionCode,
retentionPercentage: retentionPercentage!,
retentionAmount: retentionAmount!,
taxesAmount: taxesAmount!,
});
if (createResult.isFailure) {
return Result.fail(
new ValidationErrorCollection("Invoice tax group entity creation failed", [
{ path: `taxes[${index}]`, message: "Invoice tax group entity creation failed" },
])
);
}
return createResult;
}
public mapToPersistence(
source: InvoiceTaxGroup,
source: IssuedInvoiceTax,
params?: MapperParamsType
): Result<CustomerInvoiceTaxCreationAttributes, Error> {
const { errors, parent } = params as {
@ -42,10 +186,6 @@ export class SequelizeIssuedInvoiceTaxesDomainMapper extends SequelizeDomainMapp
};
try {
const { ivaAmount, recAmount, retentionAmount } = source;
const totalTaxes = ivaAmount.add(recAmount).add(retentionAmount);
const dto: CustomerInvoiceTaxCreationAttributes = {
tax_id: UniqueID.generateNewID().toPrimitive(),
invoice_id: parent.id.toPrimitive(),
@ -55,35 +195,44 @@ export class SequelizeIssuedInvoiceTaxesDomainMapper extends SequelizeDomainMapp
taxable_amount_scale: source.taxableAmount.scale,
// IVA
iva_code: source.iva.code,
iva_code: source.ivaCode,
iva_percentage_value: source.iva.value,
iva_percentage_scale: source.iva.scale,
iva_percentage_value: source.ivaPercentage.value,
iva_percentage_scale: source.ivaPercentage.scale,
iva_amount_value: ivaAmount.value,
iva_amount_scale: ivaAmount.scale,
iva_amount_value: source.ivaAmount.value,
iva_amount_scale: source.ivaAmount.scale,
// REC
rec_code: toNullable(source.rec, (v) => v.code),
rec_code: maybeToNullableString(source.recCode),
rec_percentage_value: toNullable(source.rec, (v) => v.percentage.value),
rec_percentage_scale: toNullable(source.rec, (v) => v.percentage.scale) ?? 2,
rec_percentage_value: maybeToNullable(source.recPercentage, (v) => v.toPrimitive().value),
rec_percentage_scale:
maybeToNullable(source.recPercentage, (v) => v.toPrimitive().scale) ?? 2,
rec_amount_value: recAmount.value,
rec_amount_scale: recAmount.scale,
rec_amount_value: maybeToNullable(source.recAmount, (v) => v.toPrimitive().value),
rec_amount_scale: maybeToNullable(source.recAmount, (v) => v.toPrimitive().scale) ?? 4,
// RET
retention_code: toNullable(source.retention, (v) => v.code),
retention_code: maybeToNullableString(source.retentionCode),
retention_percentage_value: toNullable(source.retention, (v) => v.percentage.value),
retention_percentage_scale: toNullable(source.retention, (v) => v.percentage.scale) ?? 2,
retention_percentage_value: maybeToNullable(
source.retentionPercentage,
(v) => v.toPrimitive().value
),
retention_percentage_scale:
maybeToNullable(source.retentionPercentage, (v) => v.toPrimitive().scale) ?? 2,
retention_amount_value: retentionAmount.value,
retention_amount_scale: retentionAmount.scale,
retention_amount_value: maybeToNullable(
source.retentionAmount,
(v) => v.toPrimitive().value
),
retention_amount_scale:
maybeToNullable(source.retentionAmount, (v) => v.toPrimitive().scale) ?? 4,
// TOTAL
taxes_amount_value: totalTaxes.value,
taxes_amount_scale: totalTaxes.scale,
taxes_amount_value: source.taxesAmount.value,
taxes_amount_scale: source.taxesAmount.scale,
};
return Result.ok(dto);

View File

@ -6,8 +6,8 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
toEmptyString,
maybeFromNullableResult,
maybeToEmptyString,
} from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
@ -48,25 +48,25 @@ export class SequelizeIssuedInvoiceVerifactuDomainMapper extends SequelizeDomain
);
const qr = extractOrPushError(
maybeFromNullableVO(source.qr, (value) => Result.ok(String(value))),
maybeFromNullableResult(source.qr, (value) => Result.ok(String(value))),
"qr",
errors
);
const url = extractOrPushError(
maybeFromNullableVO(source.url, (value) => URLAddress.create(value)),
maybeFromNullableResult(source.url, (value) => URLAddress.create(value)),
"url",
errors
);
const uuid = extractOrPushError(
maybeFromNullableVO(source.uuid, (value) => Result.ok(String(value))),
maybeFromNullableResult(source.uuid, (value) => Result.ok(String(value))),
"uuid",
errors
);
const operacion = extractOrPushError(
maybeFromNullableVO(source.operacion, (value) => Result.ok(String(value))),
maybeFromNullableResult(source.operacion, (value) => Result.ok(String(value))),
"operacion",
errors
);
@ -126,10 +126,10 @@ export class SequelizeIssuedInvoiceVerifactuDomainMapper extends SequelizeDomain
id: verifactu.id.toPrimitive(),
invoice_id: parent.id.toPrimitive(),
estado: verifactu.estado.toPrimitive(),
qr: toEmptyString(verifactu.qrCode, (v) => v),
url: toEmptyString(verifactu.url, (v) => v.toPrimitive()),
uuid: toEmptyString(verifactu.uuid, (v) => v),
operacion: toEmptyString(verifactu.operacion, (v) => v),
qr: maybeToEmptyString(verifactu.qrCode, (v) => v),
url: maybeToEmptyString(verifactu.url, (v) => v.toPrimitive()),
uuid: maybeToEmptyString(verifactu.uuid, (v) => v),
operacion: maybeToEmptyString(verifactu.operacion, (v) => v),
});
}
}

View File

@ -10,7 +10,7 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
@ -59,37 +59,37 @@ export class SequelizeIssuedInvoiceRecipientListMapper extends SequelizeQueryMap
const customerTin = extractOrPushError(TINNumber.create(_tin!), "customer_tin", errors);
const customerStreet = extractOrPushError(
maybeFromNullableVO(_street, (value) => Street.create(value)),
maybeFromNullableResult(_street, (value) => Street.create(value)),
"customer_street",
errors
);
const customerStreet2 = extractOrPushError(
maybeFromNullableVO(_street2, (value) => Street.create(value)),
maybeFromNullableResult(_street2, (value) => Street.create(value)),
"customer_street2",
errors
);
const customerCity = extractOrPushError(
maybeFromNullableVO(_city, (value) => City.create(value)),
maybeFromNullableResult(_city, (value) => City.create(value)),
"customer_city",
errors
);
const customerProvince = extractOrPushError(
maybeFromNullableVO(_province, (value) => Province.create(value)),
maybeFromNullableResult(_province, (value) => Province.create(value)),
"customer_province",
errors
);
const customerPostalCode = extractOrPushError(
maybeFromNullableVO(_postal_code, (value) => PostalCode.create(value)),
maybeFromNullableResult(_postal_code, (value) => PostalCode.create(value)),
"customer_postal_code",
errors
);
const customerCountry = extractOrPushError(
maybeFromNullableVO(_country, (value) => Country.create(value)),
maybeFromNullableResult(_country, (value) => Country.create(value)),
"customer_country",
errors
);

View File

@ -7,7 +7,7 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
@ -126,7 +126,7 @@ export class SequelizeIssuedInvoiceListMapper
const status = extractOrPushError(InvoiceStatus.create(raw.status), "status", errors);
const series = extractOrPushError(
maybeFromNullableVO(raw.series, (value) => InvoiceSerie.create(value)),
maybeFromNullableResult(raw.series, (value) => InvoiceSerie.create(value)),
"serie",
errors
);
@ -144,19 +144,19 @@ export class SequelizeIssuedInvoiceListMapper
);
const operationDate = extractOrPushError(
maybeFromNullableVO(raw.operation_date, (value) => UtcDate.createFromISO(value)),
maybeFromNullableResult(raw.operation_date, (value) => UtcDate.createFromISO(value)),
"operation_date",
errors
);
const reference = extractOrPushError(
maybeFromNullableVO(raw.reference, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.reference, (value) => Result.ok(String(value))),
"description",
errors
);
const description = extractOrPushError(
maybeFromNullableVO(raw.description, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.description, (value) => Result.ok(String(value))),
"description",
errors
);

View File

@ -5,7 +5,7 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
@ -26,25 +26,25 @@ export class SequelizeVerifactuRecordListMapper extends SequelizeQueryMapper<
const estado = extractOrPushError(VerifactuRecordEstado.create(raw.estado), "estado", errors);
const qr = extractOrPushError(
maybeFromNullableVO(raw.qr, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.qr, (value) => Result.ok(String(value))),
"qr",
errors
);
const url = extractOrPushError(
maybeFromNullableVO(raw.url, (value) => URLAddress.create(value)),
maybeFromNullableResult(raw.url, (value) => URLAddress.create(value)),
"url",
errors
);
const uuid = extractOrPushError(
maybeFromNullableVO(raw.uuid, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.uuid, (value) => Result.ok(String(value))),
"uuid",
errors
);
const operacion = extractOrPushError(
maybeFromNullableVO(raw.operacion, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.operacion, (value) => Result.ok(String(value))),
"operacion",
errors
);

View File

@ -9,8 +9,8 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
toNullable,
maybeFromNullableResult,
maybeToNullable,
} from "@repo/rdx-ddd";
import { Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils";
@ -66,7 +66,7 @@ export class SequelizeProformaDomainMapper
const isProforma = Boolean(source.is_proforma);
const proformaId = extractOrPushError(
maybeFromNullableVO(source.proforma_id, (v) => UniqueID.create(v)),
maybeFromNullableResult(source.proforma_id, (v) => UniqueID.create(v)),
"proforma_id",
errors
);
@ -74,7 +74,7 @@ export class SequelizeProformaDomainMapper
const status = extractOrPushError(InvoiceStatus.create(source.status), "status", errors);
const series = extractOrPushError(
maybeFromNullableVO(source.series, (v) => InvoiceSerie.create(v)),
maybeFromNullableResult(source.series, (v) => InvoiceSerie.create(v)),
"series",
errors
);
@ -93,7 +93,7 @@ export class SequelizeProformaDomainMapper
);
const operationDate = extractOrPushError(
maybeFromNullableVO(source.operation_date, (v) => UtcDate.createFromISO(v)),
maybeFromNullableResult(source.operation_date, (v) => UtcDate.createFromISO(v)),
"operation_date",
errors
);
@ -113,19 +113,19 @@ export class SequelizeProformaDomainMapper
// Textos opcionales
const reference = extractOrPushError(
maybeFromNullableVO(source.reference, (value) => Result.ok(String(value))),
maybeFromNullableResult(source.reference, (value) => Result.ok(String(value))),
"reference",
errors
);
const description = extractOrPushError(
maybeFromNullableVO(source.description, (value) => Result.ok(String(value))),
maybeFromNullableResult(source.description, (value) => Result.ok(String(value))),
"description",
errors
);
const notes = extractOrPushError(
maybeFromNullableVO(source.notes, (value) => TextValue.create(value)),
maybeFromNullableResult(source.notes, (value) => TextValue.create(value)),
"notes",
errors
);
@ -333,17 +333,17 @@ export class SequelizeProformaDomainMapper
// Flags / estado / serie / número
is_proforma: source.isProforma,
status: source.status.toPrimitive(),
series: toNullable(source.series, (v) => v.toPrimitive()),
series: maybeToNullable(source.series, (v) => v.toPrimitive()),
invoice_number: source.invoiceNumber.toPrimitive(),
invoice_date: source.invoiceDate.toPrimitive(),
operation_date: toNullable(source.operationDate, (v) => v.toPrimitive()),
operation_date: maybeToNullable(source.operationDate, (v) => v.toPrimitive()),
language_code: source.languageCode.toPrimitive(),
currency_code: source.currencyCode.toPrimitive(),
reference: toNullable(source.reference, (reference) => reference),
description: toNullable(source.description, (description) => description),
notes: toNullable(source.notes, (v) => v.toPrimitive()),
reference: maybeToNullable(source.reference, (reference) => reference),
description: maybeToNullable(source.description, (description) => description),
notes: maybeToNullable(source.notes, (v) => v.toPrimitive()),
subtotal_amount_value: allAmounts.subtotalAmount.value,
subtotal_amount_scale: allAmounts.subtotalAmount.scale,
@ -369,8 +369,11 @@ export class SequelizeProformaDomainMapper
total_amount_value: allAmounts.totalAmount.value,
total_amount_scale: allAmounts.totalAmount.scale,
payment_method_id: toNullable(source.paymentMethod, (payment) => payment.toObjectString().id),
payment_method_description: toNullable(
payment_method_id: maybeToNullable(
source.paymentMethod,
(payment) => payment.toObjectString().id
),
payment_method_description: maybeToNullable(
source.paymentMethod,
(payment) => payment.toObjectString().payment_description
),

View File

@ -5,15 +5,15 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
toNullable,
maybeFromNullableResult,
maybeToNullable,
} from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import {
ItemAmount,
ItemDescription,
ItemDiscount,
ItemDiscountPercentage,
ItemQuantity,
ItemTaxGroup,
type Proforma,
@ -63,19 +63,19 @@ export class SequelizeProformaItemDomainMapper extends SequelizeDomainMapper<
);
const description = extractOrPushError(
maybeFromNullableVO(source.description, (v) => ItemDescription.create(v)),
maybeFromNullableResult(source.description, (v) => ItemDescription.create(v)),
`items[${index}].description`,
errors
);
const quantity = extractOrPushError(
maybeFromNullableVO(source.quantity_value, (v) => ItemQuantity.create({ value: v })),
maybeFromNullableResult(source.quantity_value, (v) => ItemQuantity.create({ value: v })),
`items[${index}].quantity`,
errors
);
const unitAmount = extractOrPushError(
maybeFromNullableVO(source.unit_amount_value, (value) =>
maybeFromNullableResult(source.unit_amount_value, (value) =>
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code })
),
`items[${index}].unit_amount`,
@ -83,35 +83,39 @@ export class SequelizeProformaItemDomainMapper extends SequelizeDomainMapper<
);
const discountPercentage = extractOrPushError(
maybeFromNullableVO(source.discount_percentage_value, (v) =>
ItemDiscount.create({ value: v })
maybeFromNullableResult(source.discount_percentage_value, (v) =>
ItemDiscountPercentage.create({ value: v })
),
`items[${index}].discount_percentage`,
errors
);
const globalDiscountPercentage = extractOrPushError(
maybeFromNullableVO(source.global_discount_percentage_value, (v) =>
ItemDiscount.create({ value: v })
maybeFromNullableResult(source.global_discount_percentage_value, (v) =>
ItemDiscountPercentage.create({ value: v })
),
`items[${index}].discount_percentage`,
errors
);
const iva = extractOrPushError(
maybeFromNullableVO(source.iva_code, (code) => Tax.createFromCode(code, this._taxCatalog)),
maybeFromNullableResult(source.iva_code, (code) =>
Tax.createFromCode(code, this._taxCatalog)
),
`items[${index}].iva_code`,
errors
);
const rec = extractOrPushError(
maybeFromNullableVO(source.rec_code, (code) => Tax.createFromCode(code, this._taxCatalog)),
maybeFromNullableResult(source.rec_code, (code) =>
Tax.createFromCode(code, this._taxCatalog)
),
`items[${index}].rec_code`,
errors
);
const retention = extractOrPushError(
maybeFromNullableVO(source.retention_code, (code) =>
maybeFromNullableResult(source.retention_code, (code) =>
Tax.createFromCode(code, this._taxCatalog)
),
`items[${index}].retention_code`,
@ -201,40 +205,42 @@ export class SequelizeProformaItemDomainMapper extends SequelizeDomainMapper<
invoice_id: parent.id.toPrimitive(),
position: index,
description: toNullable(source.description, (v) => v.toPrimitive()),
description: maybeToNullable(source.description, (v) => v.toPrimitive()),
quantity_value: toNullable(source.quantity, (v) => v.toPrimitive().value),
quantity_value: maybeToNullable(source.quantity, (v) => v.toPrimitive().value),
quantity_scale:
toNullable(source.quantity, (v) => v.toPrimitive().scale) ?? ItemQuantity.DEFAULT_SCALE,
maybeToNullable(source.quantity, (v) => v.toPrimitive().scale) ??
ItemQuantity.DEFAULT_SCALE,
unit_amount_value: toNullable(source.unitAmount, (v) => v.toPrimitive().value),
unit_amount_value: maybeToNullable(source.unitAmount, (v) => v.toPrimitive().value),
unit_amount_scale:
toNullable(source.unitAmount, (v) => v.toPrimitive().scale) ?? ItemAmount.DEFAULT_SCALE,
maybeToNullable(source.unitAmount, (v) => v.toPrimitive().scale) ??
ItemAmount.DEFAULT_SCALE,
subtotal_amount_value: allAmounts.subtotalAmount.value,
subtotal_amount_scale: allAmounts.subtotalAmount.scale,
//
discount_percentage_value: toNullable(
discount_percentage_value: maybeToNullable(
source.itemDiscountPercentage,
(v) => v.toPrimitive().value
),
discount_percentage_scale:
toNullable(source.itemDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscount.DEFAULT_SCALE,
maybeToNullable(source.itemDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscountPercentage.DEFAULT_SCALE,
discount_amount_value: allAmounts.itemDiscountAmount.value,
discount_amount_scale: allAmounts.itemDiscountAmount.scale,
//
global_discount_percentage_value: toNullable(
global_discount_percentage_value: maybeToNullable(
source.globalDiscountPercentage,
(v) => v.toPrimitive().value
),
global_discount_percentage_scale:
toNullable(source.globalDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscount.DEFAULT_SCALE,
maybeToNullable(source.globalDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscountPercentage.DEFAULT_SCALE,
global_discount_amount_value: allAmounts.globalDiscountAmount.value,
global_discount_amount_scale: allAmounts.globalDiscountAmount.scale,
@ -248,29 +254,32 @@ export class SequelizeProformaItemDomainMapper extends SequelizeDomainMapper<
taxable_amount_scale: allAmounts.taxableAmount.scale,
// IVA
iva_code: toNullable(source.taxes.iva, (v) => v.code),
iva_code: maybeToNullable(source.taxes.iva, (v) => v.code),
iva_percentage_value: toNullable(source.taxes.iva, (v) => v.percentage.value),
iva_percentage_scale: toNullable(source.taxes.iva, (v) => v.percentage.scale) ?? 2,
iva_percentage_value: maybeToNullable(source.taxes.iva, (v) => v.percentage.value),
iva_percentage_scale: maybeToNullable(source.taxes.iva, (v) => v.percentage.scale) ?? 2,
iva_amount_value: taxesAmounts.ivaAmount.value,
iva_amount_scale: taxesAmounts.ivaAmount.scale,
// REC
rec_code: toNullable(source.taxes.rec, (v) => v.code),
rec_code: maybeToNullable(source.taxes.rec, (v) => v.code),
rec_percentage_value: toNullable(source.taxes.rec, (v) => v.percentage.value),
rec_percentage_scale: toNullable(source.taxes.rec, (v) => v.percentage.scale) ?? 2,
rec_percentage_value: maybeToNullable(source.taxes.rec, (v) => v.percentage.value),
rec_percentage_scale: maybeToNullable(source.taxes.rec, (v) => v.percentage.scale) ?? 2,
rec_amount_value: taxesAmounts.recAmount.value,
rec_amount_scale: taxesAmounts.recAmount.scale,
// RET
retention_code: toNullable(source.taxes.retention, (v) => v.code),
retention_code: maybeToNullable(source.taxes.retention, (v) => v.code),
retention_percentage_value: toNullable(source.taxes.retention, (v) => v.percentage.value),
retention_percentage_value: maybeToNullable(
source.taxes.retention,
(v) => v.percentage.value
),
retention_percentage_scale:
toNullable(source.taxes.retention, (v) => v.percentage.scale) ?? 2,
maybeToNullable(source.taxes.retention, (v) => v.percentage.scale) ?? 2,
retention_amount_value: taxesAmounts.retentionAmount.value,
retention_amount_scale: taxesAmounts.retentionAmount.scale,

View File

@ -10,7 +10,7 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
@ -55,37 +55,37 @@ export class SequelizeProformaRecipientDomainMapper {
const customerTin = extractOrPushError(TINNumber.create(_tin!), "customer_tin", errors);
const customerStreet = extractOrPushError(
maybeFromNullableVO(_street, (value) => Street.create(value)),
maybeFromNullableResult(_street, (value) => Street.create(value)),
"customer_street",
errors
);
const customerStreet2 = extractOrPushError(
maybeFromNullableVO(_street2, (value) => Street.create(value)),
maybeFromNullableResult(_street2, (value) => Street.create(value)),
"customer_street2",
errors
);
const customerCity = extractOrPushError(
maybeFromNullableVO(_city, (value) => City.create(value)),
maybeFromNullableResult(_city, (value) => City.create(value)),
"customer_city",
errors
);
const customerProvince = extractOrPushError(
maybeFromNullableVO(_province, (value) => Province.create(value)),
maybeFromNullableResult(_province, (value) => Province.create(value)),
"customer_province",
errors
);
const customerPostalCode = extractOrPushError(
maybeFromNullableVO(_postal_code, (value) => PostalCode.create(value)),
maybeFromNullableResult(_postal_code, (value) => PostalCode.create(value)),
"customer_postal_code",
errors
);
const customerCountry = extractOrPushError(
maybeFromNullableVO(_country, (value) => Country.create(value)),
maybeFromNullableResult(_country, (value) => Country.create(value)),
"customer_country",
errors
);

View File

@ -1,5 +1,5 @@
import { type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
import { UniqueID, type ValidationErrorDetail, toNullable } from "@repo/rdx-ddd";
import { UniqueID, type ValidationErrorDetail, maybeToNullable } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import type { InvoiceTaxGroup, Proforma } from "../../../../../../domain";
@ -64,19 +64,20 @@ export class SequelizeProformaTaxesDomainMapper extends SequelizeDomainMapper<
iva_amount_scale: ivaAmount.scale,
// REC
rec_code: toNullable(source.rec, (v) => v.code),
rec_code: maybeToNullable(source.rec, (v) => v.code),
rec_percentage_value: toNullable(source.rec, (v) => v.percentage.value),
rec_percentage_scale: toNullable(source.rec, (v) => v.percentage.scale) ?? 2,
rec_percentage_value: maybeToNullable(source.rec, (v) => v.percentage.value),
rec_percentage_scale: maybeToNullable(source.rec, (v) => v.percentage.scale) ?? 2,
rec_amount_value: recAmount.value,
rec_amount_scale: recAmount.scale,
// RET
retention_code: toNullable(source.retention, (v) => v.code),
retention_code: maybeToNullable(source.retention, (v) => v.code),
retention_percentage_value: toNullable(source.retention, (v) => v.percentage.value),
retention_percentage_scale: toNullable(source.retention, (v) => v.percentage.scale) ?? 2,
retention_percentage_value: maybeToNullable(source.retention, (v) => v.percentage.value),
retention_percentage_scale:
maybeToNullable(source.retention, (v) => v.percentage.scale) ?? 2,
retention_amount_value: retentionAmount.value,
retention_amount_scale: retentionAmount.scale,

View File

@ -14,7 +14,7 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
@ -65,37 +65,37 @@ export class SequelizeInvoiceRecipientListMapper
const customerTin = extractOrPushError(TINNumber.create(_tin!), "customer_tin", errors);
const customerStreet = extractOrPushError(
maybeFromNullableVO(_street, (value) => Street.create(value)),
maybeFromNullableResult(_street, (value) => Street.create(value)),
"customer_street",
errors
);
const customerStreet2 = extractOrPushError(
maybeFromNullableVO(_street2, (value) => Street.create(value)),
maybeFromNullableResult(_street2, (value) => Street.create(value)),
"customer_street2",
errors
);
const customerCity = extractOrPushError(
maybeFromNullableVO(_city, (value) => City.create(value)),
maybeFromNullableResult(_city, (value) => City.create(value)),
"customer_city",
errors
);
const customerProvince = extractOrPushError(
maybeFromNullableVO(_province, (value) => Province.create(value)),
maybeFromNullableResult(_province, (value) => Province.create(value)),
"customer_province",
errors
);
const customerPostalCode = extractOrPushError(
maybeFromNullableVO(_postal_code, (value) => PostalCode.create(value)),
maybeFromNullableResult(_postal_code, (value) => PostalCode.create(value)),
"customer_postal_code",
errors
);
const customerCountry = extractOrPushError(
maybeFromNullableVO(_country, (value) => Country.create(value)),
maybeFromNullableResult(_country, (value) => Country.create(value)),
"customer_country",
errors
);

View File

@ -7,7 +7,7 @@ import {
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
@ -105,7 +105,7 @@ export class SequelizeProformaListMapper
const status = extractOrPushError(InvoiceStatus.create(raw.status), "status", errors);
const series = extractOrPushError(
maybeFromNullableVO(raw.series, (value) => InvoiceSerie.create(value)),
maybeFromNullableResult(raw.series, (value) => InvoiceSerie.create(value)),
"serie",
errors
);
@ -123,19 +123,19 @@ export class SequelizeProformaListMapper
);
const operationDate = extractOrPushError(
maybeFromNullableVO(raw.operation_date, (value) => UtcDate.createFromISO(value)),
maybeFromNullableResult(raw.operation_date, (value) => UtcDate.createFromISO(value)),
"operation_date",
errors
);
const reference = extractOrPushError(
maybeFromNullableVO(raw.reference, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.reference, (value) => Result.ok(String(value))),
"description",
errors
);
const description = extractOrPushError(
maybeFromNullableVO(raw.description, (value) => Result.ok(String(value))),
maybeFromNullableResult(raw.description, (value) => Result.ok(String(value))),
"description",
errors
);

View File

@ -1,5 +1,5 @@
import { Presenter } from "@erp/core/api";
import { toEmptyString } from "@repo/rdx-ddd";
import { maybeToEmptyString } from "@repo/rdx-ddd";
import type { GetCustomerByIdResponseDTO } from "../../../../common/dto";
import type { Customer } from "../../../domain";
@ -12,33 +12,35 @@ export class CustomerFullPresenter extends Presenter<Customer, GetCustomerByIdRe
id: customer.id.toPrimitive(),
company_id: customer.companyId.toPrimitive(),
reference: toEmptyString(customer.reference, (value) => value.toPrimitive()),
reference: maybeToEmptyString(customer.reference, (value) => value.toPrimitive()),
is_company: String(customer.isCompany),
name: customer.name.toPrimitive(),
trade_name: toEmptyString(customer.tradeName, (value) => value.toPrimitive()),
trade_name: maybeToEmptyString(customer.tradeName, (value) => value.toPrimitive()),
tin: toEmptyString(customer.tin, (value) => value.toPrimitive()),
tin: maybeToEmptyString(customer.tin, (value) => value.toPrimitive()),
street: toEmptyString(address.street, (value) => value.toPrimitive()),
street2: toEmptyString(address.street2, (value) => value.toPrimitive()),
city: toEmptyString(address.city, (value) => value.toPrimitive()),
province: toEmptyString(address.province, (value) => value.toPrimitive()),
postal_code: toEmptyString(address.postalCode, (value) => value.toPrimitive()),
country: toEmptyString(address.country, (value) => value.toPrimitive()),
street: maybeToEmptyString(address.street, (value) => value.toPrimitive()),
street2: maybeToEmptyString(address.street2, (value) => value.toPrimitive()),
city: maybeToEmptyString(address.city, (value) => value.toPrimitive()),
province: maybeToEmptyString(address.province, (value) => value.toPrimitive()),
postal_code: maybeToEmptyString(address.postalCode, (value) => value.toPrimitive()),
country: maybeToEmptyString(address.country, (value) => value.toPrimitive()),
email_primary: toEmptyString(customer.emailPrimary, (value) => value.toPrimitive()),
email_secondary: toEmptyString(customer.emailSecondary, (value) => value.toPrimitive()),
phone_primary: toEmptyString(customer.phonePrimary, (value) => value.toPrimitive()),
phone_secondary: toEmptyString(customer.phoneSecondary, (value) => value.toPrimitive()),
mobile_primary: toEmptyString(customer.mobilePrimary, (value) => value.toPrimitive()),
mobile_secondary: toEmptyString(customer.mobileSecondary, (value) => value.toPrimitive()),
email_primary: maybeToEmptyString(customer.emailPrimary, (value) => value.toPrimitive()),
email_secondary: maybeToEmptyString(customer.emailSecondary, (value) => value.toPrimitive()),
phone_primary: maybeToEmptyString(customer.phonePrimary, (value) => value.toPrimitive()),
phone_secondary: maybeToEmptyString(customer.phoneSecondary, (value) => value.toPrimitive()),
mobile_primary: maybeToEmptyString(customer.mobilePrimary, (value) => value.toPrimitive()),
mobile_secondary: maybeToEmptyString(customer.mobileSecondary, (value) =>
value.toPrimitive()
),
fax: toEmptyString(customer.fax, (value) => value.toPrimitive()),
website: toEmptyString(customer.website, (value) => value.toPrimitive()),
fax: maybeToEmptyString(customer.fax, (value) => value.toPrimitive()),
website: maybeToEmptyString(customer.website, (value) => value.toPrimitive()),
legal_record: toEmptyString(customer.legalRecord, (value) => value.toPrimitive()),
legal_record: maybeToEmptyString(customer.legalRecord, (value) => value.toPrimitive()),
default_taxes: customer.defaultTaxes.getAll().map((tax) => tax.toString()),

View File

@ -1,7 +1,7 @@
import type { CriteriaDTO } from "@erp/core";
import { Presenter } from "@erp/core/api";
import type { Criteria } from "@repo/rdx-criteria/server";
import { toEmptyString } from "@repo/rdx-ddd";
import { maybeToEmptyString } from "@repo/rdx-ddd";
import type { Collection } from "@repo/rdx-utils";
import type { ListCustomersResponseDTO } from "../../../../common/dto";
@ -15,31 +15,33 @@ export class ListCustomersPresenter extends Presenter {
id: customer.id.toPrimitive(),
company_id: customer.companyId.toPrimitive(),
reference: toEmptyString(customer.reference, (value) => value.toPrimitive()),
reference: maybeToEmptyString(customer.reference, (value) => value.toPrimitive()),
is_company: String(customer.isCompany),
name: customer.name.toPrimitive(),
trade_name: toEmptyString(customer.tradeName, (value) => value.toPrimitive()),
trade_name: maybeToEmptyString(customer.tradeName, (value) => value.toPrimitive()),
tin: toEmptyString(customer.tin, (value) => value.toPrimitive()),
tin: maybeToEmptyString(customer.tin, (value) => value.toPrimitive()),
street: toEmptyString(address.street, (value) => value.toPrimitive()),
street2: toEmptyString(address.street2, (value) => value.toPrimitive()),
city: toEmptyString(address.city, (value) => value.toPrimitive()),
province: toEmptyString(address.province, (value) => value.toPrimitive()),
postal_code: toEmptyString(address.postalCode, (value) => value.toPrimitive()),
country: toEmptyString(address.country, (value) => value.toPrimitive()),
street: maybeToEmptyString(address.street, (value) => value.toPrimitive()),
street2: maybeToEmptyString(address.street2, (value) => value.toPrimitive()),
city: maybeToEmptyString(address.city, (value) => value.toPrimitive()),
province: maybeToEmptyString(address.province, (value) => value.toPrimitive()),
postal_code: maybeToEmptyString(address.postalCode, (value) => value.toPrimitive()),
country: maybeToEmptyString(address.country, (value) => value.toPrimitive()),
email_primary: toEmptyString(customer.emailPrimary, (value) => value.toPrimitive()),
email_secondary: toEmptyString(customer.emailSecondary, (value) => value.toPrimitive()),
phone_primary: toEmptyString(customer.phonePrimary, (value) => value.toPrimitive()),
phone_secondary: toEmptyString(customer.phoneSecondary, (value) => value.toPrimitive()),
mobile_primary: toEmptyString(customer.mobilePrimary, (value) => value.toPrimitive()),
mobile_secondary: toEmptyString(customer.mobileSecondary, (value) => value.toPrimitive()),
email_primary: maybeToEmptyString(customer.emailPrimary, (value) => value.toPrimitive()),
email_secondary: maybeToEmptyString(customer.emailSecondary, (value) => value.toPrimitive()),
phone_primary: maybeToEmptyString(customer.phonePrimary, (value) => value.toPrimitive()),
phone_secondary: maybeToEmptyString(customer.phoneSecondary, (value) => value.toPrimitive()),
mobile_primary: maybeToEmptyString(customer.mobilePrimary, (value) => value.toPrimitive()),
mobile_secondary: maybeToEmptyString(customer.mobileSecondary, (value) =>
value.toPrimitive()
),
fax: toEmptyString(customer.fax, (value) => value.toPrimitive()),
website: toEmptyString(customer.website, (value) => value.toPrimitive()),
fax: maybeToEmptyString(customer.fax, (value) => value.toPrimitive()),
website: maybeToEmptyString(customer.website, (value) => value.toPrimitive()),
status: customer.status ? "active" : "inactive",
language_code: customer.languageCode.toPrimitive(),

View File

@ -17,13 +17,14 @@ import {
URLAddress,
UniqueID,
ValidationErrorCollection,
ValidationErrorDetail,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Collection, Result, isNullishOrEmpty } from "@repo/rdx-utils";
import { CreateCustomerRequestDTO } from "../../../../common";
import { CustomerProps, CustomerStatus } from "../../../domain";
import type { CreateCustomerRequestDTO } from "../../../../common";
import { type CustomerProps, CustomerStatus } from "../../../domain";
/**
* Convierte el DTO a las props validadas (CustomerProps).
@ -45,7 +46,7 @@ export function mapDTOToCreateCustomerProps(dto: CreateCustomerRequestDTO) {
const isCompany = dto.is_company === "true";
const reference = extractOrPushError(
maybeFromNullableVO(dto.reference, (value) => Name.create(value)),
maybeFromNullableResult(dto.reference, (value) => Name.create(value)),
"reference",
errors
);
@ -53,103 +54,103 @@ export function mapDTOToCreateCustomerProps(dto: CreateCustomerRequestDTO) {
const name = extractOrPushError(Name.create(dto.name), "name", errors);
const tradeName = extractOrPushError(
maybeFromNullableVO(dto.trade_name, (value) => Name.create(value)),
maybeFromNullableResult(dto.trade_name, (value) => Name.create(value)),
"trade_name",
errors
);
const tinNumber = extractOrPushError(
maybeFromNullableVO(dto.tin, (value) => TINNumber.create(value)),
maybeFromNullableResult(dto.tin, (value) => TINNumber.create(value)),
"tin",
errors
);
const street = extractOrPushError(
maybeFromNullableVO(dto.street, (value) => Street.create(value)),
maybeFromNullableResult(dto.street, (value) => Street.create(value)),
"street",
errors
);
const street2 = extractOrPushError(
maybeFromNullableVO(dto.street2, (value) => Street.create(value)),
maybeFromNullableResult(dto.street2, (value) => Street.create(value)),
"street2",
errors
);
const city = extractOrPushError(
maybeFromNullableVO(dto.city, (value) => City.create(value)),
maybeFromNullableResult(dto.city, (value) => City.create(value)),
"city",
errors
);
const province = extractOrPushError(
maybeFromNullableVO(dto.province, (value) => Province.create(value)),
maybeFromNullableResult(dto.province, (value) => Province.create(value)),
"province",
errors
);
const postalCode = extractOrPushError(
maybeFromNullableVO(dto.postal_code, (value) => PostalCode.create(value)),
maybeFromNullableResult(dto.postal_code, (value) => PostalCode.create(value)),
"postal_code",
errors
);
const country = extractOrPushError(
maybeFromNullableVO(dto.country, (value) => Country.create(value)),
maybeFromNullableResult(dto.country, (value) => Country.create(value)),
"country",
errors
);
const primaryEmailAddress = extractOrPushError(
maybeFromNullableVO(dto.email_primary, (value) => EmailAddress.create(value)),
maybeFromNullableResult(dto.email_primary, (value) => EmailAddress.create(value)),
"email_primary",
errors
);
const secondaryEmailAddress = extractOrPushError(
maybeFromNullableVO(dto.email_secondary, (value) => EmailAddress.create(value)),
maybeFromNullableResult(dto.email_secondary, (value) => EmailAddress.create(value)),
"email_secondary",
errors
);
const primaryPhoneNumber = extractOrPushError(
maybeFromNullableVO(dto.phone_primary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(dto.phone_primary, (value) => PhoneNumber.create(value)),
"phone_primary",
errors
);
const secondaryPhoneNumber = extractOrPushError(
maybeFromNullableVO(dto.phone_secondary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(dto.phone_secondary, (value) => PhoneNumber.create(value)),
"phone_secondary",
errors
);
const primaryMobileNumber = extractOrPushError(
maybeFromNullableVO(dto.mobile_primary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(dto.mobile_primary, (value) => PhoneNumber.create(value)),
"mobile_primary",
errors
);
const secondaryMobileNumber = extractOrPushError(
maybeFromNullableVO(dto.mobile_secondary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(dto.mobile_secondary, (value) => PhoneNumber.create(value)),
"mobile_secondary",
errors
);
const faxNumber = extractOrPushError(
maybeFromNullableVO(dto.fax, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(dto.fax, (value) => PhoneNumber.create(value)),
"fax",
errors
);
const website = extractOrPushError(
maybeFromNullableVO(dto.website, (value) => URLAddress.create(value)),
maybeFromNullableResult(dto.website, (value) => URLAddress.create(value)),
"website",
errors
);
const legalRecord = extractOrPushError(
maybeFromNullableVO(dto.legal_record, (value) => TextValue.create(value)),
maybeFromNullableResult(dto.legal_record, (value) => TextValue.create(value)),
"legal_record",
errors
);

View File

@ -1,21 +1,13 @@
import {
DomainError,
ValidationErrorCollection,
ValidationErrorDetail,
extractOrPushError,
} from "@repo/rdx-ddd";
import { UpdateCustomerByIdRequestDTO } from "../../../../common";
import { CustomerPatchProps } from "../../../domain";
import {
City,
Country,
CurrencyCode,
DomainError,
EmailAddress,
LanguageCode,
Name,
PhoneNumber,
PostalAddressPatchProps,
type PostalAddressPatchProps,
PostalCode,
Province,
Street,
@ -23,10 +15,16 @@ import {
TaxCode,
TextValue,
URLAddress,
maybeFromNullableVO,
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { Collection, Result, isNullishOrEmpty, toPatchField } from "@repo/rdx-utils";
import type { UpdateCustomerByIdRequestDTO } from "../../../../common";
import type { CustomerPatchProps } from "../../../domain";
/**
* mapDTOToUpdateCustomerPatchProps
* Convierte el DTO a las props validadas (CustomerProps).
@ -48,7 +46,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerByIdRequestD
toPatchField(dto.reference).ifSet((reference) => {
customerPatchProps.reference = extractOrPushError(
maybeFromNullableVO(reference, (value) => Name.create(value)),
maybeFromNullableResult(reference, (value) => Name.create(value)),
"reference",
errors
);
@ -76,7 +74,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerByIdRequestD
toPatchField(dto.trade_name).ifSet((trade_name) => {
customerPatchProps.tradeName = extractOrPushError(
maybeFromNullableVO(trade_name, (value) => Name.create(value)),
maybeFromNullableResult(trade_name, (value) => Name.create(value)),
"trade_name",
errors
);
@ -84,7 +82,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerByIdRequestD
toPatchField(dto.tin).ifSet((tin) => {
customerPatchProps.tin = extractOrPushError(
maybeFromNullableVO(tin, (value) => TINNumber.create(value)),
maybeFromNullableResult(tin, (value) => TINNumber.create(value)),
"tin",
errors
);
@ -92,7 +90,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerByIdRequestD
toPatchField(dto.email_primary).ifSet((email_primary) => {
customerPatchProps.emailPrimary = extractOrPushError(
maybeFromNullableVO(email_primary, (value) => EmailAddress.create(value)),
maybeFromNullableResult(email_primary, (value) => EmailAddress.create(value)),
"email_primary",
errors
);
@ -100,7 +98,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerByIdRequestD
toPatchField(dto.email_secondary).ifSet((email_secondary) => {
customerPatchProps.emailSecondary = extractOrPushError(
maybeFromNullableVO(email_secondary, (value) => EmailAddress.create(value)),
maybeFromNullableResult(email_secondary, (value) => EmailAddress.create(value)),
"email_secondary",
errors
);
@ -108,7 +106,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerByIdRequestD
toPatchField(dto.mobile_primary).ifSet((mobile_primary) => {
customerPatchProps.mobilePrimary = extractOrPushError(
maybeFromNullableVO(mobile_primary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(mobile_primary, (value) => PhoneNumber.create(value)),
"mobile_primary",
errors
);
@ -116,7 +114,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerByIdRequestD
toPatchField(dto.mobile_secondary).ifSet((mobile_secondary) => {
customerPatchProps.mobilePrimary = extractOrPushError(
maybeFromNullableVO(mobile_secondary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(mobile_secondary, (value) => PhoneNumber.create(value)),
"mobile_secondary",
errors
);
@ -124,7 +122,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerByIdRequestD
toPatchField(dto.phone_primary).ifSet((phone_primary) => {
customerPatchProps.phonePrimary = extractOrPushError(
maybeFromNullableVO(phone_primary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(phone_primary, (value) => PhoneNumber.create(value)),
"phone_primary",
errors
);
@ -132,7 +130,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerByIdRequestD
toPatchField(dto.phone_secondary).ifSet((phone_secondary) => {
customerPatchProps.phoneSecondary = extractOrPushError(
maybeFromNullableVO(phone_secondary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(phone_secondary, (value) => PhoneNumber.create(value)),
"phone_secondary",
errors
);
@ -140,7 +138,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerByIdRequestD
toPatchField(dto.fax).ifSet((fax) => {
customerPatchProps.fax = extractOrPushError(
maybeFromNullableVO(fax, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(fax, (value) => PhoneNumber.create(value)),
"fax",
errors
);
@ -148,7 +146,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerByIdRequestD
toPatchField(dto.website).ifSet((website) => {
customerPatchProps.website = extractOrPushError(
maybeFromNullableVO(website, (value) => URLAddress.create(value)),
maybeFromNullableResult(website, (value) => URLAddress.create(value)),
"website",
errors
);
@ -156,7 +154,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerByIdRequestD
toPatchField(dto.legal_record).ifSet((legalRecord) => {
customerPatchProps.legalRecord = extractOrPushError(
maybeFromNullableVO(legalRecord, (value) => TextValue.create(value)),
maybeFromNullableResult(legalRecord, (value) => TextValue.create(value)),
"legal_record",
errors
);
@ -231,7 +229,7 @@ function mapDTOToUpdatePostalAddressPatchProps(
toPatchField(dto.street).ifSet((street) => {
postalAddressPatchProps.street = extractOrPushError(
maybeFromNullableVO(street, (value) => Street.create(value)),
maybeFromNullableResult(street, (value) => Street.create(value)),
"street",
errors
);
@ -239,7 +237,7 @@ function mapDTOToUpdatePostalAddressPatchProps(
toPatchField(dto.street2).ifSet((street2) => {
postalAddressPatchProps.street2 = extractOrPushError(
maybeFromNullableVO(street2, (value) => Street.create(value)),
maybeFromNullableResult(street2, (value) => Street.create(value)),
"street2",
errors
);
@ -247,7 +245,7 @@ function mapDTOToUpdatePostalAddressPatchProps(
toPatchField(dto.city).ifSet((city) => {
postalAddressPatchProps.city = extractOrPushError(
maybeFromNullableVO(city, (value) => City.create(value)),
maybeFromNullableResult(city, (value) => City.create(value)),
"city",
errors
);
@ -255,7 +253,7 @@ function mapDTOToUpdatePostalAddressPatchProps(
toPatchField(dto.province).ifSet((province) => {
postalAddressPatchProps.province = extractOrPushError(
maybeFromNullableVO(province, (value) => Province.create(value)),
maybeFromNullableResult(province, (value) => Province.create(value)),
"province",
errors
);
@ -263,7 +261,7 @@ function mapDTOToUpdatePostalAddressPatchProps(
toPatchField(dto.postal_code).ifSet((postalCode) => {
postalAddressPatchProps.postalCode = extractOrPushError(
maybeFromNullableVO(postalCode, (value) => PostalCode.create(value)),
maybeFromNullableResult(postalCode, (value) => PostalCode.create(value)),
"postal_code",
errors
);
@ -271,7 +269,7 @@ function mapDTOToUpdatePostalAddressPatchProps(
toPatchField(dto.country).ifSet((country) => {
postalAddressPatchProps.country = extractOrPushError(
maybeFromNullableVO(country, (value) => Country.create(value)),
maybeFromNullableResult(country, (value) => Country.create(value)),
"country",
errors
);

View File

@ -1,30 +1,35 @@
import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
import {
type ISequelizeDomainMapper,
type MapperParamsType,
SequelizeDomainMapper,
} from "@erp/core/api";
import {
City,
Country,
CurrencyCode,
EmailAddress,
extractOrPushError,
LanguageCode,
maybeFromNullableVO,
Name,
PhoneNumber,
PostalAddress,
PostalCode,
Province,
Street,
TINNumber,
TaxCode,
TextValue,
TINNumber,
toNullable,
UniqueID,
URLAddress,
UniqueID,
ValidationErrorCollection,
ValidationErrorDetail,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableResult,
maybeToNullable,
} from "@repo/rdx-ddd";
import { Collection, isNullishOrEmpty, Result } from "@repo/rdx-utils";
import { Customer, CustomerProps, CustomerStatus } from "../../../domain";
import { CustomerCreationAttributes, CustomerModel } from "../../sequelize";
import { Collection, Result, isNullishOrEmpty } from "@repo/rdx-utils";
import { Customer, type CustomerProps, CustomerStatus } from "../../../domain";
import type { CustomerCreationAttributes, CustomerModel } from "../../sequelize";
export interface ICustomerDomainMapper
extends ISequelizeDomainMapper<CustomerModel, CustomerCreationAttributes, Customer> {}
@ -47,7 +52,7 @@ export class CustomerDomainMapper
const isCompany = source.is_company;
const status = extractOrPushError(CustomerStatus.create(source.status), "status", errors);
const reference = extractOrPushError(
maybeFromNullableVO(source.reference, (value) => Name.create(value)),
maybeFromNullableResult(source.reference, (value) => Name.create(value)),
"reference",
errors
);
@ -55,103 +60,103 @@ export class CustomerDomainMapper
const name = extractOrPushError(Name.create(source.name), "name", errors);
const tradeName = extractOrPushError(
maybeFromNullableVO(source.trade_name, (value) => Name.create(value)),
maybeFromNullableResult(source.trade_name, (value) => Name.create(value)),
"trade_name",
errors
);
const tinNumber = extractOrPushError(
maybeFromNullableVO(source.tin, (value) => TINNumber.create(value)),
maybeFromNullableResult(source.tin, (value) => TINNumber.create(value)),
"tin",
errors
);
const street = extractOrPushError(
maybeFromNullableVO(source.street, (value) => Street.create(value)),
maybeFromNullableResult(source.street, (value) => Street.create(value)),
"street",
errors
);
const street2 = extractOrPushError(
maybeFromNullableVO(source.street2, (value) => Street.create(value)),
maybeFromNullableResult(source.street2, (value) => Street.create(value)),
"street2",
errors
);
const city = extractOrPushError(
maybeFromNullableVO(source.city, (value) => City.create(value)),
maybeFromNullableResult(source.city, (value) => City.create(value)),
"city",
errors
);
const province = extractOrPushError(
maybeFromNullableVO(source.province, (value) => Province.create(value)),
maybeFromNullableResult(source.province, (value) => Province.create(value)),
"province",
errors
);
const postalCode = extractOrPushError(
maybeFromNullableVO(source.postal_code, (value) => PostalCode.create(value)),
maybeFromNullableResult(source.postal_code, (value) => PostalCode.create(value)),
"postal_code",
errors
);
const country = extractOrPushError(
maybeFromNullableVO(source.country, (value) => Country.create(value)),
maybeFromNullableResult(source.country, (value) => Country.create(value)),
"country",
errors
);
const emailPrimaryAddress = extractOrPushError(
maybeFromNullableVO(source.email_primary, (value) => EmailAddress.create(value)),
maybeFromNullableResult(source.email_primary, (value) => EmailAddress.create(value)),
"email_primary",
errors
);
const emailSecondaryAddress = extractOrPushError(
maybeFromNullableVO(source.email_secondary, (value) => EmailAddress.create(value)),
maybeFromNullableResult(source.email_secondary, (value) => EmailAddress.create(value)),
"email_secondary",
errors
);
const phonePrimaryNumber = extractOrPushError(
maybeFromNullableVO(source.phone_primary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(source.phone_primary, (value) => PhoneNumber.create(value)),
"phone_primary",
errors
);
const phoneSecondaryNumber = extractOrPushError(
maybeFromNullableVO(source.phone_secondary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(source.phone_secondary, (value) => PhoneNumber.create(value)),
"phone_secondary",
errors
);
const mobilePrimaryNumber = extractOrPushError(
maybeFromNullableVO(source.mobile_primary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(source.mobile_primary, (value) => PhoneNumber.create(value)),
"mobile_primary",
errors
);
const mobileSecondaryNumber = extractOrPushError(
maybeFromNullableVO(source.mobile_secondary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(source.mobile_secondary, (value) => PhoneNumber.create(value)),
"mobile_secondary",
errors
);
const faxNumber = extractOrPushError(
maybeFromNullableVO(source.fax, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(source.fax, (value) => PhoneNumber.create(value)),
"fax",
errors
);
const website = extractOrPushError(
maybeFromNullableVO(source.website, (value) => URLAddress.create(value)),
maybeFromNullableResult(source.website, (value) => URLAddress.create(value)),
"website",
errors
);
const legalRecord = extractOrPushError(
maybeFromNullableVO(source.legal_record, (value) => TextValue.create(value)),
maybeFromNullableResult(source.legal_record, (value) => TextValue.create(value)),
"legal_record",
errors
);
@ -241,22 +246,22 @@ export class CustomerDomainMapper
id: source.id.toPrimitive(),
company_id: source.companyId.toPrimitive(),
reference: toNullable(source.reference, (reference) => reference.toPrimitive()),
reference: maybeToNullable(source.reference, (reference) => reference.toPrimitive()),
is_company: source.isCompany,
name: source.name.toPrimitive(),
trade_name: toNullable(source.tradeName, (tradeName) => tradeName.toPrimitive()),
tin: toNullable(source.tin, (tin) => tin.toPrimitive()),
trade_name: maybeToNullable(source.tradeName, (tradeName) => tradeName.toPrimitive()),
tin: maybeToNullable(source.tin, (tin) => tin.toPrimitive()),
email_primary: toNullable(source.emailPrimary, (email) => email.toPrimitive()),
email_secondary: toNullable(source.emailSecondary, (email) => email.toPrimitive()),
phone_primary: toNullable(source.phonePrimary, (phone) => phone.toPrimitive()),
phone_secondary: toNullable(source.phoneSecondary, (phone) => phone.toPrimitive()),
mobile_primary: toNullable(source.mobilePrimary, (mobile) => mobile.toPrimitive()),
mobile_secondary: toNullable(source.mobileSecondary, (mobile) => mobile.toPrimitive()),
fax: toNullable(source.fax, (fax) => fax.toPrimitive()),
website: toNullable(source.website, (website) => website.toPrimitive()),
email_primary: maybeToNullable(source.emailPrimary, (email) => email.toPrimitive()),
email_secondary: maybeToNullable(source.emailSecondary, (email) => email.toPrimitive()),
phone_primary: maybeToNullable(source.phonePrimary, (phone) => phone.toPrimitive()),
phone_secondary: maybeToNullable(source.phoneSecondary, (phone) => phone.toPrimitive()),
mobile_primary: maybeToNullable(source.mobilePrimary, (mobile) => mobile.toPrimitive()),
mobile_secondary: maybeToNullable(source.mobileSecondary, (mobile) => mobile.toPrimitive()),
fax: maybeToNullable(source.fax, (fax) => fax.toPrimitive()),
website: maybeToNullable(source.website, (website) => website.toPrimitive()),
legal_record: toNullable(source.legalRecord, (legalRecord) => legalRecord.toPrimitive()),
legal_record: maybeToNullable(source.legalRecord, (legalRecord) => legalRecord.toPrimitive()),
default_taxes: source.defaultTaxes.map((taxItem) => taxItem.toPrimitive()).join(", "),
status: source.isActive ? "active" : "inactive",
@ -266,14 +271,14 @@ export class CustomerDomainMapper
if (source.address) {
Object.assign(customerValues, {
street: toNullable(source.address.street, (street) => street.toPrimitive()),
street2: toNullable(source.address.street2, (street2) => street2.toPrimitive()),
city: toNullable(source.address.city, (city) => city.toPrimitive()),
province: toNullable(source.address.province, (province) => province.toPrimitive()),
postal_code: toNullable(source.address.postalCode, (postalCode) =>
street: maybeToNullable(source.address.street, (street) => street.toPrimitive()),
street2: maybeToNullable(source.address.street2, (street2) => street2.toPrimitive()),
city: maybeToNullable(source.address.city, (city) => city.toPrimitive()),
province: maybeToNullable(source.address.province, (province) => province.toPrimitive()),
postal_code: maybeToNullable(source.address.postalCode, (postalCode) =>
postalCode.toPrimitive()
),
country: toNullable(source.address.country, (country) => country.toPrimitive()),
country: maybeToNullable(source.address.country, (country) => country.toPrimitive()),
});
}

View File

@ -1,5 +1,8 @@
import { ValidationErrorCollection, ValidationErrorDetail } from "@repo/rdx-ddd";
import {
type ISequelizeQueryMapper,
type MapperParamsType,
SequelizeQueryMapper,
} from "@erp/core/api";
import {
City,
Country,
@ -16,15 +19,15 @@ import {
TextValue,
URLAddress,
UniqueID,
ValidationErrorCollection,
type ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
maybeFromNullableResult,
} from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils";
import { ISequelizeQueryMapper, MapperParamsType, SequelizeQueryMapper } from "@erp/core/api";
import { Maybe, Result } from "@repo/rdx-utils";
import { CustomerStatus } from "../../../domain";
import { CustomerModel } from "../../sequelize";
import type { CustomerModel } from "../../sequelize";
export type CustomerListDTO = {
id: UniqueID;
@ -70,7 +73,7 @@ export class CustomerListMapper
const isCompany = raw.is_company;
const status = extractOrPushError(CustomerStatus.create(raw.status), "status", errors);
const reference = extractOrPushError(
maybeFromNullableVO(raw.reference, (value) => Name.create(value)),
maybeFromNullableResult(raw.reference, (value) => Name.create(value)),
"reference",
errors
);
@ -78,103 +81,103 @@ export class CustomerListMapper
const name = extractOrPushError(Name.create(raw.name), "name", errors);
const tradeName = extractOrPushError(
maybeFromNullableVO(raw.trade_name, (value) => Name.create(value)),
maybeFromNullableResult(raw.trade_name, (value) => Name.create(value)),
"trade_name",
errors
);
const tinNumber = extractOrPushError(
maybeFromNullableVO(raw.tin, (value) => TINNumber.create(value)),
maybeFromNullableResult(raw.tin, (value) => TINNumber.create(value)),
"tin",
errors
);
const street = extractOrPushError(
maybeFromNullableVO(raw.street, (value) => Street.create(value)),
maybeFromNullableResult(raw.street, (value) => Street.create(value)),
"street",
errors
);
const street2 = extractOrPushError(
maybeFromNullableVO(raw.street2, (value) => Street.create(value)),
maybeFromNullableResult(raw.street2, (value) => Street.create(value)),
"street2",
errors
);
const city = extractOrPushError(
maybeFromNullableVO(raw.city, (value) => City.create(value)),
maybeFromNullableResult(raw.city, (value) => City.create(value)),
"city",
errors
);
const province = extractOrPushError(
maybeFromNullableVO(raw.province, (value) => Province.create(value)),
maybeFromNullableResult(raw.province, (value) => Province.create(value)),
"province",
errors
);
const postalCode = extractOrPushError(
maybeFromNullableVO(raw.postal_code, (value) => PostalCode.create(value)),
maybeFromNullableResult(raw.postal_code, (value) => PostalCode.create(value)),
"postal_code",
errors
);
const country = extractOrPushError(
maybeFromNullableVO(raw.country, (value) => Country.create(value)),
maybeFromNullableResult(raw.country, (value) => Country.create(value)),
"country",
errors
);
const emailPrimaryAddress = extractOrPushError(
maybeFromNullableVO(raw.email_primary, (value) => EmailAddress.create(value)),
maybeFromNullableResult(raw.email_primary, (value) => EmailAddress.create(value)),
"email_primary",
errors
);
const emailSecondaryAddress = extractOrPushError(
maybeFromNullableVO(raw.email_secondary, (value) => EmailAddress.create(value)),
maybeFromNullableResult(raw.email_secondary, (value) => EmailAddress.create(value)),
"email_secondary",
errors
);
const phonePrimaryNumber = extractOrPushError(
maybeFromNullableVO(raw.phone_primary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(raw.phone_primary, (value) => PhoneNumber.create(value)),
"phone_primary",
errors
);
const phoneSecondaryNumber = extractOrPushError(
maybeFromNullableVO(raw.phone_secondary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(raw.phone_secondary, (value) => PhoneNumber.create(value)),
"phone_secondary",
errors
);
const mobilePrimaryNumber = extractOrPushError(
maybeFromNullableVO(raw.mobile_primary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(raw.mobile_primary, (value) => PhoneNumber.create(value)),
"mobile_primary",
errors
);
const mobileSecondaryNumber = extractOrPushError(
maybeFromNullableVO(raw.mobile_secondary, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(raw.mobile_secondary, (value) => PhoneNumber.create(value)),
"mobile_secondary",
errors
);
const faxNumber = extractOrPushError(
maybeFromNullableVO(raw.fax, (value) => PhoneNumber.create(value)),
maybeFromNullableResult(raw.fax, (value) => PhoneNumber.create(value)),
"fax",
errors
);
const website = extractOrPushError(
maybeFromNullableVO(raw.website, (value) => URLAddress.create(value)),
maybeFromNullableResult(raw.website, (value) => URLAddress.create(value)),
"website",
errors
);
const legalRecord = extractOrPushError(
maybeFromNullableVO(raw.legal_record, (value) => TextValue.create(value)),
maybeFromNullableResult(raw.legal_record, (value) => TextValue.create(value)),
"legal_record",
errors
);

View File

@ -3,37 +3,37 @@
import { Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils";
/** any | null | undefined -> Maybe<T> usando fábrica VO */
export function maybeFromNullableVO<T>(
/** any | null | undefined -> Maybe<T> usando validación */
export function maybeFromNullableResult<T>(
input: any,
voFactory: (raw: any) => Result<T>
validate: (raw: any) => Result<T>
): Result<Maybe<T>> {
if (isNullishOrEmpty(input)) return Result.ok(Maybe.none<T>());
const vo = voFactory(input);
return vo.isSuccess ? Result.ok(Maybe.some(vo.data)) : Result.fail(vo.error);
const value = validate(input);
return value.isSuccess ? Result.ok(Maybe.some(value.data)) : Result.fail(value.error);
}
/** string | null | undefined -> Maybe<string> (trim, vacío => None) */
export function maybeFromNullableString(input?: string | null): Maybe<string> {
export function maybeFromNullableOrEmptyString(input?: string | null): Maybe<string> {
if (isNullishOrEmpty(input)) return Maybe.none<string>();
const t = (input as string).trim();
const t = input as string;
return t ? Maybe.some(t) : Maybe.none<string>();
}
/** Maybe<T> -> null para transporte */
export function toNullable<T, R>(m: Maybe<T>, map: (t: T) => R): R | null {
export function maybeToNullable<T, R>(m: Maybe<T>, map: (t: T) => R): R | null {
if (!m || m.isNone()) return null;
return map(m.unwrap() as T);
}
export function toNullable2<T>(m: Maybe<T>, map?: (t: T) => unknown): unknown | null {
export function maybeToNullableString<T>(m: Maybe<T>, map?: (t: T) => string): string | null {
if (!m || m.isNone()) return null;
const v = m.unwrap() as T;
return map ? String(map(v)) : String(v);
}
/** Maybe<T> -> "" para transporte */
export function toEmptyString<T>(m: Maybe<T>, map?: (t: T) => string): string {
export function maybeToEmptyString<T>(m: Maybe<T>, map?: (t: T) => string): string {
if (!m || m.isNone()) return "";
const v = m.unwrap() as T;
return map ? map(v) : String(v);

View File

@ -1,6 +1,8 @@
import { Result } from "@repo/rdx-utils";
import { z } from "zod/v4";
import { translateZodValidationError } from "../helpers";
import { ValueObject } from "./value-object";
const DEFAULT_SCALE = 2;
@ -62,6 +64,10 @@ export class Percentage extends ValueObject<PercentageProps> {
return Result.ok(new Percentage({ value, scale }));
}
static zero() {
return Percentage.create({ value: 0, scale: Percentage.DEFAULT_SCALE }).data;
}
get value(): number {
return this.props.value;
}

View File

@ -1,10 +1,12 @@
import { Maybe, Result } from "@repo/rdx-utils";
import { toEmptyString } from "../helpers";
import { City } from "./city";
import { Country } from "./country";
import { PostalCode } from "./postal-code";
import { Province } from "./province";
import { Street } from "./street";
import { type Maybe, Result } from "@repo/rdx-utils";
import { maybeToEmptyString } from "../helpers";
import type { City } from "./city";
import type { Country } from "./country";
import type { PostalCode } from "./postal-code";
import type { Province } from "./province";
import type { Street } from "./street";
import { ValueObject } from "./value-object";
export interface PostalAddressProps {
@ -75,12 +77,12 @@ export class PostalAddress extends ValueObject<PostalAddressProps> {
toString() {
return {
street: toEmptyString(this.street, (value) => value.toString()),
street2: toEmptyString(this.street2, (value) => value.toString()),
city: toEmptyString(this.city, (value) => value.toString()),
postal_code: toEmptyString(this.postalCode, (value) => value.toString()),
province: toEmptyString(this.province, (value) => value.toString()),
country: toEmptyString(this.country, (value) => value.toString()),
street: maybeToEmptyString(this.street, (value) => value.toString()),
street2: maybeToEmptyString(this.street2, (value) => value.toString()),
city: maybeToEmptyString(this.city, (value) => value.toString()),
postal_code: maybeToEmptyString(this.postalCode, (value) => value.toString()),
province: maybeToEmptyString(this.province, (value) => value.toString()),
country: maybeToEmptyString(this.country, (value) => value.toString()),
};
}