Android_Retail/src/Pages/StockMaster/StockForm.jsx

4371 lines
146 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
} from '@ant-design/icons';
import { Form, Tooltip, Table, Input, AutoComplete, Switch, Checkbox } from 'antd';
import { IoAddCircleSharp } from 'react-icons/io5';
import { InputField } from '../../Components/Forms/InputField.jsx';
import Buttons from '../../Components/Forms/Buttons.jsx';
import { Modal } from 'antd';
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,
} from '../../Features/StockMaster/StockMaster.js';
import '../../Styles/Stock/StockMaster.scss';
import {
getAdmin,
getUomData,
getProdCatData,
uomDataSelector,
postSupplier,
getProductTypeData,
bulkpostdata,
postFieldSetup,
getFieldSetupData,
} 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';
import {
getSupplaierIdBasedProducts,
getSupplaierIdwithTypeBasedProducts,
postSupplaierProducts,
} from '../../Features/SupplierProductMapping/SupplierProductMapping.js';
import {
ApplicationPreferences,
getCommonAppPreference,
} from '../../Features/BrachLogin/BranchLogin.js';
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';
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',
}}
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');
const formRef = useRef(null);
const productAddInfoRef = useRef(null);
const formProductRef = useRef(null);
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;
const CompId = getSession('CompId');
const BranchId = getSession('BranchId');
const AppId = getSession('AppId');
const UserId = getSession('UserId');
const UomData = useSelector(uomDataSelector);
const appPreferences = useSelector(ApplicationPreferences);
const [applicationRestrictedFields, setApplicationRestrictedFields] =
useState({
DatesAndExpiry: false,
BatchAndModelDetails: false,
AmountPerPieceDetail: false,
});
const [editingQty, setEditingQty] = useState({});
const [selectedAdditionalColumn, setSelectedAdditionalColumn] = useState([]);
const [tableFieldPreferences, setTableFieldPreferences] = useState([]);
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();
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());
const [SuppInvoiceDate, setSuppInvoiceDate] = useState();
const [invoiceType, setInvoiceType] = useState('I');
const [PurchaseDate, setPurchaseDate] = useState();
const [paymentAmount, setPaymentAmount] = useState(null);
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]);
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;
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 });
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());
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);
setSelectedSupplierData(editstate?.SuppId);
}
}, []);
useEffect(() => {
if (SupplierOpen !== 'own') {
getPurchaseOrders();
fetchPaymentOptions();
}
}, [SupplierOpen]);
useEffect(() => {
getFieldSetup();
}, [categoryId]);
useEffect(() => {
if (initialLoad && SupplierData?.length > 0) {
SetSelfSupplier();
setInitialLoad(false);
}
}, [SupplierData]);
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') || ''
);
}
}, [selectedSupplierData]);
useEffect(() => {
return () => {
debouncedScannerSearch.cancel();
};
}, []);
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 [];
}
}
};
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.',
});
}
};
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);
}
};
const AddSupplierimg = async (subSupplierData) => {
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,
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');
// 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 {
NewImageProducts.push(item?.parsedData);
}
});
// 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);
}
});
if (updatedMatchingProdIds.length > 0) {
matchingProdIds.push(...updatedMatchingProdIds);
}
}
}
}
// 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,
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);
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,
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);
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);
let tempniltax = res?.data?.data?.find(
(item) => item?.ConfigName?.toLowerCase() === 'inclusive'
);
setSelectedPurcTaxType(tempniltax?.ConfigId);
formRef.current?.setFieldsValue({ TaxType: tempniltax?.ConfigId });
}
// else {
// setPurcTaxTypeData(null);
// }
};
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) => {
const value = e.target.value;
formRef.current?.setFieldsValue({ SuppInvoiceNo: value });
setExtractorData((prev) => (prev ? { ...prev, invoiceNo: value } : null));
};
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);
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();
}
};
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,
ProdId,
option,
productsList,
extractedProduct = false,
extractedItem,
existingProducts
) => {
setProductSearchText('');
setSelectedProductName(option?.label ? option?.label : '');
formRef?.current?.resetFields(['VarProdId']);
// setselectedProductVariantData(null);
if (true) {
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
);
setMessageType('error');
setMessageData(
`Product "${existingProduct?.ProdName} - ${existingProduct?.ProdVariantName}" already exists`
);
return null;
} else {
let ProductData1 = (productsList || productData)?.find(
(e) => e.ProdId == ProdId && e?.ProdVariantName == VariantName
);
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,
TaxId: ProductData1?.TaxId,
TaxPercentage: ProductData1?.TaxPercentage,
ProdVariantName: ProductData1?.ProdVariantName,
};
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 || '');
};
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,
matchedProduct.ProdId,
productName
);
if (newProduct) {
setPurchaseData([newProduct, ...PurchaseData]);
}
if (matchedProduct?.ProdVariantPriceDetails?.length < 2) {
setSelectedProductName('');
// setSelectedProductData(null);
}
} else {
setMessageType('error');
setMessageData('The Scanned Product not Available in Product List');
}
}, 300),
[productData]
);
// Clean up debounce on unmount
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);
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,
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,
};
if (!Delete && applicationRestrictedFields.BatchAndModelDetails) {
values[`BatchRef${localId}`] = record.BatchRef;
}
form.setFieldsValue(values);
setDelete(false);
}, [editingKey]);
const save = async (localId) => {
try {
const row = await form.validateFields();
const modifiedObject = {};
for (const key in row) {
// remove localId suffix from field names
if (key.endsWith(localId)) {
const newKey = key?.replace(localId, '');
modifiedObject[newKey] = row[key];
}
}
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === localId ? { ...item, ...modifiedObject } : item
)
);
setEditingKey('');
} catch (err) {
console.error('Save failed:', err);
}
};
const handleKeyPress = async (e, record) => {
if (e.key === 'Enter') {
try {
await form.validateFields();
save(record.localId);
} 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);
};
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>
),
},
{
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 (
<Input
// autoFocus
value={editingQty[record.localId] ?? record.BalanceQty}
onChange={(e) => handleQtyTyping(e, record)}
onBlur={() => commitQtyChange(record)}
onPressEnter={() => commitQtyChange(record)}
inputMode="decimal"
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;
}}
/>
);
},
},
{
title: 'Pur Rate / unit',
dataIndex: 'InwardPrice',
key: 'InwardPrice',
width: 120,
editable: true,
align: 'center',
render: (_, record) => {
const editable = true;
return editable ? (
<Input
value={record.InwardPrice ?? 0}
maxLength={10}
inputMode="decimal"
onChange={(e) => handlePurrateTyping(e, record)}
/>
) : (
<div
// onClick={() => edit(record)} // ✅ THIS is required
style={{ cursor: 'pointer' }}
>
{record.InwardPrice || 0}
</div>
);
},
},
{
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 (
<Input
// onClick={() => edit(record)}
onFocus={(e) => e.stopPropagation()}
value={record?.SellPrice ?? ''}
maxLength={10}
inputMode="decimal"
onChange={(e) => handleSellPriceChange(e, record)}
onBlur={(e) => handleSellPriceBlur(e, record)}
onPressEnter={(e) => handleKeyPress(e, record)}
onInput={handleDecimalInput}
/>
);
},
},
...(selectedAdditionalColumn.includes('Amount Per Piece')
? [
{
title: 'Amount per piece',
dataIndex: 'OnePcsPrice',
key: 'OnePcsPrice',
width: 125,
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: 125,
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: 125,
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,
width: 100,
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,
width: 100,
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: 100,
align: 'center',
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,
width: 100,
align: 'center',
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',
width: 100,
align: 'center',
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,
width: 100,
align: 'center',
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,
width: 100,
align: 'center',
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();
}}
>
<DeleteFilled
style={{
color: '#FF4D4F',
}}
onClick={() => statusFormatters(record)}
/>
</a>
),
},
];
return baseColumns;
}, [editingKey, editingQty, selectedAdditionalColumn]);
const statusFormatters = (record) => {
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+/, '');
}
setEditingQty((prev) => ({
...prev,
[record.localId]: value,
}));
};
const commitQtyChange = (record) => {
const { localId } = record;
const qty = Number(editingQty[localId]) || 0;
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
)
);
form.setFieldsValue({
[`BalanceQty${localId}`]: qty,
[`ReceivedQty${localId}`]: qty,
[`AcceptedQty${localId}`]: qty,
[`RejectedQty${localId}`]: 0,
[`PurcDisc${localId}`]: 0,
});
};
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+/, '');
}
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);
};
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;
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
)
);
};
const handleNoOfPcsBlur = (e, record) => {
const value = e?.target?.value ?? '';
const normalized = value === '' ? '' : String(parseInt(value, 10));
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, NoOfPcs: normalized }
: item
)
);
};
const handleWhSalePriceChange = (e, record) => {
const value = e?.target?.value ?? '';
if (!/^\d*\.?\d*$/.test(value)) return;
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId ? { ...item, WhSalePrice: value } : item
)
);
};
const handleWhSalePriceBlur = (e, record) => {
const value = e?.target?.value ?? '';
const normalized = value === '' ? '' : String(Number(value));
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, WhSalePrice: normalized }
: item
)
);
};
const handleManufDateChange = (dateString, record) => {
const formattedDate =
!dateString || dateString === ''
? undefined
: moment(dateString, ['DD-MM-YYYY']).format('YYYY-MM-DDTHH:mm:ss');
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, ManufDate: formattedDate }
: item
)
);
};
const handleExpDateChange = (dateString, record) => {
const formattedDate =
!dateString || dateString === ''
? undefined
: moment(dateString, ['DD-MM-YYYY']).format('YYYY-MM-DDTHH:mm:ss');
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, ExpDate: formattedDate }
: item
)
);
};
const handleHSNChange = (value, record) => {
// optional: digits only (HSN is numeric)
const cleanValue = value.replace(/\D/g, '');
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, PurchaseHSNCode: cleanValue }
: item
)
);
};
const handleTaxTyping = (e, record) => {
let value = e?.target?.value ?? '';
// allow decimal typing only
if (!/^\d*\.?\d*$/.test(value)) return;
// fix ".5" → "0.5"
if (value.startsWith('.')) value = '0' + value;
setPurchaseData((prev) =>
prev.map((item) =>
item.localId === record.localId
? { ...item, PurchaseTax: value } // keep STRING
: item
)
);
debouncedTaxCalc(value, record);
};
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
)
);
form.setFieldsValue({
[`PurchaseTax${record.localId}`]: normalized,
});
};
const purchaseStatusChange = (value) => {
setPurchaseStatus(value);
};
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
// ===============================
const invalidTaxItems = PurchaseData?.filter((item) => {
const tax = item?.PurchaseTax;
if (tax === undefined || tax === '' || tax === null) return false;
const num = Number(tax);
return isNaN(num) || num < 0 || num > 99;
});
if (invalidTaxItems?.length > 0) {
setMessageType('error');
setMessageData(
'Please enter valid tax percentage (099%) for all items'
);
return;
}
if (
PurchaseData?.some(
(item) =>
item.SellPrice &&
item.MRP &&
Number(item.SellPrice) > Number(item.MRP)
)
) {
setMessageType('error');
setMessageData('Selling Price cannot be greater than MRP');
return;
}
if (!PurchaseData || PurchaseData?.length === 0) {
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) {
setMessageType('error');
setMessageData('Please enter Qty and Amount for at least one product');
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),
});
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',
];
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,
};
});
const isPaidSelected = PurchaseTypeData?.some(
(item) =>
item.ConfigId === SelectedPurchaseType && item.ConfigName === 'Cash'
);
if (
(SupplierOpen !== 'own' || selectedSupplierName) &&
selectedSupplierData &&
isPaidSelected
) {
postData.PaymentAmount = PaymentAmount;
postData.PaymentType = selectedPaymentMode;
} else {
postData.PaymentType = PaymentType;
}
postData.ProdDetails = processedData;
if (formType === 'edit') {
postData.ProdId = editstate?.ProdId;
postData.InwardId = editstate?.InwardId;
postData.InwardDtlId = editstate?.InwardDtlId;
postData.UpdatedBy = UserId;
}
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',
},
};
}
if (response?.data?.statusCode === 1) {
navigate(`${subDirectory}setting/purchase-entry/`, {
state: {
Notiffy: {
messageType: 'success',
messageData: response?.data?.response,
},
},
});
} else {
setMessageType('error');
setMessageData(response?.data?.response);
}
};
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('-');
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);
setSelectedProductName(null);
// setselectedProductVariantData(null);
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) => {
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);
};
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();
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');
}
};
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' }}>
<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>
</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%' }}
>
<div className="purchase-entry-form">
<div className="formDivS">
<div className="inputFormSTOCKFORM">
<div className="purchaseDetails">
<div className="supplierInfo">
{/* <h2 className="supplier-dtls">Supplier Details</h2> */}
<div className="supplierInfo1">
{!orderType && (
<Form.Item
name="PurchaseOrderNo"
rules={[
{
required: true,
message: 'Please Select Purchase Order No.',
},
]}
>
<FloatLabel
label={
<label className="required">
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
? true
: !orderType
? true
: false
}
/>
<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>
{!selectedSupplierName && (
<RadioGrpButton
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>
</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">
{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>
}
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'
);
}
return Promise.resolve();
},
},
]}
>
<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>
</>
)}
<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('_');
const product = productData.find(
(p) =>
p.ProdId === prodId &&
(p.ProdVariantName || 'Variant1') ===
variantName
);
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}`,
}))
: []
}
// 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);
}}
onSearch={handleProductSearch}
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}
/>
</Form.Item>
{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>
</div>
{/* <div
className="Product-table stockformTabele"
style={{
maxwidth: '500px',
overflowX: 'auto',
scrollbarWidth: 'thin',
maxHeight: '50vh',
}}
>
<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}
/>
</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>
</div>
{OpenSupplierModel && (
<DefaultModal
title="Add Supplier"
open={OpenSupplierModel}
footer={true}
buttonText="Submit"
handleSubmit={submitSupplier}
handleCancel={handleSupplier}
>
<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>}
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>}
/>
</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>}
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}
/>
</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,
''
))
}
/>
</Form.Item>
<Form.Item
name="SuppEmail"
rules={[{ validator: validateEmail }]}
>
<InputField
label={<label>MailId</label>}
autocomplete="off"
// isOnChange={formType == 'edit' ? true : false}
/>
</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>}
autocomplete="off"
// isOnChange={formType == 'edit' ? true : false}
/>
</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>}
autocomplete="off"
maxLength="10"
// isOnChange={formType == 'edit' ? true : false}
/>
</Form.Item>
<Form.Item
name="SuppPOCEmail"
rules={[{ validator: validateEmail }]}
>
<InputField
label={<label className="">POC MailId</label>}
autocomplete="off"
// isOnChange={formType == 'edit' ? true : false}
/>
</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>
)}
<div>
{imeiSerialOpen && (
<DefaultModal
title="IMEI /Serial Number / Mac Id"
open={imeiSerialOpen}
footer={true}
width={600}
buttonText="Submit"
handleSubmit={handleModelDataSumbitOrClose}
handleCancel={handleModelDataSumbitOrClose}
>
<div className="imeiSNTable">
<Table
components={components}
rowClassName={() => 'editable-row'}
bordered
dataSource={dataSource}
columns={IMEIcolumns}
pagination={{ showSizeChanger: false }}
/>
</div>
</DefaultModal>
)}
<InvoiceImageExtractorModal
visible={extractorModalVisible}
onClose={() => setExtractorModalVisible(false)}
onApply={handleExtractorApply}
productData={productData}
supplierData={SupplierData}
uomData={UomData}
setVisible={setExtractorModalVisible}
/>
</div>
</div>
<style>{`
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
`}</style>
</div>
);
};
export default StockForm;