import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany, Index, } from 'typeorm'; import { MembershipLevel } from './membership-level.entity'; export enum ProgramStatus { ACTIVE = 'active', INACTIVE = 'inactive', SUSPENDED = 'suspended', } export enum PointsCalculation { FIXED_PER_PURCHASE = 'fixed_per_purchase', PERCENTAGE_OF_AMOUNT = 'percentage_of_amount', POINTS_PER_CURRENCY = 'points_per_currency', } @Entity('loyalty_programs', { schema: 'retail' }) @Index(['tenantId', 'status']) @Index(['tenantId', 'code'], { unique: true }) export class LoyaltyProgram { @PrimaryGeneratedColumn('uuid') id: string; @Column({ name: 'tenant_id', type: 'uuid' }) @Index() tenantId: string; @Column({ length: 20, unique: true }) code: string; @Column({ length: 100 }) name: string; @Column({ type: 'text', nullable: true }) description: string; @Column({ type: 'enum', enum: ProgramStatus, default: ProgramStatus.ACTIVE, }) status: ProgramStatus; // Points configuration @Column({ name: 'points_calculation', type: 'enum', enum: PointsCalculation, default: PointsCalculation.POINTS_PER_CURRENCY, }) pointsCalculation: PointsCalculation; @Column({ name: 'points_per_currency', type: 'decimal', precision: 10, scale: 4, default: 1 }) pointsPerCurrency: number; // e.g., 1 point per $10 MXN @Column({ name: 'currency_per_point', type: 'decimal', precision: 10, scale: 2, default: 10 }) currencyPerPoint: number; // e.g., $10 MXN = 1 point @Column({ name: 'minimum_purchase', type: 'decimal', precision: 15, scale: 2, default: 0 }) minimumPurchase: number; @Column({ name: 'round_points', type: 'boolean', default: true }) roundPoints: boolean; @Column({ name: 'round_direction', length: 10, default: 'down' }) roundDirection: 'up' | 'down' | 'nearest'; // Redemption configuration @Column({ name: 'points_value', type: 'decimal', precision: 10, scale: 4, default: 0.1 }) pointsValue: number; // Value of 1 point in currency (e.g., 1 point = $0.10) @Column({ name: 'min_points_redemption', type: 'int', default: 100 }) minPointsRedemption: number; @Column({ name: 'max_redemption_percent', type: 'decimal', precision: 5, scale: 2, default: 100 }) maxRedemptionPercent: number; // Max % of purchase that can be paid with points @Column({ name: 'allow_partial_redemption', type: 'boolean', default: true }) allowPartialRedemption: boolean; // Expiration @Column({ name: 'points_expire', type: 'boolean', default: true }) pointsExpire: boolean; @Column({ name: 'expiration_months', type: 'int', default: 12 }) expirationMonths: number; @Column({ name: 'expiration_policy', length: 20, default: 'fifo' }) expirationPolicy: 'fifo' | 'lifo' | 'oldest_first'; // Bonus configuration @Column({ name: 'welcome_bonus', type: 'int', default: 0 }) welcomeBonus: number; @Column({ name: 'birthday_bonus', type: 'int', default: 0 }) birthdayBonus: number; @Column({ name: 'referral_bonus', type: 'int', default: 0 }) referralBonus: number; @Column({ name: 'referee_bonus', type: 'int', default: 0 }) refereeBonus: number; // Multipliers @Column({ name: 'double_points_days', type: 'jsonb', nullable: true }) doublePointsDays: string[]; // ['monday', 'tuesday', etc.] @Column({ name: 'category_multipliers', type: 'jsonb', nullable: true }) categoryMultipliers: { categoryId: string; multiplier: number; }[]; // Restrictions @Column({ name: 'excluded_categories', type: 'jsonb', nullable: true }) excludedCategories: string[]; @Column({ name: 'excluded_products', type: 'jsonb', nullable: true }) excludedProducts: string[]; @Column({ name: 'included_branches', type: 'jsonb', nullable: true }) includedBranches: string[]; // null = all branches // Valid dates @Column({ name: 'valid_from', type: 'date', nullable: true }) validFrom: Date; @Column({ name: 'valid_until', type: 'date', nullable: true }) validUntil: Date; // Terms @Column({ name: 'terms_and_conditions', type: 'text', nullable: true }) termsAndConditions: string; // Metadata @Column({ type: 'jsonb', nullable: true }) metadata: Record; @CreateDateColumn({ name: 'created_at' }) createdAt: Date; @UpdateDateColumn({ name: 'updated_at' }) updatedAt: Date; @Column({ name: 'created_by', type: 'uuid', nullable: true }) createdBy: string; @Column({ name: 'updated_by', type: 'uuid', nullable: true }) updatedBy: string; // Relations @OneToMany(() => MembershipLevel, (level) => level.program) levels: MembershipLevel[]; }