Android_Retail/src/Pages/StockPriceUpdate/StockPriceUpdate.jsx

2585 lines
77 KiB
JavaScript

import React, {
useEffect,
useState,
useRef,
useContext,
useCallback,
} from "react";
import { useDispatch, useSelector } from "react-redux";
import { Select, Tooltip } from "antd";
import { Form, Input, Table } from "antd";
import {
EditFilled,
DeleteFilled,
ArrowRightOutlined,
} from "@ant-design/icons";
import {
PutStockPrice,
bulkPriceUpdate,
getStockNames,
} from "../../Features/StockPriceUpdate/StockPriceUpdateMaster";
import {
dateFormatChange,
ExtractDateFormate,
getSession,
} from "../../Services/Others";
import { DefaultModal } from "../../Components/Modal/DefaultModal";
import { Tables } from "../../Components/Tables/Table";
import Buttons from "../../Components/Forms/Buttons";
import { Messages } from "../../Components/Notifications/Messages";
import {
changeBreadCrumb,
getEmpAccess,
} from "../../Features/AppPage/CenterPage.js";
import FormHeader from "../PageComponents/FormHeader";
import Search from "../../Components/Forms/Search";
import { RadioGrpButton } from "../../Components/Forms/RadioGroup.jsx";
import { useAuth } from "../../AuthContext.jsx";
import "../../Styles/OverAllStyle/OverAllStyle.scss";
import { getPreferenceData } from "../../Features/BookingScreen/BookingData/BookingData.js";
import { DropDowns } from "../../Components/Forms/DropDown.jsx";
import { FiDelete, FiUploadCloud } from "react-icons/fi";
import { CiExport } from "react-icons/ci";
import Excelupldimg from "../../Images/Excel.png";
import {
emptyExcelData,
fileInputRefSelector,
} from "../../Features/ExcelUploadPage/ExcelUploadPage.js";
const subDirectory = import.meta.env.BASE_URL;
import ExcelJS from "exceljs";
import { read, utils } from "xlsx";
import { v4 as uuidv4 } from "uuid";
import { InputField } from "../../Components/Forms/InputField.jsx";
import { MdOutlineAppRegistration } from "react-icons/md";
import {
getFieldSetupData,
postFieldSetup,
} from "../../Features/ProductPage/ProductPage.js";
import {
ApplicationPreferences,
getCommonAppPreference,
} from "../../Features/BrachLogin/BranchLogin.js";
import { IoClose } from "react-icons/io5";
const StockPriceUpdate = () => {
const { SadminuserAccess } = useAuth();
let SAAccessCommonMaster = SadminuserAccess?.find(
(e) => e?.MenuName === "Price Change",
);
const ApplicationPreferenceData = useSelector(ApplicationPreferences);
const categoryId = ApplicationPreferenceData?.find(
(p) => p?.PreferredCatName?.toLowerCase() === "price change fields",
)?.PreferredCatId;
const excelCategoryId = ApplicationPreferenceData?.find(
(p) => p?.PreferredCatName?.toLowerCase() === "price change sheet fields",
)?.PreferredCatId;
const fileInputRef = useRef(fileInputRefSelector);
const excelDataRef = useRef(null);
const formRef = useRef(null);
const allowedExcelTypes = [
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // .xlsx
"application/vnd.ms-excel", // .xls
];
const dispatch = useDispatch();
const [selectedFields, setSelectedFields] = useState([]);
const [selectedExcelFields, setSelectedExcelFields] = useState([]);
const [tableFieldPreferences, setTableFieldPreferences] = useState([]);
const [tableExcelFieldPreferences, setTableExcelFieldPreferences] = useState(
[],
);
const [messageType, setMessageType] = useState(null);
const [messageData, setMessageData] = useState(null);
const [StockNames, setStockNames] = useState([]);
const [ProductName, setProductName] = useState(null);
const [fieldSetup, setFieldSetup] = useState(false);
const [excelFieldSetup, setExcelFieldSetup] = useState(false);
const [StockDetails, setStockDetails] = useState([]);
const [dataSource, setDataSource] = useState([]);
const [sortedInfo, setSortedInfo] = useState({});
const [BulksortedInfo, setBulksortedInfo] = useState({});
const [searchedText, setSearchedText] = useState("");
const [BulksearchedText, setBulksearchedText] = useState("");
const [filteredInfo, setFilteredInfo] = useState({});
const [page, setpage] = useState(1);
const [Bulkchangepage, setBulkchangepage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [openEditModal, setOpenEditModal] = useState(false);
const [open, setOpen] = useState(false);
const [excelModalOpen, setExcelModalOpen] = useState(false);
const [priceChangeModalOpen, setPriceChangeModalOpen] = useState(false);
const [stockVariantBased, setStockVariantBased] = useState("Yes");
const [exportLoading, setExportLoading] = useState(false);
const [fileLoading, setFileLoading] = useState(false);
const [loading, setLoading] = useState(false);
const [headers, setHeaders] = useState([]);
const [uploadedData, setUploadedData] = useState([]);
const CompId = getSession("CompId");
const BranchId = getSession("BranchId");
const AppId = getSession("AppId");
const UserId = getSession("UserId");
const UserType = getSession("UserType");
const [empData, setEmpData] = useState();
const [addnewAccess, setaddnewAccess] = useState(true);
const [RadioSelection, setRadioSelection] = useState("Single");
const [BulkProductData, setBulkProductData] = useState([]);
const [allowDecimal, setAllowDecimal] = useState(false);
const hasFieldExists = (label) =>
headers.some((h) => h.toLowerCase() === label.toLowerCase());
const hasField = (label) =>
tableFieldPreferences?.some((f) => f.label === label && f.access === "Y");
const hasExcelField = (label) =>
tableExcelFieldPreferences?.some(
(f) => f.label === label && f.access === "Y",
);
const items = [
{
name: "Home",
link: `${subDirectory}app-page/home`,
},
];
const handleRemoveRecord = (index) => {
setUploadedData((prev) => prev.filter((_, i) => i !== index));
};
const columns = [
{
title: "Product Name",
dataIndex: "ProductName",
key: "ProductName",
width: 180,
render: (value, record, index) => (
<p>{`${value} (${record?.Size} ${record?.UOMName})`}</p>
),
},
{
title: "Variant Name",
dataIndex: "VariantName",
key: "VariantName",
width: 180,
},
{
title: "Batch No",
dataIndex: "BatchRef",
key: "BatchRef",
width: 180,
},
...(hasFieldExists("Stock")
? [
{
title: "Stock",
dataIndex: "Stock",
key: "Stock",
width: 80,
},
]
: []),
...(hasFieldExists("Stock Date")
? [
{
title: "Stock Date",
dataIndex: "StockDate",
key: "StockDate",
width: 80,
},
]
: []),
...(hasFieldExists("Old MRP")
? [
{
title: "Old MRP",
dataIndex: "OldMRP",
key: "OldMRP",
width: 100,
},
]
: []),
...(hasFieldExists("Old SP")
? [
{
title: "Old SP",
dataIndex: "OldSP",
key: "OldSP",
width: 100,
},
]
: []),
...(hasFieldExists("Old Wholesale Price")
? [
{
title: "Old Wholesale Price",
dataIndex: "OldWholeSalePrice",
key: "OldWholeSalePrice",
width: 140,
},
]
: []),
// ✅ EDITABLE
{
title: "New MRP",
dataIndex: "NewMRP",
key: "NewMRP",
width: 120,
render: (_, record, index) => (
<Form.Item name={`NewMRP${record.localId}`}>
<Input
placeholder="New MRP"
inputMode="decimal"
onChange={(e) =>
handlePriceChange(index, "NewMRP", e.target.value, record.localId)
}
onInput={(e) => {
let cleanedValue = e.target.value.replace(/[^0-9.]/g, "");
if (cleanedValue.startsWith(".")) {
cleanedValue = "0" + cleanedValue;
}
const parts = cleanedValue.split(".");
e.target.value =
parts.length > 2
? `${parts[0]}.${parts.slice(1).join("")}`
: cleanedValue;
}}
/>
</Form.Item>
),
},
{
title: "New SP",
dataIndex: "NewSellPrice",
key: "NewSellPrice",
width: 120,
render: (_, record, index) => (
<Form.Item
name={`NewSellPrice${record.localId}`}
validateTrigger={["onChange", "onBlur"]}
rules={[
({ getFieldValue }) => ({
validator(_, value) {
const mrp = getFieldValue(`NewMRP${record.localId}`);
if (mrp === undefined || mrp === null || mrp === "") {
return Promise.resolve();
}
const numMRP = Number(mrp);
const numSP =
value === undefined || value === null || value === ""
? null
: Number(value);
if (numSP === null) {
return Promise.reject(
new Error("New SP is required when New MRP is provided"),
);
}
if (numSP > numMRP) {
return Promise.reject(
new Error("New SP cannot be greater than New MRP"),
);
}
return Promise.resolve();
},
}),
]}
>
<Input
placeholder="New SP"
inputMode="decimal"
onInput={(e) => {
let cleanedValue = e.target.value.replace(/[^0-9.]/g, "");
const parts = cleanedValue.split(".");
if (cleanedValue.startsWith(".")) {
cleanedValue = "0" + cleanedValue;
}
e.target.value =
parts.length > 2
? `${parts[0]}.${parts.slice(1).join("")}`
: cleanedValue;
}}
onChange={(e) => {
{
handlePriceChange(
index,
"NewSellPrice",
e.target.value,
record.localId,
);
excelDataRef.current
?.validateFields([`NewSellPrice${record.localId}`])
.catch(() => { });
}
}}
/>
</Form.Item>
),
},
...(hasFieldExists("New Wholesale Price")
? [
{
title: "New Wholesale Price",
dataIndex: "NewWholeSalePrice",
key: "NewWholeSalePrice",
width: 160,
render: (value, record, index) => (
<Form.Item name={`NewWholeSalePrice${record?.localId}`}>
<Input
onChange={(e) =>
handlePriceChange(
index,
"NewWholeSalePrice",
e.target.value,
record.localId,
)
}
placeholder="New Wholesale"
inputMode="decimal"
onInput={(e) => {
let cleanedValue = e.target.value.replace(/[^0-9.]/g, "");
const parts = cleanedValue.split(".");
if (cleanedValue.startsWith(".")) {
cleanedValue = "0" + cleanedValue;
}
e.target.value =
parts.length > 2
? `${parts[0]}.${parts.slice(1).join("")}`
: cleanedValue;
}}
/>
</Form.Item>
),
},
]
: []),
{
title: "Action",
key: "action",
width: 80,
align: "center",
render: (_, record, index) => (
<DeleteFilled
style={{
color: "#EF4443",
cursor: "pointer",
}}
onClick={() => handleRemoveRecord(index)}
/>
),
},
];
useEffect(() => {
dispatch(changeBreadCrumb({ items: items }));
getPreference();
// if (UserType === "Employee") {
// fetchApi()
// }
}, []);
useEffect(() => {
dispatch(getCommonAppPreference(AppId));
}, [AppId, dispatch]);
useEffect(() => {
if (UserType === "Employee") {
fetchApi();
}
}, [UserType]);
useEffect(() => {
getFieldSetup();
}, [categoryId]);
useEffect(() => {
getExcelFieldSetup();
}, [excelCategoryId]);
const getFieldSetup = async () => {
try {
const response = await dispatch(
getFieldSetupData({ AppId, CompId, BranchId, categoryId, Type: "GB" }),
).unwrap();
if (response?.data?.statusCode === 1) {
console.log(response?.data?.data?.[0]?.ConfigDtl, "Field Setup Data");
setSelectedFields(
response?.data?.data?.[0]?.ConfigDtl?.filter(
(c) => c.ConfigId && c.Access === "Y",
)?.map((c) => c.ConfigId) || [],
);
setTableFieldPreferences(
response?.data?.data?.[0]?.ConfigDtl?.map((c) => ({
value: c.ConfigId,
label: c.ConfigName,
access: c.Access,
})) || [],
);
}
} catch (error) {
console.error("Error fetching field setup:", error);
}
};
const getExcelFieldSetup = async () => {
try {
const response = await dispatch(
getFieldSetupData({
AppId,
CompId,
BranchId,
categoryId: excelCategoryId,
Type: "GB",
}),
).unwrap();
if (response?.data?.statusCode === 1) {
console.log(response?.data?.data?.[0]?.ConfigDtl, "Field Setup Data");
setSelectedExcelFields(
response?.data?.data?.[0]?.ConfigDtl?.filter(
(c) => c.ConfigId && c.Access === "Y",
)?.map((c) => c.ConfigId) || [],
);
setTableExcelFieldPreferences(
response?.data?.data?.[0]?.ConfigDtl?.map((c) => ({
value: c.ConfigId,
label: c.ConfigName,
access: c.Access,
})) || [],
);
}
} catch (error) {
console.error("Error fetching field setup:", error);
}
};
useEffect(() => {
let hasAccess = false;
if (UserType === "Admin" || UserType === "Super Admin") {
hasAccess = true;
} else if (UserType === "Employee") {
hasAccess = empData?.AddAccess === "Y";
} else if (UserType === "Super Admin User") {
hasAccess = SAAccessCommonMaster?.AddAccess === "Y";
}
setaddnewAccess(!hasAccess);
}, [empData, SAAccessCommonMaster, UserType]);
useEffect(() => {
if (uploadedData?.length) {
setFormValuesFromExtractedData(uploadedData);
} else {
excelDataRef?.current?.resetFields();
}
}, [uploadedData]);
const fetchApi = async () => {
let data = {
CompId: CompId,
BranchId: BranchId,
AppId: AppId,
EmpId: UserId,
};
let response = await dispatch(getEmpAccess(data)).unwrap();
let datas = response?.data?.data?.[0]?.EmpAccessDetails?.filter(
(item) => item.ConfigName === "Price Change",
);
setEmpData(datas?.[0]);
};
const EditableContext = React.createContext(null);
const EditableRow = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
const BulkEditableRow = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
const EditableCell = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
...restProps
}) => {
const [editing, setEditing] = useState(false);
const inputRef = useRef(null);
const form = useContext(EditableContext);
useEffect(() => {
if (editing) {
inputRef?.current?.focus();
}
}, [editing]);
const toggleEdit = () => {
setEditing(!editing);
form.setFieldsValue({
[dataIndex]: record[dataIndex],
});
};
const save = async () => {
try {
const values = await form.validateFields();
toggleEdit();
handleSave({
...record,
...values,
});
} catch (errInfo) {
console.log("Save failed:", errInfo);
}
};
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item
style={{
margin: 0,
}}
name={dataIndex}
rules={[
{
pattern: /^(?!0$)(\d+)(\.\d{1,2})?$/,
message: "Invalid format (Max 2 decimals, No zero)",
},
{
validator: (_, value) =>
value?.length > 10
? Promise.reject("Max 10 characters allowed")
: Promise.resolve(),
},
]}
>
<Input
className="editable-table-input"
ref={inputRef}
onPressEnter={save}
onBlur={save}
// onMouseLeave={save}
value={
Array.isArray(record[dataIndex]) ? undefined : record[dataIndex]
}
inputMode="decimal"
onInput={(e) => {
const cleanedValue = e.target.value.replace(/[^0-9.]/g, "");
const parts = cleanedValue.split(".");
e.target.value =
parts.length > 2
? `${parts[0]}.${parts.slice(1).join("")}`
: cleanedValue;
}}
/>
</Form.Item>
) : (
<div className="editable-cell-value-wrap" onClick={toggleEdit}>
{/* {dataIndex === 'CurrentAmt' && Array.isArray(record[dataIndex])
? record[dataIndex]?.map((item, index) => (
<span key={index}>
{item.CurrentAmt + '-' + item.CurrentAmt}
</span>
))
: children} */}
<Input
ref={inputRef}
className="editable-table-input"
onPressEnter={save}
onBlur={save}
value={
Array.isArray(record[dataIndex]) ? undefined : record[dataIndex]
}
/>
</div>
);
}
return <td {...restProps}>{childNode}</td>;
};
const BulkEditableCell = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
...restProps
}) => {
const [editing, setEditing] = useState(false);
const inputRef = useRef(null);
const form = useContext(EditableContext);
useEffect(() => {
if (editing) {
inputRef?.current?.focus();
}
}, [editing]);
const toggleEdit = () => {
setEditing(!editing);
form.setFieldsValue({
[dataIndex]: record[dataIndex],
});
};
const Bulksave = async () => {
try {
const values = await form.validateFields();
toggleEdit();
handleBulkSave({
...record,
...values,
});
} catch (errInfo) {
console.log("Save failed:", errInfo);
}
};
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item
style={{
margin: 0,
}}
name={dataIndex}
rules={[
{
pattern: /^(?!0$)(\d+)(\.\d{1,2})?$/,
message: "Invalid format (Max 2 decimals, No zero)",
},
{
validator: (_, value) =>
value?.length > 10
? Promise.reject("Max 10 characters allowed")
: Promise.resolve(),
},
]}
>
<Input
ref={inputRef}
onPressEnter={Bulksave}
onBlur={Bulksave}
// onMouseLeave={Bulksave}
value={
Array.isArray(record[dataIndex]) ? undefined : record[dataIndex]
}
inputMode="decimal"
onInput={(e) => {
const cleanedValue = e.target.value.replace(/[^0-9.]/g, "");
const parts = cleanedValue.split(".");
e.target.value =
parts.length > 2
? `${parts[0]}.${parts.slice(1).join("")}`
: cleanedValue;
}}
/>
</Form.Item>
) : (
<div className="editable-cell-value-wrap" onClick={toggleEdit}>
{/* {dataIndex === "CurrentAmt" && Array.isArray(record[dataIndex])
? record[dataIndex]?.map((item, index) => (
<span key={index}>
{item.CurrentAmt + "-" + item.CurrentAmt}
</span>
))
: children} */}
<Input
ref={inputRef}
onPressEnter={Bulksave}
onBlur={Bulksave}
value={
Array.isArray(record[dataIndex]) ? undefined : record[dataIndex]
}
/>
</div>
);
}
return <td {...restProps}>{childNode}</td>;
};
useEffect(() => {
fetchData();
}, []);
const getPreference = async () => {
const data = { AppId: AppId, CompId: CompId, BranchId: BranchId };
const { data: res } = await dispatch(getPreferenceData(data)).unwrap();
const decimalSetting = res?.data?.[0]?.SettingDtlDetails?.find(
(setting) =>
setting?.SettingIdName?.toLowerCase() === "decimal" &&
setting?.SettingValue === "Y",
);
if (decimalSetting) {
setAllowDecimal(true);
}
};
const fetchData = async () => {
let data = {
CompId: CompId,
BranchId: BranchId,
AppId: AppId,
};
let res = await dispatch(getStockNames(data)).unwrap();
const stockNamesWithKeys = res?.data?.data?.map((stockName) => ({
...stockName,
stockDetails: stockName?.stockDetails?.map((item, index) => ({
...item,
key: `${stockName.ProdId}_${index}`,
})),
}));
setStockNames(stockNamesWithKeys);
const flattenedArray = res?.data?.data?.flatMap((product) =>
product.stockDetails.map((detail) => ({
ProdName: product.ProdName,
InwardDtlId: detail.InwardDtlId,
InwardDate: detail.InwardDate,
ExpDate: detail.ExpDate,
BalanceQty: detail.BalanceQty,
MRP: detail.MRP,
SellPrice: detail.SellPrice,
ProdId: product.ProdId,
Size: product.Size,
StockAvailable: product.StockAvailable,
CompId: product.CompId,
BranchId: product.BranchId,
AppId: product.AppId,
UOM: product.UOM,
UOMName: product.UOMName,
WhSalePrice: detail?.WhSalePrice,
ProdVariantName: detail?.ProdVariantName,
BatchRef: detail?.BatchRef,
key: `${product.ProdId}_${detail.InwardDtlId}`,
})),
);
setBulkProductData(flattenedArray);
};
const actionsFormatter = async (row) => {
setOpenEditModal(true);
setDataSource([row]);
};
const handlePriceChange = (rowIndex, field, value, localId) => {
const numValue = value ? parseFloat(value) : null;
setUploadedData((prev) => {
const updated = [...prev];
const currentRow = { ...updated[rowIndex] };
// If changing New MRP, clear New SP
if (field === "NewMRP") {
currentRow.NewSellPrice = null;
excelDataRef.current?.setFieldsValue({
[`NewSellPrice${localId}`]: null,
});
}
currentRow[field] = numValue;
updated[rowIndex] = currentRow;
return updated;
});
// Validate New SP against New MRP
if (field === "NewSellPrice" && numValue) {
const currentRow = uploadedData[rowIndex];
const newMRP = currentRow?.NewMRP;
if (newMRP && numValue > newMRP) {
setMessageType("error");
setMessageData("New SP cannot be greater than New MRP");
return;
}
}
excelDataRef.current?.setFieldsValue({
[`${field}${localId}`]: numValue,
});
};
const statusFormatter = async (row) => {
const updatedData = StockDetails?.filter(
(item) => item?.InwardDtlId !== row?.InwardDtlId,
);
setStockDetails(updatedData);
};
let tableValue = StockNames?.[0]?.stockDetails;
let DataSource = StockNames?.map((name, index) => ({
value: index + 1,
label: name?.ProdName + "(" + (name?.Size + " " + name?.UOMName) + ")",
ProdId: name?.ProdId,
stockDetails: name?.stockDetails,
Quantity: name?.Size,
QuantityName: name?.UOMName,
ExpDate: name.stockDetails?.[0]?.ExpDate,
}));
useEffect(() => {
setDataSource(tableValue);
}, [tableValue]);
const handleSave = (row) => {
if (row?.CurrentAmt && row?.CurrentMRP) {
if (parseFloat(row?.CurrentMRP) >= parseFloat(row?.CurrentAmt)) {
const newData = StockDetails?.map((item) => {
return item.key === row.key ? { ...item, ...row } : item;
});
const index = newData?.findIndex((item) => row.key === item.key);
const item = newData[index];
newData?.splice(index, 1, {
...item,
...row,
});
setStockDetails(newData);
} else {
setMessageType("error");
setMessageData("MRP is must be greater than or equal to selling price");
}
} else {
const newData = StockDetails?.map((item) => {
return item.key === row.key ? { ...item, ...row } : item;
});
const index = newData?.findIndex((item) => row.key === item.key);
const item = newData[index];
newData?.splice(index, 1, {
...item,
...row,
});
setStockDetails(newData);
}
};
const handleBulkSave = (row) => {
if (row?.CurrentAmt && row?.CurrentMRP) {
if (parseFloat(row?.CurrentMRP) >= parseFloat(row?.CurrentAmt)) {
const newData = BulkProductData?.map((item) => {
return item.key === row.key ? { ...item, ...row } : item;
});
const index = newData?.findIndex((item) => row.key === item.key);
const item = newData[index];
newData?.splice(index, 1, {
...item,
...row,
});
setBulkProductData(newData);
} else {
setMessageType("error");
setMessageData(
"MRP is must be greater than or equal to selling price",
);
}
} else {
const newData = BulkProductData?.map((item) => {
return item.key === row.key ? { ...item, ...row } : item;
});
const index = newData?.findIndex((item) => row.key === item.key);
const item = newData[index];
newData?.splice(index, 1, {
...item,
...row,
});
setBulkProductData(newData);
}
};
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
const BulkComponents = {
body: {
row: BulkEditableRow,
cell: BulkEditableCell,
},
};
function safeRound(amountStr) {
if (amountStr == null) return allowDecimal ? "0.00" : "0";
const cleaned = String(amountStr).replace(/[^0-9.-]+/g, "");
const num = Number(cleaned);
if (isNaN(num)) return allowDecimal ? "0.00" : "0";
return allowDecimal ? num.toFixed(2) : Math.round(num).toString();
}
const columns2 = [
{
title: "Sl.No",
align: "center",
key: "sno",
width: "60px",
render: (text, object, index) => (
<a style={{ color: "black" }}>{index + 1}</a>
),
},
{
title: "Product Name",
dataIndex: "ProdName",
key: "ProdName",
width: "20%",
filteredValue: [searchedText],
onFilter: (value, record) => {
return String(record.ProdName)
?.toLowerCase()
.includes(value?.toLowerCase());
},
sorter: (a, b) => a?.ProdName?.localeCompare(b.ProdName),
sortOrder: sortedInfo.columnKey === "ProdName" ? sortedInfo.order : null,
ellipsis: true,
render: (_, record) => (
<span>
{record.ProdName} ({record.UOM} {record.UOMName})
</span>
),
},
{
title: "stock Date",
dataIndex: "InwardDate",
key: "InwardDate",
align: "right",
width: "150px",
render: (InwardDate) =>
InwardDate !== null ? ExtractDateFormate(InwardDate) : "-",
},
{
title: "Old MRP",
dataIndex: "MRP",
key: "MRP",
align: "right",
render: (MRP) => (safeRound(MRP) !== null ? safeRound(MRP) : "-"),
},
{
title: "Old Price",
dataIndex: "SellPrice",
key: "SellPrice",
align: "right",
render: (SellPrice) =>
safeRound(SellPrice) !== null ? safeRound(SellPrice) : "-",
},
{
title: "New MRP",
dataIndex: "CurrentMRP",
key: "CurrentMRP",
align: "right",
editable: true,
render: (text, record) => <p>{text ? text : "Enter MRP"}</p>,
width: 100,
},
{
title: "New SP",
dataIndex: "CurrentAmt",
key: "CurrentAmt",
align: "right",
editable: true,
render: (text, record) => <p>{text ? text : "Enter Price"}</p>,
width: 100,
},
{
title: "Action",
dataIndex: "SellPrice",
key: "SellPrice",
align: "center",
width: "100px",
render: (_, record, index) => (
<div
style={{
display: "flex",
justifyContent: "center",
columnGap: "1rem",
}}
>
<DeleteFilled
style={{ color: "#FF4D4F" }}
onClick={() => statusFormatter(record, index)}
/>
</div>
),
},
];
const BulkPricechangeColumns = [
{
title: "Sl.No",
align: "center",
key: "sno",
width: "50px",
render: (text, object, index) => (
<a style={{ color: "black" }}>
{(Bulkchangepage - 1) * 10 + index + 1}
</a>
),
},
{
title: "Name",
dataIndex: "ProdName",
key: "ProdName",
width: "20%",
filteredValue: [BulksearchedText],
onFilter: (value, record) => {
return String(record.ProdName)
?.toLowerCase()
.includes(value?.toLowerCase());
},
sorter: (a, b) => a?.ProdName?.localeCompare(b.ProdName),
sortOrder:
BulksortedInfo.columnKey === "ProdName" ? BulksortedInfo.order : null,
ellipsis: true,
render: (_, record) => (
<span>
{record.ProdName} ({record.Size} {record.UOMName})
</span>
),
},
{
title: "Variant Name",
dataIndex: "ProdVariantName",
key: "ProdVariantName",
align: "left",
width: "120px",
},
{
title: "Batch No",
dataIndex: "BatchRef",
key: "BatchRef",
align: "left",
width: "120px",
},
...(hasField("Stock Date")
? [
{
title: "stock Date",
dataIndex: "InwardDate",
key: "InwardDate",
align: "center",
width: "100px",
render: (InwardDate) =>
InwardDate !== null ? ExtractDateFormate(InwardDate) : "-",
},
]
: []),
...(hasField("Expiry Date")
? [
{
title: "Exp date",
dataIndex: "ExpDate",
key: "ExpDate",
align: "center",
width: "100px",
render: (ExpDate) =>
ExpDate !== null ? ExtractDateFormate(ExpDate) : "-",
},
]
: []),
...(hasField("Stock")
? [
{
title: "stock",
dataIndex: "BalanceQty",
key: "BalanceQty",
align: "right",
width: "60px",
render: (BalanceQty) => (BalanceQty !== null ? BalanceQty : "-"),
},
]
: []),
...(hasField("Old MRP")
? [
{
title: "Old MRP",
dataIndex: "MRP",
key: "MRP",
align: "right",
width: "70px",
render: (MRP) => (safeRound(MRP) !== null ? safeRound(MRP) : "-"),
},
]
: []),
...(hasField("Old SP")
? [
{
title: "Old SP",
dataIndex: "SellPrice",
key: "SellPrice",
align: "right",
width: "70px",
render: (SellPrice) =>
safeRound(SellPrice) !== null ? safeRound(SellPrice) : "-",
},
]
: []),
...(hasField("Old Wholesale Price")
? [
{
title: "Old Wholesale Price",
dataIndex: "WhSalePrice",
key: "WhSalePrice",
align: "right",
width: "120px",
render: (WhSalePrice) =>
safeRound(WhSalePrice) !== null ? safeRound(WhSalePrice) : "-",
},
]
: []),
{
title: "New MRP",
dataIndex: "CurrentMRP",
key: "CurrentMRP",
align: "center",
with: "4rem",
editable: true,
render: (text, record) => <p>{text ? text : "Enter MRP"}</p>,
width: 100,
},
{
title: "New SP",
dataIndex: "CurrentAmt",
key: "CurrentAmt",
align: "center",
with: "4rem",
editable: true,
render: (text, record) => <p>{text ? text : "Enter Price"}</p>,
width: 100,
},
...(hasField("New Wholesale Price")
? [
{
title: "New Wholesale Price",
dataIndex: "CurrentWhAmt",
key: "CurrentWhAmt",
align: "center",
with: "4rem",
editable: true,
render: (text, record) => (
<p>{text ? text : "Enter Wholesale Price"}</p>
),
width: 120,
},
]
: []),
];
const HandleStockNames = (e, option) => {
const newStockDetails = option?.stockDetails || [];
// Check if any of the new products already exist in StockDetails
setStockDetails((prevStockDetails) => {
const existingInwardDtlIds = new Set(
prevStockDetails?.map((item) => item.InwardDtlId),
);
const uniqueNewStockDetails = newStockDetails
?.filter((item) => !existingInwardDtlIds.has(item.InwardDtlId))
?.map((detail) => ({
...detail,
UOMName: option?.QuantityName,
UOM: option?.Quantity,
}));
return [...prevStockDetails, ...uniqueNewStockDetails];
});
setProductName(option?.label);
};
// const handleCancel = () => {
// setOpen(false);
// setOpenEditModal(false);
// };
// const HandleSubmit = () => {
// const newStockDetails = dataSource || [];
// const filteredCurrentAmtData = newStockDetails?.filter(
// (item) =>
// item.CurrentAmt !== undefined &&
// item.CurrentAmt !== null &&
// item.CurrentMRP !== undefined &&
// item.CurrentMRP !== null
// );
// if (filteredCurrentAmtData?.length > 0) {
// setStockDetails((prevStockDetails) => {
// const existingInwardDtlIds = new Set(
// prevStockDetails?.map((item) => item.InwardDtlId)
// );
// const uniqueNewStockDetails = filteredCurrentAmtData?.filter(
// (item) => !existingInwardDtlIds.has(item.InwardDtlId)
// );
// let beforeFinal = [...prevStockDetails, ...uniqueNewStockDetails];
// const existingInwardDtlIds2 = new Set(
// filteredCurrentAmtData?.map((item) => item.InwardDtlId)
// );
// const uniqueNewStockDetails2 = beforeFinal?.filter(
// (item) => !existingInwardDtlIds2.has(item.InwardDtlId)
// );
// return [...filteredCurrentAmtData, ...uniqueNewStockDetails2];
// });
// setOpen(false);
// } else {
// setMessageType('error');
// setMessageData('Please give all details');
// }
// };
const HandlePutData = async () => {
if (RadioSelection == "Bulk") {
const filteredCurrentAmtData = BulkProductData?.filter(
(item) =>
(item.CurrentAmt !== undefined &&
item.CurrentAmt !== null &&
item.CurrentAmt !== "" &&
item.CurrentMRP !== undefined &&
item.CurrentMRP !== null &&
item.CurrentMRP !== "") ||
(item.CurrentWhAmt !== undefined &&
item.CurrentWhAmt !== null &&
item.CurrentWhAmt !== ""),
);
const hasInvalidData = BulkProductData.every((item) => {
const isAbsent = item.CurrentAmt == null && item.CurrentMRP == null;
const isPresent = item.CurrentAmt != null && item.CurrentMRP != null;
return isAbsent || isPresent;
});
if (filteredCurrentAmtData?.length > 0 && hasInvalidData) {
let Postdata = {
CompId: CompId,
BranchId: BranchId,
Type: "Variant",
ProductDetails: filteredCurrentAmtData?.map((item) => ({
ProdId: item?.ProdId,
NewMRP: item?.CurrentMRP,
NewSellPrice: item?.CurrentAmt,
AdjComment: "string",
InwardDtlId: item?.InwardDtlId,
NewWholeSalePrice: item.CurrentWhAmt,
})),
};
let response = await dispatch(bulkPriceUpdate(Postdata)).unwrap();
if (response?.data?.statusCode == 1) {
setProductName();
setMessageType("success");
setMessageData(response?.data?.response);
fetchData();
setBulksearchedText("");
setStockDetails([]);
setBulkProductData([]);
} else {
setMessageType("error");
setMessageData(response?.data?.response);
}
} else {
setMessageType("error");
setMessageData("Please give all details");
}
} else {
const filteredStockDetails = StockDetails?.filter(
(item) =>
item?.CurrentMRP &&
item?.CurrentMRP !== "" &&
item?.CurrentAmt &&
item?.CurrentAmt !== "",
);
if (filteredStockDetails?.length === 0) {
setMessageType("error");
setMessageData("Please enter price details for at least one product");
return;
}
let data = {
CompId: CompId,
BranchId: BranchId,
ProductDetails: filteredStockDetails?.map((item) => ({
ProdId: item?.ProdId,
MRP: item?.CurrentMRP,
SellPrice: item?.CurrentAmt,
AdjComment: "string",
InwardDtlId: item?.InwardDtlId,
})),
UpdatedBy: 1,
AppId: AppId,
};
let response = await dispatch(PutStockPrice(data)).unwrap();
let res = await dispatch(getStockNames(data)).unwrap();
setStockNames(res?.data?.data);
if (response?.data?.statusCode == 1) {
setProductName();
setMessageType("success");
setMessageData(response?.data?.response);
fetchData();
} else {
setMessageType("error");
setMessageData(response?.data?.response);
}
setStockDetails([]);
}
};
// const HandleEditSubmit = () => {
// let newStockDetails = dataSource || [];
// let filteredInverd = StockDetails?.filter(
// (item) => item.InwardDtlId !== newStockDetails?.[0].InwardDtlId
// );
// filteredInverd.push(newStockDetails?.[0]);
// setStockDetails(filteredInverd);
// setOpenEditModal(false);
// };
// const columns = defaultColumns?.map((col) => {
// if (!col.editable) {
// return col;
// }
// return {
// ...col,
// onCell: (record) => ({
// record,
// editable: col.editable,
// dataIndex: col.dataIndex,
// title: col.title,
// handleSave,
// }),
// };
// });
const columns2WithEditable = columns2?.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave,
}),
};
});
const BulkPriceColumns = BulkPricechangeColumns?.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave,
}),
};
});
const handlePageChange = (current) => {
setpage(current);
};
const handlePageChangeBulk = (current) => {
setBulkchangepage(current);
setPageSize(pageSize);
};
const onComplete = useCallback(() => {
setMessageData(null);
setMessageType(null);
}, []);
const onSearch = (value) => {
setSearchedText(value);
};
const onSearchBulk = (value) => {
setBulksearchedText(value);
};
const onSearchChange = (e) => {
setSearchedText(e?.target?.value);
};
const onSearchChangeBulk = (e) => {
setBulksearchedText(e?.target?.value);
};
const handleChange = (pagination, filters, sorter) => {
setFilteredInfo(filters);
setSortedInfo(sorter);
};
const handleChangeBulk = (pagination, filters, sorter) => {
setBulksortedInfo(sorter);
};
const onRadioChange = (data) => {
setRadioSelection(data);
};
const handleUploadExcel = () => {
setExcelModalOpen(true);
};
const handleFieldSetup = () => {
setFieldSetup(true);
};
const handleExcelFieldSetup = () => {
setExcelFieldSetup(true);
};
const showPriceChangeModal = () => {
setPriceChangeModalOpen(true);
};
const handleExport = async (isStockVariantBased = "Yes") => {
setExportLoading(true);
setPriceChangeModalOpen(false);
const uniqueBulkProductData =
isStockVariantBased === "Yes"
? BulkProductData || []
: (BulkProductData || [])?.filter(
(item, index, self) =>
index === self.findIndex((p) => p.ProdId === item.ProdId),
);
if (!uniqueBulkProductData?.length) {
setMessageType("error");
setMessageData("No data available to export");
setExportLoading(false);
return;
}
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet("Products");
/* ---------------- COLUMN DEFINITIONS ---------------- */
const columns = [
{ header: "Product Name", key: "ProdName", width: 20 },
...(isStockVariantBased === "Yes"
? [{ header: "Variant Name", key: "ProdVariantName", width: 20 }]
: []),
...(isStockVariantBased === "Yes"
? [{ header: "Batch No", key: "BatchRef", width: 20 }]
: []),
...(hasExcelField("Stock Date")
? [{ header: "Stock Date", key: "StockDate", width: 15 }]
: []),
...(hasExcelField("Expiry Date")
? [{ header: "Exp Date", key: "ExpDate", width: 15 }]
: []),
...(hasExcelField("Stock")
? [{ header: "Stock", key: "Stock", width: 10 }]
: []),
...(hasExcelField("Old MRP")
? [{ header: "Old MRP", key: "OldMRP", width: 10 }]
: []),
...(hasExcelField("Old SP")
? [{ header: "Old SP", key: "OldSP", width: 10 }]
: []),
...(hasExcelField("Old Wholesale Price")
? [{ header: "Old Wholesale Price", key: "WhSalePrice", width: 14 }]
: []),
...(hasExcelField("Size")
? [{ header: "Size", key: "Size", width: 10 }]
: []),
...(hasExcelField("Stock Maintainence")
? [{ header: "Stock Maintainence", key: "StockAvailable", width: 18 }]
: []),
...(hasExcelField("UOM Name")
? [{ header: "UOMName", key: "UOMName", width: 15 }]
: []),
// ...(hasExcelField("New MRP & New SP")
// ? [
{ header: "New MRP", key: "NewMRP", width: 12 },
{ header: "New SP", key: "NewSP", width: 12 },
// ]
// : []),
...(hasExcelField("New Wholesale Price")
? [{ header: "New Wholesale Price", key: "NewWhSalePrice", width: 14 }]
: []),
// --- Hidden system columns (always included) ---
{ header: "ProdId", key: "ProdId", width: 1 },
...(isStockVariantBased === "Yes"
? [{ header: "InwardDtlId", key: "InwardDtlId", width: 1 }]
: []),
{ header: "key", key: "key", width: 1 },
{ header: "downloadType", key: "downloadType", width: 1 },
];
worksheet.columns = columns;
worksheet.views = [{ state: "frozen", xSplit: 1, ySplit: 1 }];
const headerRow = worksheet.getRow(1);
const newMRPCol = headerRow.values.indexOf("New MRP");
const newSPCol = headerRow.values.indexOf("New SP");
const hasNewMRPAndSP = newMRPCol > 0 && newSPCol > 0;
/* ---------------- HEADER STYLE ---------------- */
worksheet.getRow(1).eachCell((cell) => {
const header = cell.value;
const isHiddenCol =
header === "key" ||
header === "ProdId" ||
header === "InwardDtlId" ||
header === "downloadType";
cell.font = {
bold: true,
size: isHiddenCol ? 1 : 11,
color: { argb: isHiddenCol ? "FFFFFFFF" : "FF000000" },
};
cell.alignment = {
horizontal: "center",
vertical: "middle",
wrapText: true, // ✅ prevents overflow in header
};
cell.fill = {
type: "pattern",
pattern: "solid",
fgColor: {
argb: isHiddenCol ? "FFFFFFFF" : "FF52C41A",
},
};
cell.border = isHiddenCol
? undefined
: {
right: { style: "thin", color: { argb: "FF000000" } },
bottom: { style: "thin", color: { argb: "FF000000" } },
};
});
/* ---------------- ADD DATA ---------------- */
uniqueBulkProductData.forEach((product) => {
worksheet.addRow({
ProdName: product?.ProdName,
ProdVariantName: product?.ProdVariantName,
BatchRef: product?.BatchRef,
StockDate: product?.InwardDate,
ExpDate: product?.ExpDate || "-",
Stock: product?.BalanceQty,
OldMRP: product?.MRP,
OldSP: product?.SellPrice,
WhSalePrice: product?.WhSalePrice,
Size: product?.Size,
StockAvailable: product?.StockAvailable,
UOMName: product?.UOMName,
key: product?.key,
downloadType: isStockVariantBased === "Yes" ? "Variant" : "Product",
NewMRP: "",
NewSP: "",
NewWhSalePrice: "",
ProdId: product?.ProdId,
InwardDtlId:
isStockVariantBased === "Yes" ? product?.InwardDtlId : undefined,
});
});
// ---------------- VALIDATION: New SP <= New MRP ----------------
if (hasNewMRPAndSP) {
worksheet.eachRow({ includeEmpty: true }, (row, rowNumber) => {
if (rowNumber === 1) return; // skip header
const newMRPCell = row.getCell(newMRPCol);
const newSPCell = row.getCell(newSPCol);
newSPCell.dataValidation = {
type: "custom",
allowBlank: true,
formulae: [
`IFERROR(VALUE(${newSPCell.address}),0) <= IFERROR(VALUE(${newMRPCell.address}),0)`,
],
showErrorMessage: true,
errorStyle: "stop",
errorTitle: "Invalid Selling Price",
error: "New SP cannot be greater than New MRP",
};
});
}
/* ---------------- UNLOCK EVERYTHING ---------------- */
worksheet.eachRow({ includeEmpty: true }, (row) => {
row.eachCell({ includeEmpty: true }, (cell) => {
cell.protection = { locked: false };
});
});
/* ---------------- LOCK HEADER ---------------- */
worksheet.getRow(1).eachCell((cell) => {
cell.protection = { locked: true };
});
worksheet.getRow(1).height = 32;
/* ---------------- LOCK ALL EXCEPT New MRP & New SP ---------------- */
worksheet.eachRow({ includeEmpty: true }, (row, rowNumber) => {
if (rowNumber === 1) return;
row.eachCell((cell, colNumber) => {
const header = worksheet.getRow(1).getCell(colNumber).value;
const isHiddenCol =
header === "key" ||
header === "ProdId" ||
header === "InwardDtlId" ||
header === "downloadType";
const isEditable =
header === "New MRP" ||
header === "New SP" ||
header === "New Wholesale Price";
cell.protection = { locked: !isEditable };
cell.alignment = {
vertical: "middle",
wrapText: true,
};
if (isHiddenCol) {
cell.font = {
size: 1,
color: { argb: "FFFFFFFF" },
};
cell.fill = {
type: "pattern",
pattern: "solid",
fgColor: { argb: "FFFFFFFF" },
};
}
});
});
/* ---------------- PROTECT SHEET ---------------- */
await worksheet.protect("", {
selectLockedCells: false,
selectUnlockedCells: true,
formatCells: false,
formatColumns: false,
insertRows: false,
deleteRows: false,
});
/* ---------------- DOWNLOAD ---------------- */
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "PriceChange_Data.xlsx";
link.click();
setExportLoading(false);
};
const handleClick = () => {
fileInputRef.current.click();
};
const handleFileUpload = (e) => {
setFileLoading(true);
const file = e.target.files[0];
if (!file) {
dispatch(emptyExcelData());
setFileLoading(false);
return;
}
const isAllowed = allowedExcelTypes?.includes(file.type);
if (!isAllowed) {
Modal.error({
title: "Invalid File Type",
content: "Only Excel files (.xls, .xlsx) are allowed.",
});
handleCancelData();
setFileLoading(false);
return;
}
const reader = new FileReader();
reader.onload = (event) => {
const arrayBuffer = event.target.result;
const extractedData = extractPriceChangeDataFromExcel(arrayBuffer);
console.log(extractedData, "extractedData");
if ((extractedData || [])?.length === 0) {
setMessageData("No Data Found in Uploaded Sheet");
setMessageType("error");
handleCancelData();
setFileLoading(false);
return;
}
setUploadedData(extractedData);
setFileLoading(false);
// setFormValuesFromExtractedData(extractedData);
};
reader.readAsArrayBuffer(file); // ✅ THIS IS REQUIRED
};
const setFormValuesFromExtractedData = (data) => {
const formValues = {};
data.forEach((item) => {
const id = item.localId;
if (item.NewMRP !== null && item.NewMRP !== undefined) {
formValues[`NewMRP${id}`] = item.NewMRP;
}
if (item.NewSellPrice !== null && item.NewSellPrice !== undefined) {
formValues[`NewSellPrice${id}`] = item.NewSellPrice;
}
if (
item.NewWholeSalePrice !== null &&
item.NewWholeSalePrice !== undefined
) {
formValues[`NewWholeSalePrice${id}`] = item.NewWholeSalePrice;
}
});
excelDataRef.current?.setFieldsValue(formValues);
};
const extractPriceChangeDataFromExcel = (arrayBuffer) => {
const workbook = read(arrayBuffer, { type: "array" });
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
const rows = utils.sheet_to_json(worksheet, {
header: 1,
raw: false,
defval: "",
});
if (!rows.length) return [];
const header = rows[0];
setHeaders(header);
const dataRows = rows.slice(1);
// Helper to safely get column value
const getVal = (row, colName) => {
const idx = header.indexOf(colName);
return idx !== -1 ? row[idx] : "";
};
const result = dataRows
.map((row) => {
const prodId = getVal(row, "ProdId")?.toString().trim();
if (!prodId) return null;
const newMRP =
getVal(row, "New MRP") !== "" ? Number(getVal(row, "New MRP")) : null;
const newSP =
getVal(row, "New SP") !== "" ? Number(getVal(row, "New SP")) : null;
const newWholesale =
getVal(row, "New Wholesale Price") !== ""
? Number(getVal(row, "New Wholesale Price"))
: null;
// ❌ Must have at least one change
if (newMRP === null && newSP === null && newWholesale === null) {
return null;
}
return {
// 🔹 Product identity
localId: uuidv4(),
ProdId: prodId,
InwardDtlId: getVal(row, "InwardDtlId")
? getVal(row, "InwardDtlId").toString()
: null,
// 🔹 Product info
VariantName: getVal(row, "Variant Name") || null,
BatchRef: getVal(row, "Batch No") || null,
ProductName: getVal(row, "Product Name") || null,
Size: getVal(row, "Size") || null,
UOMName: getVal(row, "UOMName") || null,
StockMaintainence: getVal(row, "Stock Maintainence") || null,
// 🔹 Stock / dates
StockDate: getVal(row, "Stock Date") || null,
ExpDate: getVal(row, "Exp Date") || null,
downloadType: getVal(row, "downloadType") || null,
Stock:
getVal(row, "Stock") !== "" ? Number(getVal(row, "Stock")) : null,
// 🔹 Old prices
OldMRP:
getVal(row, "Old MRP") !== ""
? Number(getVal(row, "Old MRP"))
: null,
OldSP:
getVal(row, "Old SP") !== "" ? Number(getVal(row, "Old SP")) : null,
OldWholeSalePrice:
getVal(row, "Old Wholesale Price") !== ""
? Number(getVal(row, "Old Wholesale Price"))
: null,
// 🔹 New prices (normalized)
NewMRP: newMRP,
NewSellPrice: newSP,
NewWholeSalePrice: newWholesale,
};
})
.filter(Boolean);
return result;
};
const handleCancelData = () => {
dispatch(emptyExcelData());
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
setUploadedData([]);
};
const handleSubmit = async () => {
try {
setLoading(true);
const formValues = excelDataRef.current?.getFieldsValue();
// Validate all records
for (let i = 0; i < uploadedData.length; i++) {
const record = uploadedData[i];
const newMRP = formValues[`NewMRP${record.localId}`];
const newSP = formValues[`NewSellPrice${record.localId}`];
// If MRP is present, SP must also be present
if (newMRP && !newSP) {
setMessageType("error");
setMessageData(
"If New MRP is provided, New SP must also be provided",
);
return;
}
// If SP is present, MRP must also be present
if (newSP && !newMRP) {
setMessageType("error");
setMessageData(
"If New SP is provided, New MRP must also be provided",
);
return;
}
// New SP cannot be greater than New MRP
if (newMRP && newSP && parseFloat(newSP) > parseFloat(newMRP)) {
setMessageType("error");
setMessageData("New SP cannot be greater than New MRP");
return;
}
}
const updatedData = uploadedData
?.filter((data) => {
const hasMRP = !!data?.NewMRP;
const hasSell = !!data?.NewSellPrice;
const hasWholesale = !!data?.NewWholeSalePrice;
// valid cases
if (hasMRP && hasSell) return true;
if (!hasMRP && !hasSell && hasWholesale) return true;
return false;
})
?.map((data) => ({
ProdId: data?.ProdId,
InwardDtlId: data?.InwardDtlId,
NewSellPrice: data?.NewSellPrice,
NewMRP: data?.NewMRP,
NewWholeSalePrice: data?.NewWholeSalePrice,
AdjComment: "string",
}));
const putData = {
AppId: AppId,
CompId: CompId,
BranchId: BranchId,
Type: uploadedData?.[0]?.downloadType,
ProductDetails: updatedData,
CreatedBy: UserId,
};
const res = await dispatch(bulkPriceUpdate(putData))?.unwrap();
if (res?.data?.statusCode === 1) {
setMessageData(res?.data?.response || "Price Updated Successfully");
setMessageType("success");
setUploadedData([]);
handleCancelData();
setExcelModalOpen(false);
await fetchData();
} else {
setMessageData(res?.data?.response);
setMessageType("error");
}
// If validation passes, proceed with submission
console.log("Form validation passed", updatedData);
} catch (error) {
setMessageType("error");
setMessageData("Please check all fields and try again");
} finally {
setLoading(false);
}
};
const handleFieldSetupSubmit = async () => {
const postData = {
AppId: AppId,
CompId: CompId,
BranchId: BranchId,
Type: "GB",
FormType: fieldSetup ? "Price Change" : "Price Change Sheet",
TypeId: fieldSetup ? categoryId : excelCategoryId,
ConfigDtl: (fieldSetup ? selectedFields : selectedExcelFields)?.map(
(field) => ({
ConfigId: field,
Access: "Y",
}),
),
CreatedBy: UserId,
};
const response = await dispatch(postFieldSetup(postData))?.unwrap();
if (response?.data?.statusCode === 1) {
setMessageType("success");
setMessageData(response?.data?.response);
if (fieldSetup) {
await getFieldSetup();
} else {
await getExcelFieldSetup();
}
} else {
setMessageType("error");
setMessageData("Failed to set up fields");
}
};
const handleFieldSelect = (value) => {
if (fieldSetup) {
setSelectedFields((prev) => {
if (prev.includes(value)) {
return prev;
}
return [value, ...prev];
});
} else {
setSelectedExcelFields((prev) => {
if (prev.includes(value)) {
return prev;
}
return [value, ...prev];
});
}
};
const handleFieldRemove = (id) => {
if (fieldSetup) {
setSelectedFields((prev) => prev.filter((field) => field !== id));
} else {
setSelectedExcelFields((prev) => prev.filter((field) => field !== id));
}
};
return (
<div className="userPageTable">
<div className="userPageContent">
<Messages
messageType={messageType}
messageData={messageData}
onComplete={onComplete}
/>
<div className="formAddNew">
<div>
<FormHeader title={"Price Change"} />
</div>
<div
style={{
display: "flex",
flexWrap: "wrap",
justifyContent: "space-between",
}}
>
{RadioSelection == "Bulk" && (
<div
className="toolbar"
style={{
display: "flex",
gap: "1rem",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
}}
>
<div className="formSearch">
<Search
value={BulksearchedText}
placeholder="Search"
onSearch={onSearchBulk}
onSearchChange={onSearchChangeBulk}
/>
</div>
<div className="uploadexcelBtn">
<Buttons
buttonText="Upload Excel"
className="tertiary_Button"
handleSubmit={handleUploadExcel}
disabled={addnewAccess}
icon={<FiUploadCloud size={18} />}
/>
</div>
<Tooltip title="Field Setup">
<div className="btn btn--info" onClick={handleFieldSetup}>
<MdOutlineAppRegistration size={19} />
</div>
</Tooltip>
</div>
)}
</div>
{RadioSelection == "Single" && (
<div
style={{ width: "100%", marginBottom: "0" }}
className="searchAddDiv"
>
<div style={{ width: "100%" }} className="formSearch">
<div
style={{
display: "flex",
gap: "1rem",
flexWrap: "wrap",
rowGap: "1.5rem",
marginBottom: "0.5rem",
// justifyContent: 'space-between',
width: "100%",
}}
className="PricePrice-input"
>
<DropDowns
label="Select Product"
valueData={ProductName}
showSearch
onChangeFunction={(value, option) =>
HandleStockNames(value, option)
}
style={{ width: "240px" }}
options={DataSource}
isOnchanges={ProductName ? true : false}
/>
<div className="formSearch">
<Search
placeholder="Search"
onSearch={onSearch}
onSearchChange={onSearchChange}
/>
</div>
</div>
</div>
</div>
)}
{/* {RadioSelection == "Bulk" &&
<div style={{ padding: "5px 5px" }}>
<Search
value={BulksearchedText}
placeholder="Search"
onSearch={onSearchBulk}
onSearchChange={onSearchChangeBulk}
/>
</div>
} */}
</div>
<div
style={{
display: "flex",
gap: "1rem",
alignItems: "center",
justifyContent: "space-between",
}}
>
<div>
<RadioGrpButton
content={[
{ value: "Single", label: "Single Product" },
{ value: "Bulk", label: "Bulk Product" },
]}
fieldState={true}
defaultSelect={RadioSelection}
onSelectFuntion={(e) => onRadioChange(e)}
/>
</div>
<div>
{(RadioSelection == "Bulk" ||
(RadioSelection == "Single" && StockDetails.length > 0)) && (
<div className="samplereportTableButton">
<Buttons
buttonText="Submit"
color="901D77"
palcement={"right"}
handleSubmit={HandlePutData}
icon={<ArrowRightOutlined />}
disabled={addnewAccess}
></Buttons>
</div>
)}
</div>
</div>
{RadioSelection == "Single" && (
<div
className="samplereportTable reportTable pricechangeTable"
style={{ height: "61vh !important" }}
>
<Table
columns={columns2WithEditable}
components={components}
bordered
dataSource={StockDetails}
data={StockDetails}
pagination={
StockDetails?.length < 11
? false
: {
current: page,
pageSize: pageSize,
onChange: handlePageChange,
}
}
onChange={handleChange}
></Table>
</div>
)}
{RadioSelection == "Bulk" && (
<div
className="samplereportTable reportTable pricechangeTable"
style={{ height: "65vh !important" }}
>
<Table
columns={BulkPriceColumns}
components={BulkComponents}
data={BulkProductData}
dataSource={BulkProductData}
onChange={handleChangeBulk}
pagination={{
current: Bulkchangepage,
pageSize: pageSize,
pageSizeOptions: ["10", "20", "30"],
onChange: handlePageChangeBulk,
showSizeChanger: false,
}}
></Table>
</div>
)}
</div>
<DefaultModal
open={excelModalOpen}
title="Bulk Upload"
footer={false}
className={"bulkuploadmodal padding-less-modal"}
handleCancel={() => {
setExcelModalOpen(false);
handleCancelData();
}}
width={uploadedData?.length > 0 ? 1200 : 600}
children={
<>
{uploadedData?.length === 0 && (
<div
style={{
width: "100%",
display: "flex",
justifyContent: "flex-end",
marginBottom: "1rem",
gap: "0.5rem",
}}
>
<Tooltip title="Field Setup">
<div
className="btn btn--info"
style={{
background: "#00694aff",
color: "#fff",
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: "5px 12px",
borderRadius: "4px",
}}
onClick={handleExcelFieldSetup}
>
<MdOutlineAppRegistration size={19} />
</div>
</Tooltip>
<div
className={`export-excel-div ${exportLoading ? "is-loading" : ""}`}
onClick={
exportLoading ? () => { } : () => showPriceChangeModal()
}
>
<div>
<CiExport size={20} strokeWidth={1} />
</div>
<div style={{ fontSize: "14px", letterSpacing: "0.2px" }}>
Export Excel
</div>
</div>
</div>
)}
{(!uploadedData || uploadedData?.length === 0) && (
<>
{!fileLoading && (
<div
style={{
display: "flex",
alignItems: "center",
border: "1px dashed #000",
width: "max-content",
padding: "10px",
borderRadius: "6px",
}}
>
<div
style={{ position: "relative", display: "inline-block" }}
>
{/* <Imageupload size="2x" /> */}
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<img
src={Excelupldimg}
width={"80px"}
style={{ cursor: "pointer", margin: "0 0 1rem 0" }}
onClick={handleClick}
/>
<input
type="file"
className="form-control"
onChange={handleFileUpload}
ref={fileInputRef}
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
opacity: 0,
cursor: "pointer",
}}
/>
<a style={{ color: "#000", fontSize: "12px" }}>
{" "}
Upload Sheet{" "}
</a>
</div>
</div>
</div>
)}
{fileLoading && (
<div style={{ textAlign: "center", marginTop: "1rem" }}>
<div
style={{
border: "4px solid #f3f3f3",
borderTop: "4px solid #3498db",
borderRadius: "50%",
width: "40px",
height: "40px",
animation: "spin 2s linear infinite",
margin: "0 auto",
}}
></div>
<p style={{ marginTop: "0.5rem", color: "#666" }}>
Processing file...
</p>
<style>{`
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`}</style>
</div>
)}
</>
)}
{uploadedData && uploadedData?.length > 0 && !loading ? (
<div
className="viewerExcelupload"
style={{ maxHeight: "90vh", overflowX: "auto" }}
>
<Form ref={excelDataRef} onFinish={handleSubmit}>
<div className="pricechangedata">
<Table
columns={columns}
rowKey={(record) => record.localId}
// components={components}
// bordered
dataSource={uploadedData}
// className="tableExcelUpload"
// onChange={handleTableChange}
className="price-change-data"
/>
</div>
<div
className="cancel-link"
onClick={handleCancelData}
style={{
display: "flex",
alignItems: "center",
gap: ".5rem",
padding: "0.3rem 0.4rem",
borderRadius: "6px",
backgroundColor: "ghostwhite",
width: "max-content",
}}
>
<FiDelete style={{ fontSize: "20px", color: "#1292EE" }} />
<a style={{ color: "#000" }}> Remove File</a>
</div>
<div
style={{
display: "flex",
justifyContent: "flex-end",
gap: "1rem",
marginTop: "1rem",
}}
>
<Buttons
buttonText="Submit"
color="901D77"
placement={"right"}
icon={<ArrowRightOutlined />}
disabled={addnewAccess}
/>
</div>
</Form>
</div>
) : (
<p></p>
)}
{loading && (
<div style={{ textAlign: "center", marginTop: "1rem" }}>
<div
style={{
border: "4px solid #f3f3f3",
borderTop: "4px solid #3498db",
borderRadius: "50%",
width: "40px",
height: "40px",
animation: "spin 2s linear infinite",
margin: "0 auto",
}}
></div>
<p style={{ marginTop: "0.5rem", color: "#666" }}>
Submitting...
</p>
<style>{`
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`}</style>
</div>
)}
<DefaultModal
open={excelFieldSetup}
handleCancel={() => setExcelFieldSetup(false)}
footer={false}
title={"Select the columns you want to include in the sheet"}
children={
<div className="field-setup-container">
{/* <div className="field-setup-header">
<FormHeader title={'Select the fields you want to include in the table'} />
</div> */}
<div className="field-setup-content">
<Form onFinish={handleFieldSetupSubmit} ref={formRef}>
<Form.Item name="fields">
<DropDowns
label="Select fields"
// valueData={selectedFields}
onChangeFunction={handleFieldSelect}
options={tableExcelFieldPreferences?.filter(
(p) =>
!selectedExcelFields?.some((s) => s === p.value),
)}
/>
</Form.Item>
<div className="field-setup-selected-fields">
{selectedExcelFields
?.map((id) =>
tableExcelFieldPreferences?.find(
(p) => p.value === id,
),
)
.filter(Boolean)
.map((field) => (
<div className="selected-field" key={field.value}>
<div>{field.label}</div>
<div
className="close-icon"
onClick={() => handleFieldRemove(field.value)}
>
<IoClose size={15} />
</div>
</div>
))}
</div>
<div
style={{ display: "flex", justifyContent: "flex-end" }}
>
<Buttons
buttonText="SUBMIT"
color="901D77"
icon={<ArrowRightOutlined />}
htmlType={true}
/>
</div>
</Form>
</div>
</div>
}
/>
</>
}
/>
<DefaultModal
open={priceChangeModalOpen}
title={
<p style={{ fontSize: "18px" }}>Stock/Variant Based Price Change?</p>
}
footer={true}
width={500}
handleCancel={() => setPriceChangeModalOpen(false)}
handleSubmit={() => handleExport(stockVariantBased)}
children={
<div style={{ padding: "0.1rem 0" }}>
<RadioGrpButton
content={[
{ value: "Yes", label: "Yes" },
{ value: "No", label: "No" },
]}
fieldState={true}
defaultSelect={stockVariantBased}
onSelectFuntion={(e) => setStockVariantBased(e)}
/>
</div>
}
/>
<DefaultModal
open={fieldSetup}
handleCancel={() => setFieldSetup(false)}
footer={false}
title={"Select the fields you want to include in the table"}
children={
<div className="field-setup-container">
{/* <div className="field-setup-header">
<FormHeader title={'Select the fields you want to include in the table'} />
</div> */}
<div className="field-setup-content">
<Form onFinish={handleFieldSetupSubmit} ref={formRef}>
<Form.Item name="fields">
<DropDowns
label="Select fields"
// valueData={selectedFields}
onChangeFunction={handleFieldSelect}
options={tableFieldPreferences?.filter(
(p) => !selectedFields?.some((s) => s === p.value),
)}
/>
</Form.Item>
<div className="field-setup-selected-fields">
{selectedFields
?.map((id) =>
tableFieldPreferences?.find((p) => p.value === id),
)
.filter(Boolean)
.map((field) => (
<div className="selected-field" key={field.value}>
<div>{field.label}</div>
<div
className="close-icon"
onClick={() => handleFieldRemove(field.value)}
>
<IoClose size={15} />
</div>
</div>
))}
</div>
<div style={{ display: "flex", justifyContent: "flex-end" }}>
<Buttons
buttonText="SUBMIT"
color="901D77"
icon={<ArrowRightOutlined />}
htmlType={true}
/>
</div>
</Form>
</div>
</div>
}
/>
</div>
);
};
export default StockPriceUpdate;