This commit is contained in:
David Arranz 2026-05-22 21:20:47 +02:00
parent f23ee948f4
commit 475ab9fec3
11 changed files with 96 additions and 49 deletions

View File

@ -1 +1 @@
export * from './payment-method-full-snapshot-builder'; export * from "./payment-method-full-snapshot-builder";

View File

@ -1 +1 @@
export * from './payment-term-repository.interface'; export * from "./payment-term-repository.interface";

View File

@ -1,6 +1,6 @@
export * from './payment-term-creator'; export * from "./payment-term-creator";
export * from './payment-term-deleter'; export * from "./payment-term-deleter";
export * from './payment-term-finder'; export * from "./payment-term-finder";
export * from './payment-term-public-services'; export * from "./payment-term-public-services";
export * from './payment-term-status-changer'; export * from "./payment-term-status-changer";
export * from './payment-term-updater'; export * from "./payment-term-updater";

View File

@ -30,6 +30,8 @@ export class PaymentTermUpdater implements IPaymentTermUpdater {
} }
const paymentTerm = existingResult.data; const paymentTerm = existingResult.data;
// Aplicar cambios en el agregado
const updateResult = paymentTerm.update(patchProps); const updateResult = paymentTerm.update(patchProps);
if (updateResult.isFailure) { if (updateResult.isFailure) {
return Result.fail(updateResult.error); return Result.fail(updateResult.error);
@ -39,6 +41,7 @@ export class PaymentTermUpdater implements IPaymentTermUpdater {
return Result.ok(paymentTerm); return Result.ok(paymentTerm);
} }
// Persistir cambios
const saveResult = await this.repository.update(paymentTerm, transaction); const saveResult = await this.repository.update(paymentTerm, transaction);
if (saveResult.isFailure) { if (saveResult.isFailure) {
return Result.fail(saveResult.error); return Result.fail(saveResult.error);

View File

@ -1,5 +1,5 @@
import type { ITransactionManager } from "@erp/core/api"; import type { ITransactionManager } from "@erp/core/api";
import type { UniqueID } from "@repo/rdx-ddd"; import { UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import type { IPaymentTermDeleter, IPaymentTermFinder } from "../services"; import type { IPaymentTermDeleter, IPaymentTermFinder } from "../services";

View File

@ -1,5 +1,5 @@
import type { ITransactionManager } from "@erp/core/api"; import type { ITransactionManager } from "@erp/core/api";
import type { UniqueID } from "@repo/rdx-ddd"; import { UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import type { IPaymentTermFinder, IPaymentTermStatusChanger } from "../services"; import type { IPaymentTermFinder, IPaymentTermStatusChanger } from "../services";

View File

@ -1,5 +1,5 @@
import type { ITransactionManager } from "@erp/core/api"; import type { ITransactionManager } from "@erp/core/api";
import type { UniqueID } from "@repo/rdx-ddd"; import { UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import type { IPaymentTermFinder, IPaymentTermStatusChanger } from "../services"; import type { IPaymentTermFinder, IPaymentTermStatusChanger } from "../services";

View File

@ -108,10 +108,10 @@ export class PaymentTermDueRuleMismatch extends DomainError {
* @param options - Opciones nativas de Error (puedes pasar `cause`). * @param options - Opciones nativas de Error (puedes pasar `cause`).
*/ */
constructor(position: number, options?: ErrorOptions) { constructor(position: number, options?: ErrorOptions) {
super( const causeMessage = options?.cause
`Error. Payment term due rule with position '${position}' rejected due to currency/language mismatch.`, ? String(options.cause)
options : `Payment term due rule with position '${position}' rejected due to mismatch with existing rules.`;
); super(causeMessage, options);
this.name = "PaymentTermDueRuleMismatch"; this.name = "PaymentTermDueRuleMismatch";
} }
} }

View File

@ -32,6 +32,10 @@ export class PaymentTermDueDays extends ValueObject<{ value: number }> {
return this.props.value; return this.props.value;
} }
public toString(): string {
return this.props.value.toString();
}
public getProps() { public getProps() {
return this.props; return this.props;
} }

View File

@ -27,13 +27,36 @@ export class PaymentTermDueRules
super(props.rules ?? []); super(props.rules ?? []);
} }
public static create(props: PaymentTermDueRulesProps): PaymentTermDueRules { public totalPercentage(): number {
const { rules } = props; return this.getAll().reduce((acc, rule) => acc + rule.percentage.value, 0);
}
if (!rules || rules.length === 0) { public hasTotalPercentage100(): boolean {
return new PaymentTermDueRules({ rules: [] }); return this.totalPercentage() === 10000;
}
public add(item: PaymentTermDueRule): boolean {
const ordered = PaymentTermDueRules.validateRules([...this.getAll(), item]);
this.items = ordered;
if (this.totalItems !== null) {
this.totalItems = ordered.length;
} }
return true;
}
public addCollection(collection: Collection<PaymentTermDueRule>): boolean {
const ordered = PaymentTermDueRules.validateRules([...this.getAll(), ...collection.getAll()]);
this.items = ordered;
if (this.totalItems !== null) {
this.totalItems = ordered.length;
}
return true;
}
private static validateRules(
rules: PaymentTermDueRule[],
requireExactTotal = false
): PaymentTermDueRule[] {
const ordered = [...rules].sort( const ordered = [...rules].sort(
(first, second) => first.dueDays.toPrimitive() - second.dueDays.toPrimitive() (first, second) => first.dueDays.toPrimitive() - second.dueDays.toPrimitive()
); );
@ -56,12 +79,30 @@ export class PaymentTermDueRules
totalPercentage += percentage.value; totalPercentage += percentage.value;
} }
if (totalPercentage !== 10000) { if (requireExactTotal) {
if (totalPercentage !== 10000) {
throw new PaymentTermPercentageSumMismatchError(
"Payment term due rule percentages must sum exactly 100.00."
);
}
} else if (totalPercentage > 10000) {
throw new PaymentTermPercentageSumMismatchError( throw new PaymentTermPercentageSumMismatchError(
"Payment term due rule percentages must sum exactly 100.00." "Payment term due rule percentages must not exceed 100.00."
); );
} }
return ordered;
}
public static create(props: PaymentTermDueRulesProps): PaymentTermDueRules {
const { rules } = props;
if (!rules || rules.length === 0) {
return new PaymentTermDueRules({ rules: [] });
}
const ordered = PaymentTermDueRules.validateRules(rules, true);
return new PaymentTermDueRules({ rules: ordered }); return new PaymentTermDueRules({ rules: ordered });
} }
} }

