123 lines
3.8 KiB
TypeScript
123 lines
3.8 KiB
TypeScript
|
|
import type { Name, UniqueID } from "@repo/rdx-ddd";
|
||
|
|
import { AggregateRoot } from "@repo/rdx-ddd";
|
||
|
|
import { Result } from "@repo/rdx-utils";
|
||
|
|
|
||
|
|
import { CompanyError } from "./company-error";
|
||
|
|
import type { Contact, ContactProps } from "./contact.entity";
|
||
|
|
import type { Site, SiteProps } from "./site.entity";
|
||
|
|
|
||
|
|
export type CompanyProps = Readonly<{
|
||
|
|
name: Name;
|
||
|
|
active: boolean;
|
||
|
|
contacts: readonly Contact[];
|
||
|
|
sites: readonly Site[];
|
||
|
|
}>;
|
||
|
|
export class Company extends AggregateRoot<CompanyProps> {
|
||
|
|
protected constructor(props: CompanyProps, id?: UniqueID) {
|
||
|
|
super(
|
||
|
|
{
|
||
|
|
...props,
|
||
|
|
contacts: Object.freeze([...props.contacts]),
|
||
|
|
sites: Object.freeze([...props.sites]),
|
||
|
|
},
|
||
|
|
id
|
||
|
|
);
|
||
|
|
}
|
||
|
|
static create(props: Pick<CompanyProps, "name" | "active">): Result<Company, CompanyError> {
|
||
|
|
if (props.name.toPrimitive().length > 100) {
|
||
|
|
return Result.fail(new CompanyError("INVALID_REQUEST"));
|
||
|
|
}
|
||
|
|
return Result.ok(new Company({ ...props, contacts: [], sites: [] }));
|
||
|
|
}
|
||
|
|
static rehydrate(props: CompanyProps, id: UniqueID): Company {
|
||
|
|
return new Company(props, id);
|
||
|
|
}
|
||
|
|
get name() {
|
||
|
|
return this.props.name;
|
||
|
|
}
|
||
|
|
get active() {
|
||
|
|
return this.props.active;
|
||
|
|
}
|
||
|
|
get contacts() {
|
||
|
|
return this.props.contacts;
|
||
|
|
}
|
||
|
|
get sites() {
|
||
|
|
return this.props.sites;
|
||
|
|
}
|
||
|
|
update(values: Pick<CompanyProps, "name" | "active">): Result<Company, CompanyError> {
|
||
|
|
if (values.name.toPrimitive().length > 100) {
|
||
|
|
return Result.fail(new CompanyError("INVALID_REQUEST"));
|
||
|
|
}
|
||
|
|
return this.name.equals(values.name) && this.active === values.active
|
||
|
|
? Result.ok(this)
|
||
|
|
: Result.ok(new Company({ ...this.props, ...values }, this.id));
|
||
|
|
}
|
||
|
|
addContact(contact: Contact): Result<Company, CompanyError> {
|
||
|
|
if (!contact.companyId.equals(this.id)) {
|
||
|
|
return Result.fail(new CompanyError("INVALID_REQUEST"));
|
||
|
|
}
|
||
|
|
if (this.contacts.some((existing) => existing.id.equals(contact.id))) {
|
||
|
|
return Result.ok(this);
|
||
|
|
}
|
||
|
|
return Result.ok(
|
||
|
|
new Company({ ...this.props, contacts: [...this.contacts, contact] }, this.id)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
updateContact(
|
||
|
|
id: UniqueID,
|
||
|
|
values: Omit<ContactProps, "companyId">
|
||
|
|
): Result<Company, CompanyError> {
|
||
|
|
const current = this.contacts.find((contact) => contact.id.equals(id));
|
||
|
|
if (!current) {
|
||
|
|
return Result.fail(new CompanyError("CONTACT_NOT_FOUND"));
|
||
|
|
}
|
||
|
|
const updated = current.update(values);
|
||
|
|
if (!updated.isSuccess) {
|
||
|
|
return updated;
|
||
|
|
}
|
||
|
|
return updated.data === current
|
||
|
|
? Result.ok(this)
|
||
|
|
: Result.ok(
|
||
|
|
new Company(
|
||
|
|
{
|
||
|
|
...this.props,
|
||
|
|
contacts: this.contacts.map((contact) =>
|
||
|
|
contact.id.equals(id) ? updated.data : contact
|
||
|
|
),
|
||
|
|
},
|
||
|
|
this.id
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
addSite(site: Site): Result<Company, CompanyError> {
|
||
|
|
if (!site.companyId.equals(this.id)) {
|
||
|
|
return Result.fail(new CompanyError("INVALID_REQUEST"));
|
||
|
|
}
|
||
|
|
if (this.sites.some((existing) => existing.id.equals(site.id))) {
|
||
|
|
return Result.ok(this);
|
||
|
|
}
|
||
|
|
return Result.ok(new Company({ ...this.props, sites: [...this.sites, site] }, this.id));
|
||
|
|
}
|
||
|
|
updateSite(id: UniqueID, values: Omit<SiteProps, "companyId">): Result<Company, CompanyError> {
|
||
|
|
const current = this.sites.find((site) => site.id.equals(id));
|
||
|
|
if (!current) {
|
||
|
|
return Result.fail(new CompanyError("SITE_NOT_FOUND"));
|
||
|
|
}
|
||
|
|
const updated = current.update(values);
|
||
|
|
if (!updated.isSuccess) {
|
||
|
|
return updated;
|
||
|
|
}
|
||
|
|
return updated.data === current
|
||
|
|
? Result.ok(this)
|
||
|
|
: Result.ok(
|
||
|
|
new Company(
|
||
|
|
{
|
||
|
|
...this.props,
|
||
|
|
sites: this.sites.map((site) => (site.id.equals(id) ? updated.data : site)),
|
||
|
|
},
|
||
|
|
this.id
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|