535 lines
24 KiB
JavaScript
535 lines
24 KiB
JavaScript
import React, { useState, useEffect } from "react";
|
|
import { Modal, Radio, message, Checkbox, Button, Tag } from "antd";
|
|
import { BiGitMerge } from "react-icons/bi";
|
|
import { PlusOutlined, DeleteOutlined } from "@ant-design/icons";
|
|
import { useDispatch } from "react-redux";
|
|
import {
|
|
getAllDineInTable,
|
|
getSelfBookingOrderId,
|
|
postMergeOrders
|
|
} from "../../../Features/TableBooking/TableBooking";
|
|
import { getSession } from "../../../Services/Others";
|
|
import { DropDowns } from "../../../Components/Forms/DropDown";
|
|
import "./MergeBill.scss";
|
|
|
|
const MergeBill = (getDiningTypefun) => {
|
|
const dispatch = useDispatch();
|
|
const AppId = getSession("AppId");
|
|
const CompId = getSession("CompId");
|
|
const BranchId = getSession("BranchId");
|
|
|
|
const [mergeModalVisible, setMergeModalVisible] = useState(false);
|
|
const [AllTableData, setAllTableData] = useState([]);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
// New state for multiple selections
|
|
const [selectedItems, setSelectedItems] = useState([
|
|
{
|
|
id: 1,
|
|
type: 'table',
|
|
tableId: null,
|
|
chairIds: [],
|
|
orderId: null,
|
|
orderIds: [], // Fixed: Initialize orderIds array
|
|
tableData: null
|
|
},
|
|
{
|
|
id: 2,
|
|
type: 'table',
|
|
tableId: null,
|
|
chairIds: [],
|
|
orderId: null,
|
|
orderIds: [], // Fixed: Initialize orderIds array
|
|
tableData: null
|
|
}
|
|
]);
|
|
|
|
useEffect(() => {
|
|
const fetchTables = async () => {
|
|
try {
|
|
let data = { AppId, CompId, BranchId };
|
|
let Response = await dispatch(getAllDineInTable(data)).unwrap();
|
|
if (Response?.data?.statusCode === 1) {
|
|
setAllTableData(Response?.data?.data);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching tables:", error);
|
|
message.error("Failed to fetch table data");
|
|
}
|
|
};
|
|
fetchTables();
|
|
}, [dispatch, AppId, CompId, BranchId]);
|
|
|
|
const openModal = () => setMergeModalVisible(true);
|
|
|
|
const closeModal = () => {
|
|
setMergeModalVisible(false);
|
|
// Reset all states
|
|
setSelectedItems([
|
|
{ id: 1, type: 'table', tableId: null, chairIds: [], orderId: null, orderIds: [], tableData: null },
|
|
{ id: 2, type: 'table', tableId: null, chairIds: [], orderId: null, orderIds: [], tableData: null }
|
|
]);
|
|
};
|
|
|
|
// Filter tables that have at least one occupied chair
|
|
const UnchoosenTables = AllTableData?.filter((table) =>
|
|
table?.ChairLevelSerDetails?.some((chair) => chair?.SalesStatus === "Y")
|
|
);
|
|
|
|
// Add new selection item
|
|
const addSelectionItem = () => {
|
|
const newId = Math.max(...selectedItems.map(item => item.id)) + 1;
|
|
setSelectedItems([...selectedItems, {
|
|
id: newId,
|
|
type: 'table',
|
|
tableId: null,
|
|
chairIds: [],
|
|
orderId: null,
|
|
orderIds: [], // Fixed: Initialize orderIds array
|
|
tableData: null
|
|
}]);
|
|
};
|
|
|
|
// Remove selection item
|
|
const removeSelectionItem = (id) => {
|
|
if (selectedItems.length > 2) {
|
|
setSelectedItems(selectedItems.filter(item => item.id !== id));
|
|
}
|
|
};
|
|
|
|
// Update selection type (table/chair)
|
|
const updateSelectionType = (id, type) => {
|
|
setSelectedItems(selectedItems.map(item =>
|
|
item.id === id
|
|
? { ...item, type, chairIds: [], orderId: null, orderIds: [] } // Fixed: Reset orderIds array
|
|
: item
|
|
));
|
|
};
|
|
|
|
// Update table selection
|
|
const updateTableSelection = (id, tableId) => {
|
|
const selectedTableData = AllTableData?.find(table => table.TableId === tableId);
|
|
|
|
setSelectedItems(selectedItems.map(item => {
|
|
if (item.id === id) {
|
|
let orderId = null;
|
|
let orderIds = []; // Fixed: Initialize orderIds array
|
|
|
|
if (item.type === 'table' && selectedTableData) {
|
|
// For table merge, get first available order ID
|
|
orderId = selectedTableData.ChairLevelSerDetails?.find(chair => chair.OrderId)?.OrderId || null;
|
|
// For table, orderIds contains just the single order ID
|
|
orderIds = orderId ? [orderId] : [];
|
|
}
|
|
|
|
return {
|
|
...item,
|
|
tableId,
|
|
tableData: selectedTableData,
|
|
chairIds: [],
|
|
orderId,
|
|
orderIds // Fixed: Set orderIds array
|
|
};
|
|
}
|
|
return item;
|
|
}));
|
|
};
|
|
|
|
// Update chair selection (multiple chairs)
|
|
const updateChairSelection = (id, chairIds) => {
|
|
setSelectedItems(selectedItems.map(item => {
|
|
if (item.id === id) {
|
|
let orderIds = [];
|
|
|
|
if (item.tableData && chairIds.length > 0) {
|
|
// Get all order IDs from selected chairs
|
|
orderIds = chairIds.map(chairId => {
|
|
const chair = item.tableData.ChairLevelSerDetails?.find(c => c.ChairId === chairId);
|
|
return chair?.OrderId;
|
|
}).filter(Boolean);
|
|
|
|
// Remove duplicates
|
|
orderIds = [...new Set(orderIds)];
|
|
}
|
|
|
|
return {
|
|
...item,
|
|
chairIds,
|
|
orderId: orderIds.length > 0 ? orderIds[0] : null, // Use first order ID for primary reference
|
|
orderIds // Fixed: Store all unique order IDs for multi-chair merge
|
|
};
|
|
}
|
|
return item;
|
|
}));
|
|
};
|
|
|
|
// Get available tables for a specific selection (excluding already selected ones)
|
|
const getAvailableTablesForSelection = (currentId, type) => {
|
|
const selectedTableIds = selectedItems
|
|
.filter(item => item.id !== currentId && item.tableId)
|
|
.map(item => item.tableId);
|
|
|
|
if (type === 'table') {
|
|
return UnchoosenTables?.filter(table => !selectedTableIds.includes(table.TableId));
|
|
} else {
|
|
return UnchoosenTables; // For chair selection, allow same table
|
|
}
|
|
};
|
|
|
|
const handleConfirmedMerge = async () => {
|
|
try {
|
|
// Validate selections - Fixed: Use orderIds array for validation
|
|
const validSelections = selectedItems.filter(item => item.orderIds && item.orderIds.length > 0);
|
|
|
|
if (validSelections.length < 2) {
|
|
message.error('Please select at least 2 tables/chairs to merge');
|
|
return;
|
|
}
|
|
|
|
setIsLoading(true);
|
|
|
|
// Fetch full order details for all selected orders
|
|
const fetchOrderDetails = async (orderId) => {
|
|
const data = { AppId, CompId, BranchId, orderId };
|
|
|
|
try {
|
|
const response = await dispatch(getSelfBookingOrderId(data)).unwrap();
|
|
if (response?.data?.statusCode === 1 && response?.data?.data?.length > 0) {
|
|
return response.data.data[0];
|
|
}
|
|
throw new Error('Order not found');
|
|
} catch (err) {
|
|
console.error(`Failed to fetch order details for ${orderId}:`, err);
|
|
throw err;
|
|
}
|
|
};
|
|
|
|
// Collect all unique order IDs - Fixed: Use orderIds array
|
|
const allOrderIds = [];
|
|
validSelections.forEach(item => {
|
|
allOrderIds.push(...item.orderIds);
|
|
});
|
|
|
|
// Remove duplicates
|
|
const uniqueOrderIds = [...new Set(allOrderIds)];
|
|
|
|
// Fetch all order details
|
|
const orderDetailsPromises = uniqueOrderIds.map(orderId => fetchOrderDetails(orderId));
|
|
const orderDetails = await Promise.all(orderDetailsPromises);
|
|
|
|
if (orderDetails.some(detail => !detail)) {
|
|
message.error('Failed to fetch some order details');
|
|
return;
|
|
}
|
|
|
|
// Build merge data
|
|
const MergeData = {
|
|
CompId,
|
|
BranchId,
|
|
AppId,
|
|
CreatedBy: 0,
|
|
MergeRequest: 'A',
|
|
DeviceUniqueId: '',
|
|
MergeOrderDetails: orderDetails.map(data => ({
|
|
OrderId: data.OrderId,
|
|
SalesId: data.SalesId || '',
|
|
OrderType: data.OrderType || 'S',
|
|
CustSuppId: data.CustSuppId || '',
|
|
Reference: data.Reference || '',
|
|
BookingType: data.BookingType || 0,
|
|
ServiceProvider: data.ServiceProvider || 0,
|
|
ActiveStatus: data.ActiveStatus || 'Y',
|
|
RefundAmount: data.RefundAmount || 0,
|
|
AddlInfo: data.AddlInfo || '',
|
|
BillAmount: data.BillAmount || 0,
|
|
OverallDiscSales: data.OverallDiscSales || 0,
|
|
OverallDiscEst: data.OverallDiscEst || 0,
|
|
TaxAmount: data.TaxAmount || 0,
|
|
VatAmount: data.VatAmount || 0,
|
|
NetAmount: data.NetAmount || 0,
|
|
GivenAmt: data.GivenAmt || 0,
|
|
BalGiven: data.BalGiven || 0,
|
|
BookingMedia: data.BookingMedia || '',
|
|
PaymentStatus: 'P',
|
|
ServiceCategory: data.ServiceCategory || 0,
|
|
OtherServiceTicketId: data.OtherServiceTicketId || '',
|
|
OtherServiceTicketAmount: data.OtherServiceTicketAmount || 0,
|
|
OrderDtlDetails: data.productDetails?.map(item => ({
|
|
ProdId: item.ProdId,
|
|
Type: item.Type || '',
|
|
OrderQty: item.OrderQty || 0,
|
|
OrderRate: item.OrderRate || 0,
|
|
TotalAmt: item.TotalAmt || 0,
|
|
TaxAmt: item.TaxAmt || 0,
|
|
RefundQty: item.RefundQty || 0,
|
|
InwardDtlId: item.InwardDtlId || '',
|
|
TaxId: item.TaxId || '',
|
|
ProdNameQty: item.ProdNameQty || '',
|
|
OrderStatus: item.OrderStatus || '',
|
|
OrderType: 'S',
|
|
SinglePc: item.SinglePc || '',
|
|
BookingType: item.BookingType || 0,
|
|
OfferValue: item.OfferValue || 0,
|
|
OfferAmt: item.OfferAmt || 0,
|
|
SetCount: item.SetCount || 0,
|
|
OfferType: item.OfferType || '',
|
|
CounterName: item.CounterName || '',
|
|
OfferMode: item.OfferMode || '',
|
|
OfferMessage: item.OfferMessage || [{}],
|
|
ProductIdentifierDtls: item.ProductIdentifierDtls?.map(prod => ({
|
|
SerialNumber: prod.SerialNumber || '',
|
|
IMEI1: prod.IMEI1 || '',
|
|
IMEI2: prod.IMEI2 || '',
|
|
MacId: prod.MacId || ''
|
|
})) || [],
|
|
PackageDtl: item.PackageDtl?.map(pkg => ({
|
|
ProdId: pkg.ProdId || '',
|
|
ParcelQty: pkg.ParcelQty || 0,
|
|
ParcelCount: pkg.ParcelCount || 0,
|
|
Total: pkg.Total || 0,
|
|
ParcelType: pkg.ParcelType || '',
|
|
ProdVariantName: pkg.ProdVariantName || '',
|
|
InwardDtlId: pkg.InwardDtlId || ''
|
|
})) || []
|
|
})) || [],
|
|
SalesTableLinkDetails: data.SalesTableLinkDetails?.map(chair => ({
|
|
TableId: chair.TableId,
|
|
WaiterId: chair.WaiterId,
|
|
ChairId: chair.ChairId,
|
|
TipsAmount: chair.TipsAmount || 0
|
|
})) || [],
|
|
ExtraChargeDetails: data.ExtraChargeDetails?.map(extra => ({
|
|
ProdId: extra.ProdId || '',
|
|
InwardDtlId: extra.InwardDtlId || '',
|
|
OrderQty: extra.OrderQty || 0,
|
|
OrderRate: extra.OrderRate || 0,
|
|
TotalAmt: extra.TotalAmt || 0,
|
|
TaxAmt: extra.TaxAmt || 0,
|
|
ExtraChargeId: extra.ExtraChargeId || '',
|
|
OrderType: extra.OrderType || 'S',
|
|
BookingType: extra.BookingType || 0,
|
|
SinglePc: extra.SinglePc || '',
|
|
TaxId: extra.TaxId || '',
|
|
TaxPercentage: extra.TaxPercentage || 0
|
|
})) || [],
|
|
Type: data.Type || 0,
|
|
ApprovalStatus: 'Y'
|
|
})),
|
|
PaymentDetail: []
|
|
};
|
|
|
|
console.log('MergeData being sent:', MergeData);
|
|
|
|
// Call the merge API
|
|
const postMerge = await dispatch(postMergeOrders(MergeData)).unwrap();
|
|
console.log('Merge API Response:', postMerge);
|
|
|
|
if (postMerge?.data?.statusCode === 1) {
|
|
message.success(`Successfully merged ${uniqueOrderIds.length} orders!`);
|
|
await getDiningTypefun.getDiningTypefun();
|
|
closeModal();
|
|
} else {
|
|
message.error(postMerge?.data?.message || 'Merge failed');
|
|
}
|
|
|
|
} catch (err) {
|
|
console.error('Merge error:', err);
|
|
message.error('Something went wrong during merge.');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<button
|
|
onClick={openModal}
|
|
className="Chair-levelbutton-takeaway"
|
|
>
|
|
<BiGitMerge className="merge-icon" />
|
|
Merge Bill
|
|
</button>
|
|
|
|
<Modal
|
|
open={mergeModalVisible}
|
|
title={
|
|
<div className="modal-title">
|
|
<BiGitMerge className="modal-title-icon" />
|
|
Multi-Table/Chair Merge
|
|
</div>
|
|
}
|
|
onOk={handleConfirmedMerge}
|
|
onCancel={closeModal}
|
|
width={900}
|
|
className="merge-modal"
|
|
okText="Merge All Orders"
|
|
cancelText="Cancel"
|
|
confirmLoading={isLoading}
|
|
okButtonProps={{
|
|
disabled: selectedItems.filter(item => item.orderIds && item.orderIds.length > 0).length < 2,
|
|
className: "merge-ok-button"
|
|
}}
|
|
cancelButtonProps={{
|
|
className: "merge-cancel-button"
|
|
}}
|
|
>
|
|
<div className="multi-merge-content">
|
|
{selectedItems.map((item, index) => (
|
|
<div key={item.id} className="merge-section">
|
|
<div className="section-header">
|
|
<h4>Selection {index + 1}</h4>
|
|
{selectedItems.length > 2 && (
|
|
<Button
|
|
type="text"
|
|
icon={<DeleteOutlined />}
|
|
onClick={() => removeSelectionItem(item.id)}
|
|
className="remove-selection-btn"
|
|
danger
|
|
/>
|
|
)}
|
|
</div>
|
|
|
|
{/* Radio for merge type */}
|
|
<div className="radio-group-wrapper">
|
|
<Radio.Group
|
|
value={item.type}
|
|
onChange={(e) => updateSelectionType(item.id, e.target.value)}
|
|
className="merge-radio-group"
|
|
>
|
|
<Radio value="table" className="merge-radio">Whole Table</Radio>
|
|
<Radio value="chair" className="merge-radio">Multiple Chairs</Radio>
|
|
</Radio.Group>
|
|
</div>
|
|
|
|
{/* Table selection */}
|
|
<div className="dropdown-wrapper">
|
|
<DropDowns
|
|
options={getAvailableTablesForSelection(item.id, item.type)?.map((option) => ({
|
|
value: option.TableId,
|
|
label: option.TableName,
|
|
})) || []}
|
|
label={<label className="dropdown-label required">Table Name</label>}
|
|
optionsNames={{ value: "TypeId", label: "TypeName" }}
|
|
className="merge-dropdown"
|
|
isOnchanges={!!item.tableId}
|
|
onChangeFunction={(selectedValues) => {
|
|
updateTableSelection(item.id, selectedValues);
|
|
}}
|
|
valueData={item.tableId}
|
|
/>
|
|
</div>
|
|
|
|
{/* Chair selection if type = chair */}
|
|
{item.type === "chair" && item.tableData && (
|
|
<div className="dropdown-wrapper">
|
|
<label className="dropdown-label required">Select Chairs</label>
|
|
<div className="chair-selection-grid">
|
|
{item.tableData.ChairLevelSerDetails?.filter(chair => chair.SalesStatus === "Y").map(chair => (
|
|
<div key={chair.ChairId} className="chair-checkbox-item">
|
|
<Checkbox
|
|
checked={item.chairIds.includes(chair.ChairId)}
|
|
onChange={(e) => {
|
|
const newChairIds = e.target.checked
|
|
? [...item.chairIds, chair.ChairId]
|
|
: item.chairIds.filter(id => id !== chair.ChairId);
|
|
updateChairSelection(item.id, newChairIds);
|
|
}}
|
|
>
|
|
<div className="chair-info">
|
|
<span className="chair-name">{chair.ChairName}</span>
|
|
<span className="chair-order-id">Order: {chair.OrderId}</span>
|
|
</div>
|
|
</Checkbox>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Selection summary */}
|
|
<div className="selection-summary">
|
|
{item.type === 'table' && item.tableData && (
|
|
<div className="summary-info">
|
|
<span>Table: {item.tableData.TableName}</span>
|
|
<span>Order ID: {item.orderId}</span>
|
|
</div>
|
|
)}
|
|
{item.type === 'chair' && item.chairIds.length > 0 && (
|
|
<div className="summary-info">
|
|
<span>Selected Chairs: {item.chairIds.length}</span>
|
|
<div className="order-ids-display">
|
|
<span>Order IDs: </span>
|
|
{item.orderIds?.map((orderId, idx) => (
|
|
<Tag key={idx} color="blue" className="order-id-tag">
|
|
{orderId}
|
|
</Tag>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
|
|
{/* Add more selection button */}
|
|
<div className="add-selection-wrapper">
|
|
<Button
|
|
type="dashed"
|
|
icon={<PlusOutlined />}
|
|
onClick={addSelectionItem}
|
|
className="add-selection-btn"
|
|
>
|
|
Add More Selection
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Final Summary */}
|
|
<div className="merge-summary">
|
|
<h5 className="summary-title">Merge Summary:</h5>
|
|
<div className="summary-content">
|
|
{selectedItems.map((item, index) => {
|
|
if (!item.orderIds || item.orderIds.length === 0) return null;
|
|
|
|
return (
|
|
<div key={item.id} className="summary-item">
|
|
<span className="summary-label">Selection {index + 1}:</span>
|
|
<div className="summary-details">
|
|
{item.type === 'table' && (
|
|
<>
|
|
<span>Table: {item.tableData?.TableName}</span>
|
|
<div className="order-tags">
|
|
{item.orderIds.map((orderId, idx) => (
|
|
<Tag key={idx} color="green">Order: {orderId}</Tag>
|
|
))}
|
|
</div>
|
|
</>
|
|
)}
|
|
{item.type === 'chair' && (
|
|
<>
|
|
<span>Chairs: {item.chairIds.length} selected from {item.tableData?.TableName}</span>
|
|
<div className="order-tags">
|
|
{item.orderIds.map((orderId, idx) => (
|
|
<Tag key={idx} color="orange">Order: {orderId}</Tag>
|
|
))}
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
|
|
{selectedItems.filter(item => item.orderIds && item.orderIds.length > 0).length < 2 && (
|
|
<div className="warning-message">
|
|
Please select at least 2 items with active orders to merge.
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Modal>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default MergeBill; |