149 lines
4.1 KiB
TypeScript
149 lines
4.1 KiB
TypeScript
/**
|
|
* Domain Entity: SeasonSponsorship
|
|
*
|
|
* Represents a sponsorship relationship between a Sponsor and a Season.
|
|
* Aggregate root for managing sponsorship slots and pricing.
|
|
*/
|
|
|
|
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
|
|
import type { IEntity } from '@gridpilot/shared/domain';
|
|
|
|
import type { Money } from '../value-objects/Money';
|
|
|
|
export type SponsorshipTier = 'main' | 'secondary';
|
|
export type SponsorshipStatus = 'pending' | 'active' | 'cancelled';
|
|
|
|
export interface SeasonSponsorshipProps {
|
|
id: string;
|
|
seasonId: string;
|
|
sponsorId: string;
|
|
tier: SponsorshipTier;
|
|
pricing: Money;
|
|
status: SponsorshipStatus;
|
|
createdAt: Date;
|
|
activatedAt?: Date;
|
|
description?: string;
|
|
}
|
|
|
|
export class SeasonSponsorship implements IEntity<string> {
|
|
readonly id: string;
|
|
readonly seasonId: string;
|
|
readonly sponsorId: string;
|
|
readonly tier: SponsorshipTier;
|
|
readonly pricing: Money;
|
|
readonly status: SponsorshipStatus;
|
|
readonly createdAt: Date;
|
|
readonly activatedAt?: Date;
|
|
readonly description?: string;
|
|
|
|
private constructor(props: SeasonSponsorshipProps) {
|
|
this.id = props.id;
|
|
this.seasonId = props.seasonId;
|
|
this.sponsorId = props.sponsorId;
|
|
this.tier = props.tier;
|
|
this.pricing = props.pricing;
|
|
this.status = props.status;
|
|
this.createdAt = props.createdAt;
|
|
this.activatedAt = props.activatedAt;
|
|
this.description = props.description;
|
|
}
|
|
|
|
static create(props: Omit<SeasonSponsorshipProps, 'createdAt' | 'status'> & {
|
|
createdAt?: Date;
|
|
status?: SponsorshipStatus;
|
|
}): SeasonSponsorship {
|
|
this.validate(props);
|
|
|
|
return new SeasonSponsorship({
|
|
id: props.id,
|
|
seasonId: props.seasonId,
|
|
sponsorId: props.sponsorId,
|
|
tier: props.tier,
|
|
pricing: props.pricing,
|
|
status: props.status ?? 'pending',
|
|
createdAt: props.createdAt ?? new Date(),
|
|
...(props.activatedAt !== undefined ? { activatedAt: props.activatedAt } : {}),
|
|
...(props.description !== undefined ? { description: props.description } : {}),
|
|
});
|
|
}
|
|
|
|
private static validate(props: Omit<SeasonSponsorshipProps, 'createdAt' | 'status'>): void {
|
|
if (!props.id || props.id.trim().length === 0) {
|
|
throw new RacingDomainValidationError('SeasonSponsorship ID is required');
|
|
}
|
|
|
|
if (!props.seasonId || props.seasonId.trim().length === 0) {
|
|
throw new RacingDomainValidationError('SeasonSponsorship seasonId is required');
|
|
}
|
|
|
|
if (!props.sponsorId || props.sponsorId.trim().length === 0) {
|
|
throw new RacingDomainValidationError('SeasonSponsorship sponsorId is required');
|
|
}
|
|
|
|
if (!props.tier) {
|
|
throw new RacingDomainValidationError('SeasonSponsorship tier is required');
|
|
}
|
|
|
|
if (!props.pricing) {
|
|
throw new RacingDomainValidationError('SeasonSponsorship pricing is required');
|
|
}
|
|
|
|
if (props.pricing.amount <= 0) {
|
|
throw new RacingDomainValidationError('SeasonSponsorship pricing must be greater than zero');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Activate the sponsorship
|
|
*/
|
|
activate(): SeasonSponsorship {
|
|
if (this.status === 'active') {
|
|
throw new RacingDomainInvariantError('SeasonSponsorship is already active');
|
|
}
|
|
|
|
if (this.status === 'cancelled') {
|
|
throw new RacingDomainInvariantError('Cannot activate a cancelled SeasonSponsorship');
|
|
}
|
|
|
|
return new SeasonSponsorship({
|
|
...this,
|
|
status: 'active',
|
|
activatedAt: new Date(),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Cancel the sponsorship
|
|
*/
|
|
cancel(): SeasonSponsorship {
|
|
if (this.status === 'cancelled') {
|
|
throw new RacingDomainInvariantError('SeasonSponsorship is already cancelled');
|
|
}
|
|
|
|
return new SeasonSponsorship({
|
|
...this,
|
|
status: 'cancelled',
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Check if sponsorship is active
|
|
*/
|
|
isActive(): boolean {
|
|
return this.status === 'active';
|
|
}
|
|
|
|
/**
|
|
* Get the platform fee for this sponsorship
|
|
*/
|
|
getPlatformFee(): Money {
|
|
return this.pricing.calculatePlatformFee();
|
|
}
|
|
|
|
/**
|
|
* Get the net amount after platform fee
|
|
*/
|
|
getNetAmount(): Money {
|
|
return this.pricing.calculateNetAmount();
|
|
}
|
|
} |