Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: apply pricing rule in POS #1037

Merged
merged 4 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 5 additions & 27 deletions models/baseModels/Invoice/Invoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import {
createLoyaltyPointEntry,
filterPricingRules,
getAddedLPWithGrandTotal,
getApplicableCouponCodesName,
getExchangeRate,
getNumberSeries,
removeUnusedCoupons,
getPricingRulesConflicts,
removeLoyaltyPoint,
roundFreeItemQty,
Expand All @@ -41,7 +41,7 @@ import { TaxSummary } from '../TaxSummary/TaxSummary';
import { ReturnDocItem } from 'models/inventory/types';
import { AccountFieldEnum, PaymentTypeEnum } from '../Payment/types';
import { PricingRule } from '../PricingRule/PricingRule';
import { ApplicableCouponCodes, ApplicablePricingRules } from './types';
import { ApplicablePricingRules } from './types';
import { PricingRuleDetail } from '../PricingRuleDetail/PricingRuleDetail';
import { LoyaltyProgram } from '../LoyaltyProgram/LoyaltyProgram';
import { AppliedCouponCodes } from '../AppliedCouponCodes/AppliedCouponCodes';
Expand Down Expand Up @@ -758,7 +758,8 @@ export abstract class Invoice extends Transactional {
}

const pricingRule = await this.getPricingRule();
if (!pricingRule) {

if (!pricingRule || !pricingRule.length) {
return false;
}

Expand Down Expand Up @@ -1074,30 +1075,7 @@ export abstract class Invoice extends Transactional {
this.clearFreeItems();
}

if (!this.coupons?.length) {
return;
}

const applicableCouponCodes = await Promise.all(
this.coupons?.map(async (coupon) => {
return await getApplicableCouponCodesName(
coupon.coupons as string,
this as SalesInvoice
);
})
);

const flattedApplicableCouponCodes = applicableCouponCodes?.flat();

const couponCodeDoc = (await this.fyo.doc.getDoc(
ModelNameEnum.CouponCode,
this.coupons[0].coupons
)) as CouponCode;

couponCodeDoc.removeUnusedCoupons(
flattedApplicableCouponCodes as ApplicableCouponCodes[],
this as SalesInvoice
);
await removeUnusedCoupons(this as SalesInvoice);
}

async beforeCancel(): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion models/baseModels/tests/testCouponCodes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ test('disabled coupon codes is not applied', async (t) => {

await sinv.runFormulas();

t.equal(sinv.pricingRuleDetail?.length, undefined);
t.equal(sinv.pricingRuleDetail?.length, 0);
});

test('Coupon code not created: coupons min amount must be lesser than coupons max.', async (t) => {
Expand Down
46 changes: 9 additions & 37 deletions models/baseModels/tests/testPricingRule.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ test('pricing rule is not applied when item qty is < min qty', async (t) => {
await sinv.append('items', { item: itemMap.Jacket.name, quantity: 3 });
await sinv.runFormulas();

t.equal(sinv.pricingRuleDetail?.length, undefined);
t.equal(sinv.pricingRuleDetail?.length, 0);
});

test('pricing rule is not applied when item qty is > max qty', async (t) => {
Expand All @@ -158,7 +158,7 @@ test('pricing rule is not applied when item qty is > max qty', async (t) => {
await sinv.append('items', { item: itemMap.Jacket.name, quantity: 10 });
await sinv.runFormulas();

t.equal(sinv.pricingRuleDetail?.length, undefined);
t.equal(sinv.pricingRuleDetail?.length, 0);
});

test('pricing rule is applied when filtered by min and max amount', async (t) => {
Expand Down Expand Up @@ -200,11 +200,7 @@ test('Pricing Rule is not applied when item amount is < min amount', async (t)
});
await sinv.runFormulas();

t.equal(
sinv.pricingRuleDetail?.length,
undefined,
'Pricing Rule is not applied'
);
t.equal(sinv.pricingRuleDetail?.length, 0, 'Pricing Rule is not applied');
});

test('Pricing Rule is not applied when item amount is > max amount', async (t) => {
Expand All @@ -220,11 +216,7 @@ test('Pricing Rule is not applied when item amount is > max amount', async (t) =
});
await sinv.runFormulas();

t.equal(
sinv.pricingRuleDetail?.length,
undefined,
'Pricing Rule is not applied'
);
t.equal(sinv.pricingRuleDetail?.length, 0, 'Pricing Rule is not applied');
});

test('Pricing Rule is not applied when sinvDate < validFrom date', async (t) => {
Expand All @@ -240,11 +232,7 @@ test('Pricing Rule is not applied when sinvDate < validFrom date', async (t) =>
});
await sinv.runFormulas();

t.equal(
sinv.pricingRuleDetail?.length,
undefined,
'Pricing Rule is not applied'
);
t.equal(sinv.pricingRuleDetail?.length, 0, 'Pricing Rule is not applied');
});

test('Pricing Rule is not applied when sinvDate > validFrom date', async (t) => {
Expand All @@ -260,11 +248,7 @@ test('Pricing Rule is not applied when sinvDate > validFrom date', async (t) =>
});
await sinv.runFormulas();

t.equal(
sinv.pricingRuleDetail?.length,
undefined,
'Pricing Rule is not applied'
);
t.equal(sinv.pricingRuleDetail?.length, 0, 'Pricing Rule is not applied');
});

test('Pricing Rule is applied when filtered by qty, amount and dates', async (t) => {
Expand Down Expand Up @@ -320,11 +304,7 @@ test('Pricing Rule is not applied when qty condition is false, rest is true', as
});
await sinv.runFormulas();

t.equal(
sinv.pricingRuleDetail?.length,
undefined,
'Pricing Rule is not applied'
);
t.equal(sinv.pricingRuleDetail?.length, 0, 'Pricing Rule is not applied');
});

