mirror of
https://github.com/frappe/books.git
synced 2025-01-02 22:50:14 +00:00
Merge pull request #1039 from AbleKSaju/fix-apply-freeitem
fix: prevent pricing rule for free items with insufficient stock
This commit is contained in:
commit
0056233b72
@ -24,6 +24,7 @@ import {
|
|||||||
getPricingRulesConflicts,
|
getPricingRulesConflicts,
|
||||||
removeLoyaltyPoint,
|
removeLoyaltyPoint,
|
||||||
roundFreeItemQty,
|
roundFreeItemQty,
|
||||||
|
getItemQtyMap,
|
||||||
} from 'models/helpers';
|
} from 'models/helpers';
|
||||||
import { StockTransfer } from 'models/inventory/StockTransfer';
|
import { StockTransfer } from 'models/inventory/StockTransfer';
|
||||||
import { validateBatch } from 'models/inventory/helpers';
|
import { validateBatch } from 'models/inventory/helpers';
|
||||||
@ -1264,34 +1265,42 @@ export abstract class Invoice extends Transactional {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let freeItemQty: number | undefined;
|
||||||
|
|
||||||
|
if (pricingRuleDoc?.freeItem) {
|
||||||
|
const itemQtyMap = await getItemQtyMap(this as SalesInvoice);
|
||||||
|
freeItemQty = itemQtyMap[pricingRuleDoc.freeItem]?.availableQty;
|
||||||
|
}
|
||||||
|
|
||||||
const canApplyPRLOnItem = canApplyPricingRule(
|
const canApplyPRLOnItem = canApplyPricingRule(
|
||||||
pricingRuleDoc,
|
pricingRuleDoc,
|
||||||
this.date as Date,
|
this.date as Date,
|
||||||
item.quantity as number,
|
item.quantity as number,
|
||||||
item.amount as Money
|
item.amount as Money,
|
||||||
|
freeItemQty as number
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!canApplyPRLOnItem) {
|
if (!canApplyPRLOnItem) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let freeItemQty = pricingRuleDoc.freeItemQuantity as number;
|
let roundFreeItemQuantity = pricingRuleDoc.freeItemQuantity as number;
|
||||||
|
|
||||||
if (pricingRuleDoc.isRecursive) {
|
if (pricingRuleDoc.isRecursive) {
|
||||||
freeItemQty =
|
roundFreeItemQuantity =
|
||||||
(item.quantity as number) / (pricingRuleDoc.recurseEvery as number);
|
(item.quantity as number) / (pricingRuleDoc.recurseEvery as number);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pricingRuleDoc.roundFreeItemQty) {
|
if (pricingRuleDoc.roundFreeItemQty) {
|
||||||
freeItemQty = roundFreeItemQty(
|
freeItemQty = roundFreeItemQty(
|
||||||
freeItemQty,
|
roundFreeItemQuantity,
|
||||||
pricingRuleDoc.roundingMethod as 'round' | 'floor' | 'ceil'
|
pricingRuleDoc.roundingMethod as 'round' | 'floor' | 'ceil'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.append('items', {
|
await this.append('items', {
|
||||||
item: pricingRuleDoc.freeItem as string,
|
item: pricingRuleDoc.freeItem as string,
|
||||||
quantity: freeItemQty,
|
quantity: roundFreeItemQuantity,
|
||||||
isFreeItem: true,
|
isFreeItem: true,
|
||||||
pricingRule: pricingRuleDoc.title,
|
pricingRule: pricingRuleDoc.title,
|
||||||
rate: pricingRuleDoc.freeItemRate,
|
rate: pricingRuleDoc.freeItemRate,
|
||||||
@ -1437,14 +1446,14 @@ export abstract class Invoice extends Transactional {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const filtered = filterPricingRules(
|
const filtered = await filterPricingRules(
|
||||||
|
this as SalesInvoice,
|
||||||
pricingRuleDocsForItem,
|
pricingRuleDocsForItem,
|
||||||
this.date as Date,
|
|
||||||
item.quantity as number,
|
item.quantity as number,
|
||||||
item.amount as Money
|
item.amount as Money
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!filtered.length) {
|
if (!filtered || !filtered.length) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -658,7 +658,7 @@ async function getItemRateFromPricingRule(
|
|||||||
(prDetail) => prDetail.referenceItem === doc.item
|
(prDetail) => prDetail.referenceItem === doc.item
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!pricingRule) {
|
if (!pricingRule || !pricingRule.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import test from 'tape';
|
import test from 'tape';
|
||||||
import { closeTestFyo, getTestFyo, setupTestFyo } from 'tests/helpers';
|
import { closeTestFyo, getTestFyo, setupTestFyo } from 'tests/helpers';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { getItem } from 'models/inventory/tests/helpers';
|
import { getItem, getStockMovement } from 'models/inventory/tests/helpers';
|
||||||
import { SalesInvoice } from '../SalesInvoice/SalesInvoice';
|
import { SalesInvoice } from '../SalesInvoice/SalesInvoice';
|
||||||
import { PricingRule } from '../PricingRule/PricingRule';
|
import { PricingRule } from '../PricingRule/PricingRule';
|
||||||
import { assertThrows } from 'backend/database/tests/helpers';
|
import { assertThrows } from 'backend/database/tests/helpers';
|
||||||
|
import { MovementTypeEnum } from 'models/inventory/types';
|
||||||
|
|
||||||
const fyo = getTestFyo();
|
const fyo = getTestFyo();
|
||||||
setupTestFyo(fyo, __filename);
|
setupTestFyo(fyo, __filename);
|
||||||
@ -20,6 +21,11 @@ const itemMap = {
|
|||||||
rate: 100,
|
rate: 100,
|
||||||
unit: 'Unit',
|
unit: 'Unit',
|
||||||
},
|
},
|
||||||
|
Pen: {
|
||||||
|
name: 'Pen',
|
||||||
|
rate: 700,
|
||||||
|
unit: 'Unit',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const partyMap = {
|
const partyMap = {
|
||||||
@ -52,7 +58,7 @@ const pricingRuleMap = [
|
|||||||
title: 'CAP PDR Offer',
|
title: 'CAP PDR Offer',
|
||||||
appliedItems: [{ item: itemMap.Cap.name }],
|
appliedItems: [{ item: itemMap.Cap.name }],
|
||||||
discountType: 'Product Discount',
|
discountType: 'Product Discount',
|
||||||
freeItem: 'Cap',
|
freeItem: 'Pen',
|
||||||
freeItemQuantity: 1,
|
freeItemQuantity: 1,
|
||||||
freeItemUnit: 'Unit',
|
freeItemUnit: 'Unit',
|
||||||
freeItemRate: 0,
|
freeItemRate: 0,
|
||||||
@ -91,6 +97,10 @@ const couponCodesMap = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const locationMap = {
|
||||||
|
LocationOne: 'LocationOne',
|
||||||
|
};
|
||||||
|
|
||||||
test(' Coupon Codes: create dummy item, party, pricing rules, coupon codes', async (t) => {
|
test(' Coupon Codes: create dummy item, party, pricing rules, coupon codes', async (t) => {
|
||||||
// Create Items
|
// Create Items
|
||||||
for (const { name, rate } of Object.values(itemMap)) {
|
for (const { name, rate } of Object.values(itemMap)) {
|
||||||
@ -123,6 +133,38 @@ test(' Coupon Codes: create dummy item, party, pricing rules, coupon codes', asy
|
|||||||
|
|
||||||
t.ok(fyo.singles.AccountingSettings?.enablePricingRule);
|
t.ok(fyo.singles.AccountingSettings?.enablePricingRule);
|
||||||
|
|
||||||
|
// Create Locations
|
||||||
|
for (const name of Object.values(locationMap)) {
|
||||||
|
await fyo.doc.getNewDoc(ModelNameEnum.Location, { name }).sync();
|
||||||
|
t.ok(await fyo.db.exists(ModelNameEnum.Location, name), `${name} exists`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create MaterialReceipt
|
||||||
|
const stockMovement = await getStockMovement(
|
||||||
|
MovementTypeEnum.MaterialReceipt,
|
||||||
|
new Date('2022-11-03T09:57:04.528'),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
item: itemMap.Pen.name,
|
||||||
|
to: locationMap.LocationOne,
|
||||||
|
quantity: 25,
|
||||||
|
rate: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fyo
|
||||||
|
);
|
||||||
|
await (await stockMovement.sync()).submit();
|
||||||
|
t.equal(
|
||||||
|
await fyo.db.getStockQuantity(
|
||||||
|
itemMap.Pen.name,
|
||||||
|
locationMap.LocationOne,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
),
|
||||||
|
25,
|
||||||
|
'Pen has quantity twenty five'
|
||||||
|
);
|
||||||
|
|
||||||
// Create Coupon Codes
|
// Create Coupon Codes
|
||||||
for (const couponCodes of Object.values(couponCodesMap)) {
|
for (const couponCodes of Object.values(couponCodesMap)) {
|
||||||
await fyo.doc.getNewDoc(ModelNameEnum.CouponCode, couponCodes).sync();
|
await fyo.doc.getNewDoc(ModelNameEnum.CouponCode, couponCodes).sync();
|
||||||
@ -291,7 +333,6 @@ test('Coupon not applied: incorrect items added.', async (t) => {
|
|||||||
|
|
||||||
await sinv.runFormulas();
|
await sinv.runFormulas();
|
||||||
await sinv.sync();
|
await sinv.sync();
|
||||||
|
|
||||||
t.equal(sinv.coupons?.length, 0, 'coupon code is not applied');
|
t.equal(sinv.coupons?.length, 0, 'coupon code is not applied');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,8 +2,9 @@ import test from 'tape';
|
|||||||
import { closeTestFyo, getTestFyo, setupTestFyo } from 'tests/helpers';
|
import { closeTestFyo, getTestFyo, setupTestFyo } from 'tests/helpers';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { SalesInvoice } from '../SalesInvoice/SalesInvoice';
|
import { SalesInvoice } from '../SalesInvoice/SalesInvoice';
|
||||||
import { getItem } from 'models/inventory/tests/helpers';
|
import { getItem, getStockMovement } from 'models/inventory/tests/helpers';
|
||||||
import { PricingRule } from '../PricingRule/PricingRule';
|
import { PricingRule } from '../PricingRule/PricingRule';
|
||||||
|
import { MovementTypeEnum } from 'models/inventory/types';
|
||||||
|
|
||||||
const fyo = getTestFyo();
|
const fyo = getTestFyo();
|
||||||
setupTestFyo(fyo, __filename);
|
setupTestFyo(fyo, __filename);
|
||||||
@ -19,6 +20,11 @@ const itemMap = {
|
|||||||
rate: 100,
|
rate: 100,
|
||||||
unit: 'Unit',
|
unit: 'Unit',
|
||||||
},
|
},
|
||||||
|
Pen: {
|
||||||
|
name: 'Pen',
|
||||||
|
rate: 700,
|
||||||
|
unit: 'Unit',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const partyMap = {
|
const partyMap = {
|
||||||
@ -48,7 +54,7 @@ const pricingRuleMap = [
|
|||||||
title: 'CAP PDR Offer',
|
title: 'CAP PDR Offer',
|
||||||
appliedItems: [{ item: itemMap.Cap.name }],
|
appliedItems: [{ item: itemMap.Cap.name }],
|
||||||
discountType: 'Product Discount',
|
discountType: 'Product Discount',
|
||||||
freeItem: 'Cap',
|
freeItem: 'Pen',
|
||||||
freeItemQuantity: 1,
|
freeItemQuantity: 1,
|
||||||
freeItemUnit: 'Unit',
|
freeItemUnit: 'Unit',
|
||||||
freeItemRate: 0,
|
freeItemRate: 0,
|
||||||
@ -62,7 +68,11 @@ const pricingRuleMap = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
test('Pricing Rule: create dummy item, party, pricing rules', async (t) => {
|
const locationMap = {
|
||||||
|
LocationOne: 'LocationOne',
|
||||||
|
};
|
||||||
|
|
||||||
|
test('Pricing Rule: create dummy item, party, pricing rules, free items, locations', async (t) => {
|
||||||
// Create Items
|
// Create Items
|
||||||
for (const { name, rate } of Object.values(itemMap)) {
|
for (const { name, rate } of Object.values(itemMap)) {
|
||||||
const item = getItem(name, rate, false);
|
const item = getItem(name, rate, false);
|
||||||
@ -87,6 +97,38 @@ test('Pricing Rule: create dummy item, party, pricing rules', async (t) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create Locations
|
||||||
|
for (const name of Object.values(locationMap)) {
|
||||||
|
await fyo.doc.getNewDoc(ModelNameEnum.Location, { name }).sync();
|
||||||
|
t.ok(await fyo.db.exists(ModelNameEnum.Location, name), `${name} exists`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create MaterialReceipt
|
||||||
|
const stockMovement = await getStockMovement(
|
||||||
|
MovementTypeEnum.MaterialReceipt,
|
||||||
|
new Date('2022-11-03T09:57:04.528'),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
item: itemMap.Pen.name,
|
||||||
|
to: locationMap.LocationOne,
|
||||||
|
quantity: 25,
|
||||||
|
rate: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fyo
|
||||||
|
);
|
||||||
|
await (await stockMovement.sync()).submit();
|
||||||
|
t.equal(
|
||||||
|
await fyo.db.getStockQuantity(
|
||||||
|
itemMap.Pen.name,
|
||||||
|
locationMap.LocationOne,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
),
|
||||||
|
25,
|
||||||
|
'Pen has quantity twenty five'
|
||||||
|
);
|
||||||
|
|
||||||
await fyo.singles.AccountingSettings?.set('enablePricingRule', true);
|
await fyo.singles.AccountingSettings?.set('enablePricingRule', true);
|
||||||
t.ok(fyo.singles.AccountingSettings?.enablePricingRule);
|
t.ok(fyo.singles.AccountingSettings?.enablePricingRule);
|
||||||
});
|
});
|
||||||
@ -487,7 +529,7 @@ test('create a product discount giving 1 free item', async (t) => {
|
|||||||
await sinv.runFormulas();
|
await sinv.runFormulas();
|
||||||
await sinv.sync();
|
await sinv.sync();
|
||||||
|
|
||||||
t.equal(!!sinv.items![1].isFreeItem, true);
|
t.equal(sinv.items![1].isFreeItem, true);
|
||||||
t.equal(sinv.items![1].rate!.float, pricingRuleMap[1].freeItemRate);
|
t.equal(sinv.items![1].rate!.float, pricingRuleMap[1].freeItemRate);
|
||||||
t.equal(sinv.items![1].quantity, pricingRuleMap[1].freeItemQuantity);
|
t.equal(sinv.items![1].quantity, pricingRuleMap[1].freeItemQuantity);
|
||||||
});
|
});
|
||||||
@ -516,7 +558,7 @@ test('create a product discount, recurse 2', async (t) => {
|
|||||||
await sinv.runFormulas();
|
await sinv.runFormulas();
|
||||||
await sinv.sync();
|
await sinv.sync();
|
||||||
|
|
||||||
t.equal(!!sinv.items![1].isFreeItem, true);
|
t.equal(sinv.items![1].isFreeItem, true);
|
||||||
t.equal(sinv.items![1].rate!.float, pricingRuleMap[1].freeItemRate);
|
t.equal(sinv.items![1].rate!.float, pricingRuleMap[1].freeItemRate);
|
||||||
t.equal(sinv.items![1].quantity, pricingRuleMap[1].freeItemQuantity);
|
t.equal(sinv.items![1].quantity, pricingRuleMap[1].freeItemQuantity);
|
||||||
});
|
});
|
||||||
|
@ -39,6 +39,13 @@ import { safeParseFloat } from 'utils/index';
|
|||||||
import { PriceList } from './baseModels/PriceList/PriceList';
|
import { PriceList } from './baseModels/PriceList/PriceList';
|
||||||
import { InvoiceItem } from './baseModels/InvoiceItem/InvoiceItem';
|
import { InvoiceItem } from './baseModels/InvoiceItem/InvoiceItem';
|
||||||
import { SalesInvoiceItem } from './baseModels/SalesInvoiceItem/SalesInvoiceItem';
|
import { SalesInvoiceItem } from './baseModels/SalesInvoiceItem/SalesInvoiceItem';
|
||||||
|
import { ItemQtyMap } from 'src/components/POS/types';
|
||||||
|
import { ValuationMethod } from './inventory/types';
|
||||||
|
import {
|
||||||
|
getRawStockLedgerEntries,
|
||||||
|
getStockBalanceEntries,
|
||||||
|
getStockLedgerEntries,
|
||||||
|
} from 'reports/inventory/helpers';
|
||||||
|
|
||||||
export function getQuoteActions(
|
export function getQuoteActions(
|
||||||
fyo: Fyo,
|
fyo: Fyo,
|
||||||
@ -63,6 +70,34 @@ export function getInvoiceActions(
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getItemQtyMap(doc: SalesInvoice): Promise<ItemQtyMap> {
|
||||||
|
const itemQtyMap: ItemQtyMap = {};
|
||||||
|
const valuationMethod =
|
||||||
|
(doc.fyo.singles.InventorySettings?.valuationMethod as ValuationMethod) ??
|
||||||
|
ValuationMethod.FIFO;
|
||||||
|
|
||||||
|
const rawSLEs = await getRawStockLedgerEntries(doc.fyo);
|
||||||
|
const rawData = getStockLedgerEntries(rawSLEs, valuationMethod);
|
||||||
|
const posInventory = doc.fyo.singles.POSSettings?.inventory;
|
||||||
|
|
||||||
|
const stockBalance = getStockBalanceEntries(rawData, {
|
||||||
|
location: posInventory,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const row of stockBalance) {
|
||||||
|
if (!itemQtyMap[row.item]) {
|
||||||
|
itemQtyMap[row.item] = { availableQty: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row.batch) {
|
||||||
|
itemQtyMap[row.item][row.batch] = row.balanceQuantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemQtyMap[row.item]!.availableQty += row.balanceQuantity;
|
||||||
|
}
|
||||||
|
return itemQtyMap;
|
||||||
|
}
|
||||||
|
|
||||||
export function getStockTransferActions(
|
export function getStockTransferActions(
|
||||||
fyo: Fyo,
|
fyo: Fyo,
|
||||||
schemaName: ModelNameEnum.Shipment | ModelNameEnum.PurchaseReceipt
|
schemaName: ModelNameEnum.Shipment | ModelNameEnum.PurchaseReceipt
|
||||||
@ -914,9 +949,9 @@ export async function getPricingRule(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filtered = filterPricingRules(
|
const filtered = await filterPricingRules(
|
||||||
|
doc as SalesInvoice,
|
||||||
pricingRuleDocsForItem,
|
pricingRuleDocsForItem,
|
||||||
doc.date as Date,
|
|
||||||
item.quantity as number,
|
item.quantity as number,
|
||||||
item.amount as Money
|
item.amount as Money
|
||||||
);
|
);
|
||||||
@ -977,16 +1012,31 @@ export async function getItemRateFromPriceList(
|
|||||||
return plItem?.rate;
|
return plItem?.rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterPricingRules(
|
export async function filterPricingRules(
|
||||||
|
doc: SalesInvoice,
|
||||||
pricingRuleDocsForItem: PricingRule[],
|
pricingRuleDocsForItem: PricingRule[],
|
||||||
sinvDate: Date,
|
|
||||||
quantity: number,
|
quantity: number,
|
||||||
amount: Money
|
amount: Money
|
||||||
): PricingRule[] | [] {
|
): Promise<PricingRule[] | []> {
|
||||||
const filteredPricingRules: PricingRule[] | undefined = [];
|
const filteredPricingRules: PricingRule[] = [];
|
||||||
|
|
||||||
for (const pricingRuleDoc of pricingRuleDocsForItem) {
|
for (const pricingRuleDoc of pricingRuleDocsForItem) {
|
||||||
if (canApplyPricingRule(pricingRuleDoc, sinvDate, quantity, amount)) {
|
let freeItemQty: number | undefined;
|
||||||
|
|
||||||
|
if (pricingRuleDoc?.freeItem) {
|
||||||
|
const itemQtyMap = await getItemQtyMap(doc);
|
||||||
|
freeItemQty = itemQtyMap[pricingRuleDoc.freeItem]?.availableQty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
canApplyPricingRule(
|
||||||
|
pricingRuleDoc,
|
||||||
|
doc.date as Date,
|
||||||
|
quantity,
|
||||||
|
amount,
|
||||||
|
freeItemQty ?? 0
|
||||||
|
)
|
||||||
|
) {
|
||||||
filteredPricingRules.push(pricingRuleDoc);
|
filteredPricingRules.push(pricingRuleDoc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -997,9 +1047,22 @@ export function canApplyPricingRule(
|
|||||||
pricingRuleDoc: PricingRule,
|
pricingRuleDoc: PricingRule,
|
||||||
sinvDate: Date,
|
sinvDate: Date,
|
||||||
quantity: number,
|
quantity: number,
|
||||||
amount: Money
|
amount: Money,
|
||||||
|
freeItemQty: number
|
||||||
): boolean {
|
): boolean {
|
||||||
|
const freeItemQuantity = pricingRuleDoc.freeItemQuantity;
|
||||||
|
|
||||||
|
if (pricingRuleDoc.isRecursive) {
|
||||||
|
freeItemQty = quantity / (pricingRuleDoc.recurseEvery as number);
|
||||||
|
}
|
||||||
|
|
||||||
// Filter by Quantity
|
// Filter by Quantity
|
||||||
|
if (pricingRuleDoc.freeItem && freeItemQuantity! >= freeItemQty) {
|
||||||
|
throw new ValidationError(
|
||||||
|
t`Free item '${pricingRuleDoc.freeItem}' does not have a specified quantity`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(pricingRuleDoc.minQuantity as number) > 0 &&
|
(pricingRuleDoc.minQuantity as number) > 0 &&
|
||||||
quantity < (pricingRuleDoc.minQuantity as number)
|
quantity < (pricingRuleDoc.minQuantity as number)
|
||||||
|
@ -117,7 +117,6 @@ import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoic
|
|||||||
import { AppliedCouponCodes } from 'models/baseModels/AppliedCouponCodes/AppliedCouponCodes';
|
import { AppliedCouponCodes } from 'models/baseModels/AppliedCouponCodes/AppliedCouponCodes';
|
||||||
import {
|
import {
|
||||||
validateSinv,
|
validateSinv,
|
||||||
getItemQtyMap,
|
|
||||||
getItemDiscounts,
|
getItemDiscounts,
|
||||||
validateShipment,
|
validateShipment,
|
||||||
getTotalQuantity,
|
getTotalQuantity,
|
||||||
@ -129,6 +128,7 @@ import {
|
|||||||
removeFreeItems,
|
removeFreeItems,
|
||||||
getAddedLPWithGrandTotal,
|
getAddedLPWithGrandTotal,
|
||||||
getItemRateFromPriceList,
|
getItemRateFromPriceList,
|
||||||
|
getItemQtyMap,
|
||||||
} from 'models/helpers';
|
} from 'models/helpers';
|
||||||
import {
|
import {
|
||||||
POSItem,
|
POSItem,
|
||||||
@ -328,7 +328,7 @@ export default defineComponent({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
async setItemQtyMap() {
|
async setItemQtyMap() {
|
||||||
this.itemQtyMap = await getItemQtyMap();
|
this.itemQtyMap = await getItemQtyMap(this.sinvDoc as SalesInvoice);
|
||||||
},
|
},
|
||||||
setSinvDoc() {
|
setSinvDoc() {
|
||||||
this.sinvDoc = this.fyo.doc.getNewDoc(ModelNameEnum.SalesInvoice, {
|
this.sinvDoc = this.fyo.doc.getNewDoc(ModelNameEnum.SalesInvoice, {
|
||||||
|
@ -5,47 +5,13 @@ import { Item } from 'models/baseModels/Item/Item';
|
|||||||
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
|
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
|
||||||
import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoiceItem';
|
import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoiceItem';
|
||||||
import { POSShift } from 'models/inventory/Point of Sale/POSShift';
|
import { POSShift } from 'models/inventory/Point of Sale/POSShift';
|
||||||
import { ValuationMethod } from 'models/inventory/types';
|
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { Money } from 'pesa';
|
import { Money } from 'pesa';
|
||||||
import {
|
|
||||||
getRawStockLedgerEntries,
|
|
||||||
getStockBalanceEntries,
|
|
||||||
getStockLedgerEntries,
|
|
||||||
} from 'reports/inventory/helpers';
|
|
||||||
import { ItemQtyMap, ItemSerialNumbers } from 'src/components/POS/types';
|
import { ItemQtyMap, ItemSerialNumbers } from 'src/components/POS/types';
|
||||||
import { fyo } from 'src/initFyo';
|
import { fyo } from 'src/initFyo';
|
||||||
import { safeParseFloat } from 'utils/index';
|
import { safeParseFloat } from 'utils/index';
|
||||||
import { showToast } from './interactive';
|
import { showToast } from './interactive';
|
||||||
|
|
||||||
export async function getItemQtyMap(): Promise<ItemQtyMap> {
|
|
||||||
const itemQtyMap: ItemQtyMap = {};
|
|
||||||
const valuationMethod =
|
|
||||||
(fyo.singles.InventorySettings?.valuationMethod as ValuationMethod) ??
|
|
||||||
ValuationMethod.FIFO;
|
|
||||||
|
|
||||||
const rawSLEs = await getRawStockLedgerEntries(fyo);
|
|
||||||
const rawData = getStockLedgerEntries(rawSLEs, valuationMethod);
|
|
||||||
const posInventory = fyo.singles.POSSettings?.inventory;
|
|
||||||
|
|
||||||
const stockBalance = getStockBalanceEntries(rawData, {
|
|
||||||
location: posInventory,
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const row of stockBalance) {
|
|
||||||
if (!itemQtyMap[row.item]) {
|
|
||||||
itemQtyMap[row.item] = { availableQty: 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row.batch) {
|
|
||||||
itemQtyMap[row.item][row.batch] = row.balanceQuantity;
|
|
||||||
}
|
|
||||||
|
|
||||||
itemQtyMap[row.item].availableQty += row.balanceQuantity;
|
|
||||||
}
|
|
||||||
return itemQtyMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getTotalQuantity(items: SalesInvoiceItem[]): number {
|
export function getTotalQuantity(items: SalesInvoiceItem[]): number {
|
||||||
let totalQuantity = safeParseFloat(0);
|
let totalQuantity = safeParseFloat(0);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user