1528 lines
60 KiB
JavaScript
1528 lines
60 KiB
JavaScript
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||
import { useNavigate, useLocation } from 'react-router-dom';
|
||
import { IoBagRemove } from 'react-icons/io5';
|
||
import { useDispatch } from 'react-redux';
|
||
import {
|
||
ArrowRightOutlined,
|
||
PlusCircleOutlined,
|
||
EditFilled,
|
||
} from '@ant-design/icons';
|
||
import { Form, Tooltip, Table, Input, Collapse, Select } from 'antd';
|
||
import { IoAddCircleSharp } from 'react-icons/io5';
|
||
import { InputField } from '../../Components/Forms/InputField.jsx';
|
||
import Buttons from '../../Components/Forms/Buttons.jsx';
|
||
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 } from '../../Services/Others.js';
|
||
import { DatePicProd } from '../../Components/Forms/DatePickerProduct.jsx';
|
||
import moment from 'moment';
|
||
import './openingStockForm.scss';
|
||
import {
|
||
getProductStockDetails,
|
||
postOpeningStock,
|
||
} from '../../Features/PurchaseOrder/PurchaseOrder.js';
|
||
|
||
const subDirectory = import.meta.env.BASE_URL;
|
||
const openingStockForm = () => {
|
||
const { Panel } = Collapse;
|
||
const formRef = useRef(null);
|
||
const productAddInfoRef = 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 [messageType, setMessageType] = useState(null);
|
||
const [messageData, setMessageData] = useState(null);
|
||
|
||
const [PurchaseData, setPurchaseData] = useState([]);
|
||
const [additionalInfoModal, setAdditionalInfoModal] = useState(false);
|
||
const [selectedRowRecord, setSelectedRowRecord] = useState({});
|
||
|
||
console.log(selectedRowRecord, 'selectedRowRecord');
|
||
const [Delete, setDelete] = useState(false);
|
||
const [ProductStockData, seProductStockData] = useState([]);
|
||
const [filteredProductStockData, setFilteredProductStockData] = useState([]);
|
||
const [eliminatedProducts, setEliminatedProducts] = useState([]);
|
||
const [eliminateModal, setEliminateModal] = useState(false);
|
||
const [additionalStockEntries, setAdditionalStockEntries] = useState([]);
|
||
const [searchText, setSearchText] = useState('');
|
||
const [activeAccordionKeys, setActiveAccordionKeys] = useState(['1']);
|
||
const [hasInitializedEditing, setHasInitializedEditing] = useState(false);
|
||
console.log(additionalStockEntries, 'additionalStockEntries');
|
||
console.log(filteredProductStockData, 'filteredProductStockData');
|
||
const items = [
|
||
{
|
||
name: 'Home',
|
||
link: `${subDirectory}app-page/home`,
|
||
},
|
||
|
||
{
|
||
name: 'Opening Stock',
|
||
link: `${subDirectory}setting/opening-stock`,
|
||
},
|
||
{
|
||
name: 'New',
|
||
link: null,
|
||
},
|
||
];
|
||
useEffect(() => {
|
||
dispatch(changeBreadCrumb({ items: items }));
|
||
|
||
ProductStockFun();
|
||
}, []);
|
||
|
||
const ProductStockFun = async () => {
|
||
try {
|
||
const Data = { AppId, CompId, BranchId, type: 'A' };
|
||
let response = await dispatch(getProductStockDetails(Data)).unwrap();
|
||
if (response?.data?.statusCode === 1) {
|
||
seProductStockData(response?.data?.data);
|
||
setFilteredProductStockData(response?.data?.data);
|
||
}
|
||
} catch (err) {
|
||
console.error('Error fetching stock details:', err);
|
||
}
|
||
};
|
||
|
||
const isEditing = (record, index) => index === editingKey;
|
||
|
||
const edit = (record, index) => {
|
||
form.setFieldsValue({ ...record });
|
||
|
||
setEditingKey(index);
|
||
setindex(index);
|
||
|
||
if (Delete) {
|
||
form.setFieldsValue({
|
||
TotalQty: { [index]: undefined },
|
||
TotalPiece: { [index]: undefined },
|
||
});
|
||
setDelete(false);
|
||
} else {
|
||
form.setFieldsValue({
|
||
TotalQty: { [index]: record?.TotalQty },
|
||
TotalPiece: { [index]: record?.TotalPiece },
|
||
});
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
if (!hasInitializedEditing && filteredProductStockData?.length > 0) {
|
||
edit(filteredProductStockData[0], 0);
|
||
setHasInitializedEditing(true);
|
||
}
|
||
}, [filteredProductStockData, hasInitializedEditing]);
|
||
|
||
const save = async (index) => {
|
||
try {
|
||
const row = await form?.validateFields();
|
||
|
||
const modifiedObject = {};
|
||
for (const key in row) {
|
||
const newKey = key.slice(0, -1); // Remove the last character ("0") from the key
|
||
modifiedObject[newKey] = row[key];
|
||
}
|
||
|
||
const newData = [...filteredProductStockData];
|
||
if (index > -1) {
|
||
const item = newData[index];
|
||
newData.splice(index, 1, { ...item, ...modifiedObject });
|
||
setFilteredProductStockData(newData);
|
||
setEditingKey('');
|
||
}
|
||
} catch (err) {
|
||
console.error('Save failed:', err);
|
||
}
|
||
};
|
||
|
||
const handleKeyPress = async (e, record, index) => {
|
||
if (e.key === 'Enter') {
|
||
try {
|
||
await form.validateFields();
|
||
save(index);
|
||
} catch (error) {
|
||
console.error('Save failed:', error);
|
||
}
|
||
}
|
||
};
|
||
|
||
const openAdditionalInfoModal = (record, index) => {
|
||
console.log(record, 'recordrecord');
|
||
|
||
if (
|
||
!record?.TotalQty ||
|
||
record?.TotalQty === '' ||
|
||
record?.TotalQty === 0
|
||
) {
|
||
setMessageType('error');
|
||
setMessageData('Please enter Total Qty first');
|
||
return;
|
||
}
|
||
|
||
// if (!record?.TotalPiece || record?.TotalPiece === "" || record?.TotalPiece === 0) {
|
||
// setMessageType("error")
|
||
// setMessageData("Please enter Total Pieces first")
|
||
// return
|
||
// }
|
||
|
||
setSelectedRowRecord(record);
|
||
|
||
// Load existing modal details for this product
|
||
if (record.modalDetails && record.modalDetails.length > 0) {
|
||
setAdditionalStockEntries(record.modalDetails);
|
||
} else {
|
||
setAdditionalStockEntries([]);
|
||
}
|
||
|
||
setAdditionalInfoModal(true);
|
||
};
|
||
|
||
const columns = [
|
||
{
|
||
title: 'SI.NO',
|
||
align: 'center',
|
||
key: 'sno',
|
||
render: (text, object, index) => (
|
||
<a className="openingStock-sno-link">{index + 1}</a>
|
||
),
|
||
},
|
||
{
|
||
title: 'Product Name',
|
||
dataIndex: 'ProdName',
|
||
key: 'ProdName',
|
||
align: 'left',
|
||
render: (text, record, index) => (
|
||
<a className="openingStock-product-name-link">
|
||
{record?.ProdName + '(' + record?.ProdVariantName + ')'}
|
||
</a>
|
||
),
|
||
},
|
||
// {
|
||
// title: 'Uom',
|
||
// dataIndex: 'UomName',
|
||
// key: 'UOMName',
|
||
// },
|
||
{
|
||
title: 'Total Qty',
|
||
dataIndex: 'TotalQty',
|
||
key: 'TotalQty',
|
||
editable: true,
|
||
render: (text, record, index) => {
|
||
return isEditing(record, index) ? (
|
||
<Form.Item
|
||
name={'TotalQty' + index}
|
||
rules={[
|
||
{
|
||
required: true,
|
||
pattern: /^(?!0+$)(?!0+\.0*$)\d*\.?\d+$/,
|
||
message: 'Please enter valid quantity',
|
||
},
|
||
{
|
||
validator: (_, value) => {
|
||
if (value && value.toString().length > 10) {
|
||
return Promise.reject(
|
||
'Quantity cannot exceed 10 characters'
|
||
);
|
||
}
|
||
return Promise.resolve();
|
||
},
|
||
},
|
||
]}
|
||
>
|
||
<Input
|
||
onPressEnter={(e) => handleKeyPress(e, record, index)}
|
||
onBlur={(e) => handleQtyChange(e, record)}
|
||
onChange={(e) => handleQtyChange(e, record, index)}
|
||
inputMode="decimal"
|
||
onInput={(e) => {
|
||
let value = e.target.value.replace(/[^0-9.]/g, '');
|
||
|
||
if (value.startsWith('.')) {
|
||
value = '0' + value;
|
||
}
|
||
|
||
const dotCount = (value.match(/\./g) || []).length;
|
||
if (dotCount > 1) {
|
||
const firstDotIndex = value.indexOf('.');
|
||
value =
|
||
value.substring(0, firstDotIndex + 1) +
|
||
value.substring(firstDotIndex + 1).replace(/\./g, '');
|
||
}
|
||
|
||
e.target.value = value;
|
||
}}
|
||
/>
|
||
</Form.Item>
|
||
) : (
|
||
text
|
||
);
|
||
},
|
||
},
|
||
{
|
||
title: 'Enter The Pieces',
|
||
dataIndex: 'TotalPiece',
|
||
key: 'TotalPiece',
|
||
editable: true,
|
||
render: (text, record, index) => {
|
||
return isEditing(record, index) ? (
|
||
<Form.Item
|
||
name={'TotalPiece' + index}
|
||
rules={[
|
||
{
|
||
required: true,
|
||
pattern: /^(?!0+$)(?!0+\.0*$)\d*\.?\d+$/,
|
||
message: 'Please enter valid quantity',
|
||
},
|
||
{
|
||
validator: (_, value) => {
|
||
if (value && value.toString().length > 10) {
|
||
return Promise.reject(
|
||
'Quantity cannot exceed 10 characters'
|
||
);
|
||
}
|
||
return Promise.resolve();
|
||
},
|
||
},
|
||
]}
|
||
>
|
||
<Input
|
||
onPressEnter={(e) => handleKeyPress(e, record, index)}
|
||
onBlur={(e) => handleTotalPieceChange(e, record)}
|
||
onChange={(e) => handleTotalPieceChange(e, record, index)}
|
||
inputMode="decimal"
|
||
onInput={(e) => {
|
||
let value = e.target.value.replace(/[^0-9.]/g, '');
|
||
|
||
if (value.startsWith('.')) {
|
||
value = '0' + value;
|
||
}
|
||
|
||
const dotCount = (value.match(/\./g) || []).length;
|
||
if (dotCount > 1) {
|
||
const firstDotIndex = value.indexOf('.');
|
||
value =
|
||
value.substring(0, firstDotIndex + 1) +
|
||
value.substring(firstDotIndex + 1).replace(/\./g, '');
|
||
}
|
||
|
||
e.target.value = value;
|
||
}}
|
||
/>
|
||
</Form.Item>
|
||
) : (
|
||
text
|
||
);
|
||
},
|
||
},
|
||
|
||
// {
|
||
// title: 'Amount',
|
||
// dataIndex: 'Amount',
|
||
// key: 'Amount',
|
||
// editable: true,
|
||
// },
|
||
{
|
||
title: 'Additional Info',
|
||
dataIndex: 'AdditionalInfo',
|
||
key: 'AdditionalInfo',
|
||
width: 120,
|
||
align: 'center',
|
||
render: (text, record, index) => {
|
||
return (
|
||
<>
|
||
{' '}
|
||
<IoAddCircleSharp
|
||
className="openingStock-additional-info-icon shape-preview"
|
||
onClick={() => openAdditionalInfoModal(record, index)}
|
||
/>
|
||
</>
|
||
);
|
||
},
|
||
},
|
||
// {
|
||
// title: "Action",
|
||
// dataIndex: "Action",
|
||
// key: "Action",
|
||
// align: "center",
|
||
// render: (_, record, index) => (
|
||
// <a
|
||
// onClick={(e) => {
|
||
// e.stopPropagation();
|
||
// }}
|
||
// >
|
||
// <DeleteFilled
|
||
// style={{
|
||
// color: "#FF4D4F",
|
||
// }}
|
||
// onClick={() => statusFormatters(record, index)}
|
||
// />
|
||
|
||
// </a>
|
||
// ),
|
||
// },
|
||
];
|
||
|
||
const statusFormatters = (record, index) => {
|
||
const Data = PurchaseData.filter((_, i) => i !== index);
|
||
setDelete(true);
|
||
form.setFieldsValue({
|
||
[`TotalQty${index}`]: undefined,
|
||
[`ReceivedQty${index}`]: undefined,
|
||
// [`AcceptedQty${index}`]: undefined,
|
||
// [`RejectedQty${index}`]: undefined,
|
||
// [`Amount${index}`]: undefined,
|
||
// [`FreeQty${index}`]: undefined,
|
||
// [`InwardPrice${index}`]: undefined,
|
||
// [`MRP${index}`]: undefined,
|
||
// [`PurcDisc${index}`]: undefined,
|
||
// [`SellPrice${index}`]: undefined,
|
||
// [`WhSalePrice${index}`]: undefined,
|
||
// [`offerSalePrice${index}`]: undefined,
|
||
// [`splSalePrice${index}`]: undefined
|
||
});
|
||
|
||
setPurchaseData(Data);
|
||
|
||
formRef?.current?.setFieldsValue({ ProdId: null });
|
||
};
|
||
|
||
const handleQtyChange = (e, record, index) => {
|
||
const inputValue = e.target.value;
|
||
|
||
// if (!inputValue) return;
|
||
|
||
if (!/^\d*\.?\d*$/.test(inputValue)) {
|
||
setMessageType('error');
|
||
setMessageData('Only numeric values are allowed');
|
||
return;
|
||
}
|
||
|
||
const qty = inputValue;
|
||
|
||
// Update the specific product by ProdId to avoid index mismatches when the
|
||
// displayed list is filtered (search). Using index from the filtered map
|
||
// can point to the wrong item in the underlying array.
|
||
const newData = filteredProductStockData.map((item) => {
|
||
if (
|
||
item?.ProdId === record?.ProdId &&
|
||
item?.ProdVariantName === record?.ProdVariantName
|
||
) {
|
||
const { modalDetails, ...rest } = item;
|
||
return {
|
||
...rest,
|
||
TotalQty: qty,
|
||
};
|
||
}
|
||
return item;
|
||
});
|
||
|
||
setFilteredProductStockData(newData);
|
||
// seProductStockData(newData);
|
||
};
|
||
|
||
const handleTotalPieceChange = (e, record, index) => {
|
||
if (record?.OnePcsAvailable !== 'Y') {
|
||
e.target.value = record?.TotalPiece ?? '';
|
||
return;
|
||
}
|
||
|
||
const inputValue = e.target.value;
|
||
|
||
if (!inputValue) return;
|
||
|
||
if (record?.OnePcsAvailable === 'N') {
|
||
setMessageType('error');
|
||
setMessageData('Total Piece entry not allowed when OnePcsAvailable is N');
|
||
return;
|
||
}
|
||
|
||
if (!/^\d*\.?\d*$/.test(inputValue)) {
|
||
setMessageType('error');
|
||
setMessageData('Only numeric values are allowed');
|
||
return;
|
||
}
|
||
|
||
const qty = inputValue;
|
||
|
||
// Update by ProdId instead of using the index from the filtered view.
|
||
const newData = filteredProductStockData.map((item) => {
|
||
if (
|
||
item?.ProdId === record?.ProdId &&
|
||
item?.ProdVariantName === record?.ProdVariantName
|
||
) {
|
||
return {
|
||
...item,
|
||
TotalPiece: qty,
|
||
};
|
||
}
|
||
return item;
|
||
});
|
||
|
||
setFilteredProductStockData(newData);
|
||
};
|
||
|
||
const onFinish = async () => {
|
||
let ProductDetails = filteredProductStockData
|
||
?.filter((item) => item?.TotalQty > 0 || item?.TotalPiece > 0)
|
||
?.map((item) => {
|
||
return {
|
||
ProdId: item?.ProdId,
|
||
ProdVariantName: item?.ProdVariantName,
|
||
OnePcsAvailable: item?.OnePcsAvailable || 'N',
|
||
TotalQty: item?.TotalQty,
|
||
TotalPcs: item?.TotalPiece || 0,
|
||
CreatedBy: UserId,
|
||
InwardDetails: item?.modalDetails
|
||
? item?.modalDetails?.map((item1) => {
|
||
return {
|
||
BatchRef: item1?.BatchRef,
|
||
AcceptedQty: item1?.Qty,
|
||
BalanceQty: item1?.Qty,
|
||
InwardPrice: item1?.SellPrice || 0,
|
||
MRP: item1?.MRP || 0,
|
||
SellPrice: item1?.SellPrice || 0,
|
||
WhSalePrice: item1?.WhSalePrice || 0,
|
||
OfferPrice: item1?.OfferPrice || 0,
|
||
SpecialPrice: item1?.SpecialPrice || 0,
|
||
InwardDate: item1?.ManufDate?.toISOString(),
|
||
ProdVariantName: item?.ProdVariantName,
|
||
OnePcsPrice: item1?.AmountPerPiece || 0,
|
||
NoOfPcs: item1?.OnePieceQty || 0,
|
||
ModelNumber: item1?.ModelNumber,
|
||
OnePcsAvailable: item?.OnePcsAvailable || 'N',
|
||
};
|
||
})
|
||
: [],
|
||
};
|
||
});
|
||
let PostData = {
|
||
AppId: AppId,
|
||
CompId: CompId,
|
||
BranchId: BranchId,
|
||
ProductDetails: ProductDetails,
|
||
};
|
||
try {
|
||
let Response = await dispatch(postOpeningStock(PostData))?.unwrap();
|
||
if (Response?.data?.statusCode === 1) {
|
||
setMessageType('success');
|
||
setMessageData('Stock added successfully');
|
||
navigate(`${subDirectory}setting/opening-stock/`, {
|
||
state: {
|
||
Notiffy: {
|
||
messageType: 'success',
|
||
messageData: 'Stock added successfully',
|
||
},
|
||
},
|
||
});
|
||
} else {
|
||
setMessageType('error');
|
||
setMessageData(
|
||
Response?.data?.message || 'Failed to add opening stock'
|
||
);
|
||
}
|
||
} catch (error) {
|
||
console.log(error, 'error');
|
||
}
|
||
};
|
||
|
||
const onComplete = useCallback(() => {
|
||
setMessageData(null);
|
||
setMessageType(null);
|
||
}, []);
|
||
|
||
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,
|
||
});
|
||
}
|
||
}, [additionalInfoModal]);
|
||
|
||
const handleMRPChange = (e) => {
|
||
let value = e?.target?.value === '' ? 0 : parseFloat(e?.target?.value);
|
||
productAddInfoRef?.current?.setFieldsValue({ SellPrice: null, MRP: value });
|
||
setSelectedRowRecord((prev) => ({ ...prev, SellPrice: null, MRP: value }));
|
||
productAddInfoRef?.current?.validateFields();
|
||
};
|
||
|
||
const handleAmountPerPiecechange = (e) => {
|
||
const inputValue = e?.target?.value;
|
||
|
||
const amountPerPiece = inputValue === '' ? '' : parseFloat(inputValue);
|
||
|
||
if (selectedRowRecord?.OnePcsAvailable === 'Y') {
|
||
setSelectedRowRecord((prev) => ({
|
||
...prev,
|
||
AmountPerPiece: amountPerPiece,
|
||
}));
|
||
}
|
||
};
|
||
|
||
const handleNumberofPieceinsidechange = (e) => {
|
||
const inputValue = e?.target?.value;
|
||
|
||
const numberOfPieceInside = inputValue === '' ? '' : parseFloat(inputValue);
|
||
|
||
if (selectedRowRecord?.OnePcsAvailable === 'Y') {
|
||
setSelectedRowRecord((prev) => ({
|
||
...prev,
|
||
NumberofPieceinside: numberOfPieceInside,
|
||
}));
|
||
}
|
||
};
|
||
|
||
const handleManufactureDate = (date, dateString) => {
|
||
const formattedDate =
|
||
dateString === ''
|
||
? undefined
|
||
: moment(dateString, ['DD-MM-YYYY']).format('YYYY-MM-DDTHH:mm:ss');
|
||
productAddInfoRef?.current?.setFieldsValue({
|
||
ManufDate: date,
|
||
});
|
||
setSelectedRowRecord((prev) => ({ ...prev, ManufDate: formattedDate }));
|
||
};
|
||
|
||
const handleBatchRefChange = (e) => {
|
||
const value = parseInt(e?.target?.value);
|
||
if (!/^\d*\.?\d*$/.test(value)) {
|
||
return;
|
||
}
|
||
productAddInfoRef?.current?.setFieldsValue({
|
||
BatchRef: value,
|
||
});
|
||
setSelectedRowRecord((prev) => ({ ...prev, BatchRef: value }));
|
||
};
|
||
|
||
const handleModelNoChange = (e) => {
|
||
const value = parseInt(e?.target?.value);
|
||
if (!/^\d*\.?\d*$/.test(value)) {
|
||
return;
|
||
}
|
||
productAddInfoRef?.current?.setFieldsValue({
|
||
ModelNumber: value,
|
||
});
|
||
setSelectedRowRecord((prev) => ({ ...prev, ModelNumber: value }));
|
||
};
|
||
|
||
const handleAddtnlDtlsSubmit = async (values) => {
|
||
const totalQty = parseFloat(selectedRowRecord?.TotalQty) || 0;
|
||
const currentQty = parseFloat(values?.Qty) || 0;
|
||
const existingQtySum = additionalStockEntries.reduce(
|
||
(sum, entry) => sum + (parseFloat(entry.Qty) || 0),
|
||
0
|
||
);
|
||
const totalUsedQty = existingQtySum + currentQty;
|
||
|
||
if (totalUsedQty !== totalQty) {
|
||
setMessageType('error');
|
||
setMessageData(
|
||
`Total quantity must equal ${totalQty}. Current total: ${totalUsedQty}`
|
||
);
|
||
return;
|
||
}
|
||
|
||
const newData = { ...values };
|
||
setAdditionalStockEntries((prev) => [...prev, newData]);
|
||
|
||
const updatedProductStockData = filteredProductStockData.map((product) => {
|
||
if (
|
||
product.ProdId === selectedRowRecord.ProdId &&
|
||
product.ProdVariantName === selectedRowRecord.ProdVariantName
|
||
) {
|
||
const details = [];
|
||
|
||
if (
|
||
Array.isArray(additionalStockEntries) &&
|
||
additionalStockEntries.length > 0
|
||
) {
|
||
details.push(...additionalStockEntries);
|
||
}
|
||
|
||
if (newData?.Qty) {
|
||
details.push(newData);
|
||
}
|
||
|
||
if (details.length > 0) {
|
||
return {
|
||
...product,
|
||
modalDetails: details,
|
||
};
|
||
}
|
||
}
|
||
return product;
|
||
});
|
||
setFilteredProductStockData(updatedProductStockData);
|
||
setAdditionalInfoModal(false);
|
||
setSelectedRowRecord({});
|
||
setActiveAccordionKeys([]);
|
||
await productAddInfoRef?.current?.resetFields();
|
||
// await handleAdditionalInfoClose();
|
||
};
|
||
|
||
const handleAdditionalInfoClose = async () => {
|
||
setAdditionalInfoModal(false);
|
||
setSelectedRowRecord({});
|
||
await productAddInfoRef?.current?.resetFields();
|
||
};
|
||
|
||
const handleAddNew = async () => {
|
||
try {
|
||
const values = await productAddInfoRef?.current?.validateFields();
|
||
if (!values?.Qty) {
|
||
setMessageType('error');
|
||
setMessageData('Please enter Qty before adding new entry');
|
||
return;
|
||
}
|
||
if (!values?.MRP) {
|
||
setMessageType('error');
|
||
setMessageData('Please enter MRP before adding new entry');
|
||
return;
|
||
}
|
||
if (!values?.SellPrice) {
|
||
setMessageType('error');
|
||
setMessageData('Please enter Selling Price before adding new entry');
|
||
return;
|
||
}
|
||
const newData = { ...selectedRowRecord, ...values };
|
||
|
||
const updatedEntries = [...additionalStockEntries, newData];
|
||
setAdditionalStockEntries(updatedEntries);
|
||
|
||
// Update ProductStockData with modal details
|
||
const updatedProductStockData = filteredProductStockData.map(
|
||
(product) => {
|
||
if (
|
||
product.ProdId === selectedRowRecord.ProdId &&
|
||
product.ProdVariantName === selectedRowRecord.ProdVariantName
|
||
) {
|
||
return {
|
||
...product,
|
||
modalDetails: updatedEntries,
|
||
};
|
||
}
|
||
return product;
|
||
}
|
||
);
|
||
seProductStockData(updatedProductStockData);
|
||
setFilteredProductStockData(
|
||
updatedProductStockData.filter(
|
||
(item) => !eliminatedProducts.includes(item.ProdId)
|
||
)
|
||
);
|
||
|
||
// Reset form for new entry
|
||
setSelectedRowRecord({
|
||
...selectedRowRecord,
|
||
Qty: '',
|
||
OnePieceQty: '',
|
||
MRP: '',
|
||
SellPrice: '',
|
||
BatchRef: '',
|
||
ModelNumber: '',
|
||
ManufDate: '',
|
||
AmountPerPiece: '',
|
||
NumberofPieceinside: '',
|
||
});
|
||
|
||
await productAddInfoRef?.current?.resetFields();
|
||
|
||
// Close all accordions after adding new entry
|
||
setActiveAccordionKeys([]);
|
||
} catch (error) {
|
||
console.error('Validation failed:', error);
|
||
}
|
||
};
|
||
|
||
const handleEditEntry = (index) => {
|
||
const entryToEdit = additionalStockEntries[index];
|
||
setSelectedRowRecord((prev) => ({ ...prev, ...entryToEdit }));
|
||
productAddInfoRef?.current?.setFieldsValue(entryToEdit);
|
||
|
||
setAdditionalStockEntries((prev) => prev.filter((_, i) => i !== index));
|
||
};
|
||
|
||
const handleDeleteEntry = (index) => {
|
||
setAdditionalStockEntries((prev) => prev.filter((_, i) => i !== index));
|
||
};
|
||
|
||
return (
|
||
<div className="pageOverAll">
|
||
<div className="userPage">
|
||
<div className="userPageContent">
|
||
<Messages
|
||
messageType={messageType}
|
||
messageData={messageData}
|
||
onComplete={onComplete}
|
||
/>
|
||
<div className="purchase-entry-fh">
|
||
<div className="formName">
|
||
<FormHeader title={'Opening Stock Entry'} />
|
||
{/* <div className="productMaster-input">
|
||
<Buttons
|
||
buttonText={ "Add New"}
|
||
handleSubmit={() => handelAddButton()}
|
||
color="901D77"
|
||
icon={<PlusOutlined />}
|
||
/>
|
||
</div> */}
|
||
{/* <div className="uploadexcelBtn">
|
||
<Buttons
|
||
buttonText="Upload Excel"
|
||
className="tertiary_Button"
|
||
handleSubmit={openModal}
|
||
disabled={false}
|
||
icon={<FiUploadCloud size={18} />}
|
||
/>
|
||
</div> */}
|
||
<Tooltip title="Ignore Products">
|
||
<button
|
||
onClick={() => setEliminateModal(true)}
|
||
className="openingStock-ignoreProductBTN"
|
||
>
|
||
<IoBagRemove />
|
||
{/* Ignore Products */}
|
||
</button>
|
||
</Tooltip>
|
||
|
||
{/* <div
|
||
style={{
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: '7px',
|
||
padding: '6px 12px',
|
||
backgroundColor: '#059645',
|
||
borderRadius: '8px',
|
||
color: 'white',
|
||
cursor: 'pointer',
|
||
fontFamily: 'Poopies',
|
||
fontWeight: '400',
|
||
whiteSpace: "nowrap",
|
||
fontSize: "15px"
|
||
|
||
}}
|
||
onClick={() => navigate(`${subDirectory}setting/purchase-entry`)}
|
||
>
|
||
<FaEye /> <p> View Entry List</p>
|
||
</div> */}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="formDiv">
|
||
<Form
|
||
ref={formRef}
|
||
initialValues={editstate}
|
||
className="formDivAnt"
|
||
onFinish={onFinish}
|
||
>
|
||
<div className="purchase-entry-form">
|
||
<div className="formDivS">
|
||
<div className="inputForm">
|
||
<div className="openingStock-search-input">
|
||
<Input
|
||
placeholder="Search products..."
|
||
value={searchText}
|
||
onChange={(e) => setSearchText(e.target.value)}
|
||
className="openingStock-search-input-field"
|
||
allowClear
|
||
/>
|
||
</div>
|
||
|
||
<div className="Product-table openingStock-product-table">
|
||
<style>
|
||
{`
|
||
.Product-table .ant-form-item-explain-error {
|
||
display: none !important;
|
||
}
|
||
.Product-table .ant-form-item {
|
||
margin-bottom: 0 !important;
|
||
}
|
||
`}
|
||
</style>
|
||
<Form form={form} component={false}>
|
||
<table className="openingStock-table-wrapper">
|
||
<thead className="openingStock-table-header">
|
||
<tr className="openingStock-table-header-row">
|
||
<th
|
||
className="openingStock-table-header-cell"
|
||
style={{ width: '20px' }}
|
||
>
|
||
S.NO
|
||
</th>
|
||
<th className="openingStock-table-header-cell-left">
|
||
Product Name
|
||
</th>
|
||
<th className="openingStock-table-header-cell">
|
||
Total Qty
|
||
</th>
|
||
<th className="openingStock-table-header-cell">
|
||
Total Pieces
|
||
</th>
|
||
<th className="openingStock-table-header-cell">
|
||
Additional Info
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{filteredProductStockData
|
||
.filter(
|
||
(record) =>
|
||
record.ProdName.toLowerCase().includes(
|
||
searchText.toLowerCase()
|
||
) ||
|
||
record.ProdVariantName.toLowerCase().includes(
|
||
searchText.toLowerCase()
|
||
)
|
||
)
|
||
.map((record, index) => (
|
||
<tr
|
||
key={index}
|
||
className="openingStock-table-row"
|
||
>
|
||
<td className="openingStock-table-cell">
|
||
{index + 1}
|
||
</td>
|
||
<td className="openingStock-table-cell-left">
|
||
{record?.ProdName} (
|
||
{record?.ProdVariantName})
|
||
</td>
|
||
<td className="openingStock-table-cell-input">
|
||
<Input
|
||
className="openingStock-input-field"
|
||
value={record?.TotalQty ?? ''}
|
||
onChange={(e) =>
|
||
handleQtyChange(e, record, index)
|
||
}
|
||
inputMode="decimal"
|
||
onBlur={(e) =>
|
||
handleQtyChange(e, record, index)
|
||
}
|
||
onInput={(e) => {
|
||
let value = e.target.value.replace(
|
||
/[^0-9.]/g,
|
||
''
|
||
);
|
||
if (value.startsWith('.'))
|
||
value = '0' + value;
|
||
const dotCount = (
|
||
value.match(/\./g) || []
|
||
).length;
|
||
if (dotCount > 1) {
|
||
const firstDotIndex =
|
||
value.indexOf('.');
|
||
value =
|
||
value.substring(
|
||
0,
|
||
firstDotIndex + 1
|
||
) +
|
||
value
|
||
.substring(firstDotIndex + 1)
|
||
.replace(/\./g, '');
|
||
}
|
||
e.target.value = value;
|
||
}}
|
||
/>
|
||
</td>
|
||
<td className="openingStock-table-cell-input">
|
||
<Input
|
||
className="openingStock-input-field"
|
||
value={record?.TotalPiece ?? ''}
|
||
onChange={(e) =>
|
||
handleTotalPieceChange(e, record, index)
|
||
}
|
||
inputMode="decimal"
|
||
onBlur={(e) =>
|
||
handleTotalPieceChange(e, record, index)
|
||
}
|
||
disabled={record?.OnePcsAvailable !== 'Y'}
|
||
onInput={(e) => {
|
||
let value = e.target.value.replace(
|
||
/[^0-9.]/g,
|
||
''
|
||
);
|
||
if (value.startsWith('.'))
|
||
value = '0' + value;
|
||
const dotCount = (
|
||
value.match(/\./g) || []
|
||
).length;
|
||
if (dotCount > 1) {
|
||
const firstDotIndex =
|
||
value.indexOf('.');
|
||
value =
|
||
value.substring(
|
||
0,
|
||
firstDotIndex + 1
|
||
) +
|
||
value
|
||
.substring(firstDotIndex + 1)
|
||
.replace(/\./g, '');
|
||
}
|
||
e.target.value = value;
|
||
}}
|
||
/>
|
||
</td>
|
||
<td className="openingStock-table-cell">
|
||
<IoAddCircleSharp
|
||
className="openingStock-add-icon"
|
||
onClick={(e) => {
|
||
e.stopPropagation();
|
||
openAdditionalInfoModal(record, index);
|
||
}}
|
||
/>
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</Form>
|
||
</div>
|
||
{/* {
|
||
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)}</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>
|
||
} */}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Form>
|
||
</div>
|
||
<Form
|
||
ref={formRef}
|
||
initialValues={editstate}
|
||
className="formDivAnt"
|
||
onFinish={onFinish}
|
||
>
|
||
<div className="openingStock-submitButton">
|
||
<Buttons
|
||
buttonText="SUBMIT"
|
||
color="901D77"
|
||
icon={<ArrowRightOutlined />}
|
||
/>
|
||
</div>
|
||
</Form>
|
||
</div>
|
||
|
||
<div>
|
||
<DefaultModal
|
||
open={additionalInfoModal}
|
||
className={'additional-dtls-modal'}
|
||
footer={false}
|
||
children={
|
||
<>
|
||
<div className="openingStock-modal-header">
|
||
<h3>{`${selectedRowRecord?.ProdName} (${selectedRowRecord?.ProdVariantName})`}</h3>
|
||
<button
|
||
onClick={handleAddNew}
|
||
className="openingStock-add-new-btn"
|
||
>
|
||
<PlusCircleOutlined />
|
||
Add New
|
||
</button>
|
||
</div>
|
||
|
||
<Form ref={productAddInfoRef} onFinish={handleAddtnlDtlsSubmit}>
|
||
<div className="collapse-data">
|
||
<div className="pricing-dtls">
|
||
<Collapse
|
||
activeKey={activeAccordionKeys}
|
||
onChange={setActiveAccordionKeys}
|
||
>
|
||
<Panel header="Pricing Details" key="1">
|
||
<div className="pricing-dtl-fields">
|
||
<Form.Item name="Qty">
|
||
<InputField
|
||
autoComplete="off"
|
||
label={
|
||
<label className="required">
|
||
Qty (Available:{' '}
|
||
{(() => {
|
||
const totalQty =
|
||
parseFloat(
|
||
selectedRowRecord?.TotalQty
|
||
) || 0;
|
||
const usedQty =
|
||
additionalStockEntries.reduce(
|
||
(sum, entry) =>
|
||
sum + (parseFloat(entry.Qty) || 0),
|
||
0
|
||
);
|
||
return totalQty - usedQty;
|
||
})()}
|
||
)
|
||
</label>
|
||
}
|
||
isOnChange={
|
||
selectedRowRecord?.Qty ? true : false
|
||
}
|
||
value={selectedRowRecord?.Qty}
|
||
onChange={(e) => {
|
||
const val = e?.target?.value;
|
||
setSelectedRowRecord((prev) => ({
|
||
...prev,
|
||
Qty: val,
|
||
}));
|
||
}}
|
||
inputMode="decimal"
|
||
onInput={(e) => {
|
||
let value = e.target.value.replace(
|
||
/[^0-9.]/g,
|
||
''
|
||
);
|
||
|
||
if (value.startsWith('.')) {
|
||
value = '0' + value;
|
||
}
|
||
|
||
const dotCount = (value.match(/\./g) || [])
|
||
.length;
|
||
if (dotCount > 1) {
|
||
const firstDotIndex = value.indexOf('.');
|
||
value =
|
||
value.substring(0, firstDotIndex + 1) +
|
||
value
|
||
.substring(firstDotIndex + 1)
|
||
.replace(/\./g, '');
|
||
}
|
||
|
||
e.target.value = value;
|
||
}}
|
||
/>
|
||
</Form.Item>
|
||
<Form.Item name="MRP">
|
||
<InputField
|
||
autoComplete="off"
|
||
label={<label>MRP</label>}
|
||
isOnChange={
|
||
selectedRowRecord?.MRP !== '' &&
|
||
selectedRowRecord.hasOwnProperty('MRP')
|
||
? true
|
||
: false
|
||
}
|
||
value={selectedRowRecord?.MRP}
|
||
onChange={handleMRPChange}
|
||
/>
|
||
</Form.Item>
|
||
<Form.Item name="SellPrice">
|
||
<InputField
|
||
autoComplete="off"
|
||
label={<label>Sales Price</label>}
|
||
isOnChange={
|
||
selectedRowRecord?.SellPrice !== '' &&
|
||
selectedRowRecord.hasOwnProperty('SellPrice')
|
||
? true
|
||
: false
|
||
}
|
||
value={selectedRowRecord?.SellPrice}
|
||
onChange={(e) => {
|
||
const val =
|
||
e?.target?.value === ''
|
||
? 0
|
||
: parseFloat(e?.target?.value);
|
||
setSelectedRowRecord((prev) => ({
|
||
...prev,
|
||
SellPrice: val,
|
||
}));
|
||
}}
|
||
/>
|
||
</Form.Item>
|
||
{/* {selectedRowRecord?.OnePcsAvailable === "Y" && (
|
||
<Form.Item name="OnePieceQty">
|
||
<InputField
|
||
autoComplete="off"
|
||
label={<label>One Piece Qty</label>}
|
||
isOnChange={
|
||
selectedRowRecord?.OnePieceQty
|
||
? true
|
||
: false
|
||
}
|
||
value={selectedRowRecord?.OnePieceQty}
|
||
onChange={(e) => {
|
||
const val = e?.target?.value;
|
||
setSelectedRowRecord((prev) => ({
|
||
...prev,
|
||
OnePieceQty: val,
|
||
}));
|
||
}}
|
||
inputMode="decimal"
|
||
onInput={(e) => {
|
||
let value = e.target.value.replace(
|
||
/[^0-9.]/g,
|
||
"",
|
||
);
|
||
if (value.startsWith("."))
|
||
value = "0" + value;
|
||
const dotCount = (value.match(/\./g) || [])
|
||
.length;
|
||
if (dotCount > 1) {
|
||
const firstDotIndex = value.indexOf(".");
|
||
value =
|
||
value.substring(0, firstDotIndex + 1) +
|
||
value
|
||
.substring(firstDotIndex + 1)
|
||
.replace(/\./g, "");
|
||
}
|
||
e.target.value = value;
|
||
}}
|
||
/>
|
||
</Form.Item>
|
||
)} */}
|
||
{selectedRowRecord?.OnePcsAvailable === 'Y' && (
|
||
<>
|
||
<Form.Item name="NumberofPieceinside">
|
||
<InputField
|
||
autoComplete="off"
|
||
label={
|
||
<label>Number of Piece Inside</label>
|
||
}
|
||
onChange={(e) =>
|
||
handleNumberofPieceinsidechange(e)
|
||
}
|
||
isOnChange={false}
|
||
value={
|
||
selectedRowRecord?.NumberofPieceinside
|
||
}
|
||
/>
|
||
</Form.Item>
|
||
<Form.Item name="AmountPerPiece">
|
||
<InputField
|
||
autoComplete="off"
|
||
label={<label>Amount / Piece</label>}
|
||
onChange={(e) =>
|
||
handleAmountPerPiecechange(e)
|
||
}
|
||
isOnChange={false}
|
||
value={selectedRowRecord?.AmountPerPiece}
|
||
/>
|
||
</Form.Item>
|
||
</>
|
||
)}
|
||
</div>
|
||
</Panel>
|
||
</Collapse>
|
||
</div>
|
||
{true && (
|
||
<div className="pricing-dtls">
|
||
<Collapse
|
||
activeKey={
|
||
activeAccordionKeys.includes('2') ? ['2'] : []
|
||
}
|
||
onChange={(keys) =>
|
||
setActiveAccordionKeys((prev) =>
|
||
keys.length > 0
|
||
? [...prev.filter((k) => k !== '2'), '2']
|
||
: prev.filter((k) => k !== '2')
|
||
)
|
||
}
|
||
>
|
||
<Panel header="Dates" key="2">
|
||
<div className="pricing-dtl-fields">
|
||
<Form.Item name="ManufDate">
|
||
<div className="dates-and-expiry">
|
||
<label>Entry Date</label>
|
||
<DatePicProd
|
||
canSelectPast={true}
|
||
onPressEnter={(e) =>
|
||
handleKeyPress(e, record, index)
|
||
}
|
||
onChange={handleManufactureDate}
|
||
valueData={selectedRowRecord?.ManufDate}
|
||
/>
|
||
</div>
|
||
</Form.Item>
|
||
{/* <Form.Item name="ExpDate">
|
||
<div className="dates-and-expiry">
|
||
<label>Expire Date</label>
|
||
<DatePicProd
|
||
canSelectPast={false}
|
||
onPressEnter={(e) => handleKeyPress(e, record, index)}
|
||
onChange={handleExpireDate}
|
||
valueData={selectedRowRecord?.ExpDate}
|
||
/>
|
||
</div>
|
||
</Form.Item> */}
|
||
</div>
|
||
</Panel>
|
||
</Collapse>
|
||
</div>
|
||
)}
|
||
{true && (
|
||
<div className="pricing-dtls">
|
||
<Collapse
|
||
activeKey={
|
||
activeAccordionKeys.includes('3') ? ['3'] : []
|
||
}
|
||
onChange={(keys) =>
|
||
setActiveAccordionKeys((prev) =>
|
||
keys.length > 0
|
||
? [...prev.filter((k) => k !== '3'), '3']
|
||
: prev.filter((k) => k !== '3')
|
||
)
|
||
}
|
||
>
|
||
<Panel header="Batch & Model Details" key="3">
|
||
<div className="pricing-dtl-fields">
|
||
<Form.Item name="BatchRef">
|
||
<InputField
|
||
autoComplete="off"
|
||
isOnChange={
|
||
selectedRowRecord?.BatchRef &&
|
||
selectedRowRecord?.BatchRef !== ''
|
||
? true
|
||
: false
|
||
}
|
||
value={selectedRowRecord?.BatchRef}
|
||
label={'Batch No.'}
|
||
onChange={handleBatchRefChange}
|
||
/>
|
||
</Form.Item>
|
||
<Form.Item name="ModelNumber">
|
||
<InputField
|
||
autoComplete="off"
|
||
isOnChange={
|
||
selectedRowRecord?.ModelNumber &&
|
||
selectedRowRecord?.ModelNumber !== ''
|
||
? true
|
||
: false
|
||
}
|
||
value={selectedRowRecord?.ModelNumber}
|
||
label={'Model No.'}
|
||
onChange={handleModelNoChange}
|
||
/>
|
||
</Form.Item>
|
||
{/* <div className="model-data-add" onClick={handleModelDataOpen}>
|
||
<IoAddCircleSharp size={18} />
|
||
<span>Add Model Data</span>
|
||
</div> */}
|
||
</div>
|
||
</Panel>
|
||
</Collapse>
|
||
</div>
|
||
)}
|
||
{additionalStockEntries.length > 0 && (
|
||
<div className="openingStock-entries-table">
|
||
<h4>Added Entries:</h4>
|
||
<Table
|
||
size="small"
|
||
dataSource={additionalStockEntries.map(
|
||
(item, index) => ({ ...item, key: index })
|
||
)}
|
||
pagination={false}
|
||
columns={[
|
||
{ title: 'Qty', dataIndex: 'Qty', key: 'Qty' },
|
||
{
|
||
title: 'One Piece Qty',
|
||
dataIndex: 'OnePieceQty',
|
||
key: 'OnePieceQty',
|
||
render: (value) => value || '-',
|
||
},
|
||
{ title: 'MRP', dataIndex: 'MRP', key: 'MRP' },
|
||
{
|
||
title: 'Sales Price',
|
||
dataIndex: 'SellPrice',
|
||
key: 'SellPrice',
|
||
},
|
||
{
|
||
title: 'Batch No.',
|
||
dataIndex: 'BatchRef',
|
||
key: 'BatchRef',
|
||
render: (value) => value || '-',
|
||
},
|
||
{
|
||
title: 'Model No.',
|
||
dataIndex: 'ModelNumber',
|
||
key: 'ModelNumber',
|
||
render: (value) => value || '-',
|
||
},
|
||
{
|
||
title: 'Entry Date',
|
||
dataIndex: 'ManufDate',
|
||
key: 'ManufDate',
|
||
render: (date) =>
|
||
date ? date.format('DD-MM-YYYY') : '-',
|
||
},
|
||
{
|
||
title: 'Action',
|
||
key: 'action',
|
||
render: (_, record, index) => (
|
||
<div className="openingStock-entries-table-actions">
|
||
<a>
|
||
<EditFilled
|
||
className="openingStock-edit-icon"
|
||
onClick={() => {
|
||
handleEditEntry(index);
|
||
setActiveAccordionKeys(['1']);
|
||
}}
|
||
/>
|
||
</a>
|
||
<a
|
||
onClick={(e) => {
|
||
e.stopPropagation();
|
||
}}
|
||
>
|
||
<DeleteFilled
|
||
className="openingStock-delete-icon"
|
||
onClick={() => handleDeleteEntry(index)}
|
||
/>
|
||
</a>
|
||
</div>
|
||
),
|
||
},
|
||
]}
|
||
/>
|
||
</div>
|
||
)}
|
||
<div className="submitButton">
|
||
<Buttons
|
||
buttonText="SUBMIT"
|
||
color="901D77"
|
||
icon={<ArrowRightOutlined />}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</Form>
|
||
</>
|
||
}
|
||
handleCancel={handleAdditionalInfoClose}
|
||
buttonText="Submit"
|
||
/>
|
||
|
||
<DefaultModal
|
||
open={eliminateModal}
|
||
title="Ignore Products"
|
||
footer={false}
|
||
children={
|
||
<div>
|
||
<div className="openingStock-modal-section">
|
||
<label className="openingStock-modal-label">
|
||
Select Products to Ignore:
|
||
</label>
|
||
<Select
|
||
mode="multiple"
|
||
placeholder="Choose products to ignore"
|
||
className="openingStock-select-full-width"
|
||
value={[]}
|
||
onChange={(selectedIds) => {
|
||
const newEliminated = [
|
||
...eliminatedProducts,
|
||
...selectedIds,
|
||
];
|
||
setEliminatedProducts(newEliminated);
|
||
setFilteredProductStockData(
|
||
ProductStockData.filter(
|
||
(item) => !newEliminated.includes(item.ProdId)
|
||
)
|
||
);
|
||
}}
|
||
options={ProductStockData.filter(
|
||
(product) => !eliminatedProducts.includes(product.ProdId)
|
||
).map((product) => ({
|
||
value: product.ProdId,
|
||
label: `${product.ProdName} (${product.ProdVariantName})`,
|
||
}))}
|
||
showSearch
|
||
filterOption={(input, option) =>
|
||
option.label.toLowerCase().includes(input.toLowerCase())
|
||
}
|
||
maxTagCount={3}
|
||
maxTagTextLength={30}
|
||
/>
|
||
</div>
|
||
|
||
{eliminatedProducts.length > 0 && (
|
||
<div className="openingStock-ignored-products-section">
|
||
<h4>Ignored Products ({eliminatedProducts.length}):</h4>
|
||
<div className="openingStock-ignored-products-container">
|
||
{eliminatedProducts.map((prodId, index) => {
|
||
const product = ProductStockData.find(
|
||
(p) => p.ProdId === prodId
|
||
);
|
||
return (
|
||
<div
|
||
key={index}
|
||
className="openingStock-ignored-product-tag"
|
||
>
|
||
<span className="openingStock-ignored-product-name">
|
||
{product?.ProdName}
|
||
</span>
|
||
<span
|
||
onClick={() => {
|
||
const updatedEliminated =
|
||
eliminatedProducts.filter(
|
||
(id) => id !== prodId
|
||
);
|
||
setEliminatedProducts(updatedEliminated);
|
||
setFilteredProductStockData(
|
||
ProductStockData.filter(
|
||
(item) =>
|
||
!updatedEliminated.includes(item.ProdId)
|
||
)
|
||
);
|
||
}}
|
||
className="openingStock-ignored-product-remove"
|
||
>
|
||
×
|
||
</span>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<div className="openingStock-modal-footer">
|
||
<button
|
||
onClick={() => setEliminateModal(false)}
|
||
className="openingStock-close-btn"
|
||
>
|
||
Close
|
||
</button>
|
||
</div>
|
||
</div>
|
||
}
|
||
handleCancel={() => setEliminateModal(false)}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default openingStockForm;
|