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-deleter';
export * from './payment-term-finder';
export * from './payment-term-public-services';
export * from './payment-term-status-changer';
export * from './payment-term-updater';
export * from "./payment-term-creator";
export * from "./payment-term-deleter";
export * from "./payment-term-finder";
export * from "./payment-term-public-services";
export * from "./payment-term-status-changer";
export * from "./payment-term-updater";

View File

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

View File

@ -1,5 +1,5 @@
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 type { IPaymentTermDeleter, IPaymentTermFinder } from "../services";

View File

@ -1,5 +1,5 @@
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 type { IPaymentTermFinder, IPaymentTermStatusChanger } from "../services";

View File

@ -1,5 +1,5 @@
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 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`).
*/
constructor(position: number, options?: ErrorOptions) {
super(
`Error. Payment term due rule with position '${position}' rejected due to currency/language mismatch.`,
options
);
const causeMessage = options?.cause
? String(options.cause)
: `Payment term due rule with position '${position}' rejected due to mismatch with existing rules.`;
super(causeMessage, options);
this.name = "PaymentTermDueRuleMismatch";
}
}

View File

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

View File

@ -27,13 +27,36 @@ export class PaymentTermDueRules
super(props.rules ?? []);
}
public static create(props: PaymentTermDueRulesProps): PaymentTermDueRules {
const { rules } = props;
if (!rules || rules.length === 0) {
return new PaymentTermDueRules({ rules: [] });
public totalPercentage(): number {
return this.getAll().reduce((acc, rule) => acc + rule.percentage.value, 0);
}
public hasTotalPercentage100(): boolean {
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(
(first, second) => first.dueDays.toPrimitive() - second.dueDays.toPrimitive()
);
@ -56,11 +79,29 @@ export class PaymentTermDueRules
totalPercentage += percentage.value;
}
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(
"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 });
}

View File

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