Android_Retail/src/Pages/StockMaster/StockBulkUpload.jsx

1758 lines
54 KiB
JavaScript

import React, {
useState,
useRef,
useEffect,
useContext,
useCallback,
} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { read, utils } from 'xlsx';
import ExcelJS from 'exceljs';
import { Table, Form, Input, Button, Tooltip, Modal } from 'antd';
import {
emptyExcelData,
excelDataSelector,
excelFileSelector,
excelFileErrorSelector,
fileInputRefSelector,
uploadExcel,
} from '../../Features/ExcelUploadPage/ExcelUploadPage.js';
import '../../Styles/Stock/StockBulkUpload.scss';
import { AiFillDelete } from 'react-icons/ai';
import upldimg from '../../Images/upldimg.png';
import { FiDelete, FiDownload } from 'react-icons/fi';
import { MdOutlineAppRegistration } from 'react-icons/md';
import { DefaultModal } from '../../Components/Modal/DefaultModal.jsx';
import Buttons from '../../Components/Forms/Buttons.jsx';
import { IoClose } from 'react-icons/io5';
import { ArrowRightOutlined } from '@ant-design/icons';
import { ApplicationPreferences } from '../../Features/BrachLogin/BranchLogin.js';
import FormHeader from '../PageComponents/FormHeader.jsx';
import { DropDowns } from '../../Components/Forms/DropDown.jsx';
import { getSession } from '../../Services/Others.js';
import {
getFieldSetupData,
postFieldSetup,
} from '../../Features/ProductPage/ProductPage.js';
import { Messages } from '../../Components/Notifications/Messages.jsx';
import { getPurchaseSupplierAndWareHouseData } from '../../Features/PurchaseOrder/PurchaseOrder.js';
import { getSupplaierIdwithTypeBasedProducts } from '../../Features/SupplierProductMapping/SupplierProductMapping.js';
const allowedExcelTypes = [
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
'application/vnd.ms-excel', // .xls
];
const StockExcel = ({
handleSubmit,
SupplierNames,
ProductId,
ProductName,
VarientName,
InwardDtlIds,
StockProductId,
TaxDatas,
Allproductdatas,
ProductSize,
ProductUomName,
}) => {
// const formatted_list = ProductName.slice(0,13).map((item, index) => {
// return item;
// });
// const formatted_list = ProductName.map((item, index) => {
// return item;
// });
const dispatch = useDispatch();
const AppId = getSession('AppId');
const CompId = getSession('CompId');
const BranchId = getSession('BranchId');
const UserId = getSession('UserId');
const formRef = useRef(null);
const fileInputRef = useRef(fileInputRefSelector);
const excelData = useSelector(excelDataSelector);
const excelFile = useSelector(excelFileSelector);
const excelFileError = useSelector(excelFileErrorSelector);
const ApplicationPreferenceData = useSelector(ApplicationPreferences);
const purchaseReceiptBulkUploadCatId = ApplicationPreferenceData?.find(
(p) => p?.PreferredCatName?.toLowerCase() === 'purchase receipt bulk'
)?.PreferredCatId;
const [worksheet1, setWorksheet1] = useState(null);
const [editdelete, seteditdelete] = useState('');
const [Datas, setDatas] = useState();
console.log(Datas, 'Datas');
const [showSupplierModal, setShowSupplierModal] = useState(false);
const [suppliers, setSuppliers] = useState([]);
const [selectedSupplier, setSelectedSupplier] = useState(null);
const [selectedSupplierData, setSelectedSupplierData] = useState(null);
const [supplierProducts, setSupplierProducts] = useState([]);
const [messageType, setMessageType] = useState(null);
const [messageData, setMessageData] = useState(null);
const [fieldSetup, setFieldSetup] = useState(false);
const [FieldValues, setFieldValues] = useState([]);
const [tableFieldPreferences, setTableFieldPreferences] = useState([]);
const [selectedFields, setSelectedFields] = useState([]);
const handleTableChange = (pagination, filters, sorter) => {
setCurrentPage(pagination.current);
};
useEffect(() => {
getFieldSetup();
}, [purchaseReceiptBulkUploadCatId]);
const onComplete = useCallback(() => {
setMessageData(null);
setMessageType(null);
}, []);
const getFieldSetup = async () => {
try {
const response = await dispatch(
getFieldSetupData({
AppId,
CompId,
BranchId,
categoryId: purchaseReceiptBulkUploadCatId,
Type: 'EB',
})
).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,
})) || []
);
setFieldValues(response?.data?.data?.[0]?.ConfigDtl);
} else {
setMessageType('error');
setMessageData('Failed to fetch field setup');
}
} catch (error) {
console.error('Error fetching field setup:', error);
}
};
useEffect(() => {
if (excelData && excelData.length > 0) {
let ExcelToJsConversion = [];
let slicedData = excelData.slice(1); // Skip header row
slicedData.forEach((item, index) => {
// Skip empty rows
if (!item || Object.values(item).every((val) => !val)) return;
const rowData = {
key: index,
InwardDate: item['0'],
Supplier: item['1'],
InvoiceDeliveryChallan: item['3'],
SupplierInvoiceNumber: item['4'],
SupplierInvoiceDate: item['5'],
DeliveryChallanNo: item['6'],
DeliveryInvoiceDate: item['7'],
PaymentType: item['8'],
PaymentMode: item['9'],
PaymentAmount: item['10'],
DueDate: item['11'],
ProductName: item['12'],
VariantName: item['13'],
Quantity: item['14'],
PurchaseRate: item['15'],
Amount: item['16'],
ReceivedQty: item['17'],
AcceptedQty: item['18'],
MRP: item['19'],
SalesPrice: item['20'],
AmountPerPiece: item['21'],
NumberofPieceInside: item['22'],
};
// Handle conditional 'Own / With Paid' column
const supplierValue = item['1'];
if (String(supplierValue).trim() === 'Self') {
rowData.OwnWithPaid = item['2'];
// Shift all subsequent columns by 1
Object.keys(rowData).forEach((key) => {
if (
!['key', 'InwardDate', 'Supplier', 'OwnWithPaid'].includes(key)
) {
const currentIndex = parseInt(Object.keys(rowData).indexOf(key));
rowData[key] = item[currentIndex.toString()];
}
});
}
// Add optional fields based on column presence
let currentColIndex = 23;
// Check for optional fields in order they appear in handleDownload
const optionalFields = [
{ key: 'ManufactureDate', header: 'Manufacture Date' },
{ key: 'ExpireDate', header: 'Expire Date' },
{ key: 'BatchNo', header: 'Batch No.' },
{ key: 'ModelNo', header: 'Model No.' },
{ key: 'RejectedQty', header: 'Rejected Qty' },
{ key: 'FreeQty', header: 'Free Qty' },
{ key: 'WholesalePrice', header: 'Wholesale Price' },
];
// Get headers from first row to determine which optional fields exist
const headers = excelData[0];
optionalFields.forEach((field) => {
if (headers.includes(field.header)) {
rowData[field.key] = item[currentColIndex.toString()];
currentColIndex++;
}
});
if (
rowData?.PurchaseRate &&
rowData?.Amount &&
rowData?.MRP &&
rowData?.SalesPrice &&
rowData?.ProductName &&
rowData?.PaymentAmount
) {
ExcelToJsConversion.push(rowData);
}
});
setDatas(ExcelToJsConversion);
}
}, [excelData]);
useEffect(() => {
if (Datas) {
handleSubmit(Datas);
}
}, [Datas, handleSubmit]);
useEffect(() => {
if (supplierProducts?.length > 0 && selectedSupplierData) {
handleDownload();
}
}, [supplierProducts, selectedSupplierData]);
const handleFileUpload = (e) => {
const file = e.target.files[0];
if (file) {
const isAllowed = allowedExcelTypes.includes(file.type);
if (!isAllowed) {
Modal.error({
title: 'Invalid File Type',
content: 'Only Excel files (.xls, .xlsx) are allowed.',
});
handleCancelData();
} else {
uploadAndProcessExcel(file);
}
} else {
dispatch(emptyExcelData());
}
};
const handleClick = () => {
// Programmatically trigger the file input click event
fileInputRef.current.click();
};
const uploadAndProcessExcel = (file) => {
const reader = new FileReader();
reader.onload = (e) => {
const data = new Uint8Array(e.target.result);
const workbook = read(data, { type: 'array' });
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
const jsonData = utils.sheet_to_json(worksheet, {
header: 1,
raw: false,
dateNF: 'DD/MM/YY',
});
const nonEmptyRows = jsonData.filter((row) =>
row.some((cell) => cell !== '')
);
const header = nonEmptyRows[0];
const rows = nonEmptyRows.slice(1);
const dateFields = ['Manufacture Date', 'Expire Date', 'Stock Date'];
function convertToDisplayFormat(dateString) {
const date = new Date(dateString);
const day = date.getDate().toString().padStart(2, '0');
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const year = date.getFullYear().toString();
return `${day}-${month}-${year}`;
}
const formattedData = rows.map((row) => {
let rowData = header.reduce((acc, col, columnIndex) => {
if (dateFields.includes(col)) {
acc[col] = convertToDisplayFormat(row[columnIndex]);
} else {
acc[col] = row[columnIndex];
}
return acc;
}, {});
return rowData;
});
dispatch(uploadExcel({ file, jsonData: nonEmptyRows }));
setWorksheet1(worksheet1);
setDatas(formattedData);
};
reader.readAsArrayBuffer(file);
};
// ...existing code...
const handleDownload = async () => {
try {
if (
!supplierProducts ||
supplierProducts.length === 0 ||
!selectedSupplierData
) {
setMessageData('No products available to generate Excel.');
setMessageType('warning');
return;
}
const supplierDisplayName =
getSupplierDisplayName(selectedSupplierData) || 'Supplier';
// Fetch excelColumns config (optional fields access)
let excelColumns = [];
try {
const response = await dispatch(
getFieldSetupData({
AppId,
CompId,
BranchId,
categoryId: purchaseReceiptBulkUploadCatId,
Type: 'EB',
})
).unwrap();
if (response?.data?.statusCode === 1) {
excelColumns = response?.data?.data?.[0]?.ConfigDtl || [];
}
} catch (err) {
console.warn(
'Failed to fetch excelColumns config, proceeding with defaults',
err
);
excelColumns = [];
}
const isOptionalFieldAllowed = (name) => {
const found = excelColumns.find(
(c) => String(c.ConfigName).trim() === String(name).trim()
);
return found ? found.Access === 'Y' : false;
};
const addNumberValidation = (cell, allowBlank = true, minValue = 0) => {
cell.dataValidation = {
type: 'decimal',
operator: 'greaterThanOrEqual',
formulae: [minValue],
allowBlank: allowBlank,
showErrorMessage: true,
errorTitle: 'Invalid Number',
error: 'Please enter a valid number',
};
};
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('Sheet1');
const hiddenSheet = workbook.addWorksheet('ProductVariantMap');
workbook.addWorksheet('ValidationHelper');
const requiredBg = 'FF2929';
const headerFontColor = { argb: 'FF000000' };
const columns = [];
const pushCol = (header, key, required = true) => {
columns.push({ header, key, required });
};
pushCol('Inward Date', 'InwardDate', true);
pushCol('Supplier', 'Supplier', true);
if (String(selectedSupplierData?.SuppName).trim() === 'Self') {
pushCol('Own / With Paid', 'OwnWithPaid', true);
}
pushCol('Invoice / Delivery Challan', 'InvoiceDeliveryChallan', true);
pushCol('Supplier Invoice Number', 'SupplierInvoiceNumber', true);
pushCol('Supplier Invoice Date', 'SupplierInvoiceDate', true);
pushCol('Delivery Challan No.', 'DeliveryChallanNo', true);
pushCol('Delivery Challan Date', 'DeliveryInvoiceDate', true);
pushCol('Payment Type', 'PaymentType', true);
pushCol('Payment Mode', 'PaymentMode', true);
pushCol('Payment Amount', 'PaymentAmount', true);
pushCol('Due Date', 'DueDate', true);
pushCol('Product Name', 'ProdName', true);
pushCol('Variant Name', 'VariantName', true);
pushCol('Quantity', 'Quantity', true);
pushCol('Purchase Rate', 'PurchaseRate', true);
pushCol('Amount', 'Amount', true);
pushCol('Received Qty', 'ReceivedQty', true);
pushCol('Accepted Qty', 'AcceptedQty', true);
pushCol('MRP', 'MRP', true);
pushCol('Sales Price', 'SalesPrice', true);
pushCol('Amount Per Piece', 'AmountPerPiece', true);
pushCol('Number of Piece Inside', 'NumberofPieceInside', true);
if (isOptionalFieldAllowed('Manufacture Date'))
pushCol('Manufacture Date', 'ManufDate', false);
if (isOptionalFieldAllowed('Expire Date'))
pushCol('Expire Date', 'ExpDate', false);
if (isOptionalFieldAllowed('Batch No.'))
pushCol('Batch No.', 'BatchNo', false);
if (isOptionalFieldAllowed('Model No.'))
pushCol('Model No.', 'ModelNo', false);
if (isOptionalFieldAllowed('Rejected Qty'))
pushCol('Rejected Qty', 'RejectedQty', false);
if (isOptionalFieldAllowed('Free Qty'))
pushCol('Free Qty', 'FreeQty', false);
if (isOptionalFieldAllowed('Wholesale Price'))
pushCol('Wholesale Price', 'WholesalePrice', false);
worksheet.columns = columns.map((c) => ({
header: c.header,
key: c.key,
width:
c.key === 'ProdName' || c.key === 'VariantName'
? 40
: c.key === 'Quantity'
? 10
: c.key === 'Supplier' ||
c.key === 'InvoiceDeliveryChallan' ||
c.key === 'SupplierInvoiceNumber'
? 30
: 20,
}));
worksheet.getRow(1).eachCell((cell, colNumber) => {
const col = columns[colNumber - 1];
const bgColor = col.required ? requiredBg : 'FF52C41A';
cell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: bgColor },
};
cell.font = { bold: true, color: headerFontColor };
cell.alignment = { horizontal: 'center', vertical: 'middle' };
cell.border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' },
};
});
hiddenSheet.addRow([
'ProductName',
'VariantName',
'MRP',
'SellPrice',
'Number of Piece Inside',
'Amount Per Piece',
]);
let currentRow = 2;
const productNames = supplierProducts.map((p) => p.ProdName);
supplierProducts.forEach((product) => {
const activeVariants = (product.ProdVariantPriceDetails || []).filter(
(v) => v.ActiveStatus === 'A'
);
const uniqueVariantsMap = new Map();
activeVariants.forEach((variant) => {
const variantName = variant.ProdVariantName;
if (!uniqueVariantsMap.has(variantName)) {
uniqueVariantsMap.set(variantName, variant);
}
});
const uniqueVariants = Array.from(uniqueVariantsMap.values());
if (uniqueVariants.length === 0) return;
const startRow = currentRow;
uniqueVariants.forEach((variant) => {
hiddenSheet.addRow([
product.ProdName,
variant.ProdVariantName,
variant.MRP || 0,
variant.SellPrice || 0,
variant.NoOfPcs || 0,
variant.OnePcsPrice || 0,
]);
currentRow++;
});
const endRow = currentRow - 1;
let safeName = String(product.ProdName || '')
.trim()
.replace(/[^A-Za-z0-9_]/g, '_')
.replace(/^(\d)/, '_$1');
if (!safeName)
safeName = `Product_${Math.random().toString(36).slice(2, 8)}`;
const rangeRef = `ProductVariantMap!$B$${startRow}:$B$${endRow}`;
try {
workbook.definedNames.add(rangeRef, safeName);
} catch (err) {
console.warn(`Could not add named range for ${safeName}`, err);
}
});
const startDataRow = 2;
const today = new Date();
// Pre-populate all products and variants as rows
supplierProducts.forEach((product) => {
const activeVariants = (product.ProdVariantPriceDetails || []).filter(
(v) => v.ActiveStatus === 'A'
);
const uniqueVariantsMap = new Map();
activeVariants.forEach((variant) => {
const variantName = variant.ProdVariantName;
if (!uniqueVariantsMap.has(variantName)) {
uniqueVariantsMap.set(variantName, variant);
}
});
const uniqueVariants = Array.from(uniqueVariantsMap.values());
uniqueVariants.forEach((variant) => {
const rowData = {
InwardDate: today,
Supplier: supplierDisplayName,
ProdName: product.ProdName,
VariantName: variant.ProdVariantName,
MRP: variant.MRP || 0,
SalesPrice: variant.SellPrice || 0,
NumberofPieceInside: variant.NoOfPcs || 0,
AmountPerPiece: variant.OnePcsPrice || 0,
};
if (String(selectedSupplierData?.SuppName).trim() === 'Self') {
rowData.OwnWithPaid = '';
}
worksheet.addRow(rowData);
});
});
const colKeyToIndex = {};
worksheet.columns.forEach((c, idx) => {
colKeyToIndex[c.key] = idx + 1;
});
const colIndex = (key) => colKeyToIndex[key];
const colLetter = (idx) => {
let s = '';
let n = idx;
while (n > 0) {
const m = (n - 1) % 26;
s = String.fromCharCode(65 + m) + s;
n = Math.floor((n - 1) / 26);
}
return s;
};
const inwardDateCol = colIndex('InwardDate');
const numberOfRows = worksheet.rowCount;
for (let r = startDataRow; r <= numberOfRows; r++) {
if (colIndex('ProdName')) {
const cIdx = colIndex('ProdName');
const cell = worksheet.getCell(r, cIdx);
cell.protection = { locked: true };
}
if (colIndex('VariantName')) {
const cIdx = colIndex('VariantName');
const cell = worksheet.getCell(r, cIdx);
cell.protection = { locked: true };
}
if (colIndex('Quantity')) {
const cIdx = colIndex('Quantity');
const letter = colLetter(cIdx);
const cell = worksheet.getCell(`${letter}${r}`);
cell.protection = { locked: false };
addNumberValidation(cell, false, 0.01);
}
if (colIndex('PurchaseRate')) {
const cIdx = colIndex('PurchaseRate');
const letter = colLetter(cIdx);
const pCell = worksheet.getCell(`${letter}${r}`);
pCell.protection = { locked: false };
addNumberValidation(pCell, false, 0);
pCell.numFmt = '#,##0.00';
}
if (colIndex('RejectedQty')) {
const cIdx = colIndex('RejectedQty');
const letter = colLetter(cIdx);
const rejCell = worksheet.getCell(`${letter}${r}`);
const qtyLetter = colLetter(colIndex('Quantity'));
rejCell.dataValidation = {
type: 'decimal',
allowBlank: true,
formulae: [`${letter}${r}<${qtyLetter}${r}`],
showErrorMessage: true,
errorTitle: 'Invalid Rejected Quantity',
error: 'Must be a valid number and less than total quantity',
showInputMessage: true,
promptTitle: 'Rejected Qty',
prompt:
'Enter rejected quantity (must be less than total quantity)',
};
rejCell.protection = { locked: false };
}
if (colIndex('FreeQty')) {
const cIdx = colIndex('FreeQty');
const letter = colLetter(cIdx);
const cell = worksheet.getCell(`${letter}${r}`);
cell.protection = { locked: false };
addNumberValidation(cell, true, 0);
}
if (inwardDateCol) {
const letter = colLetter(inwardDateCol);
const dCell = worksheet.getCell(`${letter}${r}`);
dCell.numFmt = 'yyyy-mm-dd';
dCell.dataValidation = {
type: 'date',
operator: 'greaterThan',
formulae: [new Date(1900, 0, 1)],
showErrorMessage: true,
errorTitle: 'Invalid Date',
error: 'Please enter a valid date',
};
dCell.protection = { locked: false };
}
if (colIndex('Supplier')) {
const cIdx = colIndex('Supplier');
const cell = worksheet.getCell(r, cIdx);
cell.protection = { locked: true };
}
if (colIndex('OwnWithPaid')) {
const cIdx = colIndex('OwnWithPaid');
const cell = worksheet.getCell(r, cIdx);
cell.dataValidation = {
type: 'list',
formulae: ['"Own,With Paid"'],
allowBlank: true
};
if (!cell.value) {
cell.value = 'Own';
}
cell.protection = { locked: false };
}
if (colIndex('InvoiceDeliveryChallan')) {
const cIdx = colIndex('InvoiceDeliveryChallan');
const cell = worksheet.getCell(r, cIdx);
cell.dataValidation = {
type: 'list',
formulae: ['"Invoice,Delivery Challan"'],
allowBlank: true,
};
// ✅ Default value
if (!cell.value) {
cell.value = 'Invoice';
}
cell.protection = { locked: false };
}
if (colIndex('SupplierInvoiceNumber')) {
worksheet.getCell(r, colIndex('SupplierInvoiceNumber')).protection = {
locked: false,
};
}
if (colIndex('SupplierInvoiceDate')) {
const cell = worksheet.getCell(r, colIndex('SupplierInvoiceDate'));
cell.value = today;
cell.numFmt = 'yyyy-mm-dd';
cell.dataValidation = {
type: 'date',
operator: 'greaterThan',
formulae: [new Date(1900, 0, 1)],
showErrorMessage: true,
errorTitle: 'Invalid Date',
error: 'Please enter a valid date',
};
cell.protection = { locked: false };
}
if (colIndex('DeliveryChallanNo')) {
worksheet.getCell(r, colIndex('DeliveryChallanNo')).protection = {
locked: false,
};
}
if (colIndex('DeliveryInvoiceDate')) {
const cell = worksheet.getCell(r, colIndex('DeliveryInvoiceDate'));
cell.value = today;
cell.numFmt = 'yyyy-mm-dd';
cell.dataValidation = {
type: 'date',
operator: 'greaterThan',
formulae: [new Date(1900, 0, 1)],
showErrorMessage: true,
errorTitle: 'Invalid Date',
error: 'Please enter a valid date',
};
cell.protection = { locked: false };
}
if (colIndex('MRP')) {
const mrpCol = colIndex('MRP');
const mrpCell = worksheet.getCell(r, mrpCol);
mrpCell.numFmt = '#,##0.00';
mrpCell.protection = { locked: false };
}
if (colIndex('SalesPrice')) {
const spCol = colIndex('SalesPrice');
const spCell = worksheet.getCell(r, spCol);
spCell.numFmt = '#,##0.00';
spCell.protection = { locked: false };
}
if (colIndex('NumberofPieceInside')) {
const nopCol = colIndex('NumberofPieceInside');
const nopCell = worksheet.getCell(r, nopCol);
nopCell.numFmt = '#,##0';
nopCell.protection = { locked: false };
}
if (colIndex('AmountPerPiece')) {
const appCol = colIndex('AmountPerPiece');
const appCell = worksheet.getCell(r, appCol);
appCell.numFmt = '#,##0.00';
appCell.protection = { locked: false };
}
if (colIndex('PaymentType')) {
const cIdx = colIndex('PaymentType');
const letter = colLetter(cIdx);
const pCell = worksheet.getCell(`${letter}${r}`);
pCell.dataValidation = {
type: 'list',
allowBlank: false,
formulae: ['"Credit,Paid"'],
};
pCell.protection = { locked: false };
}
// Payment Mode - Fixed to show list based on Payment Type
if (colIndex('PaymentMode')) {
const cIdx = colIndex('PaymentMode');
const letter = colLetter(cIdx);
const pmCell = worksheet.getCell(`${letter}${r}`);
const paymentTypeLetter = colLetter(colIndex('PaymentType'));
// Create named range for payment mode options in ValidationHelper sheet
const validationHelper = workbook.getWorksheet('ValidationHelper');
if (r === startDataRow) {
// Add payment mode options to ValidationHelper sheet (only once)
validationHelper.getCell('A1').value = 'Cash';
validationHelper.getCell('A2').value = 'Credit';
validationHelper.getCell('A3').value = 'UPI';
}
// Use IF formula to show list based on Payment Type
pmCell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [
`IF(${paymentTypeLetter}${r}="Paid",ValidationHelper!$A$1:$A$3,"")`,
],
showErrorMessage: true,
errorTitle: 'Invalid Payment Mode',
error: 'Select Cash, Credit, or UPI when Payment Type is Paid',
showInputMessage: true,
promptTitle: 'Payment Mode',
prompt:
'If Payment Type is Credit, leave empty. If Paid, select: Cash, Credit, or UPI',
};
pmCell.protection = { locked: false };
}
if (colIndex('DueDate')) {
const cIdx = colIndex('DueDate');
const letter = colLetter(cIdx);
const dueCell = worksheet.getCell(`${letter}${r}`);
dueCell.numFmt = 'yyyy-mm-dd';
dueCell.dataValidation = {
type: 'date',
operator: 'greaterThan',
formulae: [new Date(1900, 0, 1)],
showErrorMessage: true,
errorTitle: 'Invalid Date',
error: 'Please enter a valid date',
};
dueCell.protection = { locked: false };
}
if (colIndex('ManufDate')) {
const cIdx = colIndex('ManufDate');
const letter = colLetter(cIdx);
const manufCell = worksheet.getCell(`${letter}${r}`);
manufCell.numFmt = 'yyyy-mm-dd';
manufCell.dataValidation = {
type: 'date',
operator: 'greaterThan',
formulae: [new Date(1900, 0, 1)],
showErrorMessage: true,
errorTitle: 'Invalid Date',
error: 'Please enter a valid date',
};
manufCell.protection = { locked: false };
}
if (colIndex('ExpDate')) {
const cIdx = colIndex('ExpDate');
const letter = colLetter(cIdx);
const expCell = worksheet.getCell(`${letter}${r}`);
expCell.numFmt = 'yyyy-mm-dd';
expCell.dataValidation = {
type: 'date',
operator: 'greaterThan',
formulae: [new Date(1900, 0, 1)],
showErrorMessage: true,
errorTitle: 'Invalid Date',
error: 'Please enter a valid date',
};
expCell.protection = { locked: false };
}
if (colIndex('ReceivedQty') && colIndex('Quantity')) {
const rIdx = colIndex('ReceivedQty');
const qIdx = colIndex('Quantity');
const rLetter = colLetter(rIdx);
const qLetter = colLetter(qIdx);
const recCell = worksheet.getCell(`${rLetter}${r}`);
recCell.value = {
formula: `IF(${qLetter}${r}="","",${qLetter}${r})`,
};
addNumberValidation(recCell, true, 0);
}
if (colIndex('AcceptedQty') && colIndex('Quantity')) {
const aIdx = colIndex('AcceptedQty');
const qIdx = colIndex('Quantity');
const rjIdx = colIndex('RejectedQty');
const aLetter = colLetter(aIdx);
const qLetter = colLetter(qIdx);
const rejLetter = rjIdx ? colLetter(rjIdx) : null;
const formulaCell = worksheet.getCell(`${aLetter}${r}`);
if (rejLetter) {
formulaCell.value = {
formula: `IF(${qLetter}${r}="","",MAX(0,${qLetter}${r}-IF(${rejLetter}${r}="",0,${rejLetter}${r})))`,
};
} else {
formulaCell.value = {
formula: `IF(${qLetter}${r}="","",${qLetter}${r})`,
};
}
addNumberValidation(formulaCell, true, 0);
formulaCell.protection = { locked: true };
}
if (
colIndex('PaymentAmount') &&
colIndex('Quantity') &&
colIndex('PurchaseRate')
) {
const payIdx = colIndex('PaymentAmount');
const qIdx = colIndex('Quantity');
const pIdx = colIndex('PurchaseRate');
const rejIdx = colIndex('RejectedQty');
const payLetter = colLetter(payIdx);
const qLetter = colLetter(qIdx);
const pLetter = colLetter(pIdx);
const rejLetter = rejIdx ? colLetter(rejIdx) : null;
const formulaCell = worksheet.getCell(`${payLetter}${r}`);
if (rejLetter) {
formulaCell.value = {
formula: `IF(${qLetter}${r}="","",IF(${rejLetter}${r}="",(${qLetter}${r})*${pLetter}${r},(${qLetter}${r}-${rejLetter}${r})*${pLetter}${r}))`,
};
} else {
formulaCell.value = {
formula: `IF(${qLetter}${r}="","",(${qLetter}${r})*${pLetter}${r})`,
};
}
formulaCell.numFmt = '#,##0.00';
addNumberValidation(formulaCell, true, 0);
}
if (
colIndex('Amount') &&
colIndex('Quantity') &&
colIndex('PurchaseRate')
) {
const amtIdx = colIndex('Amount');
const qIdx = colIndex('Quantity');
const pIdx = colIndex('PurchaseRate');
const rejIdx = colIndex('RejectedQty');
const amtLetter = colLetter(amtIdx);
const qLetter = colLetter(qIdx);
const pLetter = colLetter(pIdx);
const rejLetter = rejIdx ? colLetter(rejIdx) : null;
const amtCell = worksheet.getCell(`${amtLetter}${r}`);
if (rejLetter) {
amtCell.value = {
formula: `IF(${qLetter}${r}="","",IF(${rejLetter}${r}="",(${qLetter}${r})*${pLetter}${r},(${qLetter}${r}-${rejLetter}${r})*${pLetter}${r}))`,
};
} else {
amtCell.value = {
formula: `IF(${qLetter}${r}="","",(${qLetter}${r})*${pLetter}${r})`,
};
}
amtCell.numFmt = '#,##0.00';
addNumberValidation(amtCell, true, 0);
}
if (colIndex('WholesalePrice')) {
const cIdx = colIndex('WholesalePrice');
const letter = colLetter(cIdx);
const cell = worksheet.getCell(`${letter}${r}`);
cell.protection = { locked: false };
cell.numFmt = '#,##0.00';
addNumberValidation(cell, true, 0);
}
}
await worksheet.protect('', {
selectLockedCells: false,
selectUnlockedCells: true,
formatCells: false,
formatColumns: false,
formatRows: false,
insertColumns: false,
insertRows: false,
insertHyperlinks: false,
deleteColumns: false,
deleteRows: false,
});
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
const filename = `PurchaseEntry_${supplierDisplayName}_${Date.now()}.xlsx`;
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
setSupplierProducts([]);
setSelectedSupplierData(null);
handleModalCancel();
} catch (err) {
console.error('Failed to generate Purchase Entry Excel', err);
setMessageData('Failed to generate Excel. Check console for details.');
setMessageType('error');
}
};
const getSupplierDisplayName = useCallback((supplierData) => {
if (!supplierData) return '';
if (supplierData.Type === 'Supplier') {
return supplierData.SuppName || supplierData.label;
} else {
// For Branch or WareHouse
return `${supplierData.SuppName}-${supplierData.SuppAppId}-${supplierData.SuppCompId}-${supplierData.SuppBranchId}-${supplierData?.Type === 'Branch' ? 'B' : 'W'}`;
}
}, []);
const handleFileSubmit = (e) => {
e.preventDefault();
if (excelFile === null) {
excelFileError('Please select an Excel file.');
} else {
handleSubmit(Datas);
}
};
const handleCancelData = () => {
dispatch(emptyExcelData());
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
};
const smallIconColumnTitle = 'SmallIcon';
const columns = [
{
title: 'Inward Date',
dataIndex: 'InwardDate',
key: 'InwardDate',
editable: true,
},
{
title: 'Supplier',
dataIndex: 'Supplier',
key: 'Supplier',
editable: true,
},
{
title: 'Own / With Paid',
dataIndex: 'OwnWithPaid',
key: 'OwnWithPaid',
editable: true,
},
{
title: 'Invoice / Delivery Challan',
dataIndex: 'InvoiceDeliveryChallan',
key: 'InvoiceDeliveryChallan',
editable: true,
},
{
title: 'Supplier Invoice Number',
dataIndex: 'SupplierInvoiceNumber',
key: 'SupplierInvoiceNumber',
editable: true,
},
{
title: 'Supplier Invoice Date',
dataIndex: 'SupplierInvoiceDate',
key: 'SupplierInvoiceDate',
editable: true,
},
{
title: 'Delivery Challan No.',
dataIndex: 'DeliveryChallanNo',
key: 'DeliveryChallanNo',
editable: true,
},
{
title: 'Delivery Challan Date',
dataIndex: 'DeliveryInvoiceDate',
key: 'DeliveryInvoiceDate',
editable: true,
},
{
title: 'Payment Type',
dataIndex: 'PaymentType',
key: 'PaymentType',
editable: true,
},
{
title: 'Payment Mode',
dataIndex: 'PaymentMode',
key: 'PaymentMode',
editable: true,
},
{
title: 'Payment Amount',
dataIndex: 'PaymentAmount',
key: 'PaymentAmount',
editable: true,
},
{
title: 'Due Date',
dataIndex: 'DueDate',
key: 'DueDate',
editable: true,
},
{
title: 'Product Name',
dataIndex: 'ProductName',
key: 'ProductName',
editable: true,
},
{
title: 'Variant Name',
dataIndex: 'VariantName',
key: 'VariantName',
editable: true,
},
{
title: 'Quantity',
dataIndex: 'Quantity',
key: 'Quantity',
editable: true,
},
{
title: 'Purchase Rate',
dataIndex: 'PurchaseRate',
key: 'PurchaseRate',
editable: true,
},
{
title: 'Amount',
dataIndex: 'Amount',
key: 'Amount',
editable: true,
},
{
title: 'Received Qty',
dataIndex: 'ReceivedQty',
key: 'ReceivedQty',
editable: true,
},
{
title: 'Accepted Qty',
dataIndex: 'AcceptedQty',
key: 'AcceptedQty',
editable: true,
},
{
title: 'MRP',
dataIndex: 'MRP',
key: 'MRP',
editable: true,
},
{
title: 'Sales Price',
dataIndex: 'SalesPrice',
key: 'SalesPrice',
editable: true,
},
{
title: 'Amount Per Piece',
dataIndex: 'AmountPerPiece',
key: 'AmountPerPiece',
editable: true,
},
{
title: 'Number of Piece Inside',
dataIndex: 'NumberofPieceInside',
key: 'NumberofPieceInside',
editable: true,
},
{
title: 'Manufacture Date',
dataIndex: 'ManufactureDate',
key: 'ManufactureDate',
editable: true,
},
{
title: 'Expire Date',
dataIndex: 'ExpireDate',
key: 'ExpireDate',
editable: true,
},
{
title: 'Batch No.',
dataIndex: 'BatchNo',
key: 'BatchNo',
editable: true,
},
{
title: 'Model No.',
dataIndex: 'ModelNo',
key: 'ModelNo',
editable: true,
},
{
title: 'Rejected Qty',
dataIndex: 'RejectedQty',
key: 'RejectedQty',
editable: true,
},
{
title: 'Free Qty',
dataIndex: 'FreeQty',
key: 'FreeQty',
editable: true,
},
{
title: 'Wholesale Price',
dataIndex: 'WholesalePrice',
key: 'WholesalePrice',
editable: true,
},
];
const column = columns?.map((col) => {
if (!col.editable) {
return col;
}
let onClickHandler = null;
if (editdelete === 'Delete') {
onClickHandler = (record) => removeFromTable(record);
}
return {
...col,
onCell: (record) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave,
onClick: () => onClickHandler(record),
}),
};
});
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 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) { }
};
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item
style={{
margin: 0,
}}
name={dataIndex}
>
<Input ref={inputRef} onPressEnter={save} onBlur={save} />
</Form.Item>
) : (
<div
className="editable-cell-value-wrap"
style={{
paddingRight: 24,
}}
onClick={toggleEdit}
>
{children}
</div>
);
}
return <td {...restProps}>{childNode}</td>;
};
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
const handleSave = (row) => {
const newDatas = [...Datas];
const item = newDatas.filter((item) => item.key === row.key);
const UpdatedData = {
Supplier: row?.Supplier,
SuppInvoiceNumber: row?.SuppInvoiceNumber,
SuppInvoiceDate: row?.SuppInvoiceDate,
InvoiceDate: row?.InvoiceDate,
InvoiceAmount: row?.InvoiceAmount,
PurchaseType: row?.PurchaseType,
DueDate: row?.DueDate,
ProductName: row?.ProductName,
Quantity: row?.Quantity,
PurchaseRate: row?.PurchaseRate,
PurchaseDiscount: row?.PurchaseDiscount,
Amount: row?.Amount,
ExpireDate: row?.ExpireDate,
ManufactureDate: row?.ManufactureDate,
MRP: row?.MRP,
RetailPrice: row?.RetailPrice,
AmountperPiece: row?.AmountperPiece,
NumberofPieceInside: row?.NumberofPieceInside,
WholesalePrice: row?.WholesalePrice,
Offer: row?.Offer,
SplPrice: row?.SplPrice,
ReceivedQuantity: row?.ReceivedQuantity,
RejectedQuantity: row?.RejectedQuantity,
AcceptedQuantity: row?.AcceptedQuantity,
FreeQuantity: row?.FreeQuantity,
key: row?.key,
};
newDatas.splice(item?.[0]?.key, 1, {
...item?.[0],
...UpdatedData,
});
setDatas(newDatas);
};
const handlechange22 = (index) => {
if (editdelete === 'Delete') {
seteditdelete('');
} else {
seteditdelete('Delete');
}
};
const removeFromTable = (record) => {
const indexToRemove = Datas.findIndex((item) => item.key === record.key);
if (indexToRemove !== -1) {
const updatedDataSource = [...Datas];
updatedDataSource.splice(indexToRemove, 1);
setDatas(updatedDataSource);
}
};
const handleFieldSetup = () => {
setFieldSetup(true);
};
const handleFieldSetupSubmit = async () => {
const postData = {
AppId: AppId,
CompId: CompId,
BranchId: BranchId,
Type: 'EB',
FormType: 'PurchaseEntry',
TypeId: purchaseReceiptBulkUploadCatId,
ConfigDtl: selectedFields?.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);
setFieldSetup(false);
await getFieldSetup();
} else {
setMessageType('error');
setMessageData('Failed to set up fields');
}
};
const handleFieldSelect = (value) => {
setSelectedFields((prev) => {
if (prev.includes(value)) {
return prev;
}
return [value, ...prev];
});
};
const handleFieldRemove = (id) => {
setSelectedFields((prev) => prev.filter((field) => field !== id));
};
const handleModalCancel = useCallback(() => {
setShowSupplierModal(false);
setSelectedSupplier(null);
setSelectedSupplierData(null);
}, []);
const handleSelectSupplier = useCallback(async () => {
setShowSupplierModal(true);
try {
const response = await dispatch(
getPurchaseSupplierAndWareHouseData({
CompId,
AppId,
BranchId,
})
).unwrap();
if (response?.statusCode === 1 && response?.data) {
const formattedSuppliers = response.data.map((supp) => ({
value:
supp?.Type?.toLowerCase() !== 'supplier'
? `${supp?.SuppAppId}-${supp?.SuppCompId}-${supp?.SuppBranchId}`
: supp?.SuppId,
label: supp?.SuppName || supp?.SuppMobile,
Type: supp?.Type,
SuppName: supp?.SuppName,
SuppAppId: supp?.SuppAppId,
SuppCompId: supp?.SuppCompId,
SuppBranchId: supp?.SuppBranchId,
SuppId: supp?.SuppId,
}));
setSuppliers(formattedSuppliers);
}
} catch (error) {
console.error('Error fetching suppliers:', error);
setMessageData('Failed to fetch suppliers.');
setMessageType('error');
}
}, [CompId, AppId, BranchId, dispatch]);
const getSupplierMappedProducts = async (supplierId) => {
try {
const supplierData = suppliers?.find((supp) => supp.value === supplierId);
if (!supplierData) return;
const type = supplierData.Type;
const LocationType =
type === 'Supplier'
? 'S'
: type === 'Branch'
? 'B'
: type === 'WareHouse'
? 'W'
: '';
const response = await dispatch(
getSupplaierIdwithTypeBasedProducts({
CompId: LocationType === 'S' ? CompId : supplierData?.SuppCompId,
AppId: LocationType === 'S' ? AppId : supplierData?.SuppAppId,
BranchId:
LocationType === 'S' ? BranchId : supplierData?.SuppBranchId,
SuppId:
LocationType === 'S' ? supplierId : supplierData?.SuppBranchId,
LocationType,
})
)?.unwrap();
if (response?.data?.statusCode === 1) {
if (response?.data?.data?.length > 0) {
setSupplierProducts(response?.data?.data);
} else {
setSupplierProducts([]);
setMessageData('No products are mapped to this supplier.');
setMessageType('warning');
}
} else {
setSupplierProducts([]);
setMessageData('Failed to fetch supplier products.');
setMessageType('error');
}
} catch (error) {
console.error('Error fetching supplier products:', error);
setMessageData('An error occurred while fetching products.');
setMessageType('error');
}
};
const handleSupplierBasedProducts = useCallback(async () => {
if (!selectedSupplier) {
setMessageData('Please select a supplier.');
setMessageType('warning');
return;
}
await getSupplierMappedProducts(selectedSupplier);
}, [selectedSupplier, getSupplierMappedProducts]);
const handleSupplierChange = useCallback(
(value) => {
const selected = suppliers.find((supp) => supp.value === value);
if (selected) {
formRef?.current?.setFieldsValue({ SuppId: value });
setSelectedSupplier(value);
setSelectedSupplierData(selected);
}
},
[suppliers]
);
return (
<div className="container">
<Messages
messageType={messageType}
messageData={messageData}
onComplete={onComplete}
/>
<div className="form">
<form
className="form-group"
autoComplete="off"
onSubmit={handleFileSubmit}
>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div className="upload-excel-header">
<h3>UPLOAD YOUR EXCEL</h3>
<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',
cursor: 'pointer',
}}
onClick={handleFieldSetup}
>
<MdOutlineAppRegistration size={19} />
</div>
</Tooltip>
</div>
{excelData?.length > 0 && excelData && (
<div>
<Tooltip title="Delete">
<AiFillDelete
onClick={handlechange22}
style={{
fontSize: '23px',
color: editdelete ? '#52c41a' : '#1292ee',
}}
/>
</Tooltip>
</div>
)}
</div>
<br />
{(!excelData || excelData?.length === 0) && (
<>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ position: 'relative', display: 'inline-block' }}>
{/* <Imageupload size="2x" /> */}
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<img
src={upldimg}
width={'80px'}
style={{ cursor: 'pointer' }}
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' }}> Upload File </a>
</div>
</div>
</div>
</>
)}
{excelFileError && (
<div className="text-danger" style={{ marginTop: '5px' }}>
{excelFileError}
</div>
)}
</form>
</div>
<div
className="viewerExcelupload"
style={{ maxHeight: '500px', overflow: 'scroll' }}
>
{excelData && excelData.length > 0 ? (
<Table
columns={column}
components={components}
bordered
dataSource={Datas}
onChange={handleTableChange}
></Table>
) : (
<p></p>
)}
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
width: '100%',
gap: '2rem',
margin: '1rem 0rem',
}}
>
<Tooltip title="Format Sheet" placement="bottom">
<div
className="download-link"
onClick={handleSelectSupplier}
style={{
display: 'flex',
alignItems: 'center',
gap: '.5rem',
padding: '0.3rem 0.4rem',
borderRadius: '6px',
backgroundColor: 'ghostwhite',
}}
>
<FiDownload style={{ fontSize: '20px' }} />
<a style={{ color: '#000' }}> Download</a>
</div>
</Tooltip>
{excelData && excelData?.length > 0 && (
<div
className="cancel-link"
onClick={handleCancelData}
style={{
display: 'flex',
alignItems: 'center',
gap: '.5rem',
padding: '0.3rem 0.4rem',
borderRadius: '6px',
backgroundColor: 'ghostwhite',
}}
>
<FiDelete style={{ fontSize: '20px', color: '#1292EE' }} />
<a style={{ color: '#000' }}> Remove File</a>
</div>
)}
</div>
<DefaultModal
open={fieldSetup}
handleCancel={() => setFieldSetup(false)}
footer={false}
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>
}
/>
<DefaultModal
open={showSupplierModal}
handleCancel={handleModalCancel}
footer={false}
width={600}
children={
<div className="supplier-modal-container">
<div className="supplier-modal-header">
<FormHeader title={'Select Supplier'} />
</div>
<Form
layout="vertical"
className="supplier-modal-form"
onFinish={handleSupplierBasedProducts}
ref={formRef}
>
<div className="supplier-modal-body">
<Form.Item
name="SuppId"
rules={[
{
required: true,
message: 'Please select a supplier.',
},
]}
>
<DropDowns
label={'Select Supplier'}
onChangeFunction={handleSupplierChange}
options={suppliers}
valueData={selectedSupplier}
/>
</Form.Item>
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Buttons
buttonText="SUBMIT"
color="901D77"
icon={<ArrowRightOutlined />}
htmlType={true}
/>
</div>
</Form>
</div>
}
/>
</div>
);
};
export default StockExcel;