Compare commits

..

No commits in common. "936c440cf34ee8690cd915b30bcb27178807cd5e" and "5d5959810618d17b7eb96607c623ad7bfdd744f8" have entirely different histories.

8 changed files with 45 additions and 122 deletions

View File

@ -96,10 +96,14 @@ export class IssuedInvoice
} }
static create(props: IIssuedInvoiceCreateProps, id?: UniqueID): Result<IssuedInvoice, Error> { static create(props: IIssuedInvoiceCreateProps, id?: UniqueID): Result<IssuedInvoice, Error> {
const validationResult = IssuedInvoice.validateCreateProps(props); if (!props.recipient) {
return Result.fail(
if (validationResult.isFailure) { new DomainValidationError(
return Result.fail(validationResult.error); "MISSING_RECIPIENT",
"recipient",
"Issued invoice requires recipient"
)
);
} }
const internalItems = IssuedInvoiceItems.create({ const internalItems = IssuedInvoiceItems.create({
@ -128,20 +132,6 @@ export class IssuedInvoice
return Result.ok(issuedInvoice); return Result.ok(issuedInvoice);
} }
private static validateCreateProps(props: IIssuedInvoiceCreateProps): Result<void, Error> {
if (!props.recipient) {
return Result.fail(
new DomainValidationError(
"MISSING_RECIPIENT",
"recipient",
"Issued invoice requires recipient"
)
);
}
return Result.ok();
}
// Rehidratación desde persistencia // Rehidratación desde persistencia
static rehydrate( static rehydrate(
props: InternalIssuedInvoiceProps, props: InternalIssuedInvoiceProps,

View File

@ -11,15 +11,15 @@ export type IssuedInvoiceItemsProps = {
}; };
export class IssuedInvoiceItems extends Collection<IssuedInvoiceItem> { export class IssuedInvoiceItems extends Collection<IssuedInvoiceItem> {
private languageCode!: LanguageCode; private _languageCode!: LanguageCode;
private currencyCode!: CurrencyCode; private _currencyCode!: CurrencyCode;
private globalDiscountPercentage!: Percentage; private _globalDiscountPercentage!: Percentage;
constructor(props: IssuedInvoiceItemsProps) { constructor(props: IssuedInvoiceItemsProps) {
super(props.items ?? []); super(props.items ?? []);
this.languageCode = props.languageCode; this._languageCode = props.languageCode;
this.currencyCode = props.currencyCode; this._currencyCode = props.currencyCode;
this.globalDiscountPercentage = props.globalDiscountPercentage; this._globalDiscountPercentage = props.globalDiscountPercentage;
} }
public static create(props: IssuedInvoiceItemsProps): IssuedInvoiceItems { public static create(props: IssuedInvoiceItemsProps): IssuedInvoiceItems {
@ -40,9 +40,9 @@ export class IssuedInvoiceItems extends Collection<IssuedInvoiceItem> {
// tiene el mismo "currencyCode" y "languageCode" que la colección de items. // tiene el mismo "currencyCode" y "languageCode" que la colección de items.
if ( if (
!( !(
this.languageCode.equals(item.languageCode) && this._languageCode.equals(item.languageCode) &&
this.currencyCode.equals(item.currencyCode) && this._currencyCode.equals(item.currencyCode) &&
this.globalDiscountPercentage.equals(item.globalDiscountPercentage) this._globalDiscountPercentage.equals(item.globalDiscountPercentage)
) )
) )
return false; return false;

View File

@ -1,8 +1,6 @@
import type { CurrencyCode, LanguageCode } from "@repo/rdx-ddd"; import type { CurrencyCode, LanguageCode } from "@repo/rdx-ddd";
import { Collection } from "@repo/rdx-utils"; import { Collection } from "@repo/rdx-utils";
import { InvoiceAmount } from "../../../common";
import type { IssuedInvoiceTax } from "./issued-invoice-tax.entity"; import type { IssuedInvoiceTax } from "./issued-invoice-tax.entity";
export type IssuedInvoiceTaxesProps = { export type IssuedInvoiceTaxesProps = {
@ -12,59 +10,16 @@ export type IssuedInvoiceTaxesProps = {
}; };
export class IssuedInvoiceTaxes extends Collection<IssuedInvoiceTax> { export class IssuedInvoiceTaxes extends Collection<IssuedInvoiceTax> {
private languageCode!: LanguageCode; private _languageCode!: LanguageCode;
private currencyCode!: CurrencyCode; private _currencyCode!: CurrencyCode;
constructor(props: IssuedInvoiceTaxesProps) { constructor(props: IssuedInvoiceTaxesProps) {
super(props.taxes ?? []); super(props.taxes ?? []);
this.languageCode = props.languageCode; this._languageCode = props.languageCode;
this.currencyCode = props.currencyCode; this._currencyCode = props.currencyCode;
} }
public static create(props: IssuedInvoiceTaxesProps): IssuedInvoiceTaxes { public static create(props: IssuedInvoiceTaxesProps): IssuedInvoiceTaxes {
return new IssuedInvoiceTaxes(props); return new IssuedInvoiceTaxes(props);
} }
public getTaxableAmount(): InvoiceAmount {
return this.items.reduce(
(acc, tax) => acc.add(tax.taxableAmount),
InvoiceAmount.zero(this.currencyCode.toString())
);
}
public getIvaAmount(): InvoiceAmount {
return this.items.reduce(
(acc, tax) => acc.add(tax.ivaAmount),
InvoiceAmount.zero(this.currencyCode.toString())
);
}
public getRecAmount(): InvoiceAmount {
return this.items.reduce(
(acc, tax) => acc.add(tax.recAmount),
InvoiceAmount.zero(this.currencyCode.toString())
);
}
public getRetentionAmount(): InvoiceAmount {
return this.items.reduce(
(acc, tax) => acc.add(tax.retentionAmount),
InvoiceAmount.zero(this.currencyCode.toString())
);
}
public getTaxesAmount(): InvoiceAmount {
return this.items.reduce(
(acc, tax) => acc.add(tax.taxesAmount),
InvoiceAmount.zero(this.currencyCode.toString())
);
}
public getTransferredTaxesAmount(): InvoiceAmount {
return this.getIvaAmount().add(this.getRecAmount());
}
public getNetTaxesAmount(): InvoiceAmount {
return this.getTransferredTaxesAmount().subtract(this.getRetentionAmount());
}
} }

View File

@ -116,12 +116,6 @@ export class Proforma extends AggregateRoot<InternalProformaProps> implements IP
// Creación funcional // Creación funcional
static create(props: IProformaCreateProps, id?: UniqueID): Result<Proforma, Error> { static create(props: IProformaCreateProps, id?: UniqueID): Result<Proforma, Error> {
const validationResult = Proforma.validateCreateProps(props);
if (validationResult.isFailure) {
return Result.fail(validationResult.error);
}
const internalItems = ProformaItems.create({ const internalItems = ProformaItems.create({
items: [], items: [],
languageCode: props.languageCode, languageCode: props.languageCode,
@ -147,10 +141,6 @@ export class Proforma extends AggregateRoot<InternalProformaProps> implements IP
return Result.ok(proforma); return Result.ok(proforma);
} }
private static validateCreateProps(props: IProformaCreateProps): Result<void, Error> {
return Result.ok();
}
// Rehidratación desde persistencia // Rehidratación desde persistencia
static rehydrate(props: InternalProformaProps, items: ProformaItems, id: UniqueID): Proforma { static rehydrate(props: InternalProformaProps, items: ProformaItems, id: UniqueID): Proforma {
return new Proforma(props, items, id); return new Proforma(props, items, id);
@ -247,17 +237,15 @@ export class Proforma extends AggregateRoot<InternalProformaProps> implements IP
} }
// Mutabilidad // Mutabilidad
public update(patch: ProformaPatchProps): Result<Proforma, Error> { public update(
const candidateProps: InternalProformaProps = { partialProforma: Partial<Omit<IProformaCreateProps, "companyId">>
): Result<Proforma, Error> {
const updatedProps = {
...this.props, ...this.props,
...patch, ...partialProforma,
}; } as IProformaCreateProps;
// Validacciones return Proforma.create(updatedProps, this.id);
Object.assign(this.props, candidateProps);
return Result.ok();
} }
public issue(): Result<void, Error> { public issue(): Result<void, Error> {

View File

@ -99,12 +99,6 @@ type CustomerInternalProps = Omit<ICustomerCreateProps, "address" | "defaultTaxe
export class Customer extends AggregateRoot<CustomerInternalProps> implements ICustomer { export class Customer extends AggregateRoot<CustomerInternalProps> implements ICustomer {
static create(props: ICustomerCreateProps, id?: UniqueID): Result<Customer, Error> { static create(props: ICustomerCreateProps, id?: UniqueID): Result<Customer, Error> {
const validationResult = Customer.validateCreateProps(props);
if (validationResult.isFailure) {
return Result.fail(validationResult.error);
}
const { address, defaultTaxes, ...internalProps } = props; const { address, defaultTaxes, ...internalProps } = props;
const postalAddressResult = PostalAddress.create(address); const postalAddressResult = PostalAddress.create(address);
@ -138,10 +132,6 @@ export class Customer extends AggregateRoot<CustomerInternalProps> implements IC
return Result.ok(contact); return Result.ok(contact);
} }
private static validateCreateProps(props: ICustomerCreateProps): Result<void, Error> {
return Result.ok();
}
// Rehidratación desde persistencia // Rehidratación desde persistencia
static rehydrate(props: CustomerInternalProps, id: UniqueID): Customer { static rehydrate(props: CustomerInternalProps, id: UniqueID): Customer {
return new Customer(props, id); return new Customer(props, id);

View File

@ -1,11 +1,16 @@
import type { IModuleServer } from "@erp/core/api"; import type { IModuleServer } from "@erp/core/api";
import type { ICustomerPublicServices } from "./application"; import { type CustomerPublicServices, customersRouter, models } from "./infrastructure";
import { customersRouter, models } from "./infrastructure"; import {
import { buildCustomerPublicServices, buildCustomersDependencies } from "./infrastructure/di"; type CustomersInternalDeps,
buildCustomerPublicServices,
buildCustomersDependencies,
} from "./infrastructure/di";
export * from "./infrastructure/sequelize"; export * from "./infrastructure/sequelize";
export type { CustomerPublicServices };
export const customersAPIModule: IModuleServer = { export const customersAPIModule: IModuleServer = {
name: "customers", name: "customers",
version: "1.0.0", version: "1.0.0",
@ -25,10 +30,7 @@ export const customersAPIModule: IModuleServer = {
const internal = buildCustomersDependencies(params); const internal = buildCustomersDependencies(params);
// 2) Servicios públicos (Application Services) // 2) Servicios públicos (Application Services)
const customersServices: ICustomerPublicServices = buildCustomerPublicServices( const customersServices: CustomerPublicServices = buildCustomerPublicServices(params, internal);
params,
internal
);
logger.info("🚀 Customers module dependencies registered", { logger.info("🚀 Customers module dependencies registered", {
label: this.name, label: this.name,
@ -58,8 +60,11 @@ export const customersAPIModule: IModuleServer = {
async start(params) { async start(params) {
const { app, baseRoutePath, logger, getInternal } = params; const { app, baseRoutePath, logger, getInternal } = params;
// Recuperamos el dominio interno del módulo
const customersInternalDeps = getInternal<CustomersInternalDeps>("customers");
// Registro de rutas HTTP // Registro de rutas HTTP
customersRouter(params); customersRouter(params, customersInternalDeps);
logger.info("🚀 Customers module started", { logger.info("🚀 Customers module started", {
label: this.name, label: this.name,

View File

@ -1,5 +1,5 @@
import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth/api"; import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth/api";
import { type RequestWithAuth, type StartParams, validateRequest } from "@erp/core/api"; import { type ModuleParams, type RequestWithAuth, validateRequest } from "@erp/core/api";
import { type NextFunction, type Request, type Response, Router } from "express"; import { type NextFunction, type Request, type Response, Router } from "express";
import { import {
@ -18,16 +18,11 @@ import {
UpdateCustomerController, UpdateCustomerController,
} from "./controllers"; } from "./controllers";
export const customersRouter = (params: StartParams) => { export const customersRouter = (params: ModuleParams, deps: CustomersInternalDeps) => {
const { app, config, getInternal } = params; const { app, config } = params;
// Recuperamos el dominio interno del módulo
const deps = getInternal<CustomersInternalDeps>("customers");
const router: Router = Router({ mergeParams: true }); const router: Router = Router({ mergeParams: true });
// ----------------------------------------------
// 🔐 Autenticación + Tenancy para TODO el router // 🔐 Autenticación + Tenancy para TODO el router
if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "production") { if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "production") {
router.use( router.use(

View File

@ -46,7 +46,7 @@ export const factugesAPIModule: IModuleServer = {
* - NO construye dominio * - NO construye dominio
*/ */
async start(params) { async start(params) {
const { logger } = params; const { app, baseRoutePath, logger, getInternal, getService, listServices } = params;
// Registro de rutas HTTP // Registro de rutas HTTP
factugesRouter(params); factugesRouter(params);