4372 lines
146 KiB
JavaScript
4372 lines
146 KiB
JavaScript
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 "../../Styles/OverAllStyle/OverAllStyle.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 (0–99%) 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: </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;
|