View File

@ -5,6 +5,7 @@ import {
PaymentTermCannotBeDisabledError, PaymentTermCannotBeDisabledError,
PaymentTermCannotBeUpdatedError, PaymentTermCannotBeUpdatedError,
PaymentTermDueRuleMismatch, PaymentTermDueRuleMismatch,
PaymentTermPercentageSumMismatchError,
} from "./errors"; } from "./errors";
import { import {
PaymentTermDueRule, PaymentTermDueRule,
@ -107,12 +108,25 @@ export class PaymentTerm extends AggregateRoot<PaymentTermInternalProps> {
return Result.fail(ruleResult.error); return Result.fail(ruleResult.error);
} }
const added = this._dueRules.add(ruleResult.data); try {
const added = this._dueRules.add(ruleResult.data);
if (!added) { if (!added) {
return Result.fail(new PaymentTermDueRuleMismatch(index)); return Result.fail(new PaymentTermDueRuleMismatch(index));
}
} catch (err) {
const error = err as Error;
return Result.fail(new PaymentTermDueRuleMismatch(index, { cause: error.message }));
} }
} }
if (!this._dueRules.hasTotalPercentage100()) {
return Result.fail(
new PaymentTermPercentageSumMismatchError(
"Payment term due rule percentages must sum exactly 100.00."
)
);
}
return Result.ok(); return Result.ok();
} }
@ -140,7 +154,7 @@ export class PaymentTerm extends AggregateRoot<PaymentTermInternalProps> {
return this._dueRules; return this._dueRules;
} }
public update(patchProps: PaymentTermPatchProps): Result<boolean, Error> { public update(patchProps: PaymentTermPatchProps): Result<void, Error> {
if (this.isSystem) { if (this.isSystem) {
return Result.fail( return Result.fail(
new PaymentTermCannotBeUpdatedError("System payment terms cannot be updated.") new PaymentTermCannotBeUpdatedError("System payment terms cannot be updated.")
@ -152,28 +166,15 @@ export class PaymentTerm extends AggregateRoot<PaymentTermInternalProps> {
return Result.fail(validationResult.error); return Result.fail(validationResult.error);
} }
let hasChanges = false; const { dueRules, ...otherProps } = patchProps;
if ( const candidateProps: PaymentTermInternalProps = {
patchProps.name !== undefined && ...this.props,
patchProps.name.toPrimitive() !== this.props.name.toPrimitive() ...otherProps,
) { };
this.props.name = patchProps.name;
hasChanges = true;
}
if ( // Aplicar cambios
patchProps.description !== undefined && Object.assign(this.props, candidateProps);
!PaymentTerm.sameDescription(this.props.description, patchProps.description)
) {
this.props.description = patchProps.description;
hasChanges = true;
}
if (patchProps.isActive !== undefined && this.props.isActive !== patchProps.isActive) {
this.props.isActive = patchProps.isActive;
hasChanges = true;
}
// Reemplazo de items (si se proporciona) // Reemplazo de items (si se proporciona)
if (patchProps.dueRules !== undefined) { if (patchProps.dueRules !== undefined) {
@ -181,11 +182,9 @@ export class PaymentTerm extends AggregateRoot<PaymentTermInternalProps> {
if (initializeResult.isFailure) { if (initializeResult.isFailure) {
return Result.fail(initializeResult.error); return Result.fail(initializeResult.error);
} }
hasChanges = true;
} }
return Result.ok(hasChanges); return Result.ok();
} }
public disable(): Result<boolean, Error> { public disable(): Result<boolean, Error> {