Android_Retail/src/Pages/StockMaster/StockForm.jsx

4361 lines
146 KiB
React
Raw Normal View History

2026-01-27 18:27:29 +05:30
import React, {
useState,
useEffect,
useRef,
useContext,
useCallback,
useMemo,
} from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import {
ArrowRightOutlined,
PlusCircleOutlined,
SearchOutlined,
2026-01-27 18:27:29 +05:30
} from '@ant-design/icons';
import { Form, Tooltip, Table, Input, AutoComplete, Switch, Checkbox } from 'antd';
import { IoAddCircleSharp } from 'react-icons/io5';
2026-01-27 18:27:29 +05:30
import { InputField } from '../../Components/Forms/InputField.jsx';
import Buttons from '../../Components/Forms/Buttons.jsx';
import { Modal } from 'antd';
2026-01-27 18:27:29 +05:30
import { Messages } from '../../Components/Notifications/Messages.jsx';
import { DeleteFilled } from '@ant-design/icons';
import { changeBreadCrumb } from '../../Features/AppPage/CenterPage.js';
import FormHeader from '../PageComponents/FormHeader.jsx';
import { DefaultModal } from '../../Components/Modal/DefaultModal.jsx';
import { getSession, validateSafeInput } from '../../Services/Others.js';
import { DatePicProd } from '../../Components/Forms/DatePickerProduct.jsx';
import { RadioGrpButton } from '../../Components/Forms/RadioGroup.jsx';
import moment from 'moment';
import { DropDowns } from '../../Components/Forms/DropDown.jsx';
import { BsUpcScan } from 'react-icons/bs';
import { debounce } from 'lodash';
import {
getProductData,
getSupplierData,
getPurchaseTypeData,
getPurchaseTaxTypeData,
postPurchaseData,
putStockData,
2026-01-27 18:27:29 +05:30
} from '../../Features/StockMaster/StockMaster.js';
import '../../Styles/Stock/StockMaster.scss';
import {
getAdmin,
getUomData,
getProdCatData,
uomDataSelector,
postSupplier,
getProductTypeData,
2026-01-28 15:50:57 +05:30
bulkpostdata,
postFieldSetup,
getFieldSetupData,
2026-01-27 18:27:29 +05:30
} from '../../Features/ProductPage/ProductPage.js';
import SupplierProductMappingForm from '../SupplierProductMapping/SuplierProductMappingForm.jsx';
import { getPurchaseSupplierAndWareHouseData } from '../../Features/PurchaseOrder/PurchaseOrder.js';
import FloatLabel from '../../Components/Forms/FloatLabel/index.jsx';
import { FaLink, FaEye } from 'react-icons/fa';
import { TbUpload } from 'react-icons/tb';
2026-01-27 18:27:29 +05:30
import {
getSupplaierIdBasedProducts,
getSupplaierIdwithTypeBasedProducts,
postSupplaierProducts,
} from '../../Features/SupplierProductMapping/SupplierProductMapping.js';
import {
ApplicationPreferences,
getCommonAppPreference,
} from '../../Features/BrachLogin/BranchLogin.js';
2026-01-27 18:27:29 +05:30
import {
getConfigType,
getPaymentOptionFeatureApi,
PendingOrdersPE,
} from '../../Features/BookingScreen/BookingData/BookingData.js';
import InvoiceImageExtractorModal from './InvoiceImageExtractor.jsx';
import { v4 as uuidv4 } from 'uuid';
import AddAllProductsButton from './AddAllProductGrid.jsx';
import SettingsIconWithModal from './SettingsIconWithModal.jsx';
import VirtualizedTable from './VirtualizedTable.jsx';
import { useDebounce } from './UseDebounce.jsx';
2026-01-27 18:27:29 +05:30
const subDirectory = import.meta.env.BASE_URL;
const EditableContext = React.createContext(null);
const EditableRow = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
const EditableCell = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
...restProps
}) => {
const [editing, setEditing] = useState(false);
const inputRef = useRef(null);
const form = useContext(EditableContext);
useEffect(() => {
if (editing) {
inputRef?.current?.focus();
}
}, [editing]);
const toggleEdit = () => {
setEditing(!editing);
form.setFieldsValue({
[dataIndex]: record[dataIndex],
});
};
const save = async () => {
try {
const values = await form.validateFields();
toggleEdit();
handleSave({
...record,
...values,
});
} catch (errInfo) {
console.log('Save failed:', errInfo);
}
};
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item
style={{
margin: 0,
}}
name={dataIndex}
>
<Input
ref={inputRef}
onPressEnter={save}
onBlur={save}
onMouseLeave={save}
value={
Array.isArray(record[dataIndex]) ? undefined : record[dataIndex]
}
/>
</Form.Item>
) : (
<div
className="editable-cell-value-wrap"
style={{
paddingRight: 24,
cursor: 'pointer',
2026-01-27 18:27:29 +05:30
}}
onClick={toggleEdit}
>
<Input
ref={inputRef}
onPressEnter={save}
onBlur={save}
value={
Array.isArray(record[dataIndex]) ? undefined : record[dataIndex]
}
/>
</div>
);
}
return <td {...restProps}>{childNode}</td>;
};
const StockForm = ({ formType }) => {
console.log('counttesttttttttttttttttttt');
2026-01-27 18:27:29 +05:30
const formRef = useRef(null);
const productAddInfoRef = useRef(null);
const formProductRef = useRef(null);
2026-01-27 18:27:29 +05:30
const formSupplierRef = useRef(null);
const dispatch = useDispatch();
const navigate = useNavigate();
const location = useLocation();
const [form] = Form.useForm();
const [editingKey, setEditingKey] = useState('');
const [index, setindex] = useState();
const state = location?.state;
const editstate = state?.editstate;
2026-01-27 18:27:29 +05:30
const CompId = getSession('CompId');
const BranchId = getSession('BranchId');
const AppId = getSession('AppId');
const UserId = getSession('UserId');
const UomData = useSelector(uomDataSelector);
const appPreferences = useSelector(ApplicationPreferences);
2026-01-27 18:27:29 +05:30
const [applicationRestrictedFields, setApplicationRestrictedFields] =
useState({
DatesAndExpiry: false,
BatchAndModelDetails: false,
AmountPerPieceDetail: false,
});
const [editingQty, setEditingQty] = useState({});
const [selectedAdditionalColumn, setSelectedAdditionalColumn] = useState([]);
const [tableFieldPreferences, setTableFieldPreferences] = useState([]);
2026-01-27 18:27:29 +05:30
const [SupplierData, setSupplierData] = useState([]);
const [SupplierOpen, setSupplierOpen] = useState('own');
const [purchaseOrderList, setPurchaseOrderList] = useState([]);
const [selectedSupplierName, setSelectedSupplierName] = useState();
const [messageType, setMessageType] = useState(null);
const [messageData, setMessageData] = useState(null);
const [searchText, setSearchText] = useState(null);
const [productSearchText, setProductSearchText] = useState('');
const [selectedInvoice, setSelectedInvoice] = useState(null);
const [orderType, setOrderType] = useState(true);
const [PurchaseTypeData, setPurchaseTypeData] = useState(null);
const [SelectedPurchaseType, setSelectedPurchaseType] = useState(null);
const [SelectedPurcTaxType, setSelectedPurcTaxType] = useState(null);
const [PurchaseData, setPurchaseData] = useState([]);
const [additionalInfoModal, setAdditionalInfoModal] = useState(false);
const [selectedRowIndex, setSelectedRowIndex] = useState(null);
const [selectedRowRecord, setSelectedRowRecord] = useState({});
const [purchaseOrderId, setPurchaseOrderId] = useState();
console.log(orderType, "orderType")
2026-01-27 18:27:29 +05:30
const [selectedProductName, setSelectedProductName] = useState(null);
const [scanner, setScanner] = useState(false);
const [Delete, setDelete] = useState(false);
const [selectedSupplierData, setSelectedSupplierData] = useState(null);
const [inwardDate, setInwardDate] = useState(new Date().toJSON());
2026-01-27 18:27:29 +05:30
const [SuppInvoiceDate, setSuppInvoiceDate] = useState();
const [invoiceType, setInvoiceType] = useState('I');
const [PurchaseDate, setPurchaseDate] = useState();
const [paymentAmount, setPaymentAmount] = useState(null);
2026-01-27 18:27:29 +05:30
const [OpenSupplierModel, setOpenSupplierModel] = useState(false);
const [zipCodeData, setZipCodeData] = useState(false);
const [imeiSerialOpen, setimeiSerialOpen] = useState(false);
const [dataSource, setDataSource] = useState([]);
const [dataSourceBackup, setDataSourceBackup] = useState([]);
const [purchaseStatus, setPurchaseStatus] = useState('P');
const [isModalOpen, setIsModalOpen] = useState(false);
const [productData, setProductData] = useState([]);
const [message, setMessage] = useState({ type: null, data: null });
const [initialLoad, setInitialLoad] = useState(true);
const [deliveryPerformanceData, SetDeleveryPerformanceData] = useState([]);
const [DiscountData, setDiscountData] = useState([]);
const [QualityData, setQualityData] = useState([]);
const [Delivery, setDeliveryValue] = useState();
const [DicountValue, setDicountValue] = useState();
const [QualityValue, setQualityValue] = useState();
const [ViewDtl, setViewDtl] = useState(false);
const [paymentOptions, setPaymentOptions] = useState([]);
const [selectedPaymentMode, setSelectedPaymentMode] = useState(null);
const [payAmountDisable, setPayAmountDisable] = useState(
paymentOptions
?.find((option) => option?.ModeId === selectedPaymentMode)
?.ModeName?.toLowerCase() === 'credit'
);
const [extractorModalVisible, setExtractorModalVisible] = useState(false);
const [extractorData, setExtractorData] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [loadingText, setLoadingText] = useState('');
// const [filteredPurchaseData, setFilteredPurchaseData] = useState([]);
const [searchValue, setSearchValue] = useState('');
const isSearching = Boolean(searchValue?.trim());
const [showAllSuppliers, setShowAllSuppliers] = useState(false);
const SelInvoiceAmount = useMemo(() => {
const total = PurchaseData?.reduce(
(acc, item) => acc + Number(item?.Amount || 0),
0
);
return !isNaN(total) && total !== 0 ? total : null;
}, [PurchaseData]);
const TotalTaxAmount = useMemo(() => {
return PurchaseData?.reduce(
(acc, item) => acc + Number(item?.TaxAmt || 0),
0
);
}, [PurchaseData]);
const filteredPurchaseData = useMemo(() => {
if (!searchValue?.trim()) return PurchaseData;
return PurchaseData.filter((item) =>
item?.ProdName?.toLowerCase().includes(searchValue.toLowerCase())
);
}, [PurchaseData, searchValue]);
2026-01-27 18:27:29 +05:30
const items = [
{
name: 'Home',
link: `${subDirectory}app-page/home`,
},
{
name: 'Product Receipt',
link: `${subDirectory}setting/purchase-entry`,
},
{
name: editstate ? 'Edit' : 'New',
link: null,
},
];
const categoryId = appPreferences?.find(
(p) => p?.PreferredCatName?.toLowerCase() === 'purchase entry'
)?.PreferredCatId;
2026-01-27 18:27:29 +05:30
useEffect(() => {
dispatch(changeBreadCrumb({ items: items }));
dispatch(
getProductData({ CompId: CompId, AppId: AppId, BranchId: BranchId })
).unwrap();
dispatch(getProductTypeData());
dispatch(getAdmin({ AppId: AppId, CompId: CompId }));
dispatch(getProdCatData({ AppId: AppId }));
dispatch(getUomData());
applicationApplicableFields();
getPurchaseType();
getPurchaseTaxType();
deliveryPerformance();
discountLevel();
qualityofSupp();
getSuplaierApi({ Type: null });
2026-01-27 18:27:29 +05:30
if (formType == 'add') {
formRef.current?.setFieldsValue({ SuppInvoiceDate: new Date().toJSON() });
setSuppInvoiceDate(new Date().toJSON());
formRef.current?.setFieldsValue({ InvoiceDate: new Date().toJSON() });
// setInvoiceDate(new Date().toJSON());
2026-01-27 18:27:29 +05:30
formRef.current?.setFieldsValue({ DueDate: new Date().toJSON() });
setPurchaseDate(new Date().toJSON());
formRef.current?.setFieldsValue({ InvoiceDate: new Date().toJSON() });
formRef.current?.setFieldsValue({ OrderType: 'N' });
}
if (formType == 'edit') {
formRef.current?.setFieldsValue({
ProdId: editstate?.ProdId,
ProdVariantName: editstate?.ProdVariantName,
SuppId: editstate?.SuppId,
InwardDate: editstate?.InwardDate + 'T00:00:00',
});
if (applicationRestrictedFields.DatesAndExpiry) {
formRef.current?.setFieldsValue({
ManufDate: editstate?.ManufDate,
ExpDate: editstate?.ExpDate,
});
}
handleSupplierBasedProduct(editstate?.SuppId);
// setSelectedProductData(editstate?.ProdId);
2026-01-27 18:27:29 +05:30
setSelectedSupplierData(editstate?.SuppId);
}
}, []);
useEffect(() => {
if (SupplierOpen !== 'own') {
getPurchaseOrders();
fetchPaymentOptions();
2026-01-27 18:27:29 +05:30
}
}, [SupplierOpen]);
2026-01-27 18:27:29 +05:30
useEffect(() => {
getFieldSetup();
}, [categoryId]);
2026-01-27 18:27:29 +05:30
useEffect(() => {
if (initialLoad && SupplierData?.length > 0) {
SetSelfSupplier();
setInitialLoad(false);
2026-01-27 18:27:29 +05:30
}
}, [SupplierData]);
2026-01-27 18:27:29 +05:30
useEffect(() => {
if (extractorData && selectedSupplierData) {
if (
extractorData?.invoiceNo !== null &&
extractorData?.invoiceNo !== ''
) {
formRef?.current?.setFieldsValue({
SuppInvoiceNo: extractorData?.invoiceNo,
});
}
formRef?.current?.setFieldsValue({
PaymentAmount: extractorData?.TotalAmtData,
});
setPaymentAmount(extractorData?.TotalAmtData);
onSuppInvoiceDateChange(
extractorData?.date,
extractorData?.date?.format('DD-MM-YYYY') || ''
);
2026-01-27 18:27:29 +05:30
}
}, [selectedSupplierData]);
2026-01-27 18:27:29 +05:30
useEffect(() => {
return () => {
debouncedScannerSearch.cancel();
};
}, []);
2026-01-27 18:27:29 +05:30
const SetSelfSupplier = async () => {
let SupplierId = SupplierData?.filter(
(item) => item.SuppName?.toLowerCase() === 'self'
)?.[0]?.['SuppId'];
let SupplierName = SupplierData?.filter(
(item) => item.SuppName?.toLowerCase() === 'self'
)?.[0]?.['SuppName'];
formRef.current?.setFieldsValue({ CustSuppId: SupplierId });
setSelectedSupplierData(SupplierId);
handleSupplierBasedProduct(SupplierId);
SupplierName === 'Self'
? setSelectedSupplierName(false)
: setSelectedSupplierName(true);
};
const getSuplaierApi = async ({ dataReturn = false, Type = null }) => {
const response = await dispatch(
getPurchaseSupplierAndWareHouseData({ CompId, AppId, BranchId, Type })
).unwrap();
if (response?.statusCode === 1) {
setSupplierData(response?.data);
if (dataReturn) {
return response?.data;
}
} else {
setSupplierData([]);
if (dataReturn) {
return [];
}
}
};
2026-01-27 18:27:29 +05:30
const getPurchaseOrders = async () => {
const { data: res } = await dispatch(
PendingOrdersPE({ CompId, AppId, BranchId })
).unwrap();
if (res?.statusCode === 1) {
setPurchaseOrderList(
res?.data?.PurchaseOrder?.map((item) => ({
...item,
localId: uuidv4(),
}))
);
} else {
setPurchaseOrderList([]);
setMessageType('error');
setMessageData('No pending purchase orders found.');
}
};
const fetchPaymentOptions = async () => {
try {
const response = await dispatch(
getPaymentOptionFeatureApi({ CompId, BranchId, AppId })
).unwrap();
if (response?.data?.statusCode === 1) {
const paymentDetails = response?.data?.data?.[0]?.PaymentDetails?.find(
(item) => item.FlowName === 'Sales'
)?.OptionDetails?.[0]?.ModeDetails?.filter(
(filter) =>
filter.ModeName?.toLowerCase() === 'cash' ||
filter.ModeName?.toLowerCase() === 'upi' ||
filter.ModeName?.toLowerCase() === 'credit'
);
setPaymentOptions(paymentDetails);
const modeId = paymentDetails?.find(
(item) => item.ModeName === 'Cash'
)?.ModeId;
setSelectedPaymentMode(modeId);
formRef.current?.setFieldsValue({ PaymentMode: modeId });
}
} catch (error) {
setMessage({
type: 'error',
data: error?.message || 'Failed to fetch payment options.',
});
}
};
2026-01-27 18:27:29 +05:30
const findMatchingSuppliers = async (name, mobile, supplierData) => {
let list = [];
if (mobile) {
const m = mobile;
list = supplierData.filter((x) =>
(x.SuppMobile || '').toString().includes(m)
);
}
// 2⃣ If no mobile match or mobile empty → fallback to name
if (name) {
const n = name?.trim().toLowerCase(); // 🔥 trimmed + lowercase
list = supplierData.filter(
(x) => (x.SuppName || '').trim().toLowerCase() === n // 🔥 safe compare
);
}
let finalList = list?.length > 0 ? [list[0]] : [];
const mappedSupplierProducts = await handleSupplierDropDownChange(
finalList?.[0]?.SuppId,
null,
supplierData
);
return { mappedSupplierProducts, SuppId: finalList?.[0]?.SuppId };
};
const getFieldSetup = async () => {
try {
const response = await dispatch(
getFieldSetupData({ AppId, CompId, BranchId, categoryId, Type: 'PE' })
).unwrap();
if (response?.data?.statusCode === 1) {
setSelectedAdditionalColumn(
response?.data?.data?.[0]?.ConfigDtl?.filter(
(c) => c.ConfigId && c.Access === 'Y'
)?.map((c) => c.ConfigName) || []
);
setTableFieldPreferences(
response?.data?.data?.[0]?.ConfigDtl?.map((c) => ({
value: c.ConfigId,
label: c.ConfigName,
access: c.Access,
})) || []
);
}
} catch (error) {
console.error('Error fetching field setup:', error);
}
};
2026-01-27 18:27:29 +05:30
const AddSupplierimg = async (subSupplierData) => {
const addSupplierData = {
CompId: CompId,
AppId: AppId,
BranchId: BranchId,
2026-01-27 18:27:29 +05:30
SuppName: subSupplierData?.SuppName,
SuppGSTIN: subSupplierData?.SuppGSTIN,
SuppPOC: subSupplierData?.SuppPOC,
SuppMobile: subSupplierData?.SuppMobile,
SuppEmail: subSupplierData?.SuppEmail,
Address1: subSupplierData?.Address1,
Address2: subSupplierData?.Address1,
Zip: subSupplierData?.Zip,
City: subSupplierData?.City,
State: subSupplierData?.State,
Dist: subSupplierData?.Dist,
CreatedBy: UserId,
};
let response = {};
response = await dispatch(postSupplier(addSupplierData)).unwrap();
if (response?.data?.statusCode == 1) {
setMessageType('success');
setMessageData(response?.data?.response);
let response1 = await dispatch(
getSupplierData({
CompId: CompId,
AppId: AppId,
BranchId: BranchId,
ActiveStatus: 'A',
})
).unwrap();
const suppliers = await getSuplaierApi({ dataReturn: true });
if (response1?.data?.statusCode == 1) {
setZipCodeData(null);
const mappedSupplierProducts = await findMatchingSuppliers(
subSupplierData?.SuppName,
subSupplierData?.SuppMobile,
suppliers
);
return mappedSupplierProducts;
}
} else {
setMessageType('error');
setMessageData(response?.data?.response);
return { mappedSupplierProducts: null, SuppId: null };
}
};
const handleExtractorApply = async (data) => {
setIsLoading(true);
setLoadingText('Processing extracted data...');
setExtractorData(data);
let supplierProducts = [];
let suppId = null;
if (data?.supplierSuggestions?.length > 0) {
setLoadingText('Loading supplier products...');
supplierProducts = await handleSupplierDropDownChange(
data?.supplierSuggestions?.[0]?.SuppId
);
suppId = data?.supplierSuggestions?.[0]?.SuppId;
}
if (data?.supplierSuggestions?.length === 0) {
setLoadingText('Creating new supplier...');
const Address = await getPincodeValues(data?.pincode);
let suppdata = {
SuppName: data?.name,
SuppMobile: data?.mobile,
SuppGSTIN: data?.gst,
Zip: data?.pincode,
City: Address?.City || '.',
State: Address?.State || '.',
Dist: Address?.Dist || '.',
Address1: data?.address || '.',
};
const { mappedSupplierProducts, SuppId } = await AddSupplierimg(suppdata);
suppId = SuppId;
supplierProducts = mappedSupplierProducts;
}
// check if the products from extractor exist in the supplier's products
setLoadingText('Matching products with supplier...');
let matchedProducts = data?.products?.map((imgItem) => {
const match = supplierProducts?.find(
(supp) =>
supp?.ProdName?.toLowerCase() === imgItem?.description?.toLowerCase()
);
return {
selectedProduct: match || null,
parsedData: imgItem,
matchFound: !!match,
selectedProdId: match?.ProdId || null,
};
});
const unmatchedProducts = matchedProducts.filter(
(item) => !item.matchFound
);
if (unmatchedProducts?.length > 0) {
setLoadingText('Mapping unmatched products...');
const response = await dispatch(
getSupplaierIdBasedProducts({ CompId, AppId, BranchId, SuppId: suppId })
).unwrap();
if (response?.data?.statusCode === 1) {
const allSupplierProducts = response?.data?.data || [];
const matchingProdIds = [];
const NewImageProducts = [];
console.log(unmatchedProducts, 'unmatchedProductsunmatchedProducts');
2026-01-27 18:27:29 +05:30
// check if the unmatched products exists in our products list
unmatchedProducts.forEach((item) => {
const match = allSupplierProducts?.[0]?.ProductDetails.find(
(supp) =>
supp?.ProdName?.toLowerCase() ===
item?.parsedData?.description?.toLowerCase()
);
if (match) {
matchingProdIds.push(match.ProdId);
} else {
2026-01-28 15:50:57 +05:30
NewImageProducts.push(item?.parsedData);
}
2026-01-27 18:27:29 +05:30
});
2026-01-28 15:50:57 +05:30
// Bulk upload new image products
if (NewImageProducts.length > 0) {
const newProductsData = NewImageProducts.map((product) => ({
AppId: AppId || 0,
CompId: CompId || '',
BranchId: BranchId || '',
CreatedBy: UserId || 0,
ProdName: product.description?.trim() || '',
ProdVariantName: '',
Size: 1 || '',
UOM: product.unit || '',
MRP: parseFloat(product.rate) || 0,
WhSalePrice: 0,
SellPrice: parseFloat(product.rate) || 0,
ProdCat: 'General',
ProdSubCat: '',
Brand: '',
AutoGenerateQr: '',
QRCode: '',
StockAvailable: 'No',
HSNCode: '',
PartNumber: '',
Rack: 0,
ManufDate: '',
ExpDate: '',
AvailableFrom: '',
AvailableTo: '',
ProdLogo: '',
OnePcsAvailable: 'No',
AutoGenerateSingleQr: 'No',
TaxId: 'NIL - 0%',
OnePcQR: '',
TokenAvailable: 'No',
OpeningQty: 0,
QtyBasedPrice: '',
InwardDate: '',
SuppId: suppId || '',
Reference: '',
ReceivedQty: 0,
AcceptedQty: 0,
RejectedQty: 0,
RejectionReason: '',
IssuedQty: 0,
BalanceQty: 0,
InwardPrice: 0,
OfferPrice: 0,
SpecialPrice: 0,
Cess: 0,
}));
const bulkResponse = await dispatch(
bulkpostdata({ ProdDetails: newProductsData })
).unwrap();
if (bulkResponse?.data?.statusCode === 1) {
// After successful bulk upload, check the condition again
const updatedResponse = await dispatch(
getSupplaierIdBasedProducts({
CompId,
AppId,
BranchId,
SuppId: suppId,
})
).unwrap();
if (updatedResponse?.data?.statusCode === 1) {
const updatedAllSupplierProducts =
updatedResponse?.data?.data || [];
const updatedMatchingProdIds = [];
unmatchedProducts.forEach((item) => {
const match =
updatedAllSupplierProducts?.[0]?.ProductDetails.find(
(supp) =>
supp?.ProdName?.toLowerCase() ===
item?.parsedData?.description?.toLowerCase()
);
if (match) {
updatedMatchingProdIds.push(match.ProdId);
2026-01-28 15:50:57 +05:30
}
});
if (updatedMatchingProdIds.length > 0) {
matchingProdIds.push(...updatedMatchingProdIds);
2026-01-28 15:50:57 +05:30
}
}
}
2026-01-28 15:50:57 +05:30
}
2026-01-27 18:27:29 +05:30
// if there is matching products, map them to the supplier
if (matchingProdIds.length > 0) {
setLoadingText('Mapping products to supplier...');
const payload = {
CompId,
BranchId,
AppId,
SuppId: suppId,
ProductDetails: matchingProdIds.map((item) => ({ ProdId: item })),
};
const response = await dispatch(
postSupplaierProducts(payload)
)?.unwrap();
if (response?.data?.statusCode === 1) {
const suppliers = await getSuplaierApi({ dataReturn: true });
const mappedSupplierProducts = await handleSupplierDropDownChange(
suppId,
null,
suppliers
);
// update matchedProducts with newly mapped products
matchedProducts = data?.products?.map((imgItem) => {
const match = mappedSupplierProducts?.find(
(supp) =>
supp?.ProdName?.toLowerCase() ===
imgItem?.description?.toLowerCase()
);
return {
selectedProduct: match || null,
parsedData: imgItem,
matchFound: !!match,
selectedProdId: match?.ProdId || null,
};
});
console.log(matchedProducts, 'matchedProductsmatchedProducts');
if (matchedProducts?.length > 0) {
setLoadingText('Adding products to purchase list...');
const newProducts = [];
for (const item of matchedProducts) {
if (item.matchFound) {
const newProduct = await ProductDropDownChange(
item?.selectedProduct.ProdVariantName,
2026-01-27 18:27:29 +05:30
item.selectedProdId,
{ label: item.selectedProduct.ProdName },
mappedSupplierProducts,
true,
item,
newProducts
);
if (newProduct) {
if (
!PurchaseData.some((p) => p.ProdId === newProduct.ProdId)
) {
newProducts.push(newProduct);
} else {
if (suppId !== selectedSupplierData) {
newProducts.push(newProduct);
} else {
setMessageType('error');
setMessageData(
`Product ${newProduct.ProdName} already added in the list.`
);
}
}
}
}
}
// if (newProducts.length > 0) {
const updatedPurchaseData = [...PurchaseData, ...newProducts];
const purchasedData =
selectedSupplierData === suppId
? updatedPurchaseData
: newProducts;
setPurchaseData(purchasedData);
if (purchasedData.length > 0) {
for (const item of purchasedData) {
// const index = purchasedData.indexOf(item);
2026-01-27 18:27:29 +05:30
const localId = item?.localId;
form.setFieldsValue({
...form.getFieldsValue(),
[`BalanceQty${localId}`]: item.BalanceQty,
[`ReceivedQty${localId}`]: item.ReceivedQty,
[`AcceptedQty${localId}`]: item.AcceptedQty,
[`RejectedQty${localId}`]: item?.RejectedQty,
[`InwardPrice${localId}`]: item?.InwardPrice,
[`Amount${localId}`]: item?.Amount,
});
}
}
// }
setMessageType('success');
setMessageData('Extracted data applied successfully!');
} else {
setMessageType('error');
setMessageData(
'No matching products found in your product list.'
);
console.log('Matching 1');
}
} else {
setMessageType('error');
setMessageData(
'Error Occurred while mapping products with supplier.'
);
}
} else {
setMessageType('error');
setMessageData('No matching products found in your product list.');
console.log('Matching 2');
}
} else {
setIsLoading(false);
setLoadingText('');
setMessageType('error');
setMessageData('Error Occurred while mapping products with supplier.');
return;
}
} else {
if (matchedProducts?.length > 0) {
setLoadingText('Adding products to purchase list...');
const newProducts = [];
for (const item of matchedProducts) {
if (item.matchFound) {
const newProduct = await ProductDropDownChange(
item?.selectedProduct?.ProdVariantName,
2026-01-27 18:27:29 +05:30
item.selectedProdId,
{ label: item.selectedProduct.ProdName },
supplierProducts,
true,
item,
newProducts
);
if (newProduct) {
if (!PurchaseData.some((p) => p.ProdId === newProduct.ProdId)) {
newProducts.push(newProduct);
} else {
if (suppId !== selectedSupplierData) {
newProducts.push(newProduct);
} else {
setMessageType('error');
setMessageData(
`Product ${newProduct.ProdName} already added in the list.`
);
}
}
}
}
}
// if (newProducts.length > 0) {
const updatedPurchaseData = [...PurchaseData, ...newProducts];
const purchasedData =
selectedSupplierData === suppId ? updatedPurchaseData : newProducts;
setPurchaseData(purchasedData);
if (purchasedData.length > 0) {
for (const item of purchasedData) {
// const index = purchasedData.indexOf(item);
2026-01-27 18:27:29 +05:30
const localId = item?.localId;
form.setFieldsValue({
...form.getFieldsValue(),
[`BalanceQty${localId}`]: item.BalanceQty,
[`ReceivedQty${localId}`]: item.ReceivedQty,
[`AcceptedQty${localId}`]: item.AcceptedQty,
[`RejectedQty${localId}`]: item?.RejectedQty,
[`InwardPrice${localId}`]: item?.InwardPrice,
[`Amount${localId}`]: item?.Amount,
});
}
}
// }
setMessageType('success');
setMessageData('Extracted data applied successfully!');
} else {
setMessageType('error');
setMessageData('No matching products found in your product list.');
console.log('Matching 3');
}
}
setExtractorModalVisible(false);
setIsLoading(false);
setLoadingText('');
};
const applicationApplicableFields = async () => {
const response = await dispatch(getCommonAppPreference(AppId)).unwrap();
if (response?.data?.statusCode === 1) {
const details = response?.data?.data?.[0]?.PreferenceDetails?.find(
(find) => find.PreferredCatName === 'Purchase Entry'
);
if (Object.keys(details || {})?.length > 0) {
if (details?.PreferenceCatDetails?.length > 0) {
setApplicationRestrictedFields({
DatesAndExpiry: details?.PreferenceCatDetails?.some(
(some) =>
some.PreferredSubCatName === 'Dates & Expiry' &&
some.PreferredStatus === 'Y'
),
BatchAndModelDetails: details?.PreferenceCatDetails?.some(
(some) =>
some.PreferredSubCatName === 'Batch & Model Details' &&
some.PreferredStatus === 'Y'
),
AmountPerPieceDetail: details?.PreferenceCatDetails?.some(
(some) =>
some.PreferredSubCatName === 'Amount Per Piece' &&
some.PreferredStatus === 'Y'
),
});
return;
}
}
setApplicationRestrictedFields({
DatesAndExpiry: null,
BatchAndModelDetails: null,
AmountPerPieceDetail: null,
});
}
};
const filteredOptions = useMemo(() => {
return (
purchaseOrderList?.map((data) => {
const purchaseInvoiceParts = data?.PurOrderId?.split('-');
return {
value: data?.PurOrderId,
label: purchaseInvoiceParts[purchaseInvoiceParts?.length - 1],
};
}) || []
);
}, [purchaseOrderList, selectedSupplierData]);
const getPurchaseType = async () => {
let res = await dispatch(getPurchaseTypeData()).unwrap();
if (res?.data?.statusCode === 1) {
setPurchaseTypeData(res?.data?.data);
let tempcash = res?.data?.data?.find(
(item) => item?.ConfigName?.toLowerCase() === 'cash'
);
setSelectedPurchaseType(tempcash?.ConfigId);
formRef.current?.setFieldsValue({ PaymentType: tempcash?.ConfigId });
} else {
setPurchaseTypeData(null);
}
};
const getPurchaseTaxType = async () => {
let res = await dispatch(getPurchaseTaxTypeData()).unwrap();
if (res?.data?.statusCode === 1) {
// setPurcTaxTypeData(res?.data?.data);
2026-01-27 18:27:29 +05:30
let tempniltax = res?.data?.data?.find(
(item) => item?.ConfigName?.toLowerCase() === 'inclusive'
);
setSelectedPurcTaxType(tempniltax?.ConfigId);
formRef.current?.setFieldsValue({ TaxType: tempniltax?.ConfigId });
}
// else {
// setPurcTaxTypeData(null);
// }
2026-01-27 18:27:29 +05:30
};
const pinCodeChange = async (e) => {
if (e.target.value?.length < 6) {
setZipCodeData(false);
return false;
}
await getPincodeValues(e?.target?.value);
};
const getPincodeValues = async (pinCode) => {
let response = '';
await fetch(`https://api.postalpincode.in/pincode/${pinCode}`)
.then((res) => res.text())
.then((text) => (response = JSON.parse(text)));
if (response[0]['Status'] === 'Success') {
setZipCodeData(true);
formSupplierRef.current?.setFieldsValue({
City: response[0]['PostOffice'][0]['Block'],
Dist: response[0]['PostOffice'][0]['District'],
State: response[0]['PostOffice'][0]['State'],
});
return {
City: response[0]['PostOffice'][0]['Block'],
Dist: response[0]['PostOffice'][0]['District'],
State: response[0]['PostOffice'][0]['State'],
};
} else {
setZipCodeData(false);
}
};
const handleMRP = () => {
formProductRef.current?.setFieldsValue({ SellPrice: null });
};
const handleSuppInvoiceNoChange = (e) => {
2026-01-28 15:50:57 +05:30
const value = e.target.value;
formRef.current?.setFieldsValue({ SuppInvoiceNo: value });
setExtractorData((prev) => (prev ? { ...prev, invoiceNo: value } : null));
2026-01-27 18:27:29 +05:30
};
const handleInvoiceTypeChange = (value) => {
setInvoiceType(value);
};
const handleInwardDateChange = (date) => {
setInwardDate(date);
};
// formRef?.current?.setFieldsValue({ SuppInvoiceNo: extractorData?.invoiceNo });
const handleSupplierDropDownChange = async (
SuppId,
option,
suppliers = null
) => {
let suppName = (suppliers || SupplierData)?.filter(
(item) => item.SuppId == SuppId
)?.[0]?.SuppName;
formRef?.current?.setFieldsValue({ CustSuppId: SuppId });
setSelectedSupplierData(SuppId);
const mappedSupplierProducts = await handleSupplierBasedProduct(SuppId);
formRef?.current?.resetFields(['VarProdId', 'ProdId']);
suppName === 'Self'
? setSelectedSupplierName(false)
: setSelectedSupplierName(true);
// setselectedProductVariantData(null);
// setSelectedProductData(null);
2026-01-27 18:27:29 +05:30
setSelectedProductName(null);
formRef?.current?.setFieldsValue({ ProdId: null });
setSearchText(null);
return mappedSupplierProducts;
};
const handlePurchaseTypeChange = (PurId) => {
formRef.current?.setFieldsValue({ PaymentType: PurId });
setSelectedPurchaseType(PurId);
};
const onSuppInvoiceDateChange = async (date, dateString) => {
if (dateString) {
const Date3 = moment(dateString, ['DD-MM-YYYY']).format(
'YYYY-MM-DDTHH:mm:ss'
);
formRef.current?.setFieldsValue({ SuppInvoiceDate: Date3 });
setSuppInvoiceDate(Date3);
} else if (dateString === '') {
setSuppInvoiceDate();
}
};
2026-01-27 18:27:29 +05:30
const onPurchaseDateChange = async (date, dateString) => {
if (dateString) {
const Date3 = moment(dateString, ['DD-MM-YYYY']).format(
'YYYY-MM-DDTHH:mm:ss'
);
formRef.current?.setFieldsValue({ DueDate: Date3 });
setPurchaseDate(Date3);
} else if (dateString === '') {
setPurchaseDate();
}
};
const ProductDropDownChange = async (
VariantName,
2026-01-27 18:27:29 +05:30
ProdId,
option,
productsList,
extractedProduct = false,
extractedItem,
existingProducts
) => {
setProductSearchText('');
setSelectedProductName(option?.label ? option?.label : '');
formRef?.current?.resetFields(['VarProdId']);
// setselectedProductVariantData(null);
2026-01-27 18:27:29 +05:30
if (true) {
2026-01-27 18:27:29 +05:30
formRef.current?.setFieldsValue({ ProdId: ProdId });
// await setSelectedProductData(ProdId);
if (
(existingProducts || PurchaseData)?.some(
(e) => e.ProdId == ProdId && e?.ProdVariantName == VariantName
)
) {
const existingProduct = (existingProducts || PurchaseData)?.find(
(e) => e.ProdId == ProdId && e?.ProdVariantName == VariantName
);
2026-01-27 18:27:29 +05:30
setMessageType('error');
setMessageData(
`Product "${existingProduct?.ProdName} - ${existingProduct?.ProdVariantName}" already exists`
);
2026-01-27 18:27:29 +05:30
return null;
} else {
2026-01-27 18:27:29 +05:30
let ProductData1 = (productsList || productData)?.find(
(e) => e.ProdId == ProdId && e?.ProdVariantName == VariantName
2026-01-27 18:27:29 +05:30
);
let DefaultVariant = ProductData1?.ProdVariantPriceDetails?.find(
(item) => item?.DefaultVariant === 'Y' && item?.ReceivedQty === 0
)?.DefaultVariant;
let ProduData2 = {
DefaultVariant: DefaultVariant,
ProdId: ProductData1?.ProdId,
MRP: ProductData1?.MRP,
ProdName: ProductData1?.ProdName,
UomName: ProductData1?.UomName,
SellPrice: ProductData1?.SellPrice,
StockAvailable: ProductData1?.StockAvailable,
OnePcsAvailable: ProductData1?.OnePcsAvailable,
OnePcsPrice: ProductData1?.OnePcsPrice,
NoOfPcs: ProductData1?.NoOfPcs,
2026-01-27 18:27:29 +05:30
TaxId: ProductData1?.TaxId,
TaxPercentage: ProductData1?.TaxPercentage,
ProdVariantName: ProductData1?.ProdVariantName,
2026-01-27 18:27:29 +05:30
};
const qty = extractedProduct
? parseFloat(extractedItem?.parsedData?.qty) || 0
: 0;
const acceptedQty = extractedProduct
? parseFloat(extractedItem?.parsedData?.qty) || 0
: 0;
const rate = extractedProduct
? parseFloat(extractedItem?.parsedData?.rate) || 0
: 0;
let tempPurData = {
...ProduData2,
PurchaseTax: parseFloat(extractedItem?.parsedData?.tax) || 0,
BalanceQty: qty,
InwardPrice: rate,
PurcDisc: 0,
Amount: isNaN(rate) || isNaN(acceptedQty) ? 0 : rate * acceptedQty,
WhSalePrice: 0,
ReceivedQty: qty,
AcceptedQty: acceptedQty,
RejectedQty: qty - acceptedQty,
OfferPrice: 0,
SpecialPrice: 0,
FreeItem: 0,
TaxAmt: 0,
TaxType: SelectedPurcTaxType ? SelectedPurcTaxType : 0,
PurcDiscType: 'P',
refImage: extractedItem?.parsedData?.image,
PurchaseHSNCode: extractedItem?.parsedData?.hsn || '',
localId: uuidv4(),
};
return tempPurData;
}
}
};
const handleProductSelect = async (value, option) => {
const [prodId, variantName] = value?.split('_');
const product = productData.find(
(p) => p.ProdId === prodId && p.ProdVariantName === variantName
);
if (!product) return;
// Check for duplicates
const exists = PurchaseData.some(
(p) => p.ProdId === prodId && p.ProdVariantName === variantName
);
if (exists) {
const existingProduct = PurchaseData.find(
(p) => p.ProdId === prodId && p.ProdVariantName === variantName
);
setMessageType('error');
setMessageData(
`Product "${existingProduct?.ProdName} - ${existingProduct?.ProdVariantName}" already exists`
);
return;
}
// Build product row
const defaultVariant = product.ProdVariantPriceDetails?.find(
(item) => item?.DefaultVariant === 'Y' && item?.ReceivedQty === 0
)?.DefaultVariant;
const newRow = {
ProdId: product.ProdId,
ProdName: product.ProdName,
ProdVariantName: product.ProdVariantName,
UomName: product.UomName,
MRP: product.MRP,
SellPrice: product.SellPrice,
OnePcsPrice: product.OnePcsPrice,
NoOfPcs: product.NoOfPcs,
StockAvailable: product.StockAvailable,
OnePcsAvailable: product.OnePcsAvailable,
TaxId: product.TaxId,
TaxPercentage: product.TaxPercentage,
DefaultVariant: defaultVariant,
PurchaseTax: 0,
BalanceQty: 0,
InwardPrice: 0,
ReceivedQty: 0,
AcceptedQty: 0,
RejectedQty: 0,
Amount: 0,
WhSalePrice: 0,
OfferPrice: 0,
SpecialPrice: 0,
FreeItem: 0,
TaxAmt: 0,
PurcDisc: 0,
PurcDiscType: 'P',
PurchaseHSNCode: product.PurchaseHSNCode || '',
refImage: product?.image || '',
localId: uuidv4(),
};
// ✅ Single state update: update PurchaseData + form fields together
setPurchaseData((prev) => [newRow, ...prev]);
form?.setFieldsValue({
[`SellPrice${newRow.localId}`]: newRow.SellPrice,
[`PurchaseTax${newRow.localId}`]: newRow.PurchaseTax,
ProdId: prodId,
});
// Reset search text
setProductSearchText('');
setSelectedProductName(option?.label || '');
};
2026-01-27 18:27:29 +05:30
const debouncedScannerSearch = useCallback(
debounce(async (value) => {
const qr = value?.trim();
const matchedProduct = productData.find(
(p) => p.QRCode?.toLowerCase() === qr?.toLowerCase()
);
if (matchedProduct) {
let productName = {};
productName.label = `${matchedProduct?.ProdName} (${matchedProduct.Size} ${matchedProduct.UomName})${matchedProduct?.BrandName ? ` - ${matchedProduct?.BrandName}` : ''}`;
const newProduct = await ProductDropDownChange(
matchedProduct?.ProdVariantName,
2026-01-27 18:27:29 +05:30
matchedProduct.ProdId,
productName
);
if (newProduct) {
setPurchaseData([newProduct, ...PurchaseData]);
}
if (matchedProduct?.ProdVariantPriceDetails?.length < 2) {
setSelectedProductName('');
// setSelectedProductData(null);
2026-01-27 18:27:29 +05:30
}
} else {
setMessageType('error');
setMessageData('The Scanned Product not Available in Product List');
}
}, 300),
[productData]
);
// Clean up debounce on unmount
2026-01-27 18:27:29 +05:30
const handleProductSearch = (value) => {
if (scanner) {
debouncedScannerSearch(value);
}
setProductSearchText(value);
setSelectedProductName(value);
};
const handleScanSearch = () => {
if (scanner === false) {
setMessageType('success');
setMessageData('Search by QrCode/BarCode Enabled');
} else {
setMessageType('success');
setMessageData('Search by QrCode/BarCode Disabled');
}
setScanner((prev) => !prev);
setSelectedProductName(null);
// setselectedProductVariantData(null);
// setVariants(null);
2026-01-27 18:27:29 +05:30
formRef?.current?.setFieldsValue({
ProdId: null,
VarProdId: null,
});
};
const addSupplier = () => {
setOpenSupplierModel(true);
};
const handleSupplier = () => {
setOpenSupplierModel(false);
};
const submitSupplier = async () => {
const subSupplierData = await formSupplierRef?.current?.validateFields();
const addSupplierData = {
CompId: CompId,
AppId: AppId,
BranchId: BranchId,
SuppName: subSupplierData?.SuppName,
SuppGSTIN: subSupplierData?.SuppGSTIN,
SuppPOC: subSupplierData?.SuppPOC,
SuppMobile: subSupplierData?.SuppMobile,
SuppEmail: subSupplierData?.SuppEmail,
Address1: subSupplierData?.Address1,
Address2: subSupplierData?.Address1,
Zip: subSupplierData?.Zip,
City: subSupplierData?.City,
State: subSupplierData?.State,
Dist: subSupplierData?.Dist,
SuppPOCMobile: subSupplierData?.SuppPOCMobile,
SuppPOCEmail: subSupplierData?.SuppPOCEmail,
DeliveryPerformance: subSupplierData?.DeliveryPerformance,
DiscountLevel: subSupplierData?.DiscountLevel,
QualityofSupp: subSupplierData?.QualityofSupp,
2026-01-27 18:27:29 +05:30
CreatedBy: UserId,
};
let response = {};
response = await dispatch(postSupplier(addSupplierData)).unwrap();
if (response?.data?.statusCode == 1) {
setMessageType('success');
setMessageData(response?.data?.response);
let response1 = await dispatch(
getSupplierData({
CompId: CompId,
AppId: AppId,
BranchId: BranchId,
ActiveStatus: 'A',
})
).unwrap();
if (response1?.data?.statusCode == 1) {
setOpenSupplierModel(false);
}
formSupplierRef?.current?.resetFields();
setZipCodeData(null);
} else {
setMessageType('error');
setMessageData(response?.data?.response);
formSupplierRef?.current?.resetFields();
}
};
useEffect(() => {
if (!editingKey) return;
const record = PurchaseData.find((item) => item.localId === editingKey);
if (!record) return;
const localId = record.localId;
const values = Delete
? {
[`BalanceQty${localId}`]: undefined,
[`ReceivedQty${localId}`]: undefined,
[`AcceptedQty${localId}`]: undefined,
[`RejectedQty${localId}`]: undefined,
[`Amount${localId}`]: undefined,
[`Freeqty${localId}`]: undefined,
[`InwardPrice${localId}`]: undefined,
[`MRP${localId}`]: undefined,
[`PurcDisc${localId}`]: undefined,
[`SellPrice${localId}`]: undefined,
[`WhSalePrice${localId}`]: undefined,
[`offerSalePrice${localId}`]: undefined,
[`splSalePrice${localId}`]: undefined,
[`NumberofPieceinside${localId}`]: undefined,
[`AmountPerPiece${localId}`]: undefined,
[`PurcDiscType${localId}`]: undefined,
[`ManufDate${localId}`]: undefined,
[`ExpDate${localId}`]: undefined,
[`OnePcsPrice${localId}`]: undefined,
[`NoOfPcs${localId}`]: undefined,
}
: {
[`BalanceQty${localId}`]: record.BalanceQty,
[`ReceivedQty${localId}`]: record.ReceivedQty,
[`AcceptedQty${localId}`]: record.AcceptedQty,
[`RejectedQty${localId}`]: record.RejectedQty,
[`Freeqty${localId}`]: record.FreeItem,
[`MRP${localId}`]: record.MRP,
[`ManufDate${localId}`]: record.ManufDate,
[`ExpDate${localId}`]: record.ExpDate,
[`SellPrice${localId}`]: record.SellPrice,
[`WhSalePrice${localId}`]: record.WhSalePrice,
[`PurcDiscType${localId}`]: record.PurcDiscType,
[`Amount${localId}`]: record.Amount,
[`InwardPrice${localId}`]: record.InwardPrice,
[`PurcDisc${localId}`]: record.PurcDisc,
[`offerSalePrice${localId}`]: record.OfferPrice,
[`splSalePrice${localId}`]: record.SpecialPrice,
[`OnePcsPrice${localId}`]: record.OnePcsPrice,
[`NoOfPcs${localId}`]: record.NoOfPcs,
};
2026-01-27 18:27:29 +05:30
if (!Delete && applicationRestrictedFields.BatchAndModelDetails) {
values[`BatchRef${localId}`] = record.BatchRef;
2026-01-27 18:27:29 +05:30
}
form.setFieldsValue(values);
setDelete(false);
}, [editingKey]);
2026-01-27 18:27:29 +05:30
const save = async (localId) => {
2026-01-27 18:27:29 +05:30
try {
const row = await form.validateFields();
2026-01-27 18:27:29 +05:30
const modifiedObject = {};
2026-01-27 18:27:29 +05:30
for (const key in row) {
// remove localId suffix from field names
if (key.endsWith(localId)) {
const newKey = key?.replace(localId, '');
modifiedObject[newKey] = row[key];
}
2026-01-27 18:27:29 +05:30
}
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === localId ? { ...item, ...modifiedObject } : item
)
);
setEditingKey('');
2026-01-27 18:27:29 +05:30
} catch (err) {
console.error('Save failed:', err);
}
};
const handleKeyPress = async (e, record) => {
2026-01-27 18:27:29 +05:30
if (e.key === 'Enter') {
try {
await form.validateFields();
save(record.localId);
2026-01-27 18:27:29 +05:30
} catch (error) {
console.error('Save failed:', error);
}
}
};
const formatDateForDisplay = (dateString) => {
if (!dateString) return '';
return moment(dateString).format('DD-MMM-YY');
};
const sellPriceValidator = useCallback((_, value) => {
if (value <= 0) return Promise.reject('Invalid price');
return Promise.resolve();
}, []);
const handleDecimalInput = (e) => {
let value = e.target.value?.replace(/[^0-9.]/g, '');
if (value.startsWith('.')) value = '0' + value;
const parts = value.split('.');
e.target.value =
parts?.length > 2 ? `${parts[0]}.${parts.slice(1).join('')}` : value;
};
const updateRowValues = (localId, updates) => {
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === localId ? { ...item, ...updates } : item
)
);
const formUpdates = {};
Object.keys(updates).forEach((key) => {
formUpdates[`${key}${localId}`] = updates[key];
});
form.setFieldsValue(formUpdates);
2026-01-27 18:27:29 +05:30
};
const updateRowValue = (localId, key, value) => {
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === localId ? { ...item, [key]: value } : item
)
);
form.setFieldsValue({
[`${key}${localId}`]: value,
});
};
const columns = useMemo(() => {
const baseColumns = [
{
title: 'SL.NO',
align: 'center',
key: 'sno',
width: 50,
render: (text, object, index) => (
<a style={{ color: 'black' }}>{index + 1}</a>
),
2026-01-27 18:27:29 +05:30
},
{
title: 'Name',
dataIndex: 'ProdName',
key: 'ProdName',
align: 'left',
width: 150,
render: (text, record, index) => (
<a style={{ color: 'black' }}>{record?.ProdName}</a>
),
},
{
title: 'Variant',
dataIndex: 'ProdVariantName',
key: 'ProdVariantName',
align: 'center',
width: 100,
render: (text, record, index) => (
<a style={{ color: 'black' }}>{record?.ProdVariantName}</a>
),
},
{
title: 'Uom',
dataIndex: 'UomName',
key: 'UOMName',
align: 'center',
width: 100,
},
{
title: 'Qty',
dataIndex: 'BalanceQty',
key: 'BalanceQty',
align: 'center',
editable: true,
width: 100,
render: (text, record, index) => {
return (
2026-01-27 18:27:29 +05:30
<Input
// autoFocus
value={editingQty[record.localId] ?? record.BalanceQty}
onChange={(e) => handleQtyTyping(e, record)}
onBlur={() => commitQtyChange(record)}
onPressEnter={() => commitQtyChange(record)}
2026-01-27 18:27:29 +05:30
inputMode="decimal"
onInput={(e) => {
let v = e.target.value.replace(/[^0-9.]/g, '');
if (v.startsWith('.')) v = '0' + v;
const parts = v.split('.');
2026-01-27 18:27:29 +05:30
e.target.value =
parts.length > 2
? `${parts[0]}.${parts.slice(1).join('')}`
: v;
2026-01-27 18:27:29 +05:30
}}
/>
);
},
2026-01-27 18:27:29 +05:30
},
{
title: 'Pur Rate / unit',
dataIndex: 'InwardPrice',
key: 'InwardPrice',
width: 120,
editable: true,
align: 'center',
render: (_, record) => {
const editable = true;
return editable ? (
2026-01-27 18:27:29 +05:30
<Input
value={record.InwardPrice ?? 0}
maxLength={10}
2026-01-27 18:27:29 +05:30
inputMode="decimal"
onChange={(e) => handlePurrateTyping(e, record)}
2026-01-27 18:27:29 +05:30
/>
) : (
<div
// onClick={() => edit(record)} // ✅ THIS is required
style={{ cursor: 'pointer' }}
>
{record.InwardPrice || 0}
</div>
);
},
2026-01-27 18:27:29 +05:30
},
{
title: (
<Tooltip title="GST (CGST + SGST or IGST)" placement="top">
Tax (%)
</Tooltip>
),
dataIndex: 'PurchaseTax',
key: 'PurchaseTax',
editable: true,
align: 'center',
width: 100,
render: (_, record) => (
<Input
value={record.PurchaseTax ?? ''}
inputMode="decimal"
onChange={(e) => handleTaxTyping(e, record)}
onBlur={() => debouncedTaxCalc(record.PurchaseTax, record)}
/>
),
},
{
title: 'Amount',
dataIndex: 'Amount',
key: 'Amount',
editable: true,
align: 'right',
width: 100,
},
...(selectedAdditionalColumn.includes('Mrp')
? [
{
title: 'MRP',
dataIndex: 'MRP',
key: 'MRP',
editable: true,
width: 100,
align: 'center',
render: (_, record) => {
return (
<Input
// onClick={() => edit(record)}
placeholder="MRP"
value={record?.MRP ?? ''}
maxLength={10}
inputMode="decimal"
onChange={(e) => handleMRPChange(e, record)}
onBlur={(e) => handleMRPBlur(e, record)}
onPressEnter={(e) => handleKeyPress(e, record)}
/>
);
},
},
]
: []),
{
title: (
<Tooltip title="Selling price per unit" placement="top">
Selling Price
</Tooltip>
),
dataIndex: 'SellPrice',
key: 'SellPrice',
width: 100,
editable: true,
align: 'center',
render: (_, record) => {
return (
2026-01-27 18:27:29 +05:30
<Input
// onClick={() => edit(record)}
onFocus={(e) => e.stopPropagation()}
value={record?.SellPrice ?? ''}
maxLength={10}
2026-01-27 18:27:29 +05:30
inputMode="decimal"
onChange={(e) => handleSellPriceChange(e, record)}
onBlur={(e) => handleSellPriceBlur(e, record)}
onPressEnter={(e) => handleKeyPress(e, record)}
onInput={handleDecimalInput}
2026-01-27 18:27:29 +05:30
/>
);
},
2026-01-27 18:27:29 +05:30
},
...(selectedAdditionalColumn.includes('Amount Per Piece')
? [
{
title: 'Amount per piece',
dataIndex: 'OnePcsPrice',
key: 'OnePcsPrice',
width: 70,
editable: true,
render: (_, record, index) => {
return (
<Input
// onClick={() => edit(record)}
onFocus={(e) => e.stopPropagation()}
disabled={record?.OnePcsAvailable === 'N'}
placeholder="OnePcsPrice"
value={record?.OnePcsPrice ?? ''}
maxLength={10}
inputMode="decimal"
onChange={(e) => handleOnePcsChange(e, record)}
onBlur={(e) => handleOnePcsBlur(e, record)}
onPressEnter={() => handleKeyPress(e, record)}
onInput={(e) => {
let v = e.target.value.replace(/[^0-9.]/g, '');
if (v.startsWith('.')) v = '0' + v;
const parts = v.split('.');
e.target.value =
parts.length > 2
? `${parts[0]}.${parts.slice(1).join('')}`
: v;
}}
/>
);
},
},
]
: []),
...(selectedAdditionalColumn.includes('Amount Per Piece')
? [
{
title: 'Number of piece',
dataIndex: 'NoOfPcs',
key: 'NoOfPcs',
width: 70,
editable: true,
render: (_, record, index) => {
return (
<Input
// onClick={() => edit(record)}
onFocus={(e) => e.stopPropagation()}
disabled={record?.OnePcsAvailable === 'N'}
placeholder="NoOfPcs"
value={record.NoOfPcs ?? ''}
maxLength={5}
inputMode="numeric"
onChange={(e) => handleNoOfPcsChange(e, record)}
onBlur={(e) => handleNoOfPcsBlur(e, record)}
onPressEnter={() => handleKeyPress(e, record)}
onInput={(e) => {
// remove invalid characters & leading zeros
let val = e.target.value.replace(/[^0-9]/g, '');
val = val.replace(/^0+(?=\d)/, ''); // removes leading zeros but keeps single 0
e.target.value = val;
}}
/>
);
},
},
]
: []),
...(selectedAdditionalColumn.includes('Wholesale Price')
? [
{
title: 'Wholesale price',
dataIndex: 'WhSalePrice',
key: 'WhSalePrice',
width: 70,
editable: true,
render: (_, record) => {
return (
<Input
// onClick={() => edit(record)}
onFocus={(e) => e.stopPropagation()}
placeholder="WhSalePrice"
value={record.WhSalePrice ?? ''}
inputMode="decimal"
maxLength={10}
onChange={(e) => handleWhSalePriceChange(e, record)}
onBlur={(e) => handleWhSalePriceBlur(e, record)}
onPressEnter={() => handleKeyPress(record)}
onInput={(e) => {
let val = e.target.value.replace(/[^0-9.]/g, ''); // remove invalid chars
if (val.startsWith('.')) val = '0' + val; // leading dot fix
const parts = val.split('.');
if (parts.length > 2)
val = `${parts[0]}.${parts.slice(1).join('')}`; // max one dot
val = val.replace(/^0+(?=\d)/, ''); // remove leading zeros
e.target.value = val;
}}
/>
);
},
},
]
: []),
...(selectedAdditionalColumn.includes('Manufacture Date')
? [
{
title: 'Manuf.Date',
dataIndex: 'ManufDate',
key: 'ManufDate',
editable: true,
render: (text, record, index) => {
return (
<DatePicProd
canSelectPast={true}
cancelFuture={true}
valueData={record.ManufDate}
onChange={(date, dateString) => {
// Format date to your preferred format
const formattedDate = dateString
? moment(dateString, ['DD-MM-YYYY']).format(
'YYYY-MM-DDTHH:mm:ss'
)
: undefined;
// Update the row data immutably
const newData = PurchaseData.map((item) =>
item.localId === record.localId
? { ...item, ManufDate: formattedDate }
: item
);
setPurchaseData(newData);
}}
/>
);
},
},
]
: []),
...(selectedAdditionalColumn.includes('Expiry Date')
? [
{
title: 'Exp.Date',
dataIndex: 'ExpDate',
key: 'ExpDate',
editable: true,
render: (text, record, index) => {
return (
<DatePicProd
canSelectPast={false}
cancelFuture={false}
valueData={record.ExpDate}
onChange={(date, dateString) =>
handleExpDateChange(dateString, record)
}
/>
);
},
},
]
: []),
...(selectedAdditionalColumn.includes('Hsn')
? [
{
title: 'Hsn',
dataIndex: 'Hsn',
key: 'Hsn',
editable: true,
// width: 130,
render: (text, record, index) => {
return (
<Input
placeholder="HSN code"
value={record.PurchaseHSNCode || ''}
// onClick={() => edit(record)}
onFocus={(e) => e.stopPropagation()}
onPressEnter={() => handleKeyPress(record)}
onChange={(e) => {
const value = e.target.value;
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, PurchaseHSNCode: value }
: item
)
);
}}
/>
);
},
},
]
: []),
...(selectedAdditionalColumn.includes('Model No')
? [
{
title: 'Model',
dataIndex: 'Model',
key: 'Model',
editable: true,
render: (text, record, index) => {
return (
<Input
value={record.ModelNumber || ''}
placeholder="Model No"
// autoFocus={isEditing(record)} // autofocus only if this row is being edited
// onClick={() => edit(record)}
onFocus={(e) => e.stopPropagation()}
onPressEnter={() => handleKeyPress(record)}
onChange={(e) => {
const value = e.target.value;
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, ModelNumber: value }
: item
)
);
}}
/>
);
},
},
]
: []),
...(selectedAdditionalColumn.includes('Batch No')
? [
{
title: 'Batch',
dataIndex: 'Batch',
key: 'Batch',
editable: true,
render: (text, record, index) => {
return (
<Input
value={record.BatchRef || ''}
placeholder="Batch No"
// autoFocus={isEditing(record, index)}
// onClick={() => edit(record)}
onFocus={(e) => e.stopPropagation()}
onPressEnter={() => handleKeyPress(record, index)}
onChange={(e) => {
const value = e.target.value;
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, BatchRef: value }
: item
)
);
}}
/>
);
},
},
]
: []),
...(selectedAdditionalColumn.includes('Rejected Qty')
? [
{
title: 'Rejected Qty',
dataIndex: 'RejectedQty',
key: 'RejectedQty',
editable: true,
render: (text, record, index) => {
return (
<Input
value={record.RejectedQty ?? 0}
placeholder="Rejected Qty"
// autoFocus={isEditing(record, index)}
// onClick={() => edit(record)}
onFocus={(e) => e.stopPropagation()}
onPressEnter={() => handleKeyPress(record, index)}
onChange={(e) => {
let inputValue = e.target.value.replace(/[^0-9.]/g, ''); // allow only numbers
if (inputValue.startsWith('0') && inputValue.length > 1) {
inputValue = inputValue.replace(/^0+/, ''); // remove leading zeros
}
const rejectedQty = parseFloat(inputValue) || 0;
const receivedQty = parseFloat(record.ReceivedQty) || 0;
// validation
if (rejectedQty >= receivedQty) {
setMessageType('error');
setMessageData(
'Rejected Qty cannot be greater than or equal to Received Qty'
);
return;
}
const acceptedQty = receivedQty - rejectedQty;
const price = parseFloat(record.InwardPrice) || 0;
const amount = acceptedQty * price;
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? {
...item,
RejectedQty: rejectedQty,
AcceptedQty: acceptedQty,
Amount: amount,
}
: item
)
);
}}
/>
);
},
},
]
: []),
...(selectedAdditionalColumn.includes('Free Qty')
? [
{
title: 'Free Qty',
dataIndex: 'Freeqty',
key: 'Freeqty',
editable: true,
render: (text, record) => {
return (
<Input
value={record.Freeqty ?? ''}
placeholder="Free Qty"
// autoFocus={isEditing(record)}
inputMode="numeric"
// onClick={() => edit(record)}
onFocus={(e) => e.stopPropagation()}
onPressEnter={() => handleKeyPress(record)}
onChange={(e) => {
let value = e.target.value.replace(/[^0-9]/g, ''); // only digits
if (value.startsWith('0') && value.length > 1) {
value = value.replace(/^0+/, ''); // remove leading zeros
}
updateRowValue(record.localId, 'Freeqty', value);
}}
/>
);
},
},
]
: []),
...(selectedAdditionalColumn.includes('IMEI')
? [
{
title: 'IMEI',
dataIndex: 'AdditionalInfo',
key: 'AdditionalInfo',
width: 120,
align: 'center',
render: (text, record, index) => {
const receivedQty = parseFloat(record?.ReceivedQty) || 0;
const isClickable = receivedQty > 0;
return (
<IoAddCircleSharp
style={{
cursor: isClickable ? 'pointer' : 'not-allowed',
color: isClickable ? '#1890ff' : '#d9d9d9',
fontSize: 20,
}}
onClick={() => {
if (!isClickable) {
setMessageType('error');
setMessageData('Please Enter Qty');
return;
}
setSelectedRowIndex(index);
handleModelDataOpen(record);
}}
className="shape-preview"
/>
);
},
},
]
: []),
{
title: 'Action',
dataIndex: 'Action',
key: 'Action',
align: 'center',
width: 80,
render: (_, record, index) => (
<a
onClick={(e) => {
e.stopPropagation();
2026-01-27 18:27:29 +05:30
}}
>
<DeleteFilled
style={{
color: '#FF4D4F',
}}
onClick={() => statusFormatters(record)}
/>
</a>
),
},
];
return baseColumns;
}, [editingKey, editingQty, selectedAdditionalColumn]);
2026-01-27 18:27:29 +05:30
const statusFormatters = (record) => {
2026-01-27 18:27:29 +05:30
const localId = record?.localId;
setPurchaseData((prev) => prev.filter((item) => item.localId !== localId));
const formValues = form.getFieldsValue();
const keysToRemove = Object.keys(formValues).filter((key) =>
key.endsWith(localId)
);
if (keysToRemove.length) {
const newValues = { ...formValues };
keysToRemove.forEach((key) => {
newValues[key] = undefined;
});
form.setFieldsValue(newValues);
}
// 3⃣ Clear other single fields
formRef?.current?.setFieldsValue({
ProdId: null,
});
setDelete(true); // only if absolutely necessary
};
const handleQtyTyping = (e, record) => {
let value = e.target.value;
// allow only numbers + decimal
if (!/^\d*\.?\d*$/.test(value)) return;
// 🔥 remove leading zero (but keep "0." valid)
if (value.length > 1 && value.startsWith('0') && !value.startsWith('0.')) {
value = value.replace(/^0+/, '');
2026-01-27 18:27:29 +05:30
}
setEditingQty((prev) => ({
...prev,
[record.localId]: value,
}));
};
const commitQtyChange = (record) => {
const { localId } = record;
const qty = Number(editingQty[localId]) || 0;
2026-01-27 18:27:29 +05:30
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === localId
? {
...item,
BalanceQty: qty,
ReceivedQty: qty,
AcceptedQty: qty,
RejectedQty: 0,
PurcDisc: 0,
Amount:
isNaN(item.InwardPrice) || isNaN(qty)
? 0
: item.InwardPrice * qty,
ProductIdentifierDtls: [],
}
: item
)
);
2026-01-27 18:27:29 +05:30
form.setFieldsValue({
[`BalanceQty${localId}`]: qty,
[`ReceivedQty${localId}`]: qty,
[`AcceptedQty${localId}`]: qty,
[`RejectedQty${localId}`]: 0,
[`PurcDisc${localId}`]: 0,
2026-01-27 18:27:29 +05:30
});
};
useEffect(() => {
const id = setTimeout(() => {
const total = PurchaseData.reduce(
(acc, d) => acc + Number(d.Amount || 0),
0
);
setPaymentAmount(total);
formRef?.current?.setFieldsValue({ PaymentAmount: total });
}, 150);
return () => clearTimeout(id);
}, [PurchaseData]);
const handleMRPChange = (e, record) => {
const value = e.target.value ?? '';
if (!/^\d*\.?\d*$/.test(value)) return;
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? {
...item,
MRP: value,
SellPrice: value,
}
: item
)
);
};
const handleMRPBlur = (e, record) => {
const value = e.target.value ?? '';
const normalized = value === '' ? '' : String(Number(value));
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? {
...item,
MRP: normalized,
SellPrice: normalized,
}
: item
)
);
};
const handlePurrateChange = useCallback((e, record) => {
let value = e?.target?.value ?? '';
// allow decimals only
if (!/^\d*\.?\d*$/.test(value)) return;
// 🔥 remove leading zero when user starts typing
if (value.length > 1 && value.startsWith('0') && !value.startsWith('0.')) {
value = value.replace(/^0+/, '');
2026-01-27 18:27:29 +05:30
}
setPurchaseData((prev) =>
prev.map((item) => {
if (item.localId !== record.localId) return item;
const qty = Number(item.AcceptedQty) || 0;
const rateNum = Number(value || 0);
const amount = rateNum * qty;
let discountAmt = 0;
if (item.PurcDiscType === 'P') {
discountAmt = (amount * Number(item.PurcDisc || 0)) / 100;
} else {
discountAmt = Number(item.PurcDisc || 0);
}
const taxPercent = Number(item.TaxPercentage || 0);
const taxAmt =
taxPercent > 0
? ((amount - discountAmt) * taxPercent) / (100 + taxPercent)
: 0;
return {
...item,
InwardPrice: value, // 👈 clean value
Amount: amount,
TaxAmt: +taxAmt.toFixed(2),
};
})
);
}, []);
const handlePurrateTyping = (e, record) => {
let value = e?.target?.value ?? '';
if (!/^\d*\.?\d*$/.test(value)) return;
if (value.length > 1 && value.startsWith('0') && !value.startsWith('0.')) {
value = value.replace(/^0+/, '');
}
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId ? { ...item, InwardPrice: value } : item
)
);
debouncedCalc(value, record);
2026-01-27 18:27:29 +05:30
};
const calculateRate = useCallback((value, record) => {
setPurchaseData((prev) =>
prev.map((item) => {
if (item.localId !== record.localId) return item;
const qty = Number(item.AcceptedQty) || 0;
const rateNum = Number(value || 0);
const amount = rateNum * qty;
2026-01-27 18:27:29 +05:30
let discountAmt = 0;
if (item.PurcDiscType === 'P') {
discountAmt = (amount * Number(item.PurcDisc || 0)) / 100;
} else {
discountAmt = Number(item.PurcDisc || 0);
}
const taxPercent = Number(item.TaxPercentage || 0);
const taxAmt =
taxPercent > 0
? ((amount - discountAmt) * taxPercent) / (100 + taxPercent)
: 0;
return {
...item,
Amount: amount,
TaxAmt: +taxAmt.toFixed(2),
};
})
);
}, []);
const debouncedCalc = useDebounce(calculateRate, 300);
const handleSellPriceChange = (e, record) => {
const value = e?.target?.value ?? '';
// allow smooth typing
if (!/^\d*\.?\d*$/.test(value)) return;
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId ? { ...item, SellPrice: value } : item
)
);
};
const handleSellPriceBlur = (e, record) => {
const value = e?.target?.value ?? '';
const normalized = value === '' ? '' : String(Number(value));
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, SellPrice: normalized }
: item
)
);
};
const handleOnePcsChange = (e, record) => {
const value = e?.target?.value ?? '';
if (!/^\d*\.?\d*$/.test(value)) return;
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId ? { ...item, OnePcsPrice: value } : item
)
);
};
const handleOnePcsBlur = (e, record) => {
const value = e?.target?.value ?? '';
const normalized = value === '' ? '' : String(Number(value));
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, OnePcsPrice: normalized }
: item
)
);
};
const handleNoOfPcsChange = (e, record) => {
const value = e?.target?.value ?? '';
// only whole numbers
if (!/^\d*$/.test(value)) return;
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId ? { ...item, NoOfPcs: value } : item
)
);
2026-01-27 18:27:29 +05:30
};
const handleNoOfPcsBlur = (e, record) => {
const value = e?.target?.value ?? '';
const normalized = value === '' ? '' : String(parseInt(value, 10));
2026-01-27 18:27:29 +05:30
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, NoOfPcs: normalized }
: item
)
);
2026-01-27 18:27:29 +05:30
};
const handleWhSalePriceChange = (e, record) => {
const value = e?.target?.value ?? '';
2026-01-27 18:27:29 +05:30
if (!/^\d*\.?\d*$/.test(value)) return;
2026-01-27 18:27:29 +05:30
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId ? { ...item, WhSalePrice: value } : item
)
);
2026-01-27 18:27:29 +05:30
};
const handleWhSalePriceBlur = (e, record) => {
const value = e?.target?.value ?? '';
const normalized = value === '' ? '' : String(Number(value));
2026-01-27 18:27:29 +05:30
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, WhSalePrice: normalized }
: item
)
);
2026-01-27 18:27:29 +05:30
};
const handleManufDateChange = (dateString, record) => {
const formattedDate =
!dateString || dateString === ''
? undefined
: moment(dateString, ['DD-MM-YYYY']).format('YYYY-MM-DDTHH:mm:ss');
2026-01-27 18:27:29 +05:30
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, ManufDate: formattedDate }
: item
)
);
2026-01-27 18:27:29 +05:30
};
const handleExpDateChange = (dateString, record) => {
const formattedDate =
!dateString || dateString === ''
? undefined
: moment(dateString, ['DD-MM-YYYY']).format('YYYY-MM-DDTHH:mm:ss');
2026-01-27 18:27:29 +05:30
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, ExpDate: formattedDate }
: item
)
);
2026-01-27 18:27:29 +05:30
};
const handleHSNChange = (value, record) => {
// optional: digits only (HSN is numeric)
const cleanValue = value.replace(/\D/g, '');
2026-01-27 18:27:29 +05:30
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, PurchaseHSNCode: cleanValue }
: item
)
);
2026-01-27 18:27:29 +05:30
};
const handleTaxTyping = (e, record) => {
let value = e?.target?.value ?? '';
2026-01-27 18:27:29 +05:30
// allow decimal typing only
if (!/^\d*\.?\d*$/.test(value)) return;
2026-01-27 18:27:29 +05:30
// fix ".5" → "0.5"
if (value.startsWith('.')) value = '0' + value;
2026-01-27 18:27:29 +05:30
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, PurchaseTax: value } // keep STRING
: item
)
);
2026-01-27 18:27:29 +05:30
debouncedTaxCalc(value, record);
2026-01-27 18:27:29 +05:30
};
const calculateTax = useCallback((value, record) => {
setPurchaseData((prev) =>
prev.map((item) => {
if (item.localId !== record.localId) return item;
const amount = Number(item.Amount) || 0;
const taxPercent = Number(value || 0);
const taxAmt =
taxPercent > 0 ? (amount * taxPercent) / (100 + taxPercent) : 0;
return {
...item,
TaxPercentage: taxPercent,
TaxAmt: +taxAmt.toFixed(2),
};
})
);
}, []);
const debouncedTaxCalc = useDebounce(calculateTax, 300);
// const handleTaxChange = (e, record) => {
// const { localId } = record;
// const value = e?.target?.value ?? '';
// // ✅ allow natural typing
// if (!/^\d*\.?\d*$/.test(value)) return;
// setPurchaseData((prev) =>
// prev.map((item) =>
// item.localId === localId
// ? {
// ...item,
// PurchaseTax: value, // 👈 KEEP STRING
// }
// : item
// )
// );
// // 👇 keep form in sync (string only)
// form.setFieldsValue({
// [`PurchaseTax${localId}`]: value,
// });
// };
const handleTaxBlur = (e, record) => {
const value = e?.target?.value ?? '';
const normalized = value === '' ? '' : String(Number(value));
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, PurchaseTax: normalized }
: item
)
);
2026-01-27 18:27:29 +05:30
form.setFieldsValue({
[`PurchaseTax${record.localId}`]: normalized,
});
2026-01-27 18:27:29 +05:30
};
const purchaseStatusChange = (value) => {
setPurchaseStatus(value);
2026-01-27 18:27:29 +05:30
};
const SupplierOpenfun = (value) => {
setSupplierOpen(value);
};
const getUniqueImageArray = (data = []) => {
const seen = new Set();
return data.reduce((acc, item) => {
const url = item?.refImage;
if (url && !seen.has(url)) {
seen.add(url);
acc.push({ ImageUrl: url });
}
return acc;
}, []);
};
const onFinish = async ({ PaymentType, PaymentAmount = 0, ...values }) => {
try {
// ===============================
// 1⃣ TAX VALIDATION
// ===============================
2026-01-27 18:27:29 +05:30
const invalidTaxItems = PurchaseData?.filter((item) => {
const tax = item?.PurchaseTax;
if (tax === undefined || tax === '' || tax === null) return false;
2026-01-27 18:27:29 +05:30
const num = Number(tax);
return isNaN(num) || num < 0 || num > 99;
2026-01-27 18:27:29 +05:30
});
if (invalidTaxItems?.length > 0) {
2026-01-27 18:27:29 +05:30
setMessageType('error');
setMessageData(
'Please enter valid tax percentage (099%) for all items'
);
2026-01-27 18:27:29 +05:30
return;
}
2026-01-27 18:27:29 +05:30
if (
PurchaseData?.some(
(item) =>
item.SellPrice &&
item.MRP &&
Number(item.SellPrice) > Number(item.MRP)
2026-01-27 18:27:29 +05:30
)
) {
setMessageType('error');
setMessageData('Selling Price cannot be greater than MRP');
return;
}
if (!PurchaseData || PurchaseData?.length === 0) {
2026-01-27 18:27:29 +05:30
setMessageType('error');
setMessageData('No Product Selected');
return;
}
const validRows = [];
const invalidRowNumbers = [];
PurchaseData.forEach((item, index) => {
const qty = Number(item.BalanceQty) || 0;
const amount = Number(item.Amount) || 0;
if (qty > 0 && amount > 0) {
validRows.push(item);
} else {
invalidRowNumbers.push(index + 1);
}
});
if (validRows?.length === 0) {
2026-01-27 18:27:29 +05:30
setMessageType('error');
setMessageData('Please enter Qty and Amount for at least one product');
2026-01-27 18:27:29 +05:30
return;
}
// ===============================
// 5⃣ CONFIRMATION FOR INVALID ROWS
// ===============================
if (invalidRowNumbers.length > 0) {
Modal.confirm({
title: '⚠️ Incomplete Product Rows',
width: 520,
centered: true,
content: (
<div style={{ fontSize: 14 }}>
<p>
Some products are missing <b>Qty or Amount</b>.
</p>
<p>
Affected row(s): <b>{invalidRowNumbers.join(', ')}</b>
</p>
<p style={{ marginTop: 8 }}>
Are you sure you want to continue without these products?
</p>
</div>
),
okText: 'Continue Anyway',
cancelText: 'Go Back',
okButtonProps: { danger: true },
onOk: () =>
proceedSubmit(validRows, values, PaymentType, PaymentAmount),
});
2026-01-27 18:27:29 +05:30
return;
}
// If all rows valid
await proceedSubmit(validRows, values, PaymentType, PaymentAmount);
} catch (error) {
console.error(error);
setMessageType('error');
setMessageData('Something went wrong');
}
};
const proceedSubmit = async (
filteredPurchaseData,
values,
PaymentType,
PaymentAmount
) => {
const images = getUniqueImageArray(filteredPurchaseData);
const postData = {
...values,
InvoiceAmount: filteredPurchaseData.reduce(
(acc, item) => acc + Number(item.Amount || 0),
0
),
CompId,
BranchId,
AppId,
CreatedBy: UserId,
PurOrderStatus: purchaseStatus,
TaxAmount: TotalTaxAmount?.toFixed(2),
BillAmount: (SelInvoiceAmount - TotalTaxAmount)?.toFixed(2),
TotalAmount: SelInvoiceAmount,
PurOrderInvoiceNo: purchaseOrderId,
PaymentStatus: 'S',
LocationType: SupplierData?.find(
(item) => item?.SuppId === selectedSupplierData
)?.Type,
ImageDetails: images || [],
};
const fieldsToRemove = [
'InwardDtlId',
'InwardId',
'UomName',
'ProdName',
'UOMName',
];
2026-01-27 18:27:29 +05:30
const processedData = filteredPurchaseData.map((item) => {
const cleanItem = { ...item };
fieldsToRemove.forEach((field) => delete cleanItem[field]);
return {
...cleanItem,
TaxType: SelectedPurcTaxType || 0,
BalanceQty: cleanItem.AcceptedQty,
OnePcsPrice: cleanItem.OnePcsPrice,
NoOfPcs: cleanItem.NoOfPcs,
2026-01-27 18:27:29 +05:30
};
});
2026-01-27 18:27:29 +05:30
const isPaidSelected = PurchaseTypeData?.some(
(item) =>
item.ConfigId === SelectedPurchaseType && item.ConfigName === 'Cash'
);
2026-01-27 18:27:29 +05:30
if (
(SupplierOpen !== 'own' || selectedSupplierName) &&
selectedSupplierData &&
isPaidSelected
) {
postData.PaymentAmount = PaymentAmount;
postData.PaymentType = selectedPaymentMode;
} else {
postData.PaymentType = PaymentType;
}
2026-01-27 18:27:29 +05:30
postData.ProdDetails = processedData;
2026-01-27 18:27:29 +05:30
if (formType === 'edit') {
postData.ProdId = editstate?.ProdId;
postData.InwardId = editstate?.InwardId;
postData.InwardDtlId = editstate?.InwardDtlId;
postData.UpdatedBy = UserId;
}
2026-01-27 18:27:29 +05:30
let response;
try {
response =
formType === 'add'
? await dispatch(postPurchaseData(postData)).unwrap()
: await dispatch(putStockData(postData)).unwrap();
} catch (err) {
response = {
data: {
statusCode: 0,
response: 'Please Give Required Fields',
},
};
}
2026-01-27 18:27:29 +05:30
if (response?.data?.statusCode === 1) {
navigate(`${subDirectory}setting/purchase-entry/`, {
state: {
Notiffy: {
messageType: 'success',
messageData: response?.data?.response,
2026-01-27 18:27:29 +05:30
},
},
});
} else {
2026-01-27 18:27:29 +05:30
setMessageType('error');
setMessageData(response?.data?.response);
2026-01-27 18:27:29 +05:30
}
};
const onComplete = useCallback(() => {
setMessageData(null);
setMessageType(null);
}, []);
const defaultColumns = [
{
title: 'IMEI 1',
dataIndex: 'imei1',
width: '30%',
editable: true,
},
{
title: 'IMEI 2',
dataIndex: 'imei2',
editable: true,
},
{
title: 'Serial No',
dataIndex: 'serialno',
editable: true,
},
{
title: 'Mac Id',
dataIndex: 'Macid',
editable: true,
},
];
const handleSave = (row) => {
const newData = [...dataSource];
const index = newData.findIndex((item) => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
setDataSource(newData);
};
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
const IMEIcolumns = defaultColumns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave,
}),
};
});
const handleInvoiceSearch = (value) => {
setSelectedSupplierData(null);
formRef?.current?.setFieldsValue({ CustSuppId: null });
setSelectedSupplierName(true);
setSearchText(value);
setPurchaseOrderId(null);
setSelectedInvoice(null);
setPurchaseData([]);
setProductData([]);
form?.resetFields();
};
const handleInvoiceSelect = async (value) => {
if (value === selectedInvoice) return;
const invoiceParts = value?.split('-');
2026-01-27 18:27:29 +05:30
const shortInvoice = invoiceParts[invoiceParts.length - 1];
formRef?.current?.setFieldsValue({ SuppInvoiceNo: value });
formRef?.current?.setFieldsValue({ PurchaseOrderNo: value });
const purchaseOrders = purchaseOrderList?.filter(
(order) => order?.PurOrderId === value
);
const supplierExists = SupplierData?.filter(
(item) => item?.SuppId === purchaseOrders?.[0]?.SuppId
);
console.log(supplierExists, 'supplierExists');
let supplierMappedProducts = [];
if (supplierExists?.length > 0) {
setSelectedSupplierData(purchaseOrders?.[0]?.SuppId);
supplierMappedProducts =
(await handleSupplierBasedProduct(purchaseOrders?.[0]?.SuppId)) || [];
formRef?.current?.setFieldsValue({
CustSuppId: purchaseOrders?.[0]?.SuppId,
});
} else {
setMessageType('error');
setMessageData("Supplier for this Order isn't Active");
return;
}
if (supplierExists?.[0]?.SuppName === 'Self') {
setSelectedSupplierName(false);
setSupplierOpen('own');
} else {
setSelectedSupplierName(true);
}
setSelectedInvoice(value);
setSearchText(shortInvoice);
if (!purchaseOrders?.[0]?.OrderDetails?.length) {
setPurchaseData([]);
return;
}
let unMappedProducts = [];
const orderedData = purchaseOrders[0].OrderDetails.map((order, index) => {
const orderedProduct = supplierMappedProducts?.find(
(prod) => prod?.ProdId === order?.ProdId
);
if (orderedProduct === undefined) {
unMappedProducts.push(`${order?.ProdName}`);
}
const DefaultVariant = orderedProduct?.ProdVariantPriceDetails?.find(
(item) =>
item?.ReceivedQty === 0 &&
item?.ProdVariantName === order?.ProdVariantName &&
item?.ProdId === order?.ProdId
)?.DefaultVariant;
const ProduData2 = {
DefaultVariant,
ProdId: orderedProduct?.ProdId,
MRP: orderedProduct?.MRP,
ProdName: orderedProduct?.ProdName,
UomName: orderedProduct?.UomName,
SellPrice: orderedProduct?.SellPrice,
StockAvailable: orderedProduct?.StockAvailable,
OnePcsAvailable: orderedProduct?.OnePcsAvailable,
NumberofPieceinside: orderedProduct?.NoOfPcs,
AmountPerPiece: orderedProduct?.OnePcsPrice,
TaxId: orderedProduct?.TaxId,
TaxPercentage: orderedProduct?.TaxPercentage,
PurOrderInvoiceNo: order?.PurOrderId,
};
const uniqueId = uuidv4();
const tempPurData = {
...ProduData2,
BalanceQty: JSON.stringify(order?.OrderQty),
InwardPrice: 0,
PurcDisc: 0,
Amount: 0,
WhSalePrice: 0,
ReceivedQty: order?.OrderQty,
AcceptedQty: JSON.stringify(order?.OrderQty),
RejectedQty: 0,
OfferPrice: 0,
SpecialPrice: 0,
ProdVariantName: order?.ProdVariantName,
FreeItem: 0,
TaxAmt: 0,
TaxType: SelectedPurcTaxType || 0,
PurcDiscType: 'P',
localId: uniqueId,
};
form.setFieldsValue({
[`BalanceQty${uniqueId}`]: JSON.stringify(order?.OrderQty),
[`ReceivedQty${uniqueId}`]: order?.OrderQty,
[`AcceptedQty${uniqueId}`]: JSON.stringify(order?.OrderQty),
});
return tempPurData;
});
const filterOrders = orderedData?.filter(
(order) => order?.ProdId !== null && order?.ProdId !== undefined
);
if (filterOrders?.length > 0) {
setPurchaseOrderId(filterOrders[0]?.PurOrderInvoiceNo);
}
setPurchaseData(filterOrders);
if (unMappedProducts?.length > 0) {
setMessageType('error');
setMessageData(
`Please map the products that are not mapped with the selected supplier.`
);
}
};
const handleOrderTypeChange = (checked) => {
const isNewOrder = checked;
const supplierData = isNewOrder
? SupplierData?.find((supplier) => supplier?.SuppName === 'Self')?.SuppId
: null;
setOrderType(isNewOrder);
setSelectedInvoice(null);
setSearchText(null);
setSelectedSupplierName(!isNewOrder);
setSupplierOpen(isNewOrder ? 'own' : null);
setSelectedSupplierData(supplierData);
setPurchaseData([]);
// setSelectedProductData(null);
2026-01-27 18:27:29 +05:30
setSelectedProductName(null);
// setselectedProductVariantData(null);
2026-01-27 18:27:29 +05:30
setInvoiceType('I');
setPurchaseStatus('P');
setProductData([]);
handleSupplierBasedProduct(supplierData);
formRef?.current?.setFieldsValue({
InvoiceType: 'I',
PurchaseOrderNo: null,
SuppInvoiceNo: null,
CustSuppId: supplierData,
ProdId: null,
OrderType: isNewOrder ? 'N' : 'O',
VarProdId: null,
});
form?.resetFields();
};
useEffect(() => {
if (additionalInfoModal) {
productAddInfoRef?.current?.setFieldsValue({
ReceivedQty: selectedRowRecord?.ReceivedQty,
RejectedQty: selectedRowRecord?.RejectedQty,
AcceptedQty: selectedRowRecord?.AcceptedQty,
FreeItem: selectedRowRecord?.FreeItem,
OfferPrice: selectedRowRecord?.OfferPrice,
WhSalePrice: selectedRowRecord?.WhSalePrice,
SpecialPrice: selectedRowRecord?.SpecialPrice,
MRP: selectedRowRecord?.MRP,
SellPrice: selectedRowRecord?.SellPrice,
AmountPerPiece: selectedRowRecord?.AmountPerPiece,
NumberofPieceinside: selectedRowRecord?.NumberofPieceinside,
});
if (applicationRestrictedFields.BatchAndModelDetails) {
productAddInfoRef?.current?.setFieldsValue({
BatchRef: selectedRowRecord?.BatchRef,
ModelNumber: selectedRowRecord?.ModelNumber,
});
}
if (applicationRestrictedFields.DatesAndExpiry) {
productAddInfoRef?.current?.setFieldsValue({
ManufDate: selectedRowRecord?.ManufDate,
ExpDate: selectedRowRecord?.ExpDate,
});
}
}
}, [additionalInfoModal]);
// command
const validateSellPrice = (record) => (_, value) => {
if (!value) {
return Promise.reject('Selling Price is required');
}
if (!/^[1-9]\d*(\.\d+)?$/.test(value)) {
return Promise.reject('Please enter a valid selling price');
}
const mrp = record?.MRP;
// const mrp = productAddInfoRef?.current?.getFieldValue('MRP');
if (parseFloat(value) > parseFloat(mrp)) {
return Promise.reject(
'Please enter selling price less than or equal to MRP'
);
}
return Promise.resolve();
};
const handleModelDataOpen = (selectedRowRecord) => {
2026-01-27 18:27:29 +05:30
const acceptedQty = parseInt(selectedRowRecord?.AcceptedQty) || 0;
const productIdentifiers = selectedRowRecord?.ProductIdentifierDtls || [];
let finalCombined = productIdentifiers.map((device, idx) => ({
key: idx,
column1: `Row ${idx + 1} Col 1`,
imei1: device?.IMEI1 || null,
imei2: device?.IMEI2 || null,
Macid: device?.MacId || null,
serialno: device?.SerialNumber || null,
}));
if (finalCombined.length > 0) {
if (finalCombined.length < acceptedQty) {
const rowsToAdd = acceptedQty - finalCombined.length;
for (let i = 0; i < rowsToAdd; i++) {
finalCombined.push({
key: finalCombined.length + i,
column1: `Row ${finalCombined.length + i + 1} Col 1`,
imei1: null,
imei2: null,
Macid: null,
serialno: null,
});
}
}
setDataSource(finalCombined);
setimeiSerialOpen(true);
} else {
if (acceptedQty > 0) {
if (dataSourceBackup.length === 0 || dataSource.length === 0) {
const newEmptyData = Array(acceptedQty)
.fill(null)
.map((_, index) => ({
key: index,
column1: `Row ${index + 1} Col 1`,
}));
setDataSource(newEmptyData);
} else {
setDataSource(dataSourceBackup);
}
setimeiSerialOpen(true);
} else {
setMessageType('error');
setMessageData('Enter Qty');
}
}
};
const handleModelDataSumbitOrClose = () => {
setimeiSerialOpen(false);
const productIdentifierDtls = dataSource
?.filter(
(item) => item.serialno || item.imei1 || item.imei2 || item.Macid
)
?.map((item) => ({
SerialNumber: item.serialno || '',
IMEI1: item.imei1 || '',
IMEI2: item.imei2 || '',
MacId: item.Macid || '',
}));
const newData = PurchaseData.map((item, index) =>
index === selectedRowIndex
? { ...item, ProductIdentifierDtls: productIdentifierDtls }
: item
);
setPurchaseData(newData);
2026-01-27 18:27:29 +05:30
};
const handleFieldSetupSubmit = async ({
selectedFields,
tempSelectedColumns,
closeModal,
resetTemp,
}) => {
const postData = {
AppId,
CompId,
BranchId,
Type: 'PE',
FormType: 'Purchase Entry',
TypeId: categoryId,
ConfigDtl: selectedFields.map((field) => ({
ConfigId: field,
Access: 'Y',
})),
CreatedBy: UserId,
};
const response = await dispatch(postFieldSetup(postData))?.unwrap();
2026-01-27 18:27:29 +05:30
if (response?.data?.statusCode === 1) {
setSelectedAdditionalColumn([...tempSelectedColumns]);
closeModal();
resetTemp();
setMessageType('success');
setMessageData(response?.data?.response);
await getFieldSetup();
} else {
setMessageType('error');
setMessageData('Failed to set up fields');
}
};
2026-01-27 18:27:29 +05:30
const handleModalSubmited = () => {
setIsModalOpen(false);
};
const handleSupplierBasedProduct = async (SuppId) => {
const Type = SupplierData?.find((find) => find.SuppId === SuppId)?.Type;
const LocationType =
Type === 'Supplier'
? 'S'
: Type === 'Branch'
? 'B'
: Type === 'WareHouse'
? 'W'
: '';
const productResponse = await dispatch(
getSupplaierIdwithTypeBasedProducts({
CompId,
AppId,
BranchId,
SuppId,
LocationType,
})
).unwrap();
setPurchaseData([]);
if (productResponse?.data?.statusCode === 1) {
setProductData(productResponse?.data?.data);
return productResponse?.data?.data;
} else {
setProductData([]);
return [];
}
};
const validatePhoneNumber = (rule, value, callback) => {
if (value && value?.length !== 10) {
callback();
} else {
callback();
}
};
const validateEmail = (rule, value, callback) => {
const regex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
if (!value || regex.test(value)) {
callback();
} else {
callback('Please enter a valid Mail Id');
}
};
const deliveryPerformance = async () => {
let response = await dispatch(
getConfigType({ TypeName: 'Delivery Performance' })
).unwrap();
SetDeleveryPerformanceData(response?.data?.data);
};
const discountLevel = async () => {
let response = await dispatch(
getConfigType({ TypeName: 'Discount Level' })
).unwrap();
setDiscountData(response?.data?.data);
};
const qualityofSupp = async () => {
let response = await dispatch(
getConfigType({ TypeName: 'Quality Of Supplier' })
).unwrap();
setQualityData(response?.data?.data);
};
const handleDeliveryPerformanceChange = (value) => {
formSupplierRef?.current?.setFieldsValue({ DeliveryPerformance: value });
setDeliveryValue(value);
};
const handleDiscountLevelChange = (value) => {
formSupplierRef?.current?.setFieldsValue({ DiscountLevel: value });
setDicountValue(value);
};
const handleQualityofSuppChange = (value) => {
formSupplierRef?.current?.setFieldsValue({ QualityofSupp: value });
setQualityValue(value);
};
const ViewMoreFields = () => setViewDtl((prev) => !prev);
const handlePaymentModeChange = (modeId) => {
setPayAmountDisable(
paymentOptions
?.find((option) => option?.ModeId === modeId)
?.ModeName?.toLowerCase() === 'credit'
);
setSelectedPaymentMode(modeId);
formRef.current?.setFieldsValue({ PaymentMode: modeId });
};
return (
<div className="pageOverAll" style={{ position: 'relative' }}>
{isLoading && (
<div className="loading-overlay">
<div className="loading-spinner" />
{loadingText && <div className="loading-text">{loadingText}</div>}
</div>
)}
<div className="userPage">
<div className="userPageContent" style={{ paddingBottom: '1rem' }}>
2026-01-27 18:27:29 +05:30
<Messages
messageType={messageType}
messageData={messageData}
onComplete={onComplete}
/>
<div className="purchase-entry-fh">
<div
className="formName"
style={{
flexWrap: 'wrap',
gap: '10px',
justifyContent: 'space-between',
}}
>
<FormHeader title={'Product Receipt'} />
<div
className="header-buttons"
style={{ flexWrap: 'wrap', width: 'unset' }}
>
<div
className="pruchaseCatBTN scan-receipt-btn"
onClick={() => setExtractorModalVisible(true)}
>
<TbUpload size={18} strokeWidth={3} />{' '}
<p> Scan Receipt (Auto-Fill)</p>
2026-01-27 18:27:29 +05:30
</div>
<div
className="pruchaseCatBTN"
onClick={() =>
navigate(`${subDirectory}setting/purchase-entry`)
}
>
<FaEye size={18} /> <p> View Entry List</p>
</div>
</div>
</div>
</div>
<div className="formDiv">
<Form
ref={formRef}
initialValues={editstate}
className="formDivAnt"
onFinish={onFinish}
style={{ width: '100%' }}
2026-01-27 18:27:29 +05:30
>
<div className="purchase-entry-form">
<div className="formDivS">
<div className="inputFormSTOCKFORM">
2026-01-27 18:27:29 +05:30
<div className="purchaseDetails">
<div className="supplierInfo">
{/* <h2 className="supplier-dtls">Supplier Details</h2> */}
2026-01-27 18:27:29 +05:30
<div className="supplierInfo1">
{!orderType && (
<Form.Item
name="PurchaseOrderNo"
rules={[
{
required: true,
message: 'Please Select Purchase Order No.',
},
]}
>
<FloatLabel
label={
<label className="required">
2026-01-27 18:27:29 +05:30
Purchase Order No.
</label>
}
isOnChange={searchText ? true : false}
>
<AutoComplete
style={{ width: '180px' }}
options={filteredOptions}
className="supplier-invoice-select"
autoComplete="off"
allowClear
value={searchText}
onSelect={handleInvoiceSelect}
onSearch={handleInvoiceSearch}
filterOption={(inputValue, option) =>
option.label
?.toLowerCase()
.includes(inputValue?.toLowerCase())
}
/>
</FloatLabel>
</Form.Item>
)}
<div className={`${orderType ? 'div-flex' : ''}`}>
<Form.Item
name="CustSuppId"
rules={[
{
required: true,
message: 'Please Select Supplier',
},
]}
>
<DropDowns
options={
SupplierData
? SupplierData.filter(
(obj, index, self) =>
index ===
self.findIndex(
(t) => t.SuppId === obj.SuppId
) // remove dup SuppId
).map((option) => ({
key: `${option.SuppId}-${option.Type}`, // 👈 unique key
value: option.SuppId,
label:
option.Type === 'Branch'
? `${option.SuppName} (${option.Type}) [${option.SuppId}]`
: `${option.SuppName} (${option.Type})`,
}))
: []
}
label={
<label className="required">Supplier</label>
}
className="field-DropDown"
onChangeFunction={handleSupplierDropDownChange}
valueData={selectedSupplierData}
disabled={
editstate?.SuppId
2026-01-27 18:27:29 +05:30
? true
: !orderType
? true
: false
}
2026-01-27 18:27:29 +05:30
/>
<Tooltip title="Add Supplier" placement="top">
<PlusCircleOutlined
className="product-add-iconMargin"
onClick={addSupplier}
/>
</Tooltip>
</Form.Item>
{orderType && <Form.Item
name="showAll"
valuePropName="checked"
>
<Tooltip title="Show all branches and warehouses" placement="top">
<span>
<Checkbox
checked={showAllSuppliers}
onChange={(e) => {
const checked = e.target.checked;
setShowAllSuppliers(checked);
setInitialLoad(true);
getSuplaierApi({ Type: checked ? 'Y' : null });
}}
>
All
</Checkbox>
</span>
</Tooltip>
</Form.Item>
}
</div>
2026-01-27 18:27:29 +05:30
{!selectedSupplierName && (
<RadioGrpButton
2026-01-27 18:27:29 +05:30
content={[
{ value: 'own', label: 'Own' },
{ value: 'paid', label: 'With paid' },
]}
fieldState={true}
defaultSelect={SupplierOpen}
// Header={"Stock Available"}
onSelectFuntion={(e) => SupplierOpenfun(e)}
/>
)}
<div className="pe-inward-date">
<label className="required">Inward Date</label>
<Form.Item
name={'InvoiceDate'}
rules={[
{
required: true,
message: 'Please Select Inward Date',
},
]}
>
<DatePicProd
canSelectPast={true}
onChange={handleInwardDateChange}
valueData={inwardDate}
disabled={formType == 'edit' ? true : false}
cancelFuture={true}
/>
</Form.Item>
</div>
<Form.Item
name="OrderType"
rules={[
{
required: true,
message: 'Please Select Order Type',
},
]}
>
<div className="switch-order-type">
<div className="switch-header required">
Order Type
</div>
<Switch
checked={orderType}
onChange={handleOrderTypeChange}
checkedChildren="New Order"
unCheckedChildren="Pending Orders"
/>
</div>
</Form.Item>
2026-01-27 18:27:29 +05:30
</div>
<div className="supplierInfo2">
{(SupplierOpen !== 'own' || selectedSupplierName) &&
selectedSupplierData && (
<>
<Form.Item name={'InvoiceType'}>
<RadioGrpButton
content={[
{ value: 'I', label: 'Invoice' },
{
value: 'DC',
label: 'Delivery Challan',
},
]}
defaultSelect={invoiceType}
onSelectFuntion={handleInvoiceTypeChange}
/>
</Form.Item>
<Form.Item
name="SuppInvoiceNo"
rules={[
{
required: true,
// pattern:/^[1-9]\d*(\.\d+)?$/,
message:
invoiceType === 'I'
? 'Please Enter Invoice Number'
: 'Please Enter Delivery Challan',
},
{
validator: async (_, value) => {
await validateSafeInput(value);
if (value?.length > 30) {
if (invoiceType === 'I') {
return Promise.reject(
'Invoice Number cannot exceeds more than 30 chars'
);
}
return Promise.reject(
'Delivery Challan cannot exceeds more than 30 chars'
);
}
return Promise.resolve();
},
},
]}
>
<div className="stock-input">
<InputField
autoComplete="off"
label={
<label className="required">
2026-01-27 18:27:29 +05:30
{invoiceType === 'I'
? 'Supplier Invoice Number'
: 'Delivery Challan No.'}
</label>
}
onChange={handleSuppInvoiceNoChange}
isOnChange={
extractorData?.invoiceNo ? true : false
}
value={extractorData?.invoiceNo}
/>
</div>
</Form.Item>
<Form.Item name="SuppInvoiceDate">
<div className="supplier-invoice-date">
<label className="required">
{invoiceType === 'I'
? 'Supplier Invoice Date'
: 'Delivery Challan Date'}
</label>
<DatePicProd
canSelectPast={true}
onChange={onSuppInvoiceDateChange}
valueData={SuppInvoiceDate}
disabled={
formType == 'edit' ? true : false
}
cancelFuture={true}
/>
</div>
</Form.Item>
</>
)}
</div>
</div>
{(SupplierOpen !== 'own' || selectedSupplierName) &&
selectedSupplierData && (
<div className="invoiceDetails">
<>
<Form.Item
name="PaymentType"
rules={[
{
required: true,
message: 'Please Select Pruchase Type ',
},
]}
>
<DropDowns
options={PurchaseTypeData?.map((option) => ({
value: option.ConfigId,
label:
option.ConfigName === 'Cash'
? 'Paid'
: option.ConfigName,
}))}
label={
<label className="required">
Payment Type
</label>
2026-01-27 18:27:29 +05:30
}
className="field-DropDown"
onChangeFunction={handlePurchaseTypeChange}
valueData={SelectedPurchaseType}
disabled={editstate?.SuppId ? true : false}
/>
</Form.Item>
{PurchaseTypeData?.some(
(item) =>
item.ConfigId === SelectedPurchaseType &&
item.ConfigName === 'Cash'
) && (
<>
<Form.Item
name="PaymentMode"
rules={[
{
required: true,
message: 'Please Select Payment Mode',
},
]}
>
<DropDowns
options={paymentOptions?.map((mode) => ({
value: mode.ModeId,
label: mode.ModeName,
}))}
label={
<label className="required">
Payment Mode
</label>
}
className="field-DropDown"
onChangeFunction={handlePaymentModeChange}
valueData={selectedPaymentMode}
/>
</Form.Item>
<Form.Item
name="PaymentAmount"
rules={[
{
required: true,
message: 'Please enter Payment Amount',
},
{
validator: (_, value) => {
if (
value === undefined ||
value === null ||
value === ''
) {
return Promise.resolve();
}
if (Number(value) === 0) {
return Promise.reject(
'Payment Amount cannot be 0'
);
}
if (
isNaN(value) ||
Number(value) < 0
) {
return Promise.reject(
'Payment Amount cannot be less than 0'
);
}
2026-01-27 18:27:29 +05:30
return Promise.resolve();
},
2026-01-27 18:27:29 +05:30
},
]}
>
<InputField
autoComplete="off"
label={
<label className="required">
Payment Amount
</label>
}
inputMode="decimal"
disable={!payAmountDisable}
isOnChange={paymentAmount ? true : false}
min={0}
type="number"
value={paymentAmount}
onChange={(e) => {
formRef?.current?.setFieldsValue({
PaymentAmount: e?.target?.value,
});
setPaymentAmount(e?.target?.value);
}}
/>
</Form.Item>
</>
)}
2026-01-27 18:27:29 +05:30
<Form.Item name="DueDate">
<label className="required">Due Date</label>
<br />
<DatePicProd
canSelectPast={true}
onChange={onPurchaseDateChange}
valueData={PurchaseDate}
disabled={formType == 'edit' ? true : false}
cancelFuture={false}
/>
</Form.Item>
</>
</div>
)}
</div>
<div className="stockDiv">
<Form.Item name="ProdId">
<div className="product-scan">
<FloatLabel
label={'Product Name'}
value={selectedProductName}
>
<AutoComplete
className="field-DropDown"
value={selectedProductName}
filterOption={(input, option) => {
const [prodId, variantName] =
option?.value?.split('_');
2026-01-27 18:27:29 +05:30
const product = productData.find(
(p) =>
p.ProdId === prodId &&
(p.ProdVariantName || 'Variant1') ===
variantName
2026-01-27 18:27:29 +05:30
);
const nameMatch = option.label
?.toLowerCase()
.includes(productSearchText?.toLowerCase());
const qrMatch =
product?.QRCode?.toLowerCase() ==
productSearchText?.toLowerCase();
return nameMatch || qrMatch;
}}
options={
!scanner
? productData?.map((option) => ({
value: `${option.ProdId}_${option.ProdVariantName}`,
label: `${option.ProdName} (${option.Size} ${option.UomName})${option?.BrandName ? ` - ${option?.BrandName}` : ''} - ${option.ProdVariantName}`,
}))
2026-01-27 18:27:29 +05:30
: []
}
// onSelect={async (value, option) => {
// const [prodId, variantName] = value?.split('_');
// const product = productData.find(
// (p) =>
// p.ProdId === prodId &&
// p.ProdVariantName === variantName
// );
// const newProduct = await ProductDropDownChange(
// product?.ProdVariantName,
// prodId,
// option
// );
// if (newProduct) {
// form?.setFieldsValue({
// [`SellPrice${newProduct?.localId}`]:
// newProduct?.SellPrice,
// [`PurchaseTax${newProduct?.localId}`]:
// newProduct?.PurchaseTax,
// });
// setPurchaseData([
// newProduct,
// ...PurchaseData,
// ]);
// }
// }}
onSelect={(value, option) => {
handleProductSelect(value, option);
2026-01-27 18:27:29 +05:30
}}
onSearch={handleProductSearch}
2026-01-27 18:27:29 +05:30
onKeyDown={(e) => {
if (scanner && e.key === 'Enter') {
e.preventDefault();
}
}}
style={{ width: '250px' }}
></AutoComplete>
</FloatLabel>
<Tooltip title={'BarCode/QrCode'} placement={'top'}>
<BsUpcScan
strokeWidth={0.5}
color="#000"
style={{
fontSize: '16px',
color: scanner ? '#52c41a' : '#888',
fontWeight: '600',
}}
className="product-scan-Icon"
onClick={handleScanSearch}
/>
</Tooltip>
</div>
<Tooltip
title={'Supplier Product Mapping'}
placement={'top'}
>
<FaLink
onClick={() => setIsModalOpen(true)}
size={18}
/>
</Tooltip>
<AddAllProductsButton
productData={productData}
purchaseData={PurchaseData}
setPurchaseData={setPurchaseData}
form={form}
ProductDropDownChange={ProductDropDownChange}
setIsLoading={setIsLoading}
setLoadingText={setLoadingText}
/>
2026-01-27 18:27:29 +05:30
</Form.Item>
2026-01-27 18:27:29 +05:30
{isModalOpen && (
<SupplierProductMappingForm
isModalOpen={isModalOpen}
setIsModalOpen={() => {
(setIsModalOpen(false),
handleSupplierDropDownChange(
selectedSupplierData
));
}}
CompId={CompId}
BranchId={BranchId}
AppId={AppId}
setMessage={setMessage}
onMappingAdded={handleModalSubmited}
/>
)}
<div className="productSearchRecipt">
<SettingsIconWithModal
tableFieldPreferences={tableFieldPreferences}
selectedAdditionalColumn={selectedAdditionalColumn}
onSubmit={handleFieldSetupSubmit}
/>
<div style={{ marginBottom: '10px' }}>
<Input
placeholder="Search products..."
prefix={<SearchOutlined />}
onChange={(e) => {
const value = e.target.value.toLowerCase();
setSearchValue(value);
}}
style={{ width: '300px' }}
/>
</div>
</div>
2026-01-27 18:27:29 +05:30
</div>
{/* <div
2026-01-27 18:27:29 +05:30
className="Product-table stockformTabele"
style={{
maxwidth: '500px',
overflowX: 'auto',
scrollbarWidth: 'thin',
maxHeight: '50vh',
2026-01-27 18:27:29 +05:30
}}
>
<Table
rowKey="localId"
className="purchase-receipt-table"
bordered
dataSource={
isSearching ? filteredPurchaseData : PurchaseData
}
columns={columns}
rowClassName="editable-row"
pagination={false}
locale={{
emptyText: isSearching
? 'No matching products found'
: 'No data',
}}
/>
</div> */}
<div className="Product-table stockformTabele">
<VirtualizedTable
rowKey="localId"
dataSource={
isSearching ? filteredPurchaseData : PurchaseData
}
columns={columns}
rowHeight={54} // same as your previous row height
height="50vh"
overscan={10}
/>
2026-01-27 18:27:29 +05:30
</div>
</div>
</div>
</div>
</Form>
</div>
<Form
ref={formRef}
initialValues={editstate}
className="finalSubmitPRBTN"
onFinish={onFinish}
>
{PurchaseData?.length > 0 && (
<div className="purchase-status-data">
<div className="total-purchase-amnt">
<label>Total Amount:&nbsp;</label>
<div>
{PurchaseData.reduce(
(acc, data) => acc + data?.Amount,
0
)?.toFixed(2)}
</div>
</div>
{!orderType && (
<div className="purchase-status">
<RadioGrpButton
content={[
{ value: 'P', label: 'Pending' },
{ value: 'C', label: 'Completed' },
]}
Header={'Purchase Status'}
defaultSelect={purchaseStatus}
onSelectFuntion={purchaseStatusChange}
/>
</div>
)}
</div>
)}
<Buttons
buttonText="SUBMIT"
color="901D77"
icon={<ArrowRightOutlined />}
/>
</Form>
2026-01-27 18:27:29 +05:30
</div>
{OpenSupplierModel && (
<DefaultModal
title="Add Supplier"
open={OpenSupplierModel}
footer={true}
buttonText="Submit"
handleSubmit={submitSupplier}
handleCancel={handleSupplier}
>
2026-01-27 18:27:29 +05:30
<Form ref={formSupplierRef} className="formDivAnt">
<div className="subRowFlex">
<Form.Item
name="SuppName"
rules={[
{
required: true,
pattern: /^(?!\s*$).+/,
message: 'Please Enter Supplier Name',
},
{
validator: async (_, value) => {
await validateSafeInput(value);
if (value?.length > 30) {
return Promise.reject(
'Supplier Name cannot exceeds more than 30 chars'
);
}
return Promise.resolve();
},
},
]}
>
<InputField
label={<label className="required">Supplier Name</label>}
2026-01-27 18:27:29 +05:30
autocomplete="off"
/>
</Form.Item>
<Form.Item
name="Address1"
rules={[
{
required: true,
pattern: /^(?!\s*$).+/,
message: 'Please Enter Address1',
},
{
validator: async (_, value) => {
await validateSafeInput(value); // To block HTML tags & SQL keywords
return Promise.resolve();
},
},
]}
>
<InputField
autoComplete="off"
label={<label className="required">Address</label>}
2026-01-27 18:27:29 +05:30
/>
</Form.Item>
<Form.Item
name="Zip"
rules={[
{
required: true,
pattern: /^(?!\s*$).+/,
message: 'Please Enter Zipcode',
},
{
validator: async (_, value) => {
await validateSafeInput(value); // To block HTML tags & SQL keywords
return Promise.resolve();
},
},
]}
>
<InputField
autoComplete="off"
label={<label className="required">Zipcode</label>}
2026-01-27 18:27:29 +05:30
maxLength="6"
onChange={pinCodeChange}
/>
</Form.Item>
<p
style={{ color: '#1292ee', justifyContent: 'end' }}
onClick={ViewMoreFields}
>
{' '}
{ViewDtl ? 'Less More' : 'View More'}
</p>
{ViewDtl && (
<>
<Form.Item
name="SuppGSTIN"
rules={[
{
pattern:
/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Za-z]{1}[Z]{1}[0-9A-Za-z]{1}$/,
message: 'Enter a valid GSTIN',
},
]}
>
<InputField
style={{ textTransform: 'uppercase' }}
autoComplete="off"
label="GST No"
autocapitalize="on"
// isOnChange={formType == 'edit' ? true : false}
2026-01-27 18:27:29 +05:30
/>
</Form.Item>
<Form.Item
name="SuppMobile"
rules={[
{
pattern: /^\d{10}$/,
message: 'Please Enter a Valid Mobile Number',
},
{ validator: validatePhoneNumber },
]}
>
<InputField
label={<label>Mobile Number</label>}
maxLength="10"
autocomplete="off"
// isOnChange={formType == 'edit' ? true : false}
inputMode="numeric"
onInput={(e) =>
(e.target.value = e.target.value?.replace(
/[^0-9]/g,
''
))
2026-01-27 18:27:29 +05:30
}
/>
</Form.Item>
<Form.Item
name="SuppEmail"
rules={[{ validator: validateEmail }]}
>
<InputField
label={<label>MailId</label>}
autocomplete="off"
// isOnChange={formType == 'edit' ? true : false}
2026-01-27 18:27:29 +05:30
/>
</Form.Item>
<Form.Item
name="SuppPOC"
rules={[
{
pattern: /^(?!\s*$).+/,
message: 'Please Enter Point of Contact Name',
},
{
validator: async (_, value) => {
await validateSafeInput(value);
if (value?.length > 30) {
return Promise.reject(
'Point of Contact cannot exceeds more than 30 chars'
);
}
return Promise.resolve();
},
},
]}
>
<InputField
label={<label className="">Point of Contact</label>}
2026-01-27 18:27:29 +05:30
autocomplete="off"
// isOnChange={formType == 'edit' ? true : false}
2026-01-27 18:27:29 +05:30
/>
</Form.Item>
<Form.Item
name="SuppPOCMobile"
rules={[
{
pattern: /^\d{10}$/,
message: 'Please Enter a Valid Mobile Number',
},
{
// required: true,
message: 'Please Enter Mobile Number',
},
{ validator: validatePhoneNumber },
]}
>
<InputField
label={<label className="">POC Mobile Number</label>}
2026-01-27 18:27:29 +05:30
autocomplete="off"
maxLength="10"
// isOnChange={formType == 'edit' ? true : false}
2026-01-27 18:27:29 +05:30
/>
</Form.Item>
<Form.Item
name="SuppPOCEmail"
rules={[{ validator: validateEmail }]}
>
<InputField
label={<label className="">POC MailId</label>}
2026-01-27 18:27:29 +05:30
autocomplete="off"
// isOnChange={formType == 'edit' ? true : false}
2026-01-27 18:27:29 +05:30
/>
</Form.Item>
<Form.Item
name="DeliveryPerformance"
rules={[
{
required: false,
message: 'Please Select DeliveryPerformance',
},
]}
>
<DropDowns
options={deliveryPerformanceData?.map((option) => ({
value: option.ConfigId,
label: option.ConfigName,
}))}
label={<label>Delivery Performance</label>}
className="field-DropDown"
id="DeliveryPerformance"
onChangeFunction={(e) =>
handleDeliveryPerformanceChange(e)
}
// isOnChange={formType == 'edit' ? true : false}
valueData={Delivery}
/>
</Form.Item>
<Form.Item
name="DiscountLevel"
rules={[
{
required: false,
message: 'Please Select DiscountLevel',
},
]}
>
<DropDowns
options={DiscountData?.map((option) => ({
value: option.ConfigId,
label: option.ConfigName,
}))}
label={<label>Discount Level</label>}
className="field-DropDown"
id="DiscountLevel"
onChangeFunction={(e) => handleDiscountLevelChange(e)}
// isOnChange={formType == 'edit' ? true : false}
valueData={DicountValue}
/>
</Form.Item>
<Form.Item
name="QualityofSupp"
rules={[
{
required: false,
message: 'Please Select QualityofSupp',
},
]}
>
<DropDowns
options={QualityData?.map((option) => ({
value: option.ConfigId,
label: option.ConfigName,
}))}
label={<label>Quality of Supplier</label>}
className="field-DropDown"
id="QualityofSupp"
onChangeFunction={(e) => handleQualityofSuppChange(e)}
// isOnChange={formType == 'edit' ? true : false}
valueData={QualityValue}
/>
</Form.Item>
</>
)}
{zipCodeData ? (
<>
<Form.Item
name="City"
rules={[
{
required: true,
},
{
validator: async (_, value) => {
await validateSafeInput(value); // To block HTML tags & SQL keywords
return Promise.resolve();
},
},
]}
>
<InputField
disabled={true}
isOnChange={true}
label="City"
/>
</Form.Item>
<Form.Item
name="Dist"
rules={[
{
required: true,
},
{
validator: async (_, value) => {
await validateSafeInput(value); // To block HTML tags & SQL keywords
return Promise.resolve();
},
},
]}
>
<InputField
disabled={true}
isOnChange={true}
label="District"
/>
</Form.Item>
<Form.Item
name="State"
rules={[
{
required: true,
},
{
validator: async (_, value) => {
await validateSafeInput(value); // To block HTML tags & SQL keywords
return Promise.resolve();
},
},
]}
>
<InputField
disabled={true}
isOnChange={true}
label="State"
/>
</Form.Item>
</>
) : (
''
)}
</div>
</Form>
</DefaultModal>
)}
2026-01-27 18:27:29 +05:30
<div>
{imeiSerialOpen && (
<DefaultModal
title="IMEI /Serial Number / Mac Id"
open={imeiSerialOpen}
footer={true}
width={600}
buttonText="Submit"
handleSubmit={handleModelDataSumbitOrClose}
handleCancel={handleModelDataSumbitOrClose}
>
<div className="imeiSNTable">
2026-01-27 18:27:29 +05:30
<Table
components={components}
rowClassName={() => 'editable-row'}
bordered
dataSource={dataSource}
columns={IMEIcolumns}
pagination={{ showSizeChanger: false }}
/>
</div>
</DefaultModal>
)}
2026-01-27 18:27:29 +05:30
<InvoiceImageExtractorModal
visible={extractorModalVisible}
onClose={() => setExtractorModalVisible(false)}
onApply={handleExtractorApply}
productData={productData}
supplierData={SupplierData}
uomData={UomData}
setVisible={setExtractorModalVisible}
/>
</div>
</div>
<style>{`
2026-01-27 18:27:29 +05:30
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
`}</style>
</div>
);
};
export default StockForm;