From 8e10bf313df965bb2b7bc5bdc3a2061235585d31 Mon Sep 17 00:00:00 2001 From: Gadha2311 Date: Mon, 2 Feb 2026 17:11:24 +0530 Subject: [PATCH] fix: update status when loyalty program reaches maximum usage --- jobs/checkLoyaltyProgramExpiry.ts | 28 ++++++++++++------- models/baseModels/Invoice/Invoice.ts | 4 +-- .../LoyaltyProgram/LoyaltyProgram.ts | 10 +++++++ models/helpers.ts | 14 +++++++--- src/components/StatusPill.vue | 23 +++++++++++++-- 5 files changed, 60 insertions(+), 19 deletions(-) diff --git a/jobs/checkLoyaltyProgramExpiry.ts b/jobs/checkLoyaltyProgramExpiry.ts index 838aeb44..e6e6b2ab 100644 --- a/jobs/checkLoyaltyProgramExpiry.ts +++ b/jobs/checkLoyaltyProgramExpiry.ts @@ -7,22 +7,30 @@ export async function checkLoyaltyProgramExpiry() { try { const currentDate = new Date(); - const loyaltyPrograms = (await dm.db?.getAll(ModelNameEnum.LoyaltyProgram, { - fields: ['name', 'toDate', 'status', 'isEnabled'], + const loyaltyPrograms = await dm.db?.getAll(ModelNameEnum.LoyaltyProgram, { + fields: ['name', 'toDate', 'status', 'isEnabled', 'maximumUse', 'used'], filters: { - status: ['!=', 'Expired'], + status: ['not in', ['Expired', 'Maxed']], isEnabled: true, }, - })) as Array<{ - name: string; - toDate: string; - status: string; - isEnabled: boolean; - }>; + }); if (loyaltyPrograms) { for (const program of loyaltyPrograms) { - if (program.toDate && new Date(program.toDate) <= currentDate) { + const maximumUse = Number(program.maximumUse) || 0; + const used = Number(program.used) || 0; + + if (maximumUse > 0 && used >= maximumUse) { + await dm.db?.knex!(ModelNameEnum.LoyaltyProgram) + .where({ name: program.name }) + .update({ + status: 'Maxed', + isEnabled: false, + }); + continue; + } + + if (program.toDate && new Date(String(program.toDate)) <= currentDate) { await dm.db?.knex!(ModelNameEnum.LoyaltyProgram) .where({ name: program.name }) .update({ diff --git a/models/baseModels/Invoice/Invoice.ts b/models/baseModels/Invoice/Invoice.ts index 51eed4bb..655c6230 100644 --- a/models/baseModels/Invoice/Invoice.ts +++ b/models/baseModels/Invoice/Invoice.ts @@ -886,7 +886,7 @@ export abstract class Invoice extends Transactional { } async updateUsedCountOfLoyaltyProgram() { - if (!this.loyaltyProgram) { + if (!this.loyaltyProgram || !this.redeemLoyaltyPoints) { return; } @@ -901,7 +901,7 @@ export abstract class Invoice extends Transactional { } async reduceUsedCountOfLoyaltyProgram() { - if (!this.loyaltyProgram) { + if (!this.loyaltyProgram || !this.redeemLoyaltyPoints) { return; } diff --git a/models/baseModels/LoyaltyProgram/LoyaltyProgram.ts b/models/baseModels/LoyaltyProgram/LoyaltyProgram.ts index 45d5b3f7..d2bb47bd 100644 --- a/models/baseModels/LoyaltyProgram/LoyaltyProgram.ts +++ b/models/baseModels/LoyaltyProgram/LoyaltyProgram.ts @@ -11,6 +11,7 @@ export class LoyaltyProgram extends Doc { expiryDuration?: number; maximumUse?: number; used?: number; + status?: 'Active' | 'Expired' | 'Maxed' | 'Disabled'; validations: ValidationMap = { used: (value: DocValue) => { @@ -34,6 +35,15 @@ export class LoyaltyProgram extends Doc { }, }; + async afterSubmit() { + const maximumUse = (this.maximumUse as number) || 0; + const used = (this.used as number) || 0; + + if (maximumUse > 0 && used >= maximumUse) { + await this.setAndSync({ status: 'Maxed', isEnabled: false }); + } + } + static filters: FiltersMap = { expenseAccount: () => ({ rootType: AccountRootTypeEnum.Expense, diff --git a/models/helpers.ts b/models/helpers.ts index 802877ef..f0a21bc8 100644 --- a/models/helpers.ts +++ b/models/helpers.ts @@ -765,6 +765,13 @@ export function getLoyaltyProgramStatus(doc?: RenderData | Doc): string { return ''; } + const maximumUse = doc.maximumUse as number; + const used = doc.used as number; + + if (maximumUse > 0 && used >= maximumUse) { + return 'Maxed'; + } + const currentDate = new Date(); currentDate.setHours(0, 0, 0, 0); @@ -781,6 +788,7 @@ export const loyaltyProgramStatusColor: Record = { Active: 'green', Disabled: 'gray', Expired: 'red', + Maxed: 'orange', }; export function getLoyaltyProgramStatusText(status: string): string { @@ -791,6 +799,8 @@ export function getLoyaltyProgramStatusText(status: string): string { return t`Disabled`; case 'Expired': return t`Expired`; + case 'Maxed': + return t`Maxed`; default: return ''; } @@ -1658,10 +1668,6 @@ export async function isLoyaltyProgramMaxedOut( fyo: Fyo, loyaltyProgramName: string ): Promise { - if (!loyaltyProgramName) { - return false; - } - const loyaltyProgram = await fyo.db.getAll(ModelNameEnum.LoyaltyProgram, { fields: ['maximumUse', 'used', 'isEnabled'], filters: { name: loyaltyProgramName }, diff --git a/src/components/StatusPill.vue b/src/components/StatusPill.vue index b9252a99..358d9f4e 100644 --- a/src/components/StatusPill.vue +++ b/src/components/StatusPill.vue @@ -79,6 +79,8 @@ export default defineComponent({ Unpaid: this.t`Unpaid`, PartlyPaid: this.t`Partly Paid`, Expired: this.t`Expired`, + Maxed: this.t`Maxed`, + Active: this.t`Active`, }[this.status]; }, color(): UIColors { @@ -102,6 +104,8 @@ const statusColorMap: Record = { Unpaid: 'red', PartlyPaid: 'yellow', Expired: 'red', + Maxed: 'orange', + Active: 'green', }; function getStatus(doc: Doc) { @@ -115,10 +119,23 @@ function getStatus(doc: Doc) { if (doc instanceof LoyaltyProgram) { const currentDate = new Date(); - if (doc.toDate && doc.toDate instanceof Date && doc.toDate <= currentDate) { - return 'Expired'; + currentDate.setHours(0, 0, 0, 0); + + const maximumUse = doc.maximumUse as number; + const used = doc.used as number; + + if (maximumUse > 0 && used >= maximumUse) { + return 'Maxed'; } - return 'Saved'; + + if (doc.toDate && doc.toDate instanceof Date) { + const toDate = new Date(doc.toDate); + toDate.setHours(0, 0, 0, 0); + if (toDate <= currentDate) { + return 'Expired'; + } + } + return 'Active'; } if (doc instanceof Party && doc.outstandingAmount?.isZero() !== true) {