import { AggregateRoot, type CurrencyCode, type EmailAddress, type LanguageCode, type Name, type PhoneNumber, PostalAddress, type PostalAddressPatchProps, type PostalAddressProps, type TINNumber, type URLAddress, type UniqueID, } from "@repo/rdx-ddd"; import { type Maybe, Result } from "@repo/rdx-utils"; import { SupplierStatus } from "../value-objects/supplier-status.value-object"; /** * Props de creación */ export interface ISupplierCreateProps { companyId: UniqueID; status?: SupplierStatus; reference: Maybe; isCompany: boolean; name: Name; tradeName: Maybe; tin: Maybe; address: PostalAddressProps; emailPrimary: Maybe; emailSecondary: Maybe; phonePrimary: Maybe; phoneSecondary: Maybe; mobilePrimary: Maybe; mobileSecondary: Maybe; fax: Maybe; website: Maybe; languageCode: LanguageCode; currencyCode: CurrencyCode; } export type SupplierPatchProps = Partial< Omit > & { address?: PostalAddressPatchProps; }; // Supplier export interface ISupplier { // comportamiento update(partialSupplier: SupplierPatchProps): Result; // propiedades (getters) readonly isIndividual: boolean; readonly isCompany: boolean; readonly isActive: boolean; readonly companyId: UniqueID; readonly reference: Maybe; readonly name: Name; readonly tradeName: Maybe; readonly tin: Maybe; readonly address: PostalAddress; readonly emailPrimary: Maybe; readonly emailSecondary: Maybe; readonly phonePrimary: Maybe; readonly phoneSecondary: Maybe; readonly mobilePrimary: Maybe; readonly mobileSecondary: Maybe; readonly fax: Maybe; readonly website: Maybe; readonly languageCode: LanguageCode; readonly currencyCode: CurrencyCode; } export type SupplierInternalProps = Omit & { address: PostalAddress; status: SupplierStatus; }; /** * Aggregate Root: Supplier */ export class Supplier extends AggregateRoot implements ISupplier { static create(props: ISupplierCreateProps, id?: UniqueID): Result { const validationResult = Supplier.validateCreateProps(props); if (validationResult.isFailure) { return Result.fail(validationResult.error); } const { address, ...internalProps } = props; const postalAddressResult = PostalAddress.create(address); if (postalAddressResult.isFailure) { return Result.fail(postalAddressResult.error); } const contact = new Supplier( { ...internalProps, address: postalAddressResult.data, status: props.status ?? SupplierStatus.active(), }, id ); // Reglas de negocio / validaciones // ... // ... // Disparar eventos de dominio // ... // ... return Result.ok(contact); } private static validateCreateProps(_props: ISupplierCreateProps): Result { return Result.ok(); } // Rehidratación desde persistencia static rehydrate(props: SupplierInternalProps, id: UniqueID): Supplier { return new Supplier(props, id); } // ------------------------- // BEHAVIOUR // ------------------------- public update(partialSupplier: SupplierPatchProps): Result { const { address: partialAddress, ...rest } = partialSupplier; const nextProps: SupplierInternalProps = { ...this.props }; let hasChanges = false; if ( rest.reference !== undefined && !Supplier.sameMaybeValueObject(this.reference, rest.reference) ) { nextProps.reference = rest.reference; hasChanges = true; } if (rest.isCompany !== undefined && this.isCompany !== rest.isCompany) { nextProps.isCompany = rest.isCompany; hasChanges = true; } if (rest.name !== undefined && !this.name.equals(rest.name)) { nextProps.name = rest.name; hasChanges = true; } if ( rest.tradeName !== undefined && !Supplier.sameMaybeValueObject(this.tradeName, rest.tradeName) ) { nextProps.tradeName = rest.tradeName; hasChanges = true; } if (rest.tin !== undefined && !Supplier.sameMaybeValueObject(this.tin, rest.tin)) { nextProps.tin = rest.tin; hasChanges = true; } if ( rest.emailPrimary !== undefined && !Supplier.sameMaybeValueObject(this.emailPrimary, rest.emailPrimary) ) { nextProps.emailPrimary = rest.emailPrimary; hasChanges = true; } if ( rest.emailSecondary !== undefined && !Supplier.sameMaybeValueObject(this.emailSecondary, rest.emailSecondary) ) { nextProps.emailSecondary = rest.emailSecondary; hasChanges = true; } if ( rest.phonePrimary !== undefined && !Supplier.sameMaybeValueObject(this.phonePrimary, rest.phonePrimary) ) { nextProps.phonePrimary = rest.phonePrimary; hasChanges = true; } if ( rest.phoneSecondary !== undefined && !Supplier.sameMaybeValueObject(this.phoneSecondary, rest.phoneSecondary) ) { nextProps.phoneSecondary = rest.phoneSecondary; hasChanges = true; } if ( rest.mobilePrimary !== undefined && !Supplier.sameMaybeValueObject(this.mobilePrimary, rest.mobilePrimary) ) { nextProps.mobilePrimary = rest.mobilePrimary; hasChanges = true; } if ( rest.mobileSecondary !== undefined && !Supplier.sameMaybeValueObject(this.mobileSecondary, rest.mobileSecondary) ) { nextProps.mobileSecondary = rest.mobileSecondary; hasChanges = true; } if (rest.fax !== undefined && !Supplier.sameMaybeValueObject(this.fax, rest.fax)) { nextProps.fax = rest.fax; hasChanges = true; } if (rest.website !== undefined && !Supplier.sameMaybeValueObject(this.website, rest.website)) { nextProps.website = rest.website; hasChanges = true; } if (rest.languageCode !== undefined && !this.languageCode.equals(rest.languageCode)) { nextProps.languageCode = rest.languageCode; hasChanges = true; } if (rest.currencyCode !== undefined && !this.currencyCode.equals(rest.currencyCode)) { nextProps.currencyCode = rest.currencyCode; hasChanges = true; } if (partialAddress) { const addressResult = PostalAddress.create({ ...this.address.getProps(), ...partialAddress, }); if (addressResult.isFailure) { return Result.fail(addressResult.error); } if (!this.address.equals(addressResult.data)) { nextProps.address = addressResult.data; hasChanges = true; } } if (hasChanges) { Object.assign(this.props, nextProps); } return Result.ok(hasChanges); } private static sameMaybeValueObject( current: Maybe, next: Maybe ): boolean { return current.match( (currentValue) => next.match( (nextValue) => currentValue.equals(nextValue), () => false ), () => next.isNone() ); } public activate(): Result { this.props.status = SupplierStatus.active(); return Result.ok(this); } public deactivate(): Result { this.props.status = SupplierStatus.inactive(); return Result.ok(this); } // ------------------------- // GETTERS // ------------------------- public get isIndividual(): boolean { return !this.props.isCompany; } public get isCompany(): boolean { return this.props.isCompany; } public get isActive(): boolean { return this.props.status.isActive(); } public get companyId(): UniqueID { return this.props.companyId; } public get reference(): Maybe { return this.props.reference; } public get name(): Name { return this.props.name; } public get tradeName(): Maybe { return this.props.tradeName; } public get tin(): Maybe { return this.props.tin; } public get address(): PostalAddress { return this.props.address; } public get emailPrimary(): Maybe { return this.props.emailPrimary; } public get emailSecondary(): Maybe { return this.props.emailSecondary; } public get phonePrimary(): Maybe { return this.props.phonePrimary; } public get phoneSecondary(): Maybe { return this.props.phoneSecondary; } public get mobilePrimary(): Maybe { return this.props.mobilePrimary; } public get mobileSecondary(): Maybe { return this.props.mobileSecondary; } public get fax(): Maybe { return this.props.fax; } public get website(): Maybe { return this.props.website; } public get languageCode(): LanguageCode { return this.props.languageCode; } public get currencyCode(): CurrencyCode { return this.props.currencyCode; } }