mirror of
https://github.com/frappe/books.git
synced 2024-11-12 16:36:27 +00:00
feat: create LoyaltyPointEntry
This commit is contained in:
parent
31f97d0f39
commit
3163f835dd
@ -14,7 +14,9 @@ import { Transactional } from 'models/Transactional/Transactional';
|
||||
import {
|
||||
addItem,
|
||||
canApplyPricingRule,
|
||||
createLoyaltyPointEntry,
|
||||
filterPricingRules,
|
||||
getAddedLPWithGrandTotal,
|
||||
getExchangeRate,
|
||||
getNumberSeries,
|
||||
getPricingRulesConflicts,
|
||||
@ -38,6 +40,7 @@ import { AccountFieldEnum, PaymentTypeEnum } from '../Payment/types';
|
||||
import { PricingRule } from '../PricingRule/PricingRule';
|
||||
import { ApplicablePricingRules } from './types';
|
||||
import { PricingRuleDetail } from '../PricingRuleDetail/PricingRuleDetail';
|
||||
import { LoyaltyProgram } from '../LoyaltyProgram/LoyaltyProgram';
|
||||
|
||||
export type TaxDetail = {
|
||||
account: string;
|
||||
@ -69,8 +72,10 @@ export abstract class Invoice extends Transactional {
|
||||
setDiscountAmount?: boolean;
|
||||
discountAmount?: Money;
|
||||
discountPercent?: number;
|
||||
loyaltyPoints?: number;
|
||||
discountAfterTax?: boolean;
|
||||
stockNotTransferred?: number;
|
||||
loyaltyProgram?: string;
|
||||
backReference?: string;
|
||||
|
||||
submitted?: boolean;
|
||||
@ -207,6 +212,7 @@ export abstract class Invoice extends Transactional {
|
||||
}
|
||||
|
||||
await this._updateIsItemsReturned();
|
||||
await this._createLoyaltyPointEntry();
|
||||
}
|
||||
|
||||
async afterCancel() {
|
||||
@ -538,6 +544,28 @@ export abstract class Invoice extends Transactional {
|
||||
await invoiceDoc.submit();
|
||||
}
|
||||
|
||||
async _createLoyaltyPointEntry() {
|
||||
if (!this.loyaltyProgram) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loyaltyProgramDoc = (await this.fyo.doc.getDoc(
|
||||
ModelNameEnum.LoyaltyProgram,
|
||||
this.loyaltyProgram
|
||||
)) as LoyaltyProgram;
|
||||
|
||||
const expiryDate = this.date as Date;
|
||||
const fromDate = loyaltyProgramDoc.fromDate as Date;
|
||||
const toDate = loyaltyProgramDoc.toDate as Date;
|
||||
|
||||
if (fromDate <= expiryDate && toDate >= expiryDate) {
|
||||
const party = (await this.loadAndGetLink('party')) as Party;
|
||||
|
||||
await createLoyaltyPointEntry(this);
|
||||
await party.updateLoyaltyPoints();
|
||||
}
|
||||
}
|
||||
|
||||
async _validateHasLinkedReturnInvoices() {
|
||||
if (!this.name || this.isReturn || this.isQuote) {
|
||||
return;
|
||||
@ -560,6 +588,16 @@ export abstract class Invoice extends Transactional {
|
||||
);
|
||||
}
|
||||
|
||||
async getLPAddedBaseGrandTotal() {
|
||||
const totalLotaltyAmount = await getAddedLPWithGrandTotal(
|
||||
this.fyo,
|
||||
this.loyaltyProgram as string,
|
||||
this.loyaltyPoints as number
|
||||
);
|
||||
|
||||
return totalLotaltyAmount.sub(this.baseGrandTotal as Money).abs();
|
||||
}
|
||||
|
||||
formulas: FormulaMap = {
|
||||
account: {
|
||||
formula: async () => {
|
||||
@ -571,6 +609,16 @@ export abstract class Invoice extends Transactional {
|
||||
},
|
||||
dependsOn: ['party'],
|
||||
},
|
||||
loyaltyProgram: {
|
||||
formula: async () => {
|
||||
const partyDoc = await this.fyo.doc.getDoc(
|
||||
ModelNameEnum.Party,
|
||||
this.party
|
||||
);
|
||||
return partyDoc?.loyaltyProgram as string;
|
||||
},
|
||||
dependsOn: ['party', 'name'],
|
||||
},
|
||||
currency: {
|
||||
formula: async () => {
|
||||
const currency = (await this.fyo.getValue(
|
||||
|
@ -54,6 +54,40 @@ export class Party extends Doc {
|
||||
await this.setAndSync({ outstandingAmount });
|
||||
}
|
||||
|
||||
async updateLoyaltyPoints() {
|
||||
let loyaltyPoints = 0;
|
||||
|
||||
if (this.role === 'Customer' || this.role === 'Both') {
|
||||
loyaltyPoints = await this._getTotalLoyaltyPoints();
|
||||
}
|
||||
|
||||
await this.setAndSync({ loyaltyPoints });
|
||||
}
|
||||
|
||||
async _getTotalLoyaltyPoints() {
|
||||
const data = (await this.fyo.db.getAll(ModelNameEnum.LoyaltyPointEntry, {
|
||||
fields: ['name', 'loyaltyPoints', 'expiryDate', 'postingDate'],
|
||||
filters: {
|
||||
customer: this.name as string,
|
||||
},
|
||||
})) as {
|
||||
name: string;
|
||||
loyaltyPoints: number;
|
||||
expiryDate: Date;
|
||||
postingDate: Date;
|
||||
}[];
|
||||
|
||||
const totalLoyaltyPoints = data.reduce((total, entry) => {
|
||||
if (entry.expiryDate > entry.postingDate) {
|
||||
return total + entry.loyaltyPoints;
|
||||
}
|
||||
|
||||
return total;
|
||||
}, 0);
|
||||
|
||||
return totalLoyaltyPoints;
|
||||
}
|
||||
|
||||
async _getTotalOutstandingAmount(
|
||||
schemaName: 'SalesInvoice' | 'PurchaseInvoice'
|
||||
) {
|
||||
|
@ -25,6 +25,9 @@ import { Lead } from './baseModels/Lead/Lead';
|
||||
import { PricingRule } from './baseModels/PricingRule/PricingRule';
|
||||
import { ValidationError } from 'fyo/utils/errors';
|
||||
import { ApplicablePricingRules } from './baseModels/Invoice/types';
|
||||
import { LoyaltyProgram } from './baseModels/LoyaltyProgram/LoyaltyProgram';
|
||||
import { CollectionRulesItems } from './baseModels/CollectionRulesItems/CollectionRulesItems';
|
||||
import { isPesa } from 'fyo/utils';
|
||||
|
||||
export function getQuoteActions(
|
||||
fyo: Fyo,
|
||||
@ -663,6 +666,95 @@ export async function addItem<M extends ModelsWithItems>(name: string, doc: M) {
|
||||
await item.set('item', name);
|
||||
}
|
||||
|
||||
export async function createLoyaltyPointEntry(doc: Invoice) {
|
||||
const loyaltyProgramDoc = (await doc.fyo.doc.getDoc(
|
||||
ModelNameEnum.LoyaltyProgram,
|
||||
doc?.loyaltyProgram
|
||||
)) as LoyaltyProgram;
|
||||
|
||||
if (!loyaltyProgramDoc.isEnabled) {
|
||||
return;
|
||||
}
|
||||
const expiryDate = new Date(Date.now());
|
||||
|
||||
expiryDate.setDate(
|
||||
expiryDate.getDate() + (loyaltyProgramDoc.expiryDuration || 0)
|
||||
);
|
||||
|
||||
const loyaltyProgramTier = getLoyaltyProgramTier(
|
||||
loyaltyProgramDoc,
|
||||
doc?.grandTotal as Money
|
||||
) as CollectionRulesItems;
|
||||
|
||||
if (!loyaltyProgramTier) {
|
||||
return;
|
||||
}
|
||||
|
||||
const collectionFactor = loyaltyProgramTier.collectionFactor as number;
|
||||
const loyaltyPoint =
|
||||
Math.round(doc?.grandTotal?.float || 0) * collectionFactor;
|
||||
|
||||
const newLoyaltyPointEntry = doc.fyo.doc.getNewDoc(
|
||||
ModelNameEnum.LoyaltyPointEntry,
|
||||
{
|
||||
loyaltyProgram: doc.loyaltyProgram,
|
||||
customer: doc.party,
|
||||
invoice: doc.name,
|
||||
postingDate: doc.date,
|
||||
purchaseAmount: doc.grandTotal,
|
||||
expiryDate: expiryDate,
|
||||
loyaltyProgramTier: loyaltyProgramTier?.tierName,
|
||||
loyaltyPoints: loyaltyPoint,
|
||||
}
|
||||
);
|
||||
|
||||
return await newLoyaltyPointEntry.sync();
|
||||
}
|
||||
|
||||
export async function getAddedLPWithGrandTotal(
|
||||
fyo: Fyo,
|
||||
loyaltyProgram: string,
|
||||
loyaltyPoints: number
|
||||
) {
|
||||
const loyaltyProgramDoc = (await fyo.doc.getDoc(
|
||||
ModelNameEnum.LoyaltyProgram,
|
||||
loyaltyProgram
|
||||
)) as LoyaltyProgram;
|
||||
|
||||
const conversionFactor = loyaltyProgramDoc.conversionFactor as number;
|
||||
|
||||
return fyo.pesa((loyaltyPoints || 0) * conversionFactor);
|
||||
}
|
||||
|
||||
export function getLoyaltyProgramTier(
|
||||
loyaltyProgramData: LoyaltyProgram,
|
||||
grandTotal: Money
|
||||
): CollectionRulesItems | undefined {
|
||||
if (!loyaltyProgramData.collectionRules) {
|
||||
return;
|
||||
}
|
||||
|
||||
let loyaltyProgramTier: CollectionRulesItems | undefined;
|
||||
|
||||
for (const row of loyaltyProgramData.collectionRules) {
|
||||
if (isPesa(row.minimumTotalSpent)) {
|
||||
const minimumSpent = row.minimumTotalSpent;
|
||||
|
||||
if (!minimumSpent.lte(grandTotal)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
!loyaltyProgramTier ||
|
||||
minimumSpent.gt(loyaltyProgramTier.minimumTotalSpent as Money)
|
||||
) {
|
||||
loyaltyProgramTier = row;
|
||||
}
|
||||
}
|
||||
}
|
||||
return loyaltyProgramTier;
|
||||
}
|
||||
|
||||
export async function getPricingRule(
|
||||
doc: Invoice
|
||||
): Promise<ApplicablePricingRules[] | null> {
|
||||
|
Loading…
Reference in New Issue
Block a user