erp-retail-backend/src/modules/customers/entities/loyalty-program.entity.ts

162 lines
4.6 KiB
TypeScript

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<string, any>;
@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[];
}