1542 lines
48 KiB
JavaScript
1542 lines
48 KiB
JavaScript
import { useDispatch } from 'react-redux';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { getSession } from '../../Services/Others';
|
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
import { Messages } from '../../Components/Notifications/Messages';
|
|
import { changeBreadCrumb } from '../../Features/AppPage/CenterPage';
|
|
import { Tables } from '../../Components/Tables/Table';
|
|
import Search from '../../Components/Forms/Search';
|
|
import FormHeader from '../PageComponents/FormHeader';
|
|
import { Input, Form, DatePicker, Collapse, Tooltip } from 'antd';
|
|
import { IoEye } from 'react-icons/io5';
|
|
import './Dispatch.scss';
|
|
import "../../Styles/BookingScreen/Components/SelectionComponent/SelectionComponent.scss"
|
|
import { DefaultModal } from '../../Components/Modal/DefaultModal';
|
|
import {
|
|
getRequestedStockQty,
|
|
postDispatchStock,
|
|
getPendingAndCompletedDispatches,
|
|
getDispatchSummary,
|
|
} from '../../Features/StockTransfer/StockTransfer';
|
|
import { ArrowRightOutlined, PrinterFilled } from '@ant-design/icons';
|
|
import Buttons from '../../Components/Forms/Buttons';
|
|
import { isMobile } from 'react-device-detect';
|
|
import { printDiv } from '../../Services/WSOthers';
|
|
import { getPrintSelectionComponentData } from '../../Features/ThemeChange/ThemeChange';
|
|
import DeliveryPrint from '../DeliveryChallan/DeliveryPrint';
|
|
import { DCPrintStyleFunction } from '../DeliveryChallan/DCPrintStyleFunction';
|
|
// import { IoEye } from "react-icons/io5";
|
|
|
|
const subDirectory = import.meta.env.BASE_URL;
|
|
|
|
const items = [
|
|
{
|
|
name: 'Home',
|
|
link: `${subDirectory}app-page/home`,
|
|
},
|
|
{
|
|
name: 'Stock Dispatch',
|
|
link: `${subDirectory}setting/stock-dispatch`,
|
|
},
|
|
];
|
|
|
|
const Dispatch = () => {
|
|
const formatDate = (dateString = '') => {
|
|
const date = new Date(dateString);
|
|
const day = date.getDate().toString().padStart(2, '0');
|
|
const months = [
|
|
'Jan',
|
|
'Feb',
|
|
'Mar',
|
|
'Apr',
|
|
'May',
|
|
'Jun',
|
|
'Jul',
|
|
'Aug',
|
|
'Sep',
|
|
'Oct',
|
|
'Nov',
|
|
'Dec',
|
|
];
|
|
const month = months[date.getMonth()];
|
|
const year = date.getFullYear();
|
|
return day && month && year ? `${day}-${month}-${year}` : '-';
|
|
};
|
|
const formatTime12 = (dateString = '') => {
|
|
if (!dateString) return '-';
|
|
|
|
const date = new Date(dateString);
|
|
if (isNaN(date)) return '-';
|
|
|
|
let hours = date.getHours();
|
|
const minutes = date.getMinutes().toString().padStart(2, '0');
|
|
const ampm = hours >= 12 ? 'PM' : 'AM';
|
|
|
|
hours = hours % 12 || 12;
|
|
|
|
return `${hours}:${minutes} ${ampm}`;
|
|
};
|
|
|
|
const queueColumns = [
|
|
{
|
|
title: 'SL.NO',
|
|
dataIndex: 'slno',
|
|
key: 'slno',
|
|
align: 'center',
|
|
width: '80px',
|
|
render: (text, record, index) => (
|
|
<span>{(page - 1) * 10 + index + 1}</span>
|
|
),
|
|
},
|
|
{
|
|
title: 'Queue No',
|
|
dataIndex: 'RequestId',
|
|
key: 'RequestId',
|
|
},
|
|
{
|
|
title: 'Queue Date',
|
|
dataIndex: 'CreatedDate',
|
|
key: 'CreatedDate',
|
|
render: (text) => formatDate(text),
|
|
},
|
|
{
|
|
title: 'Requested From',
|
|
dataIndex: 'FromLocationName',
|
|
key: 'FromLocationName',
|
|
},
|
|
{
|
|
title: 'Requested By',
|
|
dataIndex: 'RequestByName',
|
|
key: 'RequestByName',
|
|
align: 'center',
|
|
},
|
|
{
|
|
title: 'View',
|
|
dataIndex: 'view',
|
|
key: 'view',
|
|
align: 'center',
|
|
render: (text, record) => (
|
|
<IoEye
|
|
style={{
|
|
color: record?.ProductDetail?.length === 0 ? 'grey' : '#1292EE',
|
|
fontSize: '25px',
|
|
cursor:
|
|
record?.ProductDetail?.length === 0 ? 'not-allowed' : 'pointer',
|
|
}}
|
|
onClick={
|
|
record?.ProductDetail?.length === 0
|
|
? () => {}
|
|
: () => viewProductDetails(record.ProductDetail)
|
|
}
|
|
/>
|
|
),
|
|
},
|
|
{
|
|
title: 'Action',
|
|
dataIndex: 'action',
|
|
key: 'action',
|
|
align: 'center',
|
|
render: (text, record) => (
|
|
<div
|
|
className={`dispatch-btn ${record?.ProductDetail?.length === 0 ? 'disable' : ''}`}
|
|
onClick={
|
|
record?.ProductDetail?.length === 0
|
|
? () => {}
|
|
: () => openDispatchModal(record.ProductDetail, record)
|
|
}
|
|
>
|
|
Dispatch
|
|
</div>
|
|
),
|
|
},
|
|
];
|
|
|
|
const dispatchedColumns = [
|
|
{
|
|
title: 'SL.NO',
|
|
dataIndex: 'slno',
|
|
key: 'slno',
|
|
align: 'center',
|
|
width: '60px',
|
|
render: (text, record, index) => index + 1,
|
|
},
|
|
{
|
|
title: 'Request Id',
|
|
dataIndex: 'RequestId',
|
|
key: 'RequestId',
|
|
},
|
|
{
|
|
title: 'Dispatch No',
|
|
dataIndex: 'DispatchId',
|
|
key: 'DispatchId',
|
|
},
|
|
{
|
|
title: 'Dispatch Date',
|
|
dataIndex: 'CreatedDate',
|
|
key: 'CreatedDate',
|
|
render: (text) => formatDate(text),
|
|
},
|
|
{
|
|
title: 'Branch Name',
|
|
dataIndex: 'ToBranchName',
|
|
key: 'ToBranchName',
|
|
},
|
|
{
|
|
title: "Receiver's Name",
|
|
dataIndex: 'ReceiverName',
|
|
key: 'ReceiverName',
|
|
align: 'center',
|
|
},
|
|
{
|
|
title: 'Total Items Accepted',
|
|
dataIndex: 'TotalItemsAccepted',
|
|
key: 'TotalItemsAccepted',
|
|
align: 'center',
|
|
render: (text, record, index) =>
|
|
record?.ProductDetails?.filter((prod) => prod?.AcceptedQty > 0)?.length,
|
|
},
|
|
{
|
|
title: 'Total Qty Accepted',
|
|
dataIndex: 'TotalItemsAccepted',
|
|
key: 'TotalItemsAccepted',
|
|
align: 'center',
|
|
render: (text, record) =>
|
|
record?.ProductDetails?.reduce(
|
|
(acc, item) => acc + (Number(item?.AcceptedQty) || 0),
|
|
0
|
|
) || 0,
|
|
},
|
|
{
|
|
title: 'View',
|
|
dataIndex: 'view',
|
|
key: 'view',
|
|
align: 'center',
|
|
render: (text, record) => (
|
|
<IoEye
|
|
style={{ color: '#1292EE', fontSize: '25px', cursor: 'pointer' }}
|
|
onClick={() => openCompleteModal(record.ProductDetails)}
|
|
/>
|
|
),
|
|
},
|
|
];
|
|
|
|
const navigate = useNavigate();
|
|
const dispatch = useDispatch();
|
|
const CompId = getSession('CompId');
|
|
const BranchId = getSession('BranchId');
|
|
const AppId = getSession('AppId');
|
|
const UserId = getSession('UserId');
|
|
const UserType = getSession('UserType');
|
|
const formRef = useRef(null);
|
|
|
|
const [messageType, setMessageType] = useState(null);
|
|
const [messageData, setMessageData] = useState(null);
|
|
const [dispatchData, setDispatchData] = useState([]);
|
|
const [deliveryData, setDeliveryData] = useState(null);
|
|
|
|
const totalQty = deliveryData?.ProductDetails?.reduce((sum, p) => sum + (p.DispatchedQty || 0), 0) || 0;
|
|
console.log(dispatchData, 'dispatchData');
|
|
const [filteredInfo, setFilteredInfo] = useState({});
|
|
const [searchedText, setSearchedText] = useState('');
|
|
const [sortedInfo, setSortedInfo] = useState({});
|
|
const [page, setpage] = useState(1);
|
|
const [prodDtlsPage, setProdDtlsPage] = useState(1);
|
|
const [isDispatchQueue, setIsDispatchQueue] = useState('queue');
|
|
const [viewModalVisible, setViewModalVisible] = useState(false);
|
|
const [dispatchModalVisible, setDispatchModalVisible] = useState(false);
|
|
const [selectedProducts, setSelectedProducts] = useState([]);
|
|
const [dispatchProducts, setDispatchProducts] = useState([]);
|
|
const [allocatedDispatch, setAllocatedDispatch] = useState([]);
|
|
const [dateRange, setDateRange] = useState([]);
|
|
const [originalData, setOriginalData] = useState([]);
|
|
const [count, setCount] = useState(null);
|
|
const [printStyle, setPrintStyle] = useState(null);
|
|
const [printTemplateDtl, setPrintTemplateDtl] = useState(null);
|
|
const [transportMode, setTransportMode] = useState('');
|
|
const [vehicleNumber, setVehicleNumber] = useState('');
|
|
const [driverMobile, setDriverMobile] = useState('');
|
|
const [driverLicense, setDriverLicense] = useState('');
|
|
const [completeModalVisible, setCompleteModalVisible] = useState(false);
|
|
const [completeProducts, setCompleteProducts] = useState([]);
|
|
const [remarks, setRemarks] = useState({});
|
|
const [allocateModalVisible, setAllocateModalVisible] = useState(false);
|
|
const [currentProduct, setCurrentProduct] = useState(null);
|
|
const [currentProductIndex, setCurrentProductIndex] = useState(null);
|
|
const [allocatedStocks, setAllocatedStocks] = useState({});
|
|
const [initialFetch, setInitialFetch] = useState(true);
|
|
console.log(dateRange, 'dateRange');
|
|
useEffect(() => {
|
|
try {
|
|
dispatch(changeBreadCrumb({ items: items }));
|
|
fetchData();
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
}, [isDispatchQueue]);
|
|
|
|
// useEffect(() => {
|
|
// if (!initialFetch) {
|
|
// fetchData();
|
|
// }
|
|
// }, [dateRange])
|
|
|
|
useEffect(() => {
|
|
filterData(searchedText);
|
|
}, [originalData]);
|
|
|
|
useEffect(() => {
|
|
getPrintData();
|
|
}, []);
|
|
|
|
const onSearch = (value) => {
|
|
setSearchedText(value);
|
|
filterData(value);
|
|
};
|
|
const onSearchChange = (e) => {
|
|
const value = e?.target?.value;
|
|
setSearchedText(value);
|
|
filterData(value);
|
|
};
|
|
|
|
const filterData = (searchText) => {
|
|
if (!searchText) {
|
|
setDispatchData(originalData);
|
|
return;
|
|
}
|
|
|
|
const filtered = originalData.filter((item) => {
|
|
const searchLower = searchText.toLowerCase();
|
|
|
|
// Filter by branch name
|
|
const branchName = (
|
|
isDispatchQueue === 'queue'
|
|
? item.FromLocationName
|
|
: item?.ToBranchName || ''
|
|
).toLowerCase();
|
|
if (branchName.includes(searchLower)) return true;
|
|
|
|
// Filter by ID numbers based on current tab
|
|
if (isDispatchQueue === 'queue') {
|
|
const queueNo = (
|
|
item.RequestId ||
|
|
item.FromLocationName ||
|
|
''
|
|
).toLowerCase();
|
|
if (queueNo.includes(searchLower)) return true;
|
|
} else if (isDispatchQueue === 'transit') {
|
|
const transitNo = (
|
|
item.RequestId ||
|
|
item.DispatchId ||
|
|
item?.ToBranchName ||
|
|
''
|
|
).toLowerCase();
|
|
if (transitNo.includes(searchLower)) return true;
|
|
} else if (isDispatchQueue === 'dispatched') {
|
|
const dispatchNo = (
|
|
item.RequestId ||
|
|
item.DispatchId ||
|
|
item?.ToBranchName ||
|
|
''
|
|
).toLowerCase();
|
|
if (dispatchNo.includes(searchLower)) return true;
|
|
}
|
|
|
|
return false;
|
|
});
|
|
|
|
setpage(1);
|
|
|
|
setDispatchData(filtered);
|
|
};
|
|
|
|
const fetchData = async (fromDate = null, toDate = null) => {
|
|
try {
|
|
let data;
|
|
if (isDispatchQueue === 'queue') {
|
|
// Call dispatch queue API
|
|
const response = await dispatch(
|
|
getRequestedStockQty({
|
|
CompId,
|
|
BranchId,
|
|
AppId,
|
|
UserId,
|
|
UserType,
|
|
fromDate,
|
|
toDate,
|
|
})
|
|
)?.unwrap();
|
|
if (response?.data?.statusCode === 1) {
|
|
const filteredData = response?.data?.data?.filter((parent) =>
|
|
parent?.ProductDetail?.some((item) => Number(item?.PendingQty) >= 1)
|
|
);
|
|
data = filteredData || response?.data?.data || [];
|
|
} else {
|
|
data = [];
|
|
}
|
|
// data = queueSampleData;
|
|
} else if (isDispatchQueue === 'dispatched') {
|
|
// Call dispatched API
|
|
const response = await dispatch(
|
|
getPendingAndCompletedDispatches({
|
|
CompId,
|
|
BranchId,
|
|
AppId,
|
|
Type: 'C',
|
|
fromDate,
|
|
toDate,
|
|
})
|
|
)?.unwrap();
|
|
if (response?.data?.statusCode === 1) {
|
|
data = response?.data?.data || [];
|
|
} else {
|
|
data = [];
|
|
}
|
|
} else if (isDispatchQueue === 'transit') {
|
|
const response = await dispatch(
|
|
getPendingAndCompletedDispatches({
|
|
CompId,
|
|
BranchId,
|
|
AppId,
|
|
Type: 'P',
|
|
fromDate,
|
|
toDate,
|
|
})
|
|
)?.unwrap();
|
|
if (response?.data?.statusCode === 1) {
|
|
data = response?.data?.data || [];
|
|
} else {
|
|
data = [];
|
|
}
|
|
}
|
|
|
|
const res = await dispatch(
|
|
getDispatchSummary({ CompId, BranchId, AppId, fromDate, toDate })
|
|
)?.unwrap();
|
|
if (res?.data?.statusCode === 1 && res?.data?.data?.length > 0) {
|
|
console?.log(res?.data?.data, 'res?.data?.data');
|
|
setCount(res?.data?.data?.[0]);
|
|
} else {
|
|
setCount(null);
|
|
}
|
|
setOriginalData(data);
|
|
setDispatchData(data);
|
|
setInitialFetch(false);
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
};
|
|
|
|
const handleToggleChange = (checked) => {
|
|
setIsDispatchQueue(checked);
|
|
};
|
|
|
|
const handleChange = (pagination, filters, sorter) => {
|
|
setFilteredInfo(filters);
|
|
setSortedInfo(sorter);
|
|
};
|
|
|
|
const handlePageChange = (current) => {
|
|
setpage(current);
|
|
};
|
|
const handleProdDtlsPageChange = (current) => {
|
|
setProdDtlsPage(current);
|
|
};
|
|
|
|
const convertRangeToYYYYMMDD = (dates) => {
|
|
if (!dates || dates.length !== 2) return [];
|
|
|
|
return dates.map((d) => d?.format('YYYY-MM-DD'));
|
|
};
|
|
|
|
const handleDateRangeChange = async (dates) => {
|
|
setDateRange(dates);
|
|
|
|
if (dates && dates.length === 2) {
|
|
const [startDate, endDate] = convertRangeToYYYYMMDD(dates);
|
|
if (startDate && endDate) {
|
|
await fetchData(startDate, endDate);
|
|
}
|
|
// const dateField =
|
|
// isDispatchQueue === 'queue'
|
|
// ? 'CreatedDate' : isDispatchQueue === 'transit'
|
|
// ? 'transit_date'
|
|
// : 'dispatch_date';
|
|
|
|
// const start = new Date(startDate);
|
|
// start.setHours(0, 0, 0, 0);
|
|
|
|
// const end = new Date(endDate);
|
|
// end.setHours(23, 59, 59, 999);
|
|
|
|
// const filtered = originalData.filter(item => {
|
|
// const itemDate = new Date(item[dateField]);
|
|
// return itemDate >= start && itemDate <= end;
|
|
// });
|
|
|
|
// setDispatchData(filtered);
|
|
} else {
|
|
await fetchData();
|
|
}
|
|
};
|
|
|
|
const transitColumns = [
|
|
{
|
|
title: 'SL.NO',
|
|
dataIndex: 'slno',
|
|
key: 'slno',
|
|
align: 'center',
|
|
width: '60px',
|
|
render: (text, record, index) => index + 1,
|
|
},
|
|
{
|
|
title: 'Request Id',
|
|
dataIndex: 'RequestId',
|
|
key: 'RequestId',
|
|
},
|
|
{
|
|
title: 'Dispatch No',
|
|
dataIndex: 'DispatchId',
|
|
key: 'DispatchId',
|
|
},
|
|
{
|
|
title: 'Transit Date',
|
|
dataIndex: 'CreatedDate',
|
|
key: 'CreatedDate',
|
|
align: 'center',
|
|
render: (text) => formatDate(text),
|
|
},
|
|
{
|
|
title: 'Transit Time',
|
|
dataIndex: 'CreatedDate',
|
|
key: 'CreatedDate',
|
|
align: 'right',
|
|
render: (text) => formatTime12(text),
|
|
},
|
|
{
|
|
title: 'Dispatched To',
|
|
dataIndex: 'ToBranchName',
|
|
key: 'ToBranchName',
|
|
align: 'center',
|
|
},
|
|
{
|
|
title: 'Dispatched By',
|
|
dataIndex: 'DispatcherName',
|
|
key: 'DispatcherName',
|
|
},
|
|
{
|
|
title: 'View',
|
|
dataIndex: 'view',
|
|
key: 'view',
|
|
align: 'center',
|
|
render: (text, record) => (
|
|
<IoEye
|
|
style={{ color: '#1292EE', fontSize: '25px', cursor: 'pointer' }}
|
|
onClick={() => viewProductDetails(record.ProductDetails)}
|
|
/>
|
|
),
|
|
},
|
|
{
|
|
title: 'Print',
|
|
key: 'Print',
|
|
dataIndex: 'Print',
|
|
width: '60px',
|
|
align: 'center',
|
|
render: (_, record) => (
|
|
<PrinterFilled
|
|
style={{ fontSize: '20px' }}
|
|
onClick={() => generateDeliveryChallan(record)}
|
|
/>
|
|
),
|
|
},
|
|
];
|
|
|
|
const generateDeliveryChallan = async (record) => {
|
|
|
|
setDeliveryData([{ ...record, dispatchPrint: true }]);
|
|
const DCstyle = await DCPrintStyleFunction(
|
|
(printStyle == undefined || printStyle === 'TaxInvoice') ? 'Style 13' : printStyle);
|
|
|
|
const stylesMap = {
|
|
'Style 1': 'DCPrintStyle1',
|
|
'Style 2': 'DCPrintStyle2',
|
|
'Style 3': 'DCPrintStyle3',
|
|
'Style 4': 'DCPrintStyle4',
|
|
'Style 5': 'DCPrintStyle5',
|
|
'Style 6': 'DCPrintStyle6',
|
|
'Style 7': 'DCPrintStyle7',
|
|
'Style 8': 'DCPrintStyle8',
|
|
'Style 9': 'DCPrintStyle9',
|
|
'Style 10': 'DCPrintStyle10',
|
|
'Style 11': 'DCPrintStyle11',
|
|
'Style 12': 'DCPrintStyle12',
|
|
'Style 13': 'DC-Default-Print',
|
|
A4: 'DCPrintStyleA4',
|
|
A5: 'DCPrintStyleA5',
|
|
A4Standard: 'DCPrintStyleA4Standard',
|
|
};
|
|
|
|
const selectedStyle =
|
|
stylesMap[
|
|
(printStyle == undefined || printStyle === 'TaxInvoice') ? 'Style 13' : printStyle
|
|
];
|
|
console.log(selectedStyle, DCstyle, 'selectedStyle');
|
|
setTimeout(() => {
|
|
|
|
printDiv(`${selectedStyle}`, DCstyle);
|
|
}, 100);
|
|
};
|
|
|
|
const getPrintData = async () => {
|
|
let data = {
|
|
CompId: CompId,
|
|
BranchId: BranchId,
|
|
AppId: AppId,
|
|
};
|
|
let response = await dispatch(getPrintSelectionComponentData(data)).unwrap();
|
|
if (response?.data?.statusCode === 1) {
|
|
let DCPrintTemplate =
|
|
response?.data?.data?.[0]?.ComponentDetails?.find(
|
|
(item) => item.SetasDefault == 'Y' && item.PrintTypeName === 'Delivery Chalan'
|
|
);
|
|
console.log(DCPrintTemplate, response?.data?.data?.[0]?.ComponentDetails?.find(
|
|
(item) => item.SetasDefault == 'Y' && item.PrintTypeName === 'Delivery Chalan'
|
|
), 'DCPrintTemplate');
|
|
setPrintStyle(DCPrintTemplate?.StyleName);
|
|
setPrintTemplateDtl(DCPrintTemplate);
|
|
}
|
|
|
|
|
|
};
|
|
|
|
const openCompleteModal = (products) => {
|
|
setCompleteProducts(products);
|
|
setCompleteModalVisible(true);
|
|
};
|
|
|
|
const handleRemarksChange = (index, value) => {
|
|
setRemarks((prev) => ({ ...prev, [index]: value }));
|
|
};
|
|
|
|
const completeColumns = [
|
|
{
|
|
title: 'S.No',
|
|
dataIndex: 'slno',
|
|
key: 'slno',
|
|
align: 'center',
|
|
width: '60px',
|
|
render: (text, record, index) => index + 1,
|
|
},
|
|
{
|
|
title: 'Product Name',
|
|
dataIndex: 'ProdName',
|
|
key: 'ProdName',
|
|
width: 150,
|
|
},
|
|
{
|
|
title: 'Variant Name',
|
|
dataIndex: 'RequestVariantName',
|
|
key: 'RequestVariantName',
|
|
width: 150,
|
|
},
|
|
{
|
|
title: 'QTY',
|
|
children: [
|
|
{
|
|
title: 'Dispatched',
|
|
dataIndex: 'DispatchedQty',
|
|
key: 'DispatchedQty',
|
|
align: 'right',
|
|
width: 100,
|
|
},
|
|
{
|
|
title: 'Delivered',
|
|
dataIndex: 'ReceivedQty',
|
|
key: 'ReceivedQty',
|
|
align: 'right',
|
|
width: 100,
|
|
},
|
|
{
|
|
title: 'Accepted',
|
|
dataIndex: 'AcceptedQty',
|
|
key: 'AcceptedQty',
|
|
align: 'right',
|
|
width: 100,
|
|
},
|
|
// {
|
|
// title: 'Received',
|
|
// dataIndex: 'receivedQty',
|
|
// key: 'receivedQty',
|
|
// align: 'right',
|
|
// width: 100,
|
|
// },
|
|
],
|
|
},
|
|
// {
|
|
// title: 'Remarks',
|
|
// dataIndex: 'remarks',
|
|
// key: 'remarks',
|
|
// width: 200,
|
|
// render: (text, record, index) => (
|
|
// record.dispatchedQty !== record.acceptedQty ?
|
|
// <Input
|
|
// placeholder="Enter remarks"
|
|
// value={remarks[index] || ''}
|
|
// onChange={(e) => handleRemarksChange(index, e.target.value)}
|
|
// /> : null
|
|
// )
|
|
// }
|
|
];
|
|
|
|
const viewProductDetails = (products) => {
|
|
const productDetails =
|
|
isDispatchQueue === 'queue'
|
|
? products?.filter((prod) => prod?.PendingQty > 0)
|
|
: products;
|
|
setSelectedProducts(productDetails);
|
|
setViewModalVisible(true);
|
|
};
|
|
|
|
const openDispatchModal = (products, record) => {
|
|
const productsWithDispatchQty = products
|
|
?.filter((prod) => prod?.PendingQty > 0)
|
|
.map((product) => ({
|
|
...product,
|
|
requestId: record?.RequestId,
|
|
toLocationId: record?.FromLocationId,
|
|
}));
|
|
setDispatchProducts(productsWithDispatchQty);
|
|
setDispatchModalVisible(true);
|
|
};
|
|
|
|
const openAllocateModal = (product, index) => {
|
|
// Check if ProdVariantDetails is an array
|
|
if (!Array.isArray(product?.ProdVariantDetails)) {
|
|
console.error('ProdVariantDetails is not an array');
|
|
return;
|
|
}
|
|
|
|
// Check if at least one variant has StockDetails with length > 1
|
|
const hasValidStockDetails = product.ProdVariantDetails.some(
|
|
(variant) =>
|
|
Array.isArray(variant?.StockDetails) && variant.StockDetails.length > 0
|
|
);
|
|
|
|
if (!hasValidStockDetails) {
|
|
// console.error('No variant found with StockDetails length > 1');
|
|
setMessageData(
|
|
'This product cannot be allocated. No valid stock details found.'
|
|
);
|
|
setMessageType('error');
|
|
// Optionally show a user-friendly message
|
|
// alert('This product cannot be allocated. No valid stock details found.');
|
|
return;
|
|
}
|
|
|
|
// Proceed with setting state if validation passes
|
|
setCurrentProduct({
|
|
...product,
|
|
ProdVariantDetails: product?.ProdVariantDetails?.map((variant) => ({
|
|
...variant,
|
|
RequestId: product?.requestId,
|
|
})),
|
|
});
|
|
setCurrentProductIndex(index);
|
|
setAllocateModalVisible(true);
|
|
};
|
|
|
|
const handleStockAllocation = (value, balanceQty, inwardDtlId, requestId) => {
|
|
const numericValue = parseInt(value || 0, 10);
|
|
if (isNaN(numericValue) || numericValue < 0) return;
|
|
|
|
if (numericValue > balanceQty) {
|
|
setMessageType('error');
|
|
setMessageData(`Cannot exceed available qty of ${balanceQty}`);
|
|
return;
|
|
}
|
|
|
|
const productKey = `${currentProduct.ProdId}_${currentProductIndex}`;
|
|
const updatedAllocations = { ...allocatedStocks };
|
|
|
|
if (!updatedAllocations[productKey]) {
|
|
updatedAllocations[productKey] = {};
|
|
}
|
|
|
|
const stockKey = `${requestId}_${inwardDtlId}`;
|
|
updatedAllocations[productKey][stockKey] = numericValue;
|
|
|
|
const totalAllocated = Object.values(updatedAllocations[productKey]).reduce(
|
|
(sum, qty) => sum + qty,
|
|
0
|
|
);
|
|
|
|
if (totalAllocated > currentProduct.PendingQty) {
|
|
setMessageType('error');
|
|
setMessageData(
|
|
`Total allocated quantity cannot exceed pending quantity of ${currentProduct.PendingQty}`
|
|
);
|
|
updatedAllocations[productKey][stockKey] = 0;
|
|
return;
|
|
}
|
|
|
|
setAllocatedStocks(updatedAllocations);
|
|
};
|
|
|
|
const submitAllocation = () => {
|
|
const productKey = `${currentProduct.ProdId}_${currentProductIndex}`;
|
|
const allocations = allocatedStocks[productKey] || {};
|
|
const totalAllocated = Object.values(allocations).reduce(
|
|
(sum, qty) => sum + qty,
|
|
0
|
|
);
|
|
|
|
const updatedProducts = [...dispatchProducts];
|
|
updatedProducts[currentProductIndex].dispatchQty = totalAllocated;
|
|
setDispatchProducts(updatedProducts);
|
|
setAllocatedDispatch((prev) => {
|
|
let updated = [...prev];
|
|
|
|
Object.entries(allocations).forEach(([key, qty]) => {
|
|
const [, inwardDtlId] = key.split('_'); // take second part
|
|
if (!inwardDtlId) return;
|
|
|
|
const index = updated.findIndex(
|
|
(item) => item.InwardDtlId === inwardDtlId
|
|
);
|
|
|
|
if (index !== -1) {
|
|
// update existing
|
|
updated[index] = {
|
|
...updated[index],
|
|
dispatchQty: qty,
|
|
};
|
|
} else {
|
|
// push new
|
|
updated.push({
|
|
ProdId: currentProduct?.ProdId,
|
|
InwardDtlId: inwardDtlId,
|
|
CreatedBy: UserId,
|
|
dispatchQty: qty,
|
|
toLocationId: currentProduct?.toLocationId,
|
|
RequestId: currentProduct?.requestId,
|
|
});
|
|
}
|
|
});
|
|
|
|
return updated;
|
|
});
|
|
setAllocateModalVisible(false);
|
|
setCurrentProduct(null);
|
|
setCurrentProductIndex(null);
|
|
};
|
|
|
|
const getTotalAllocatedQty = () => {
|
|
if (!currentProduct) return 0;
|
|
const productKey = `${currentProduct.ProdId}_${currentProductIndex}`;
|
|
const allocations = allocatedStocks[productKey] || {};
|
|
return Object.values(allocations).reduce((sum, qty) => sum + qty, 0);
|
|
};
|
|
|
|
const handleDispatchSubmit = async () => {
|
|
try {
|
|
if (
|
|
allocatedDispatch?.filter(
|
|
(prod) => parseFloat(prod?.dispatchQty) || 0 > 0
|
|
)?.length === 0
|
|
) {
|
|
setMessageType('warning');
|
|
setMessageData('No products to dispatch');
|
|
return;
|
|
}
|
|
|
|
let data = {
|
|
RequestId: allocatedDispatch?.[0]?.RequestId,
|
|
AppId: AppId,
|
|
CompId: CompId,
|
|
FromLocationId: BranchId,
|
|
ToLocationId: allocatedDispatch?.[0]?.toLocationId,
|
|
DispatchStatus: '',
|
|
DispatchStockDetails: allocatedDispatch
|
|
?.filter((prod) => parseFloat(prod?.dispatchQty) || 0 > 0)
|
|
?.map((prod) => ({
|
|
ProdId: prod?.ProdId,
|
|
InwardDtlId: prod?.InwardDtlId,
|
|
DispatchedQty: parseFloat(prod?.dispatchQty) || 0,
|
|
CreatedBy: UserId,
|
|
})),
|
|
CreatedBy: UserId,
|
|
};
|
|
console.log(data, 'dispatchingdata');
|
|
const res = await dispatch(postDispatchStock(data))?.unwrap();
|
|
if (res?.data?.statusCode === 1) {
|
|
setMessageData(res?.data?.response);
|
|
setMessageType('success');
|
|
} else {
|
|
throw new Error(res?.data?.response);
|
|
}
|
|
console.log(res?.data, 'response');
|
|
await fetchData();
|
|
setDispatchModalVisible(false);
|
|
setAllocatedDispatch([]);
|
|
setAllocatedStocks({});
|
|
setCurrentProduct(null);
|
|
setDispatchProducts([]);
|
|
formRef?.current?.resetFields();
|
|
} catch (error) {
|
|
console.log(error);
|
|
setMessageType('error');
|
|
setMessageData('Failed to submit dispatch');
|
|
}
|
|
};
|
|
|
|
const viewColumns = [
|
|
{
|
|
title: 'SL.NO',
|
|
dataIndex: 'index',
|
|
key: 'index',
|
|
align: 'center',
|
|
width: '60px',
|
|
render: (text, record, index) => (
|
|
<span>{(prodDtlsPage - 1) * 10 + index + 1}</span>
|
|
),
|
|
},
|
|
{
|
|
title: 'Product Name',
|
|
dataIndex: 'ProdName',
|
|
key: 'ProdName',
|
|
},
|
|
{
|
|
title: 'Requested Variant',
|
|
dataIndex: 'RequestVariantName',
|
|
key: 'RequestVariantName',
|
|
},
|
|
{
|
|
title: isDispatchQueue ? 'Requested Qty' : 'Requested Qty',
|
|
dataIndex: isDispatchQueue === 'queue' ? 'RequestQty' : 'RequestedQty',
|
|
key: isDispatchQueue === 'queue' ? 'RequestQty' : 'RequestedQty',
|
|
align: 'right',
|
|
},
|
|
...(isDispatchQueue === 'queue'
|
|
? [
|
|
{
|
|
title: isDispatchQueue ? 'Pending Qty' : 'Pending Qty',
|
|
dataIndex:
|
|
isDispatchQueue === 'queue' ? 'PendingQty' : 'PendingQty',
|
|
key: isDispatchQueue === 'queue' ? 'PendingQty' : 'PendingQty',
|
|
align: 'right',
|
|
},
|
|
]
|
|
: []),
|
|
...(isDispatchQueue === 'queue'
|
|
? []
|
|
: [
|
|
{
|
|
title: 'Dispatched Qty',
|
|
dataIndex: 'DispatchedQty',
|
|
key: 'DispatchedQty',
|
|
align: 'right',
|
|
},
|
|
]),
|
|
];
|
|
|
|
const dispatchColumns = [
|
|
{
|
|
title: 'SL.No',
|
|
dataIndex: 'index',
|
|
key: 'index',
|
|
align: 'center',
|
|
width: '60px',
|
|
render: (text, record, index) => (
|
|
<span>{(prodDtlsPage - 1) * 10 + index + 1}</span>
|
|
),
|
|
},
|
|
{
|
|
title: 'Product Name',
|
|
dataIndex: 'ProdName',
|
|
key: 'ProdName',
|
|
width: '200px',
|
|
},
|
|
{
|
|
title: 'Requested Variant',
|
|
dataIndex: 'RequestVariantName',
|
|
key: 'RequestVariantName',
|
|
width: '200px',
|
|
},
|
|
{
|
|
title: 'Requested Qty',
|
|
dataIndex: 'RequestQty',
|
|
key: 'RequestQty',
|
|
align: 'right',
|
|
width: '130px',
|
|
},
|
|
{
|
|
title: 'Pending Qty',
|
|
dataIndex: 'PendingQty',
|
|
key: 'PendingQty',
|
|
align: 'right',
|
|
width: '130px',
|
|
},
|
|
{
|
|
title: 'Allocated Qty',
|
|
dataIndex: 'dispatchQty',
|
|
key: 'dispatchQty',
|
|
align: 'center',
|
|
width: '120px',
|
|
render: (text, record, index) => (
|
|
<span style={{ fontWeight: 'bold', color: '#1890ff' }}>
|
|
{text || 0}
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
title: 'Qty Allocation',
|
|
key: 'action',
|
|
align: 'center',
|
|
width: '120px',
|
|
render: (text, record, index) => {
|
|
if (record?.StockAvailable === 'Y') {
|
|
return (
|
|
<div
|
|
className="allocate-btn"
|
|
onClick={() => openAllocateModal(record, index)}
|
|
>
|
|
Allocate Qty
|
|
</div>
|
|
);
|
|
} else if (record?.StockAvailable === 'N') {
|
|
return (
|
|
<Form.Item
|
|
name={`DispatchQty${record?.requestId}${index}`}
|
|
rules={[
|
|
{
|
|
validator: (_, value) => {
|
|
if (!value) {
|
|
return Promise.resolve();
|
|
}
|
|
if (Number(value) > record.RequestQty) {
|
|
return Promise.reject(
|
|
new Error(
|
|
'Dispatch quantity cannot be greater than requested quantity'
|
|
)
|
|
);
|
|
}
|
|
if (Number(value) < 0) {
|
|
return Promise.reject(
|
|
new Error('Dispatch quantity cannot be negative')
|
|
);
|
|
}
|
|
return Promise.resolve();
|
|
},
|
|
},
|
|
]}
|
|
>
|
|
<Input
|
|
inputMode="decimal"
|
|
onInput={(e) => {
|
|
let cleanedValue = e.target.value.replace(/[^0-9.]/g, ''); // Remove invalid characters
|
|
const parts = cleanedValue.split('.');
|
|
|
|
if (cleanedValue.startsWith('.')) {
|
|
cleanedValue = '0' + cleanedValue;
|
|
}
|
|
|
|
e.target.value =
|
|
parts.length > 2
|
|
? `${parts[0]}.${parts.slice(1).join('')}`
|
|
: cleanedValue;
|
|
}}
|
|
min={0}
|
|
max={record.RequestQty}
|
|
value={text}
|
|
onChange={(e) =>
|
|
handleDispatchQtyChange(index, e.target.value, record)
|
|
}
|
|
/>
|
|
</Form.Item>
|
|
);
|
|
}
|
|
},
|
|
},
|
|
];
|
|
|
|
const handleDispatchQtyChange = (idx, value, record) => {
|
|
const dispatchingQty = value > record?.PendingQty ? '' : value;
|
|
if ((value || 0) > record?.PendingQty) {
|
|
setMessageData(
|
|
'Dispatch quantity cannot be greater than requested quantity'
|
|
);
|
|
setMessageType('error');
|
|
}
|
|
setDispatchProducts((prevProducts) => {
|
|
const updatedProducts = [...prevProducts];
|
|
updatedProducts[idx].dispatchQty = dispatchingQty;
|
|
return updatedProducts;
|
|
});
|
|
const inwardDtlId =
|
|
record?.ProdVariantDetails?.[0]?.StockDetails?.[0]?.InwardDtlId;
|
|
if (!inwardDtlId) return;
|
|
setAllocatedDispatch((prev) => {
|
|
const index = prev?.findIndex((item) => item.InwardDtlId === inwardDtlId);
|
|
|
|
// If exists → update
|
|
if (index !== -1) {
|
|
const updated = [...prev];
|
|
updated[index] = {
|
|
...updated[index],
|
|
dispatchQty: dispatchingQty,
|
|
};
|
|
return updated;
|
|
}
|
|
|
|
// Else → push new
|
|
return [
|
|
...prev,
|
|
{
|
|
ProdId: record?.ProdId,
|
|
InwardDtlId: inwardDtlId,
|
|
CreatedBy: UserId,
|
|
dispatchQty: dispatchingQty,
|
|
toLocationId: record?.toLocationId,
|
|
RequestId: record?.requestId,
|
|
},
|
|
];
|
|
});
|
|
formRef?.current?.setFieldsValue({
|
|
[`DispatchQty${record?.requestId}${idx}`]: dispatchingQty,
|
|
});
|
|
};
|
|
|
|
const onComplete = useCallback(() => {
|
|
setMessageType(null);
|
|
setMessageData(null);
|
|
}, []);
|
|
|
|
return (
|
|
<div className="userPageTable">
|
|
<Messages
|
|
messageType={messageType}
|
|
messageData={messageData}
|
|
onComplete={onComplete}
|
|
/>
|
|
<div className="userPageContent">
|
|
<div className="ProductMasterListSwitchBTN ">
|
|
<div
|
|
className={`${isDispatchQueue === 'queue' ? 'selected' : ''}`}
|
|
onClick={() => {
|
|
setDateRange([]);
|
|
setIsDispatchQueue('queue');
|
|
setpage(1);
|
|
setSearchedText('');
|
|
}}
|
|
>
|
|
Dispatch Queue
|
|
{count?.RequestCount > 0 && (
|
|
<span className="badgeForTab">{count?.RequestCount || 0}</span>
|
|
)}
|
|
</div>
|
|
<div
|
|
className={`${isDispatchQueue === 'transit' ? 'selected' : ''}`}
|
|
onClick={() => {
|
|
setDateRange([]);
|
|
setIsDispatchQueue('transit');
|
|
setpage(1);
|
|
setSearchedText('');
|
|
}}
|
|
>
|
|
In-Transit
|
|
{count?.PendingCount > 0 && (
|
|
<span className="badgeForTab">{count?.PendingCount || 0}</span>
|
|
)}
|
|
</div>
|
|
<div
|
|
className={`${isDispatchQueue === 'dispatched' ? 'selected' : ''}`}
|
|
onClick={() => {
|
|
setDateRange([]);
|
|
setIsDispatchQueue('dispatched');
|
|
setpage(1);
|
|
setSearchedText('');
|
|
}}
|
|
>
|
|
Completed
|
|
</div>
|
|
</div>
|
|
<div className="formAddNew">
|
|
{/* <div>
|
|
<FormHeader title={"Stock Dispatch"} />
|
|
</div> */}
|
|
<div className="searchAddDiv">
|
|
<Tooltip
|
|
title={`Search by ${isDispatchQueue === 'queue' ? 'Queue No or' : 'Request Id or Dispatch No or'} Branch Name`}
|
|
>
|
|
<div className="formSearch">
|
|
<Search
|
|
placeholder={`Search by ${isDispatchQueue === 'queue' ? 'Queue No or' : 'Request Id or Dispatch No or'} Branch Name`}
|
|
onSearch={onSearch}
|
|
onSearchChange={onSearchChange}
|
|
value={searchedText}
|
|
/>
|
|
</div>
|
|
</Tooltip>
|
|
<div className="dateRange">
|
|
<DatePicker.RangePicker
|
|
value={dateRange}
|
|
onChange={handleDateRangeChange}
|
|
placeholder={['Start Date', 'End Date']}
|
|
disabledDate={(current) => current && current > new Date()}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="reportTable stock-dispatch-table">
|
|
<Tables
|
|
columns={
|
|
isDispatchQueue === 'queue'
|
|
? queueColumns
|
|
: isDispatchQueue === 'transit'
|
|
? transitColumns
|
|
: dispatchedColumns
|
|
}
|
|
data={dispatchData}
|
|
dataSource={dispatchData}
|
|
onChange={handleChange}
|
|
// pagination={handlePageChange}
|
|
ownPagination={true}
|
|
pagination={{
|
|
current: page,
|
|
onChange: handlePageChange,
|
|
defaultPageSize: 10,
|
|
showSizeChanger: false,
|
|
hideOnSinglePage: true,
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<DefaultModal
|
|
className={`product-dtls-modal`}
|
|
title="Product Details"
|
|
open={viewModalVisible}
|
|
handleCancel={() => {
|
|
setViewModalVisible(false);
|
|
setProdDtlsPage(1);
|
|
}}
|
|
footer={null}
|
|
width={600}
|
|
children={
|
|
<div className="dispatch-view-table">
|
|
<Tables
|
|
columns={viewColumns}
|
|
data={selectedProducts}
|
|
dataSource={selectedProducts}
|
|
ownPagination={true}
|
|
pagination={{
|
|
current: prodDtlsPage,
|
|
onChange: handleProdDtlsPageChange,
|
|
defaultPageSize: 10,
|
|
showSizeChanger: false,
|
|
hideOnSinglePage: true,
|
|
}}
|
|
/>
|
|
</div>
|
|
}
|
|
/>
|
|
|
|
<DefaultModal
|
|
className={`product-dtls-modal`}
|
|
title="Dispatch Products"
|
|
open={dispatchModalVisible}
|
|
handleCancel={() => {
|
|
setDispatchModalVisible(false);
|
|
setAllocatedStocks({});
|
|
setAllocatedDispatch([]);
|
|
setProdDtlsPage(1);
|
|
formRef?.current?.resetFields();
|
|
}}
|
|
handleSubmit={handleDispatchSubmit}
|
|
footer={true}
|
|
width={750}
|
|
children={
|
|
<>
|
|
<Form ref={formRef}>
|
|
{/* <div className="dispatch-form-fields">
|
|
<div className="subRowFlex">
|
|
<Form.Item
|
|
name="transportMode"
|
|
rules={[{ required: true, message: 'Please select mode of transport' }]}
|
|
>
|
|
<Select
|
|
placeholder="Mode of Transport"
|
|
value={transportMode}
|
|
onChange={setTransportMode}
|
|
options={[
|
|
{ value: 'truck', label: 'Truck' },
|
|
{ value: 'rail', label: 'Rail' },
|
|
{ value: 'courier', label: 'Courier' },
|
|
{ value: 'air', label: 'Air' },
|
|
{ value: 'ship', label: 'Ship' }
|
|
]}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item
|
|
name="vehicleNumber"
|
|
rules={[{ required: true, message: 'Please enter vehicle number' }]}
|
|
>
|
|
<Input
|
|
placeholder="Vehicle Number"
|
|
value={vehicleNumber}
|
|
onChange={(e) => setVehicleNumber(e.target.value)}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
<div className="subRowFlex">
|
|
<Form.Item
|
|
name="driverMobile"
|
|
rules={[
|
|
{ required: true, message: 'Please enter driver mobile number' },
|
|
{ pattern: /^[0-9]{10}$/, message: 'Please enter valid 10-digit mobile number' }
|
|
]}
|
|
>
|
|
<Input
|
|
placeholder="Driver Mobile Number"
|
|
value={driverMobile}
|
|
onChange={(e) => setDriverMobile(e.target.value)}
|
|
maxLength={10}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item
|
|
name="driverLicense"
|
|
rules={[{ required: true, message: 'Please enter driver license number' }]}
|
|
>
|
|
<Input
|
|
placeholder="Driver License Number"
|
|
value={driverLicense}
|
|
onChange={(e) => setDriverLicense(e.target.value)}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
</div> */}
|
|
<div className="dispatch-table">
|
|
<Tables
|
|
columns={dispatchColumns}
|
|
data={dispatchProducts}
|
|
dataSource={dispatchProducts}
|
|
ownPagination={true}
|
|
pagination={{
|
|
current: prodDtlsPage,
|
|
onChange: handleProdDtlsPageChange,
|
|
defaultPageSize: 10,
|
|
showSizeChanger: false,
|
|
hideOnSinglePage: true,
|
|
}}
|
|
/>
|
|
</div>
|
|
</Form>
|
|
<DefaultModal
|
|
className="allocate-qty-modal"
|
|
title={`Allocate Quantity - ${currentProduct?.ProdName}`}
|
|
open={allocateModalVisible}
|
|
handleCancel={() => {
|
|
setAllocateModalVisible(false);
|
|
setCurrentProduct(null);
|
|
setCurrentProductIndex(null);
|
|
}}
|
|
footer={false}
|
|
width={1000}
|
|
children={
|
|
currentProduct && (
|
|
<div className="allocate-qty-container">
|
|
<div className="qty-summary">
|
|
<div className="qty-item">
|
|
<span className="qty-label">Requested Qty:</span>
|
|
<span className="qty-value">
|
|
{currentProduct.RequestQty}
|
|
</span>
|
|
</div>
|
|
<div className="qty-item">
|
|
<span className="qty-label">Pending Qty:</span>
|
|
<span className="qty-value">
|
|
{currentProduct.PendingQty}
|
|
</span>
|
|
</div>
|
|
<div className="qty-item">
|
|
<span className="qty-label">Allocated:</span>
|
|
<span className="qty-value">
|
|
{getTotalAllocatedQty()} / {currentProduct.PendingQty}
|
|
</span>
|
|
{getTotalAllocatedQty() < currentProduct.RequestQty && (
|
|
<span className="qty-remaining">
|
|
(Remaining:{' '}
|
|
{currentProduct.PendingQty - getTotalAllocatedQty()}
|
|
)
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="variants-collapse">
|
|
<Collapse
|
|
items={
|
|
currentProduct.ProdVariantDetails?.filter(
|
|
(variant) => variant?.StockDetails?.length > 0
|
|
)?.map((variant, variantIndex) => ({
|
|
key: String(variantIndex),
|
|
label: (
|
|
<span
|
|
style={{
|
|
display: 'flex',
|
|
gap: '10px',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
}}
|
|
>
|
|
<span>{variant.ProdVariantName}</span>
|
|
<span
|
|
style={{ fontWeight: 500, color: '#d46b08' }}
|
|
>
|
|
Total Available Qty: {variant.OverAllQty || 0}
|
|
</span>
|
|
</span>
|
|
),
|
|
children: (
|
|
<div>
|
|
{variant.StockDetails?.map(
|
|
(stock, stockIndex) => {
|
|
const productKey = `${currentProduct.ProdId}_${currentProductIndex}`;
|
|
const stockKey = `${variant?.RequestId}_${stock?.InwardDtlId}`;
|
|
const currentValue =
|
|
allocatedStocks[productKey]?.[stockKey] ||
|
|
0;
|
|
|
|
return (
|
|
<div
|
|
key={stockIndex}
|
|
style={{
|
|
marginBottom: '20px',
|
|
display: 'flex',
|
|
gap: '1rem',
|
|
alignItems: 'center',
|
|
flexWrap: 'wrap',
|
|
}}
|
|
>
|
|
<p>
|
|
<strong>Batch No:</strong>{' '}
|
|
{stock.BatchRef || 'N/A'}
|
|
</p>
|
|
<p>
|
|
<strong>Available Qty:</strong>{' '}
|
|
{stock.BalanceQty}
|
|
</p>
|
|
<p>
|
|
<strong>Expiry Date:</strong>{' '}
|
|
{stock.ExpiryDate || 'N/A'}
|
|
</p>
|
|
<div
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '10px',
|
|
}}
|
|
className="stockqtyDiv"
|
|
>
|
|
<strong>Allocate Qty:</strong>
|
|
<Input
|
|
type="number"
|
|
min={0}
|
|
max={stock.BalanceQty}
|
|
placeholder="Enter qty"
|
|
style={{ width: 100 }}
|
|
value={currentValue}
|
|
onChange={(e) =>
|
|
handleStockAllocation(
|
|
e.target.value,
|
|
stock.BalanceQty,
|
|
stock?.InwardDtlId,
|
|
variant?.RequestId
|
|
)
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
)}
|
|
</div>
|
|
),
|
|
})) || []
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
<div
|
|
className="modal-footer"
|
|
style={{ marginTop: '20px', textAlign: 'right' }}
|
|
>
|
|
<Tooltip
|
|
title={
|
|
getTotalAllocatedQty() === 0
|
|
? 'Please allocate some quantity to continue'
|
|
: ''
|
|
}
|
|
>
|
|
<span>
|
|
<Buttons
|
|
buttonText="SUBMIT"
|
|
color="901D77"
|
|
icon={<ArrowRightOutlined />}
|
|
handleSubmit={submitAllocation}
|
|
disabled={getTotalAllocatedQty() === 0}
|
|
/>
|
|
</span>
|
|
</Tooltip>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
/>
|
|
</>
|
|
}
|
|
/>
|
|
|
|
<DefaultModal
|
|
className={`product-dtls-modal`}
|
|
title="Delivered Details"
|
|
open={completeModalVisible}
|
|
handleCancel={() => {
|
|
setCompleteModalVisible(false);
|
|
setProdDtlsPage(1);
|
|
}}
|
|
footer={false}
|
|
width={800}
|
|
children={
|
|
<div className="complete-table">
|
|
<Tables
|
|
columns={completeColumns}
|
|
data={completeProducts}
|
|
dataSource={completeProducts}
|
|
ownPagination={true}
|
|
pagination={{
|
|
current: prodDtlsPage,
|
|
onChange: handleProdDtlsPageChange,
|
|
defaultPageSize: 10,
|
|
showSizeChanger: false,
|
|
hideOnSinglePage: true,
|
|
}}
|
|
/>
|
|
</div>
|
|
}
|
|
/>
|
|
{deliveryData?.length > 0 &&
|
|
<div
|
|
style={{ display: "none" }}
|
|
>
|
|
<DeliveryPrint
|
|
PrintingData1={[deliveryData]}
|
|
DCprinterTemplateStyle={printStyle}
|
|
DCPrintTemplateDtl={printTemplateDtl}
|
|
/>
|
|
</div>
|
|
}
|
|
</div >
|
|
);
|
|
};
|
|
export default Dispatch;
|