Android_Retail/src/Pages/openingStock/openingstockForm.jsx

1528 lines
60 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

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

import React, { useState, useEffect, useRef, 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:&nbsp;</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;