Merge pull request 'Fix #44 : Validate Stock Availability Before Adding Items to Table' (#45) from bug/lastpurchasedOrder into main

Reviewed-on: Pozomind/pozo-retail-app#45
This commit is contained in:
karthikalakshmi 2026-02-02 10:27:44 +05:30
commit 5b0328e5bc
1 changed files with 458 additions and 316 deletions

View File

@ -1,15 +1,20 @@
import { useDispatch } from "react-redux";
import { changeOrderCardDetails, CustomerOrderList, getProductsearch } from "../../../../Features/BookingScreen/BookingData/BookingData";
import { useEffect, useState } from "react";
import { ExtractDateFormate, getSession } from "../../../../Services/Others";
import { Tables } from "../../../../Components/Tables/Table";
import { IoEye } from "react-icons/io5";
import { DatePicker, Modal } from "antd";
import dayjs from "dayjs";
import { useDispatch } from 'react-redux';
import {
changeOrderCardDetails,
CustomerOrderList,
getProductsearch,
getStockDetailsByVariantName,
} from '../../../../Features/BookingScreen/BookingData/BookingData';
import { useEffect, useState } from 'react';
import { ExtractDateFormate, getSession } from '../../../../Services/Others';
import { Tables } from '../../../../Components/Tables/Table';
import { IoEye } from 'react-icons/io5';
import { DatePicker, Modal } from 'antd';
import dayjs from 'dayjs';
import { v4 as uuidv4 } from 'uuid';
import { Buttons, DefaultModal } from '../../../../../ownLib/my-ui-lib';
import { ArrowRightOutlined } from '@ant-design/icons';
const CustomerOrders = ({ CustomerDetails, close }) => {
const CompId = getSession('CompId');
const BranchId = getSession('BranchId');
const AppId = getSession('AppId');
@ -22,12 +27,9 @@ const CustomerOrders = ({ CustomerDetails, close }) => {
const [selectedProducts, setSelectedProducts] = useState();
const [productModal, setProductModal] = useState(false);
const [selectedRange, setSelectedRange] = useState([dayjs(), dayjs()]);
useEffect(() => {
setSelectedRange([dayjs(), dayjs()]);
customerFetch();
}, []);
const [stockDetailModel, setStockDetailModel] = useState(false);
const [StockDetails, setStockDetails] = useState([]);
console.log(StockDetails, 'StockDetails');
useEffect(() => {
if (selectedRange && selectedRange[0] && selectedRange[1]) {
customerFetch();
@ -35,7 +37,8 @@ const CustomerOrders = ({ CustomerDetails, close }) => {
}, [selectedRange]);
const getApiRange = () => {
if (!selectedRange || !selectedRange[0] || !selectedRange[1]) return { fromDate: '', toDate: '' };
if (!selectedRange || !selectedRange[0] || !selectedRange[1])
return { fromDate: '', toDate: '' };
return {
fromDate: selectedRange[0].format('YYYY-MM-DD'),
@ -52,17 +55,14 @@ const CustomerOrders = ({ CustomerDetails, close }) => {
MobileNo: CustomerDetails?.value,
FromDate: fromDate,
ToDate: toDate,
}
};
const response = await dispatch(CustomerOrderList(data)).unwrap();
if (response?.data?.statusCode === 1) {
setCustpmerOrderData(response?.data?.data)
setCustpmerOrderData(response?.data?.data);
} else {
setCustpmerOrderData();
}
else {
setCustpmerOrderData()
}
}
};
const ViewProductdetails = (products) => {
setSelectedProducts(products);
@ -76,121 +76,198 @@ const CustomerOrders = ({ CustomerDetails, close }) => {
// close();
// };
// const getProductsearchData = async (prodName) => {
// const data = {
// CompId,
// BranchId,
// AppId,
// ProdName: prodName
// }
// const response = await dispatch(getProductsearch(data)).unwrap();
// console.log(response, 'response23232323');
// }
const getLastBuyQty = (index, variant, prodId) => {
const productDetails = custpmerOrderData?.[index]?.productDetails;
if (!productDetails) return 0;
const getProductsearchData = async (prodName) => {
const data = {
CompId,
BranchId,
AppId,
ProdName: prodName
let SalesQty = 0;
for (let i = 0; i < productDetails.length; i++) {
const product = productDetails[i];
if (product?.ProdId === prodId && product?.ProdVariantName === variant) {
SalesQty += product?.SalesQty;
}
const response = await dispatch(getProductsearch(data)).unwrap();
console.log(response, 'response23232323');
}
const handleRowDataClick = (record) => {
if (!record?.productDetails?.length) return;
const prodName = record.productDetails[0]?.ProdName;
getProductsearchData(prodName);
console.log(record.productDetails[0]?.ProdName, 'record?.productDetails');
const formattedData = record.productDetails.map((prod) => ({
ProdCat: prod.ProdCat,
Type: prod.Type,
OverallQuantity: prod.OverallQuantity ?? 0,
ActiveStatus: record.ActiveStatus,
AvailableFrom: prod.AvailableFrom,
AvailableTo: prod.AvailableTo,
Cess: prod.Cess ?? 0,
HSNCode: prod.HSN ?? null,
BrandName: prod.BrandName ?? null,
OpeningQty: prod.OpeningQty ?? 0,
PartNumber: prod.PartNumber ?? null,
ProdId: prod.ProdId,
ProdLogo: prod.ProdLogo ?? null,
ProdName: prod.ProdName,
QRCode: prod.QRCode ?? null,
QtyBasedPrice: prod.QtyBasedPrice ?? null,
Rack: prod.Rack ?? null,
Size: prod.Size,
StockAvailable: prod.StockAvailable ?? null,
TokenAvailable: prod.TokenAvailable ?? null,
TaxId: prod.ProdTaxId ?? null,
TaxPercentage: prod.ProdTaxPercentage ?? 0,
TaxName: prod.TaxName ?? null,
UniqueId: prod.UniqueId ?? null,
UomName: prod.UomName,
SinglePc: prod.SinglePc ?? null,
NoOfPcs: prod.NoOfPcs ?? 0,
ProdVariantName: prod.ProdVariantName ?? null,
VariantAvailableFrom: prod.VariantAvailableFrom ?? null,
VariantAvailableTo: prod.VariantAvailableTo ?? null,
BalanceQty: prod.BalanceQty ?? 0,
BatchRef: prod.BatchRef ?? null,
FullProductIdentifierDtls: prod.ProductIdentifierDtls ?? [],
InwardDate: prod.BookingDate ?? null,
InwardDtlId: prod.InwardDtlId ?? null,
InwardId: prod.InwardId ?? null,
MRP: prod.MRP ?? 0,
OrderRate: prod.Rate ?? 0,
SellingPrice: prod.Rate ?? 0,
SuppId: prod.SuppId ?? null,
SuppName: prod.SuppName ?? null,
OverAllPcs: prod.OverAllPcs ?? 0,
Offer: prod.OfferAmt ?? 0,
BookingTypeName: prod.BookingTypeName ?? null,
CounterName: prod.CounterName ?? null,
DiscountLimitType: prod.DiscountLimitType ?? null,
DiscountLimit: prod.DiscountLimit ?? 0,
BookedDate: prod.BookedDate ?? [],
BalanceBookedQty: prod.BalanceBookedQty ?? null,
ScaleType: prod.ScaleType ?? "",
localId: prod.localId ?? uuidv4(),
OrderQty: prod.SalesQty ?? 0,
TotalAmt: prod.TotalAmt ?? 0,
TaxAmt: prod.TaxAmt ?? 0,
WithoutTaxRate: prod.WithoutTaxRate ?? 0,
}));
dispatch(changeOrderCardDetails(formattedData));
close();
return SalesQty;
};
const getStockAllocation = (StockDtls, Qty) => {
let AllocatedData = [];
let RemainngQty = Qty;
for (let i = 0; i < StockDtls?.length; i++) {
const stock = StockDtls[i];
if (RemainngQty <= 0) break;
if (stock?.BalanceQty > 0) {
const allocQty = Math.min(stock.BalanceQty, RemainngQty);
AllocatedData.push({
InwardDtlId: stock.InwardDtlId,
AllocatedQty: allocQty,
MRP: stock?.MRP,
SellPrice: stock?.SellPrice,
InwardId: stock?.InwardId,
BatchRef: stock?.BatchRef,
SuppId: stock?.SuppName,
SuppName: stock?.SuppName,
InwardDate: stock?.InwardDate,
});
RemainngQty -= allocQty;
}
}
return AllocatedData;
};
const handleRowDataClick = async (record, index) => {
if (!record?.productDetails?.length) return;
let products = record?.productDetails?.map((e) => ({
prodId: e?.ProdId,
ProdVariantName: e?.ProdVariantName,
}));
let Data = {
products: products,
};
let Response = await dispatch(getStockDetailsByVariantName(Data)).unwrap();
if (Response?.data?.statusCode == 1) {
const result =
Response?.data?.data?.flatMap((product) =>
product?.ProductDetail?.flatMap((detail) =>
detail?.ProdVariantDetails?.flatMap((variant) => {
const prevQty = getLastBuyQty(
index,
variant.ProdVariantName,
variant.ProdId
);
const allocations = getStockAllocation(
variant?.StockDetails,
prevQty
);
const safeAllocations =
allocations?.length > 0
? allocations
: [
{
InwardDtlId: variant?.StockDetails?.[0]?.InwardDtlId,
InwardId: variant?.StockDetails?.[0]?.InwardId,
InwardDate: variant?.StockDetails?.[0]?.InwardDate,
AllocatedQty: prevQty,
SellPrice: variant?.StockDetails?.[0]?.SellPrice ?? 0,
MRP: variant?.StockDetails?.[0]?.MRP ?? 0,
OrderRate: variant?.StockDetails?.[0]?.SellPrice ?? 0,
TotalAmt:
(prevQty ?? 0) *
(variant?.StockDetails?.[0]?.SellPrice ?? 0),
BatchRef: null,
SuppId: null,
SuppName: null,
},
];
return safeAllocations.map((allocation) => ({
...variant,
// ===== PRODUCT LEVEL =====
ProdId: detail.ProdId,
ProdName: detail.ProdName,
ProdCat: product.ProdCat,
BrandName: detail.BrandName ?? null,
// ===== VARIANT / STOCK =====
PreviousPurchasedQty: prevQty,
available:
detail?.StockAvailable === 'N'
? true
: (variant.OverAllQty ?? 0) >= prevQty,
InwardDtlId: allocation.InwardDtlId,
InwardId: allocation.InwardId,
OrderQty: allocation.AllocatedQty,
InwardDate: allocation.InwardDate,
TotalAmt:
(allocation.AllocatedQty ?? 0) * (allocation.SellPrice ?? 0),
BalanceQty: detail.BalanceQty ?? 0,
BatchRef: allocation.BatchRef,
// ===== PRICE =====
MRP: allocation.MRP ?? 0,
OrderRate: allocation.SellPrice ?? 0,
SellingPrice: allocation.SellPrice ?? 0,
// ===== TAX =====
TaxId: detail.TaxId ?? null,
TaxPercentage: detail.TaxPercentage ?? 0,
TaxAmt: detail.TaxAmt ?? 0,
TaxName: detail.TaxName,
WithoutTaxRate: detail.WithoutTaxRate ?? 0,
// ===== OTHERS =====
SinglePc: 'N',
Size: detail.Size,
StockAvailable: detail.StockAvailable,
PartNumber: detail.PartNumber,
SuppId: allocation.SuppId,
SuppName: allocation.SuppName,
QRCode: detail.QRCode,
QtyBasedPrice: detail.QtyBasedPrice,
HSNCode: detail.HSNCode ?? null,
Rack: detail.Rack ?? null,
BookingTypeName: 'TakeAway',
ActiveStatus: 'A',
ModelNumber: null,
NoOfPcs: 0,
Offer: 0,
OpeningQty: 0,
FullProductIdentifierDtls: [],
CounterName: product.CounterName,
Type: 'P',
// ===== META =====
UniqueId: detail.UniqueId,
UomName: detail.UomName,
ScaleType: detail.ScaleType ?? '',
TokenAvailable: detail.TokenAvailable,
localId: uuidv4(),
}));
})
)
) ?? [];
setStockDetails(result);
setpage(1);
setStockDetailModel(true);
}
// close();
};
const handleSave = () => {
let Data = StockDetails?.filter((e) => e?.available == true)?.map(
({ stockDetails, available, PreviousPurchasedQty, ...rest }) => rest
);
dispatch(changeOrderCardDetails(Data));
close();
};
const columns = [
{
title: 'Sl.No',
key: 'sno',
align: 'center',
render: (_, __, index) => (page - 1) * 10 + index + 1,
onCell: (record) => ({
onClick: () => handleRowDataClick(record),
onCell: (record, index) => ({
onClick: () => handleRowDataClick(record, index),
style: { cursor: 'pointer' },
}),
},
@ -200,8 +277,8 @@ const CustomerOrders = ({ CustomerDetails, close }) => {
key: 'SalesDate',
align: 'center',
render: (text) => ExtractDateFormate(text),
onCell: (record) => ({
onClick: () => handleRowDataClick(record),
onCell: (record, index) => ({
onClick: () => handleRowDataClick(record, index),
style: { cursor: 'pointer' },
}),
},
@ -210,8 +287,8 @@ const CustomerOrders = ({ CustomerDetails, close }) => {
dataIndex: 'NetAmount',
key: 'NetAmount',
align: 'center',
onCell: (record) => ({
onClick: () => handleRowDataClick(record),
onCell: (record, index) => ({
onClick: () => handleRowDataClick(record, index),
style: { cursor: 'pointer' },
}),
},
@ -233,6 +310,49 @@ const CustomerOrders = ({ CustomerDetails, close }) => {
),
},
];
const stockcolumns = [
{
title: 'Sl.No',
key: 'sno',
align: 'center',
render: (_, __, index) => (page - 1) * 10 + index + 1,
},
{
title: 'Product',
key: 'ProdName',
dataIndex: 'ProdName',
align: 'center',
},
{
title: 'Variant',
key: 'ProdVariantName',
dataIndex: 'ProdVariantName',
align: 'center',
},
{
title: 'Purchased Qty',
key: 'PreviousPurchasedQty',
dataIndex: 'PreviousPurchasedQty',
align: 'center',
},
{
title: 'Total Qty',
key: 'OverAllQty',
dataIndex: 'OverAllQty',
align: 'center',
render: (value, record) =>
record?.StockAvailable == 'N' ? '-' : record?.OverAllQty,
},
{
title: 'available',
key: 'available',
dataIndex: 'available',
align: 'center',
render: (value) => (value ? 'Yes' : 'No'),
},
];
const productColumns = [
{
@ -254,29 +374,25 @@ const CustomerOrders = ({ CustomerDetails, close }) => {
dataIndex: 'SalesQty',
key: 'SalesQty',
align: 'center',
render: (_, record) =>
record?.SalesQty || '-',
render: (_, record) => record?.SalesQty || '-',
},
{
title: 'Variant',
key: 'ProdVariantName',
align: 'ProdVariantName',
render: (_, record) =>
record?.ProdVariantName || '-',
render: (_, record) => record?.ProdVariantName || '-',
},
{
title: 'Rate',
key: 'Rate',
align: 'right',
render: (_, record) =>
record?.Rate || 0,
render: (_, record) => record?.Rate || 0,
},
{
title: 'MRP',
key: 'MRP',
align: 'right',
render: (_, record) =>
record?.MRP || 0,
render: (_, record) => record?.MRP || 0,
},
];
@ -288,7 +404,6 @@ const CustomerOrders = ({ CustomerDetails, close }) => {
};
const getRangePickerValue = () => selectedRange || [dayjs(), dayjs()];
return (
<>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
@ -296,7 +411,7 @@ const CustomerOrders = ({ CustomerDetails, close }) => {
<p>Customer Name : {CustomerDetails?.CustName}</p>
<p>Customer Name : {CustomerDetails?.value}</p>
</div>
<div className='fromlabelDate'>
<div className="fromlabelDate">
<label>Select Date</label>
<RangePicker
onChange={onRangeChange}
@ -315,8 +430,6 @@ const CustomerOrders = ({ CustomerDetails, close }) => {
dataSource={custpmerOrderData}
pagination={handlePageChange}
/>{' '}
<Modal
open={productModal}
title="Product Details"
@ -330,10 +443,39 @@ const CustomerOrders = ({ CustomerDetails, close }) => {
pagination={false}
/>
</Modal>
</div>
</>
)
<DefaultModal
open={stockDetailModel}
title="Stock Details"
handleCancel={() => {
setStockDetailModel(false);
setStockDetails([]);
}}
footer={false}
width={700}
children={
<div>
<Tables
columns={stockcolumns}
data={StockDetails}
pagination={handlePageChange}
/>{' '}
<div style={{ display: 'flex', flexDirection: 'row-reverse' }}>
<Buttons
buttonText="SUBMIT"
color="901D77"
icon={<ArrowRightOutlined />}
handleSubmit={() => {
handleSave();
}}
/>
</div>
</div>
}
/>
</>
);
};
export default CustomerOrders;