test('Pricing Rule is not applied when amount condition is false, rest is true', async (t) => {
Expand All @@ -340,11 +320,7 @@ test('Pricing Rule is not applied when amount condition is false, rest is true',
});
await sinv.runFormulas();

t.equal(
sinv.pricingRuleDetail?.length,
undefined,
'Pricing Rule is not applied'
);
t.equal(sinv.pricingRuleDetail?.length, 0, 'Pricing Rule is not applied');
});

test('Pricing Rule is not applied when validity condition is false, rest is true', async (t) => {
Expand All @@ -360,11 +336,7 @@ test('Pricing Rule is not applied when validity condition is false, rest is true
});
await sinv.runFormulas();

t.equal(
sinv.pricingRuleDetail?.length,
undefined,
'Pricing Rule is not applied'
);
t.equal(sinv.pricingRuleDetail?.length, 0, 'Pricing Rule is not applied');
});

test('create two pricing rules, Highest priority pricing rule is applied', async (t) => {
Expand Down
69 changes: 31 additions & 38 deletions models/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import {
import { Fyo, t } from 'fyo';
import { InvoiceStatus, ModelNameEnum } from './types';

import { ApplicablePricingRules } from './baseModels/Invoice/types';
import {
ApplicableCouponCodes,
ApplicablePricingRules,
} from './baseModels/Invoice/types';
import { AppliedCouponCodes } from './baseModels/AppliedCouponCodes/AppliedCouponCodes';
import { CollectionRulesItems } from './baseModels/CollectionRulesItems/CollectionRulesItems';
import { CouponCode } from './baseModels/CouponCode/CouponCode';
Expand Down Expand Up @@ -764,19 +767,6 @@ export function getLoyaltyProgramTier(
return loyaltyProgramTier;
}

export async function updatePricingRuleItem(doc: SalesInvoice) {
const pricingRule = (await getPricingRule(doc)) as ApplicablePricingRules[];

let appliedPricingRuleCount = doc?.pricingRuleDetail?.length;

if (appliedPricingRuleCount !== pricingRule?.length) {
appliedPricingRuleCount = pricingRule?.length;

await doc?.appendPricingRuleDetail(pricingRule);
await doc?.applyProductDiscount();
}
}

export async function removeLoyaltyPoint(doc: Doc) {
if (!doc.loyaltyProgram) {
return;
Expand Down Expand Up @@ -1058,6 +1048,33 @@ export function canApplyCouponCode(
return true;
}

export async function removeUnusedCoupons(sinvDoc: SalesInvoice) {
if (!sinvDoc.coupons?.length) {
return;
}

const applicableCouponCodes = await Promise.all(
sinvDoc.coupons?.map(async (coupon) => {
return await getApplicableCouponCodesName(
coupon.coupons as string,
sinvDoc
);
})
);

const flattedApplicableCouponCodes = applicableCouponCodes?.flat();

const couponCodeDoc = (await sinvDoc.fyo.doc.getDoc(
ModelNameEnum.CouponCode,
sinvDoc.coupons[0].coupons
)) as CouponCode;

couponCodeDoc.removeUnusedCoupons(
flattedApplicableCouponCodes as ApplicableCouponCodes[],
sinvDoc
);
}

export async function getApplicableCouponCodesName(
couponName: string,
sinvDoc: SalesInvoice
Expand Down Expand Up @@ -1200,30 +1217,6 @@ export function removeFreeItems(sinvDoc: SalesInvoice) {
}
}

export async function updatePricingRule(sinvDoc: SalesInvoice) {
const applicablePricingRuleNames = await getPricingRule(sinvDoc);

if (!applicablePricingRuleNames || !applicablePricingRuleNames.length) {
sinvDoc.pricingRuleDetail = undefined;
sinvDoc.isPricingRuleApplied = false;
removeFreeItems(sinvDoc);
return;
}

const appliedPricingRuleCount = sinvDoc?.items?.filter(
(val) => val.isFreeItem
).length;

setTimeout(() => {
void (async () => {
if (appliedPricingRuleCount !== applicablePricingRuleNames?.length) {
await sinvDoc.appendPricingRuleDetail(applicablePricingRuleNames);
await sinvDoc.applyProductDiscount();
}
})();
}, 1);
}

export function getPricingRulesConflicts(
pricingRules: PricingRule[]
): undefined | boolean {
Expand Down
24 changes: 3 additions & 21 deletions src/components/POS/Classic/SelectedItemRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -275,17 +275,14 @@ import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoic
import { Money } from 'pesa';
import { DiscountType } from '../types';
import { validateSerialNumberCount } from 'src/utils/pos';
import { getPricingRule } from 'models/helpers';
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
import { ApplicablePricingRules } from 'models/baseModels/Invoice/types';

export default defineComponent({
name: 'SelectedItemRow',
components: { Currency, Data, Float, Int, Link, Text },
props: {
row: { type: SalesInvoiceItem, required: true },
},
emits: ['runSinvFormulas', 'setItemSerialNumbers', 'addItem'],
emits: ['runSinvFormulas', 'applyPricingRule'],
setup() {
return {
isDiscountingEnabled: inject('isDiscountingEnabled') as boolean,
Expand Down Expand Up @@ -363,30 +360,15 @@ export default defineComponent({
this.row.set('quantity', quantity);

if (!this.row.isFreeItem) {
await this.updatePricingRuleItem();
this.$emit('applyPricingRule');
this.$emit('runSinvFormulas');
}
},
async removeAddedItem(row: SalesInvoiceItem) {
this.row.parentdoc?.remove('items', row?.idx as number);

if (!row.isFreeItem) {
await this.updatePricingRuleItem();
}
},
async updatePricingRuleItem() {
const pricingRule = (await getPricingRule(
this.row.parentdoc as SalesInvoice
)) as ApplicablePricingRules[];

let appliedPricingRuleCount =
this.row.parentdoc?.pricingRuleDetail?.length;

if (appliedPricingRuleCount !== pricingRule?.length) {
appliedPricingRuleCount = pricingRule?.length;

await this.row.parentdoc?.appendPricingRuleDetail(pricingRule);
await this.row.parentdoc?.applyProductDiscount();
this.$emit('applyPricingRule');
}
},
},
Expand Down
2 changes: 2 additions & 0 deletions src/components/POS/Classic/SelectedItemTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
<SelectedItemRow
:row="(row as SalesInvoiceItem)"
@run-sinv-formulas="runSinvFormulas"
@apply-pricing-rule="$emit('applyPricingRule')"
/>
</Row>
</div>
Expand Down Expand Up @@ -93,6 +94,7 @@ export default defineComponent({
isExapanded: false,
};
},
emits: ['applyPricingRule'],
computed: {
ratio() {
return [0.1, 1, 0.8, 0.8, 0.8, 0.8, 0.2];
Expand Down
6 changes: 2 additions & 4 deletions src/components/POS/Modern/ModernPOSSelectedItemRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -248,16 +248,14 @@ import { defineComponent } from 'vue';
import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoiceItem';
import { Money } from 'pesa';
import { validateSerialNumberCount } from 'src/utils/pos';
import { updatePricingRuleItem } from 'models/helpers';
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';

export default defineComponent({
name: 'ModernPOSSelectedItemRow',
components: { Currency, Data, Float, Int, Link, Text },
props: {
row: { type: SalesInvoiceItem, required: true },
},
emits: ['toggleModal', 'runSinvFormulas', 'selectedRow'],
emits: ['toggleModal', 'runSinvFormulas', 'selectedRow', 'applyPricingRule'],

setup() {
return {
Expand Down Expand Up @@ -331,7 +329,7 @@ export default defineComponent({
this.row.parentdoc?.remove('items', row?.idx as number);

if (!row.isFreeItem) {
await updatePricingRuleItem(this.row.parentdoc as SalesInvoice);
this.$emit('applyPricingRule');
}
},
},
Expand Down
5 changes: 4 additions & 1 deletion src/components/POS/Modern/ModernPOSSelectedItemTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default defineComponent({
isExapanded: false,
};
},
emits: ['toggleModal', 'selectedRow'],
emits: ['toggleModal', 'selectedRow', 'applyPricingRule'],
computed: {
ratio() {
return [0.1, 0.8, 0.4, 0.8, 0.8, 0.3];
Expand Down Expand Up @@ -141,6 +141,9 @@ export default defineComponent({
selectedItemRow(row: SalesInvoiceItem, field: string) {
this.$emit('selectedRow', row, field);
},
emitApplyPricingRule() {
this.$emit('applyPricingRule');
},
isNumeric,
},
});
Expand Down
1 change: 1 addition & 0 deletions src/components/POS/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export type PosEmits =
| 'setCashAmount'
| 'setCouponsCount'
| 'routeToSinvList'
| 'applyPricingRule'
| 'setTransferRefNo'
| 'setLoyaltyPoints'
| 'setTransferAmount'
Expand Down
Loading
Loading