UNPKG

@magic.batua/points

Version:

The Points module powers the loyalty points features of the Magic Batua platform.

184 lines (163 loc) 6.99 kB
/** * @module @magic.batua/points * @overview Defines the `Ledger` class that manages loyalty points logic across the * application. * * @author Animesh Mishra <hello@animesh.ltd> * @copyright © 2018 Animesh Ltd. All Rights Reserved. */ import * as Crypto from "crypto" import * as Request from "request-promise-native" import * as Points from "./Source/Points" import { ObjectID } from "mongodb" import { ExternalError } from "@magic.batua/error" import { ClientError } from "@magic.batua/error" import { Code } from "@magic.batua/error" import { Transaction, InitTransaction } from "./Source/Transaction" /** * The Points Ledger keeps track of a user's Magic Points, their status, their availability * and other points related arithmetic. * * Magic Points are awarded to users as rewards for continued usage and word-of-mouth publicity. * The points are rewarded as following: * * - 600 points at signup. These points aren't redeemable until you've spent ₹1,000 or more on the * Magic Batua platform. * * - 400 points if somebody signs up using your referral code. These points become redeemable once * you've amassed 10 or more referrals, and each of those referrals have spent ₹1,000 or more. * * - 1 point for mobile recharge. Redeemable immediately. If were referred by someone, their account * is credited with a point too. * * - 20 points for tuition fee payments. Redeemable immediately. If were referred by someone, their * account is credited with 20 points too. * * To manage the points logic, we make use of three buckets: Signup, Referrals and Available. As points * earned through Signup and Referrals become redeemable they are emptied into the Available bucket. * Points earned through other activities such as mobile recharge or tuition fee payment are credited to * the Available bucket straightaway. */ export class Ledger { /** Number of points available for redemption */ public available: number = 0 /** * Points earned at signup. Becomes redeemable once the account has spent * ₹1,000 or more on the Magic Batua platform. */ public signup: number = 0 /** * Points earned through referrals. Becomes redeemable once the account has accrued * 10 or more referrals. */ public referral: number = 0 /** Number of points accured since the creation of account */ public redeemable: number = 0 /** Points that have expired. */ public expired: number = 0 /** Points that have been redeemed since the creation of account */ public redeemed: number = 0 /** Points that have been refunded */ public refunded: number = 0 /** Points transactions listing all the issuance, redemption and refund transactions. */ public transactions = new Array<Transaction>() public constructor(transactions: Array<InitTransaction>) { for(var entry of transactions) { this.transactions.push(new Transaction(entry)) } // Count all points this.count() } /** * Goes over all point transactions one by one and separates all the points * in their respective baskets. * * @param transactions An array of Points `Transaction` */ private count() { this.signup = 0 this.referral = 0 this.expired = 0 this.redeemable = 0 this.refunded = 0 this.redeemed = 0 for(var transaction of this.transactions) { switch(transaction.type) { case "Issue": if(transaction.expiryDate!.valueOf() <= Date.now()) { this.expired += transaction.points; break } if(transaction.notes == "Signup") { this.signup += transaction.points; break } if(transaction.notes == "Referral") { this.referral += transaction.points; break } else { this.redeemable += transaction.points; break } case "Redeem": this.redeemed += transaction.points break case "Refund": this.refunded += transaction.points } } this.available = this.redeemable - this.redeemed } /** Issues a Magic Point for the given `reason` */ public Issue(points: number, reason: string): Transaction { let transaction = new Transaction({ points: points, type: "Issue", notes: reason }) // Add this transaction to the transactions array and count // the points again this.transactions.push(transaction) this.count() return transaction } /** Redeems `points` from the balance */ public Redeem(points: number, reason: string): Transaction { if(points > this.available) { throw new ClientError(Code.BadRequest, "Trying to redeem " + points + " points, but the account has " + this.redeemable + " redeemable points only.") } let transaction = new Transaction({ points: points, type: "Redeem", notes: reason }) // Add this transaction to the transactions array and count // the points again this.transactions.push(transaction) this.count() return transaction } /** * This method will be called by the `Account` module when the condition for * unlocking signup bonus is met. * To move points from Signup bucket `this.signup` to Redeemable bucket `this.redeemable`, * we change the `notes` property of signup points issuance from "Signup" to * "Signup Points become active". */ public MakeSignupRedeemable() { for(var transaction of this.transactions) { if(transaction.notes == "Signup" && transaction.expiryDate!.valueOf() >= Date.now()) { transaction.notes = "Signup points can be redeemed now." } } this.count() } /** * This method will be called by the `Account` module when the condition for * unlocking signup bonus is met. * To move points from Referrals bucket `this.signup` to Redeemable bucket `this.redeemable`, * we change the `notes` property of signup points issuance from "Signup" to * "Referral points can be redeemed now.". */ public MakeReferralsRedeemable() { for(var transaction of this.transactions) { if(transaction.notes == "Referral" && transaction.expiryDate!.valueOf() >= Date.now()) { transaction.notes = "Referral points can now be redeemed." } } this.count() } } export { Transaction } export { InitTransaction } export { Points }