All files / marketing-platform/domain/entities RewardCoupon.ts

100% Statements 27/27
100% Branches 16/16
100% Functions 11/11
100% Lines 27/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175                                                                                            13x         13x 13x                   7x             5x 1x     4x 1x     3x                     12x 12x 12x                   8x 8x                     2x 1x     1x                   3x 3x 3x                     3x 1x     2x       2x                     1x                 1x       1x      
import { Coupon } from "./Coupon";
import {
  PromotionType,
  DistributionType,
  ProductType,
  ImageType,
  ExhaustionAlarmPercentages,
  YesNo,
  FlexibleDaysType,
} from "../types";
 
/**
 * Reward Coupon entity
 * Handles reward-based coupon distribution with automatic granting
 */
export class RewardCoupon extends Coupon {
  private couponGrantYn: YesNo;
  private couponGrantMinPrice: number | null;
 
  constructor(params: {
    id: string;
    title: string;
    startDate: Date;
    endDate: Date;
    promotionType: PromotionType;
    distributionType: DistributionType;
    productType: ProductType;
    imageType: ImageType;
    imageObsId: string;
    imageObsHash: string;
    imageUrl: string;
    exhaustionAlarmYn: YesNo;
    exhaustionAlarmPercentageList: ExhaustionAlarmPercentages[];
    couponDiscountPrice: number;
    purchasedCouponQuantity: number;
    usedCouponQuantity: number;
    remainingCouponQuantity: number;
    fullPaymentYn: YesNo;
    fullPaymentMinPrice: number;
    validityPeriodType: FlexibleDaysType;
    validityPeriodDays: number;
    receivedCouponQuantity: number;
    // Reward Coupon specific
    couponGrantYn: YesNo;
    couponGrantMinPrice: number | null;
  }) {
    super({
      ...params,
      distributionType: "REWARD",
    });
 
    this.couponGrantYn = params.couponGrantYn;
    this.couponGrantMinPrice = params.couponGrantMinPrice;
  }
 
  // -------------------------------------------
  // BUSINESS LOGIC
  // -------------------------------------------
  /**
   * Checks if automatic coupon granting is enabled
   */
  public isAutomaticGrantEnabled(): boolean {
    return this.couponGrantYn === "Y";
  }
 
  /**
   * Checks if a payment amount qualifies for automatic coupon grant
   */
  public qualifiesForAutoGrant(paymentAmount: number): boolean {
    if (!this.isAutomaticGrantEnabled()) {
      return false;
    }
 
    if (this.couponGrantMinPrice === null) {
      return true;
    }
 
    return paymentAmount >= this.couponGrantMinPrice;
  }
 
  /**
   * Calculates the coupon expiration date based on validity period
   * Implements abstract method from Coupon base class
   * @param issueDate - The issue date to calculate expiration from
   * @returns Expiration date
   */
  public calculateCouponExpirationDate(issueDate: Date = this.getEndDate()): Date {
    // FLEXIBLE_DATE: Add validity period days to issue date
    const endDate = new Date(issueDate);
    endDate.setDate(endDate.getDate() + this.validityPeriodDays);
    return endDate;
  }
 
  /**
   * Checks if a coupon is still valid
   */
  public isCouponValid(
    issueDate: Date,
    currentDate: Date = new Date()
  ): boolean {
    const validityEndDate = this.calculateCouponExpirationDate(issueDate);
    return currentDate <= validityEndDate;
  }
 
  /**
   * Calculates discount with validity period check
   */
  public calculateDiscountWithValidity(
    issueDate: Date,
    paymentAmount: number,
    currentDate: Date = new Date()
  ): number {
    if (!this.isCouponValid(issueDate, currentDate)) {
      return 0;
    }
 
    return this.calculateDiscount(paymentAmount);
  }
 
  /**
   * Gets the number of days until the coupon expires
   */
  public getDaysUntilExpiration(
    issueDate: Date,
    currentDate: Date = new Date()
  ): number {
    const validityEndDate = this.calculateCouponExpirationDate(issueDate);
    const diffTime = validityEndDate.getTime() - currentDate.getTime();
    return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
  }
 
  /**
   * Checks if a coupon is expiring soon (within specified days)
   */
  public isExpiringSoon(
    issueDate: Date,
    daysThreshold: number = 3,
    currentDate: Date = new Date()
  ): boolean {
    if (!this.isCouponValid(issueDate, currentDate)) {
      return false;
    }
 
    const daysUntilExpiration = this.getDaysUntilExpiration(
      issueDate,
      currentDate
    );
    return daysUntilExpiration <= daysThreshold && daysUntilExpiration > 0;
  }
 
  /**
   * Gets information about the validity period
   */
  public getValidityPeriodInfo(): {
    type: FlexibleDaysType;
    days: number;
    description: string;
  } {
    return {
      type: this.validityPeriodType,
      days: this.validityPeriodDays,
      description: `Valid for ${this.validityPeriodDays} days from issue date`,
    };
  }
 
  // Getters
  public getCouponGrantYn(): YesNo {
    return this.couponGrantYn;
  }
 
  public getCouponGrantMinPrice(): number | null {
    return this.couponGrantMinPrice;
  }
}