xpeditis2.0/apps/backend/src/domain/entities/organization.entity.ts
2025-10-20 12:30:08 +02:00

202 lines
4.7 KiB
TypeScript

/**
* Organization Entity
*
* Represents a business organization (freight forwarder, carrier, or shipper)
* in the Xpeditis platform.
*
* Business Rules:
* - SCAC code must be unique across all carrier organizations
* - Name must be unique
* - Type must be valid (FREIGHT_FORWARDER, CARRIER, SHIPPER)
*/
export enum OrganizationType {
FREIGHT_FORWARDER = 'FREIGHT_FORWARDER',
CARRIER = 'CARRIER',
SHIPPER = 'SHIPPER',
}
export interface OrganizationAddress {
street: string;
city: string;
state?: string;
postalCode: string;
country: string;
}
export interface OrganizationDocument {
id: string;
type: string;
name: string;
url: string;
uploadedAt: Date;
}
export interface OrganizationProps {
id: string;
name: string;
type: OrganizationType;
scac?: string; // Standard Carrier Alpha Code (for carriers only)
address: OrganizationAddress;
logoUrl?: string;
documents: OrganizationDocument[];
createdAt: Date;
updatedAt: Date;
isActive: boolean;
}
export class Organization {
private readonly props: OrganizationProps;
private constructor(props: OrganizationProps) {
this.props = props;
}
/**
* Factory method to create a new Organization
*/
static create(props: Omit<OrganizationProps, 'createdAt' | 'updatedAt'>): Organization {
const now = new Date();
// Validate SCAC code if provided
if (props.scac && !Organization.isValidSCAC(props.scac)) {
throw new Error('Invalid SCAC code format. Must be 4 uppercase letters.');
}
// Validate that carriers have SCAC codes
if (props.type === OrganizationType.CARRIER && !props.scac) {
throw new Error('Carrier organizations must have a SCAC code.');
}
// Validate that non-carriers don't have SCAC codes
if (props.type !== OrganizationType.CARRIER && props.scac) {
throw new Error('Only carrier organizations can have SCAC codes.');
}
return new Organization({
...props,
createdAt: now,
updatedAt: now,
});
}
/**
* Factory method to reconstitute from persistence
*/
static fromPersistence(props: OrganizationProps): Organization {
return new Organization(props);
}
/**
* Validate SCAC code format
* SCAC = Standard Carrier Alpha Code (4 uppercase letters)
*/
private static isValidSCAC(scac: string): boolean {
const scacPattern = /^[A-Z]{4}$/;
return scacPattern.test(scac);
}
// Getters
get id(): string {
return this.props.id;
}
get name(): string {
return this.props.name;
}
get type(): OrganizationType {
return this.props.type;
}
get scac(): string | undefined {
return this.props.scac;
}
get address(): OrganizationAddress {
return { ...this.props.address };
}
get logoUrl(): string | undefined {
return this.props.logoUrl;
}
get documents(): OrganizationDocument[] {
return [...this.props.documents];
}
get createdAt(): Date {
return this.props.createdAt;
}
get updatedAt(): Date {
return this.props.updatedAt;
}
get isActive(): boolean {
return this.props.isActive;
}
// Business methods
isCarrier(): boolean {
return this.props.type === OrganizationType.CARRIER;
}
isFreightForwarder(): boolean {
return this.props.type === OrganizationType.FREIGHT_FORWARDER;
}
isShipper(): boolean {
return this.props.type === OrganizationType.SHIPPER;
}
updateName(name: string): void {
if (!name || name.trim().length === 0) {
throw new Error('Organization name cannot be empty.');
}
this.props.name = name.trim();
this.props.updatedAt = new Date();
}
updateAddress(address: OrganizationAddress): void {
this.props.address = { ...address };
this.props.updatedAt = new Date();
}
updateLogoUrl(logoUrl: string): void {
this.props.logoUrl = logoUrl;
this.props.updatedAt = new Date();
}
addDocument(document: OrganizationDocument): void {
this.props.documents.push(document);
this.props.updatedAt = new Date();
}
removeDocument(documentId: string): void {
this.props.documents = this.props.documents.filter(doc => doc.id !== documentId);
this.props.updatedAt = new Date();
}
deactivate(): void {
this.props.isActive = false;
this.props.updatedAt = new Date();
}
activate(): void {
this.props.isActive = true;
this.props.updatedAt = new Date();
}
/**
* Convert to plain object for persistence
*/
toObject(): OrganizationProps {
return {
...this.props,
address: { ...this.props.address },
documents: [...this.props.documents],
};
}
}