Merge pull request 'temp/direct-sales-feature' (#134) from temp/direct-sales-feature into main
Reviewed-on: Pozomind/pozo-retail-app#134
This commit is contained in:
commit
8fc0e75122
|
|
@ -60,7 +60,7 @@ export const getTopSellingProduct = createAsyncThunk(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
export const PostPedalOCR = createAsyncThunk(
|
export const PostPedalOCR = createAsyncThunk(
|
||||||
'Stock/PostPedalOCR',
|
'Stock/PostPedalOCR',
|
||||||
async (fileOrFormData) => {
|
async (fileOrFormData) => {
|
||||||
let formdata;
|
let formdata;
|
||||||
if (fileOrFormData instanceof FormData) {
|
if (fileOrFormData instanceof FormData) {
|
||||||
|
|
@ -244,6 +244,22 @@ export const getProductDataPageNo = createAsyncThunk(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getDirectSaleProducts = createAsyncThunk(
|
||||||
|
'product/getDirectSaleProducts',
|
||||||
|
async (data) => {
|
||||||
|
const { CompId, BranchId, AppId, page } = data
|
||||||
|
if (
|
||||||
|
CompId != null && CompId != undefined &&
|
||||||
|
BranchId != null && BranchId != undefined &&
|
||||||
|
AppId != null && AppId != undefined
|
||||||
|
) {
|
||||||
|
return await axiosRetailInstanceData.get(
|
||||||
|
`/product?CompId=${CompId}&BranchId=${BranchId}&AppId=${AppId}&SalesType=DS&PageNumber=${page}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export const getAllprodIds = createAsyncThunk(
|
export const getAllprodIds = createAsyncThunk(
|
||||||
'product/getAllprodIds',
|
'product/getAllprodIds',
|
||||||
async (ProdData) => {
|
async (ProdData) => {
|
||||||
|
|
@ -273,6 +289,13 @@ export const getAllprodIds = createAsyncThunk(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getAllProductShortCodes = createAsyncThunk('product/getAllProductShortCodes', async (data) => {
|
||||||
|
const { AppId, CompId, BranchId } = data;
|
||||||
|
if (AppId != null && AppId && BranchId != null && BranchId && AppId && AppId != null) {
|
||||||
|
return await axiosRetailInstanceData.get(`/Product/ProdShortCodes?CompId=${CompId}&BranchId=${BranchId}&AppId=${AppId}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export const getProductDataSearch = createAsyncThunk(
|
export const getProductDataSearch = createAsyncThunk(
|
||||||
'product/getProductDataSearch',
|
'product/getProductDataSearch',
|
||||||
async (ProdData) => {
|
async (ProdData) => {
|
||||||
|
|
@ -291,6 +314,22 @@ export const getProductDataSearch = createAsyncThunk(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getDirectSaleSearchedProduct = createAsyncThunk(
|
||||||
|
'product/getDirectSaleSearchedProduct',
|
||||||
|
async (data) => {
|
||||||
|
const { CompId, BranchId, AppId, prodName } = data;
|
||||||
|
if (
|
||||||
|
CompId != null && CompId != undefined &&
|
||||||
|
BranchId != null && BranchId != undefined &&
|
||||||
|
AppId != undefined && AppId != null
|
||||||
|
) {
|
||||||
|
return await axiosRetailInstanceData.get(
|
||||||
|
`/ProductMasterSearch?CompId=${CompId}&BranchId=${BranchId}&AppId=${AppId}&prodName=${prodName}&SalesType=DS`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export const getBrandData = createAsyncThunk(
|
export const getBrandData = createAsyncThunk(
|
||||||
'product/getBrandData',
|
'product/getBrandData',
|
||||||
async ({ ConfigId }) => {
|
async ({ ConfigId }) => {
|
||||||
|
|
@ -581,7 +620,7 @@ export const putRackData = createAsyncThunk(
|
||||||
|
|
||||||
export const getRackData = createAsyncThunk(
|
export const getRackData = createAsyncThunk(
|
||||||
'getRackData/RackMaster',
|
'getRackData/RackMaster',
|
||||||
async ({ AppId, CompId, BranchId}) => {
|
async ({ AppId, CompId, BranchId }) => {
|
||||||
if (
|
if (
|
||||||
AppId != null &&
|
AppId != null &&
|
||||||
AppId != undefined &&
|
AppId != undefined &&
|
||||||
|
|
@ -599,7 +638,7 @@ export const getRackData = createAsyncThunk(
|
||||||
|
|
||||||
export const deleteRackData = createAsyncThunk(
|
export const deleteRackData = createAsyncThunk(
|
||||||
'deleteRackData/RackMaster',
|
'deleteRackData/RackMaster',
|
||||||
async ({ activeStatus, updatedBy, rackId}) => {
|
async ({ activeStatus, updatedBy, rackId }) => {
|
||||||
if (
|
if (
|
||||||
updatedBy != null &&
|
updatedBy != null &&
|
||||||
updatedBy != undefined &&
|
updatedBy != undefined &&
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,29 @@ export const getProductData = createAsyncThunk(
|
||||||
|
|
||||||
export const getInvoiceImageData = createAsyncThunk(
|
export const getInvoiceImageData = createAsyncThunk(
|
||||||
'Stock/getInvoiceImageData',
|
'Stock/getInvoiceImageData',
|
||||||
async (file) => {
|
async ({ file, AppId, CompId, BranchId }) => {
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file); // 👈 actual file here
|
formData.append('file', file); // file
|
||||||
|
formData.append('AppId', AppId); // 👈 add AppId
|
||||||
|
formData.append('CompId', CompId); // 👈 add CompId
|
||||||
|
formData.append('BranchId', BranchId); // 👈 add BranchId
|
||||||
|
|
||||||
const response = await axiosRetailOcrData.post('/invoice', formData, {
|
const response = await axiosRetailOcrData.post(
|
||||||
headers: {
|
'/invoice',
|
||||||
'Content-Type': 'multipart/form-data',
|
formData,
|
||||||
},
|
{
|
||||||
});
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getPurchaseTypeData = createAsyncThunk(
|
export const getPurchaseTypeData = createAsyncThunk(
|
||||||
'product/getPurchaseTypeData',
|
'product/getPurchaseTypeData',
|
||||||
async () => {
|
async () => {
|
||||||
|
|
|
||||||
|
|
@ -358,6 +358,7 @@ const StandardTablePayment = () => {
|
||||||
const [creditCustomerOpen, setcreditCustomerOpen] = useState(false);
|
const [creditCustomerOpen, setcreditCustomerOpen] = useState(false);
|
||||||
const [Splitpayment, setSplitpayment] = useState(false);
|
const [Splitpayment, setSplitpayment] = useState(false);
|
||||||
const [customerPreviesOrders, setCustomerPreviesOrders] = useState(false);
|
const [customerPreviesOrders, setCustomerPreviesOrders] = useState(false);
|
||||||
|
const paymentGatewayRef = React.useRef(null);
|
||||||
const [PrintOrderDetails, setPrintOrderDetails] = useState([]);
|
const [PrintOrderDetails, setPrintOrderDetails] = useState([]);
|
||||||
const [PaymentUpiOptions, setPaymentUpiOptions] = useState([]);
|
const [PaymentUpiOptions, setPaymentUpiOptions] = useState([]);
|
||||||
const [PaymentDeviceCard, setPaymentDeviceCard] = useState([]);
|
const [PaymentDeviceCard, setPaymentDeviceCard] = useState([]);
|
||||||
|
|
@ -4125,6 +4126,7 @@ const StandardTablePayment = () => {
|
||||||
children={
|
children={
|
||||||
<>
|
<>
|
||||||
<PaymentGatewayEmbedded
|
<PaymentGatewayEmbedded
|
||||||
|
ref={paymentGatewayRef}
|
||||||
orderId={businessUPIOrderId}
|
orderId={businessUPIOrderId}
|
||||||
setBusinessUPIOrderId={setBusinessUPIOrderId}
|
setBusinessUPIOrderId={setBusinessUPIOrderId}
|
||||||
businessUPILink={businessUPILink}
|
businessUPILink={businessUPILink}
|
||||||
|
|
@ -4142,6 +4144,7 @@ const StandardTablePayment = () => {
|
||||||
open={showCancelConfirm}
|
open={showCancelConfirm}
|
||||||
title="Cancel Payment"
|
title="Cancel Payment"
|
||||||
onOk={() => {
|
onOk={() => {
|
||||||
|
paymentGatewayRef.current?.clearPolling();
|
||||||
setBusinessUPI(false);
|
setBusinessUPI(false);
|
||||||
setShowCancelConfirm(false);
|
setShowCancelConfirm(false);
|
||||||
ClearAllGlobalStateDatas();
|
ClearAllGlobalStateDatas();
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import {
|
||||||
Switch,
|
Switch,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Popconfirm,
|
Popconfirm,
|
||||||
|
Tabs,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { TiDelete } from 'react-icons/ti';
|
import { TiDelete } from 'react-icons/ti';
|
||||||
import { CiCalculator1 } from 'react-icons/ci';
|
import { CiCalculator1 } from 'react-icons/ci';
|
||||||
|
|
@ -171,6 +172,7 @@ const FeaturesFunctionalities = (props) => {
|
||||||
const [WeightScale, setWeightScale] = useState(props.WeightScale);
|
const [WeightScale, setWeightScale] = useState(props.WeightScale);
|
||||||
const [WeightScale2, setWeightScale2] = useState(true);
|
const [WeightScale2, setWeightScale2] = useState(true);
|
||||||
|
|
||||||
|
const [activeTab, setActiveTab] = useState('quickAdd');
|
||||||
const [CusDetails, setCusDetails] = useState(false);
|
const [CusDetails, setCusDetails] = useState(false);
|
||||||
const [zipCodeData, setZipCodeData] = useState(false);
|
const [zipCodeData, setZipCodeData] = useState(false);
|
||||||
const [messageType, setMessageType] = useState(null);
|
const [messageType, setMessageType] = useState(null);
|
||||||
|
|
@ -3904,17 +3906,27 @@ const FeaturesFunctionalities = (props) => {
|
||||||
<div>
|
<div>
|
||||||
<DefaultModal
|
<DefaultModal
|
||||||
open={props.QuickAdd}
|
open={props.QuickAdd}
|
||||||
width={800}
|
width={activeTab === 'quickAdd' ? 800 : 1000}
|
||||||
footer={false}
|
footer={false}
|
||||||
children={
|
children={
|
||||||
// cardData Mohan 18-4-2025
|
<>
|
||||||
<ProductForm
|
<Tabs
|
||||||
formType="add"
|
activeKey={activeTab}
|
||||||
handleCancelData={props.handleQuickAddCancel}
|
onChange={setActiveTab}
|
||||||
ProductSearch={props.ProductSearch}
|
items={[
|
||||||
ProductSearchType={props.ProductSearchType}
|
{ key: 'quickAdd', label: 'Quick Add' },
|
||||||
cardData={props.cardData}
|
{ key: 'directSale', label: 'Direct Sale' }
|
||||||
/>
|
]}
|
||||||
|
/>
|
||||||
|
<ProductForm
|
||||||
|
formType="add"
|
||||||
|
activeTab={activeTab}
|
||||||
|
handleCancelData={props.handleQuickAddCancel}
|
||||||
|
ProductSearch={props.ProductSearch}
|
||||||
|
ProductSearchType={props.ProductSearchType}
|
||||||
|
cardData={props.cardData}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
handleCancel={props.handleQuickAddCancel}
|
handleCancel={props.handleQuickAddCancel}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1280,6 +1280,7 @@ function BSReprint({ setOpenModel = () => { } }) {
|
||||||
OverallDisc,
|
OverallDisc,
|
||||||
OrderPaymentDtl,
|
OrderPaymentDtl,
|
||||||
} = orderDtls;
|
} = orderDtls;
|
||||||
|
|
||||||
const updated = productDetails?.map((item) => ({
|
const updated = productDetails?.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
Offer: item?.OfferAmt,
|
Offer: item?.OfferAmt,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState, useImperativeHandle, forwardRef } from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { getPaymentStatusForBusinessUPI } from '../../../../Features/BookingScreen/BookingData/BookingData';
|
import { getPaymentStatusForBusinessUPI } from '../../../../Features/BookingScreen/BookingData/BookingData';
|
||||||
|
|
||||||
const PaymentGatewayEmbedded = ({
|
const PaymentGatewayEmbedded = forwardRef(({
|
||||||
orderId,
|
orderId,
|
||||||
businessUPILink,
|
businessUPILink,
|
||||||
setBusinessUPI,
|
setBusinessUPI,
|
||||||
|
|
@ -12,12 +12,21 @@ const PaymentGatewayEmbedded = ({
|
||||||
ClearAllGlobalStateDatas = () => {},
|
ClearAllGlobalStateDatas = () => {},
|
||||||
CustomerDisplay = () => {},
|
CustomerDisplay = () => {},
|
||||||
Type=null
|
Type=null
|
||||||
}) => {
|
}, ref) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const intervalRef = useRef(null);
|
const intervalRef = useRef(null);
|
||||||
const [timedOut, setTimedOut] = useState(false);
|
const [timedOut, setTimedOut] = useState(false);
|
||||||
const [showTimeout, setShowTimeout] = useState(false);
|
const [showTimeout, setShowTimeout] = useState(false);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
clearPolling: () => {
|
||||||
|
if (intervalRef.current) {
|
||||||
|
clearInterval(intervalRef.current);
|
||||||
|
intervalRef.current = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (paymentSucess || timedOut) return;
|
if (paymentSucess || timedOut) return;
|
||||||
|
|
||||||
|
|
@ -174,6 +183,6 @@ const PaymentGatewayEmbedded = ({
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default PaymentGatewayEmbedded;
|
export default PaymentGatewayEmbedded;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,901 @@
|
||||||
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import { Table, Input, Select, Button } from 'antd';
|
||||||
|
import { PlusOutlined, DeleteOutlined, SaveOutlined, PrinterOutlined } from '@ant-design/icons';
|
||||||
|
import { Tables } from '../../../../Components/Tables/Table.jsx';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { postProductData, getDirectSaleProducts, getAllProductShortCodes, deleteProductData, getDirectSaleSearchedProduct } from '../../../../Features/ProductPage/ProductPage.js';
|
||||||
|
import { getSession, printDiv } from '../../../../Services/Others.js';
|
||||||
|
import { Messages } from '../../../../Components/Notifications/Messages.jsx';
|
||||||
|
import { FaStarOfLife } from "react-icons/fa6";
|
||||||
|
import { IoMdRefresh } from "react-icons/io";
|
||||||
|
import StickerPrintModel from '../../../Product/StickerPrintModal.jsx';
|
||||||
|
import { getPageStyle, style } from '../../../Product/Printstyles.js';
|
||||||
|
import { getBarcodeSessionsIDs, getBarcodeTemplate } from '../../../../Features/Barcode/Barcode.js';
|
||||||
|
import { generateBarcode, generateQRCode, generateQRCodeCopy } from '../../../../Services/utils.js';
|
||||||
|
import StickerPrintTemplates from '../../../Product/StickerTemplates.jsx';
|
||||||
|
import useBarcodeGenerator from './useBarcodeQRGenerator.js';
|
||||||
|
|
||||||
|
const DirectSale = ({ UomData = [], TaxData = [], ProductTypeData = [], ProdCatData = [], ProdSubCatData = [], SupplierData = [], activeTab = null }) => {
|
||||||
|
|
||||||
|
const formRef = useRef();
|
||||||
|
const tableRef = useRef();
|
||||||
|
const searchInputRef = useRef();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [messageType, setMessageType] = useState(null);
|
||||||
|
const [messageData, setMessageData] = useState(null);
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
|
const CompId = getSession('CompId');
|
||||||
|
const BranchId = getSession('BranchId');
|
||||||
|
const AppId = getSession('AppId');
|
||||||
|
const UserId = getSession('UserId');
|
||||||
|
|
||||||
|
const [dataSource, setDataSource] = useState([]);
|
||||||
|
const [editingKey, setEditingKey] = useState(null);
|
||||||
|
|
||||||
|
const [copies, setCopies] = useState(null);
|
||||||
|
const [dropdownValue, setDropdownValue] = useState(null);
|
||||||
|
const [multiProduct, setMultiProduct] = useState(false);
|
||||||
|
const [printReady, setPrintReady] = useState(false);
|
||||||
|
const [printProductName, setPrintProductName] = useState(false);
|
||||||
|
const [productShortCodes, setAllProductShortCodes] = useState([]);
|
||||||
|
const [searchText, setSearchText] = useState('');
|
||||||
|
console.log(searchText, "searchText")
|
||||||
|
const [stickerPrintModalOpen, setStickerPrintModalOpen] = useState(false);
|
||||||
|
const [barcodeTemplateDetails, setBarcodeTemplateDetails] = useState([]);
|
||||||
|
const [filteredProducts, setFilteredProducts] = useState([]);
|
||||||
|
const [labelPrintData, setLabelPrintData] = useState(null);
|
||||||
|
const [prodId, setProdId] = useState(null);
|
||||||
|
const [nickName, setNickName] = useState(null);
|
||||||
|
const [crossData, setCrossData] = useState(null);
|
||||||
|
const [triggerState, setTriggerState] = useState(1);
|
||||||
|
const [templateOptions, setTemplateOptions] = useState({
|
||||||
|
mrp: false,
|
||||||
|
barcode: false,
|
||||||
|
color: false,
|
||||||
|
eDate: false,
|
||||||
|
mDate: false,
|
||||||
|
sellingPrice: false,
|
||||||
|
productName: false,
|
||||||
|
value: '',
|
||||||
|
secondValue: '',
|
||||||
|
qrcode: false,
|
||||||
|
codeType: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log((searchText != null && searchText != ''), "filteredProducts")
|
||||||
|
|
||||||
|
const { qrBase64, barcodeBase64 } = useBarcodeGenerator(prodId);
|
||||||
|
|
||||||
|
const handleAdd = () => {
|
||||||
|
setDataSource([{
|
||||||
|
key: dataSource.length + 1,
|
||||||
|
productName: '',
|
||||||
|
shortCode: '',
|
||||||
|
sellingPrice: '',
|
||||||
|
uom: null,
|
||||||
|
tax: null,
|
||||||
|
prodId: null,
|
||||||
|
size: null,
|
||||||
|
uomName: null,
|
||||||
|
activeStatus: 'A'
|
||||||
|
}, ...dataSource]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async (record) => {
|
||||||
|
if (record?.prodId) {
|
||||||
|
const deleteData = {
|
||||||
|
ProdId: record.prodId,
|
||||||
|
ActiveStatus: record.activeStatus === 'A' ? 'D' : 'A',
|
||||||
|
UpdatedBy: UserId,
|
||||||
|
};
|
||||||
|
const response = await dispatch(deleteProductData(deleteData))?.unwrap();
|
||||||
|
if (response?.data?.statusCode === 1) {
|
||||||
|
setMessageData(record.activeStatus === 'A' ? 'Product De-Activated Successfully' : 'Product Activated Successfully');
|
||||||
|
setMessageType('success');
|
||||||
|
await fetchDirectSaleProducts();
|
||||||
|
} else {
|
||||||
|
setMessageData(response?.data?.response || 'Failed to update status');
|
||||||
|
setMessageType('error');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// const { key } = record;
|
||||||
|
// setDataSource(dataSource.filter(item => item.key !== key));
|
||||||
|
setMessageData('Atleast one empty row should be in table.');
|
||||||
|
setMessageType('warning');
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = useCallback((key, field, value) => {
|
||||||
|
if (searchText) {
|
||||||
|
setFilteredProducts(prev =>
|
||||||
|
prev.map(item =>
|
||||||
|
item.key === key ? { ...item, [field]: value } : item
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setDataSource(prev =>
|
||||||
|
prev.map(item =>
|
||||||
|
item.key === key ? { ...item, [field]: value } : item
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [searchText]);
|
||||||
|
|
||||||
|
const handleSave = async (record) => {
|
||||||
|
|
||||||
|
if (!record.productName || !record.sellingPrice || !record.uom || !record.tax || !record?.shortCode) {
|
||||||
|
setMessageData('Please fill all the required fields!');
|
||||||
|
setMessageType('warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const exists = isShortCodeDuplicate(record?.shortCode, record?.prodId);
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
setMessageData('Short code already exists for another product');
|
||||||
|
setMessageType('error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const postData = {
|
||||||
|
ProdName: record.productName,
|
||||||
|
Size: '1',
|
||||||
|
UOM: record.uom,
|
||||||
|
MRP: record.sellingPrice,
|
||||||
|
SellPrice: record.sellingPrice,
|
||||||
|
TaxId: record.tax,
|
||||||
|
CompId: CompId,
|
||||||
|
BranchId: BranchId,
|
||||||
|
AppId: AppId,
|
||||||
|
ProdType: ProductTypeData?.find(item => item.ConfigName === 'Product')?.ConfigId,
|
||||||
|
ProdCat: ProdCatData?.find(item => item.ConfigName === 'General')?.ConfigId,
|
||||||
|
ProdSubCat: ProdSubCatData?.find(item => item.ConfigName === 'General')?.ConfigId,
|
||||||
|
QtyBasedPrice: 'N',
|
||||||
|
ProdQtywisePriceDetails: [],
|
||||||
|
StockAvailable: 'N',
|
||||||
|
TokenAvailable: 'N',
|
||||||
|
InwardDate: new Date().toJSON(),
|
||||||
|
SuppId: SupplierData?.find(item => item.SuppName?.toLowerCase() === 'self')?.SuppId,
|
||||||
|
AutoGenerateQr: 'N',
|
||||||
|
QRCode: null,
|
||||||
|
CreatedBy: UserId,
|
||||||
|
ProdLogo: '',
|
||||||
|
Cess: 0,
|
||||||
|
ProdShortCode: record?.shortCode
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (record?.prodId) {
|
||||||
|
console.log('Updating Product')
|
||||||
|
} else {
|
||||||
|
const response = await dispatch(postProductData(postData)).unwrap();
|
||||||
|
if (response?.data?.statusCode === 1) {
|
||||||
|
const prodId = response?.data?.ProductDetails?.[0]?.ProdId;
|
||||||
|
const size = response?.data?.ProductDetails?.[0]?.Size;
|
||||||
|
const uomName = response?.data?.ProductDetails?.[0]?.UomName;
|
||||||
|
const totalProducts = response?.data?.ProductDetails?.[0]?.TotalCount;
|
||||||
|
setSearchText(currentSearchText => {
|
||||||
|
if (currentSearchText != null && currentSearchText !== '') {
|
||||||
|
setCurrentPage(1);
|
||||||
|
setTriggerState(prev => prev + 1);
|
||||||
|
if (searchInputRef.current) {
|
||||||
|
searchInputRef.current.value = '';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
} else {
|
||||||
|
setDataSource(prev => prev.map(item =>
|
||||||
|
item.key === record.key ? { ...item, prodId, size, uomName, totalProducts } : { ...item, totalProducts }
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return currentSearchText;
|
||||||
|
});
|
||||||
|
setMessageData('Product saved successfully!');
|
||||||
|
setMessageType('success');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving product:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePrint = async (record) => {
|
||||||
|
const exists = isShortCodeDuplicate(record?.shortCode, record?.prodId);
|
||||||
|
if (exists) {
|
||||||
|
setMessageData('Short code already exists for another product. Cannot print.');
|
||||||
|
setMessageType('error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setStickerPrintModalOpen(true);
|
||||||
|
setLabelPrintData({ ...record, MRP: record?.sellingPrice || 0, SellPrice: record?.sellingPrice || 0 });
|
||||||
|
setProdId(record?.shortCode + '' + record?.sellingPrice)
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = useMemo(() => [
|
||||||
|
{
|
||||||
|
title: 'SL.No',
|
||||||
|
width: 50,
|
||||||
|
align: 'center',
|
||||||
|
render: (_, __, index) => (currentPage - 1) * 10 + index + 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => <div style={{ textWrap: 'nowrap', display: "flex", gap: "4px " }}>Product Name
|
||||||
|
<span>
|
||||||
|
<FaStarOfLife size={10} color='red' className='requiredIcons' />
|
||||||
|
</span>
|
||||||
|
</div>,
|
||||||
|
dataIndex: 'productName',
|
||||||
|
width: 150,
|
||||||
|
render: (text, record) => (
|
||||||
|
record.prodId && editingKey !== record.key ?
|
||||||
|
<div>{text}</div> :
|
||||||
|
<Input
|
||||||
|
value={text}
|
||||||
|
onChange={(e) => handleChange(record.key, 'productName', e.target.value)}
|
||||||
|
placeholder="Enter product name"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => <div style={{ textWrap: 'nowrap', display: "flex", gap: "4px " }}>Short Code
|
||||||
|
<span>
|
||||||
|
<FaStarOfLife size={10} color='red' className='requiredIcons' />
|
||||||
|
</span>
|
||||||
|
</div>,
|
||||||
|
dataIndex: 'shortCode',
|
||||||
|
width: 100,
|
||||||
|
render: (text, record) => (
|
||||||
|
record.prodId && editingKey !== record.key ?
|
||||||
|
<div>{text}</div> :
|
||||||
|
<Input
|
||||||
|
value={text}
|
||||||
|
onInput={(e) => {
|
||||||
|
const originalValue = e.target.value;
|
||||||
|
const hasNonAlpha = /[^a-zA-Z]/.test(originalValue);
|
||||||
|
if (hasNonAlpha) {
|
||||||
|
setMessageData('Enter Alphabet Characters only');
|
||||||
|
setMessageType('warning');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = e.target.value.replace(/[^a-zA-Z]/g, '').toUpperCase();
|
||||||
|
const exists = isShortCodeDuplicate(value, record?.prodId);
|
||||||
|
if (exists && value) {
|
||||||
|
setMessageData('Short code already exists for another product');
|
||||||
|
setMessageType('error');
|
||||||
|
}
|
||||||
|
handleChange(record.key, 'shortCode', value);
|
||||||
|
}}
|
||||||
|
placeholder="Short Code"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => <div style={{ textWrap: 'nowrap', display: "flex", gap: "4px " }}>Selling Price
|
||||||
|
<span>
|
||||||
|
<FaStarOfLife size={10} color='red' className='requiredIcons' />
|
||||||
|
</span>
|
||||||
|
</div>,
|
||||||
|
dataIndex: 'sellingPrice',
|
||||||
|
width: 120,
|
||||||
|
render: (text, record) => (
|
||||||
|
record.prodId && editingKey !== record.key ?
|
||||||
|
<div>{text}</div> :
|
||||||
|
<Input
|
||||||
|
value={text}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = e.target.value.replace(/[^0-9.]/g, '');
|
||||||
|
const parts = value.split('.');
|
||||||
|
const formatted = parts.length > 2 ? `${parts[0]}.${parts.slice(1).join('')}` : value;
|
||||||
|
handleChange(record.key, 'sellingPrice', formatted);
|
||||||
|
}}
|
||||||
|
placeholder="0.00"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => <div style={{ textWrap: 'nowrap', display: "flex", gap: "4px " }}>UOM
|
||||||
|
<span>
|
||||||
|
<FaStarOfLife size={10} color='red' className='requiredIcons' />
|
||||||
|
</span>
|
||||||
|
</div>,
|
||||||
|
dataIndex: 'uom',
|
||||||
|
width: 60,
|
||||||
|
render: (text, record) => (
|
||||||
|
record.prodId && editingKey !== record.key ?
|
||||||
|
<div>{UomData?.find(item => item.ConfigId === text)?.ConfigName || '-'}</div> :
|
||||||
|
<Select
|
||||||
|
disabled={record?.prodId}
|
||||||
|
className='uom-select'
|
||||||
|
value={text}
|
||||||
|
onChange={(value) => handleChange(record.key, 'uom', value)}
|
||||||
|
placeholder="UOM"
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
options={UomData?.map(item => ({
|
||||||
|
value: item.ConfigId,
|
||||||
|
label: item.ConfigName
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => <div style={{ textWrap: 'nowrap', display: "flex", gap: "4px " }}>Tax
|
||||||
|
<span>
|
||||||
|
<FaStarOfLife size={10} color='red' className='requiredIcons' />
|
||||||
|
</span>
|
||||||
|
</div>,
|
||||||
|
dataIndex: 'tax',
|
||||||
|
width: 70,
|
||||||
|
render: (text, record) => {
|
||||||
|
if (record.prodId && editingKey !== record.key) {
|
||||||
|
const tax = TaxData?.find(item => item.TaxId === text);
|
||||||
|
return <div>{tax ? tax?.TaxIdName + ' - ' + `${tax?.TaxPercentage}%` : '-'}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
className='tax-select'
|
||||||
|
value={text}
|
||||||
|
disabled={record?.prodId}
|
||||||
|
onChange={(value) => handleChange(record.key, 'tax', value)}
|
||||||
|
placeholder="Tax"
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
options={TaxData?.map(item => ({
|
||||||
|
value: item.TaxId,
|
||||||
|
label: `${item.TaxIdName} - ${item.TaxPercentage}%`
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Save / Print',
|
||||||
|
width: 90,
|
||||||
|
align: 'center',
|
||||||
|
render: (_, record) => {
|
||||||
|
const existingProduct = record?.prodId;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='directsaleBTN' style={{ display: 'flex', gap: '8px', justifyContent: 'center' }}>
|
||||||
|
{!existingProduct && <Button
|
||||||
|
icon={<SaveOutlined />}
|
||||||
|
size="small"
|
||||||
|
disabled={existingProduct}
|
||||||
|
className='save-btn'
|
||||||
|
onClick={() => handleSave(record)}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
{/* {!existingProduct ? 'Save' : 'Update'} */}
|
||||||
|
</Button>}
|
||||||
|
{existingProduct && <Button
|
||||||
|
icon={<PrinterOutlined />}
|
||||||
|
size="small"
|
||||||
|
disabled={!existingProduct}
|
||||||
|
onClick={() => handlePrint(record)}
|
||||||
|
>
|
||||||
|
Print
|
||||||
|
</Button>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Action',
|
||||||
|
width: 60,
|
||||||
|
align: "center",
|
||||||
|
render: (_, record) => {
|
||||||
|
if (record?.activeStatus === 'A') {
|
||||||
|
return <DeleteOutlined
|
||||||
|
onClick={() => handleDelete(record)}
|
||||||
|
style={{ color: 'red', cursor: 'pointer', fontSize: '18px' }}
|
||||||
|
/>
|
||||||
|
} else {
|
||||||
|
return <IoMdRefresh
|
||||||
|
style={{ color: 'blue', cursor: 'pointer', fontSize: '18px' }}
|
||||||
|
onClick={() => handleDelete(record)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
], [
|
||||||
|
searchText,
|
||||||
|
editingKey,
|
||||||
|
productShortCodes,
|
||||||
|
UomData,
|
||||||
|
TaxData
|
||||||
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeTab === 'directSale' && AppId && CompId && BranchId) {
|
||||||
|
fetchDirectSaleProducts();
|
||||||
|
fetchAllProductShortCodes();
|
||||||
|
}
|
||||||
|
}, [activeTab, AppId, BranchId, CompId, currentPage, triggerState]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (dataSource.every(row => row.prodId)) {
|
||||||
|
handleAdd();
|
||||||
|
}
|
||||||
|
}, [dataSource]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
if (searchText) {
|
||||||
|
fetchSearchedProducts();
|
||||||
|
} else {
|
||||||
|
setFilteredProducts([{
|
||||||
|
key: 1,
|
||||||
|
productName: '',
|
||||||
|
shortCode: '',
|
||||||
|
sellingPrice: '',
|
||||||
|
uom: null,
|
||||||
|
tax: null,
|
||||||
|
prodId: null,
|
||||||
|
size: null,
|
||||||
|
uomName: null,
|
||||||
|
activeStatus: 'A'
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [searchText, AppId, BranchId, CompId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (event) => {
|
||||||
|
if (tableRef.current && !tableRef.current.contains(event.target) && !event.target.closest('.ant-select-dropdown')) {
|
||||||
|
setEditingKey(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeTab === 'directSale' && AppId && CompId && BranchId && stickerPrintModalOpen) {
|
||||||
|
getBarcodeSessionData();
|
||||||
|
fetchBarcodeTemplateDetails();
|
||||||
|
}
|
||||||
|
}, [activeTab, AppId, BranchId, CompId, stickerPrintModalOpen]);
|
||||||
|
|
||||||
|
const fetchSearchedProducts = async () => {
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
AppId: AppId,
|
||||||
|
CompId: CompId,
|
||||||
|
BranchId: BranchId,
|
||||||
|
prodName: searchText
|
||||||
|
}
|
||||||
|
const response = await dispatch(getDirectSaleSearchedProduct(data))?.unwrap();
|
||||||
|
if (response?.data?.data?.length > 0 && response?.data?.statusCode === 1) {
|
||||||
|
const products = response?.data?.data?.map((item, index) => ({
|
||||||
|
key: index + 1,
|
||||||
|
productName: item.ProdName,
|
||||||
|
shortCode: item.ProdShortCode,
|
||||||
|
sellingPrice: item.SellPrice,
|
||||||
|
uom: item.UOM,
|
||||||
|
tax: item.TaxId,
|
||||||
|
prodId: item.ProdId,
|
||||||
|
activeStatus: item?.ActiveStatus,
|
||||||
|
size: item.Size || 1,
|
||||||
|
uomName: item.UomName || 'PCS',
|
||||||
|
totalProducts: item?.TotalCount
|
||||||
|
}))
|
||||||
|
setFilteredProducts([{
|
||||||
|
key: products.length + 1,
|
||||||
|
productName: '',
|
||||||
|
shortCode: '',
|
||||||
|
sellingPrice: '',
|
||||||
|
uom: null,
|
||||||
|
tax: null,
|
||||||
|
prodId: null,
|
||||||
|
size: null,
|
||||||
|
uomName: null,
|
||||||
|
activeStatus: 'A'
|
||||||
|
}, ...products]);
|
||||||
|
} else {
|
||||||
|
setFilteredProducts([{
|
||||||
|
key: 1,
|
||||||
|
productName: '',
|
||||||
|
shortCode: '',
|
||||||
|
sellingPrice: '',
|
||||||
|
uom: null,
|
||||||
|
tax: null,
|
||||||
|
prodId: null,
|
||||||
|
size: null,
|
||||||
|
uomName: null,
|
||||||
|
activeStatus: 'A'
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error?.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const isShortCodeDuplicate = useCallback((code, prodId) => {
|
||||||
|
return productShortCodes.some(
|
||||||
|
item =>
|
||||||
|
item.ProdShortCode.toUpperCase() === code.toUpperCase() &&
|
||||||
|
item.ProdId !== prodId
|
||||||
|
);
|
||||||
|
}, [productShortCodes]);
|
||||||
|
|
||||||
|
const fetchBarcodeTemplateDetails = async () => {
|
||||||
|
const response = await dispatch(
|
||||||
|
getBarcodeTemplate({ compId: CompId, appId: AppId, branchId: BranchId })
|
||||||
|
).unwrap();
|
||||||
|
if (response?.data?.statusCode === 1 && response?.data?.data?.length > 0) {
|
||||||
|
const { TemplateDetails } = response?.data?.data?.[0];
|
||||||
|
setBarcodeTemplateDetails(TemplateDetails);
|
||||||
|
} else {
|
||||||
|
setBarcodeTemplateDetails([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBarcodeSessionData = async () => {
|
||||||
|
try {
|
||||||
|
let response = await dispatch(getBarcodeSessionsIDs()).unwrap();
|
||||||
|
if (response?.data?.statusCode === 1 && response?.data?.data?.length > 0) {
|
||||||
|
setCrossData(
|
||||||
|
response?.data?.data?.filter(
|
||||||
|
(item) => !item.ConfigName?.includes('100X13 (55MM Printable Gold)')
|
||||||
|
) || []
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setCrossData([]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching barcode session data:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchDirectSaleProducts = async () => {
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
AppId: AppId,
|
||||||
|
CompId: CompId,
|
||||||
|
BranchId: BranchId,
|
||||||
|
page: currentPage
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await dispatch(getDirectSaleProducts(data))?.unwrap();
|
||||||
|
if (response?.data?.statusCode === 1 && response?.data?.data?.length > 0) {
|
||||||
|
const products = response?.data?.data?.map((item, index) => ({
|
||||||
|
key: index + 1,
|
||||||
|
productName: item.ProdName,
|
||||||
|
shortCode: item.ProdShortCode,
|
||||||
|
sellingPrice: item.SellPrice,
|
||||||
|
uom: item.UOM,
|
||||||
|
tax: item.TaxId,
|
||||||
|
prodId: item.ProdId,
|
||||||
|
activeStatus: item?.ActiveStatus,
|
||||||
|
size: item.Size || 1,
|
||||||
|
uomName: item.UomName || 'PCS',
|
||||||
|
totalProducts: item?.TotalCount
|
||||||
|
}));
|
||||||
|
setDataSource([{
|
||||||
|
key: products.length + 1,
|
||||||
|
productName: '',
|
||||||
|
shortCode: '',
|
||||||
|
sellingPrice: '',
|
||||||
|
uom: null,
|
||||||
|
tax: null,
|
||||||
|
prodId: null,
|
||||||
|
size: null,
|
||||||
|
uomName: null,
|
||||||
|
activeStatus: 'A'
|
||||||
|
}, ...products]);
|
||||||
|
} else {
|
||||||
|
setDataSource([])
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching direct sale products:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchAllProductShortCodes = async () => {
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
AppId: AppId,
|
||||||
|
CompId: CompId,
|
||||||
|
BranchId: BranchId,
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await dispatch(getAllProductShortCodes(data))?.unwrap();
|
||||||
|
if (response?.data?.statusCode === 1 && response?.data?.data?.length > 0) {
|
||||||
|
setAllProductShortCodes(response?.data?.data);
|
||||||
|
} else {
|
||||||
|
setAllProductShortCodes([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching product short codes:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePageChange = (current) => {
|
||||||
|
setCurrentPage(current);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFinish = async () => {
|
||||||
|
const template = barcodeTemplateDetails?.find(
|
||||||
|
(find) => find.SessionName === dropdownValue
|
||||||
|
);
|
||||||
|
const templateAvailable = template === undefined ? false : true;
|
||||||
|
|
||||||
|
if (templateAvailable) {
|
||||||
|
const checkComponent = (name) =>
|
||||||
|
template?.OptionDetails?.some((some) => some.OptionName === name);
|
||||||
|
const mrp = checkComponent('MRP');
|
||||||
|
const barcode = checkComponent('Barcode');
|
||||||
|
const color = checkComponent('Color');
|
||||||
|
const eDate = checkComponent('E Date');
|
||||||
|
const mDate = checkComponent('M Date');
|
||||||
|
const sellingPrice = checkComponent('Selling Price');
|
||||||
|
const productName = checkComponent('Product Name');
|
||||||
|
const qrcode = checkComponent('Qrcode');
|
||||||
|
const position = template.Position !== '' ? template.Position : '';
|
||||||
|
const positionString =
|
||||||
|
template.PositionString !== '' ? template.PositionString : '';
|
||||||
|
|
||||||
|
setTemplateOptions({
|
||||||
|
mrp: mrp,
|
||||||
|
barcode: barcode,
|
||||||
|
color: color,
|
||||||
|
eDate: eDate,
|
||||||
|
mDate: mDate,
|
||||||
|
sellingPrice: sellingPrice,
|
||||||
|
productName: productName,
|
||||||
|
value: position,
|
||||||
|
secondValue: positionString,
|
||||||
|
qrcode: qrcode,
|
||||||
|
codeType: template?.CodeType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setPrintReady(true);
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
|
switch (dropdownValue) {
|
||||||
|
case '15X15 6cross': // COMPLETE
|
||||||
|
await printDiv(
|
||||||
|
templateAvailable ? 'label-15X156cross' : 'StickerPrints0',
|
||||||
|
style + getPageStyle(92)
|
||||||
|
);
|
||||||
|
handleModalClose();
|
||||||
|
break;
|
||||||
|
case '22X35 3cross': // Need to check
|
||||||
|
await printDiv(
|
||||||
|
templateAvailable ? '22X353cross' : 'StickerPrints1',
|
||||||
|
style + getPageStyle(105)
|
||||||
|
);
|
||||||
|
handleModalClose();
|
||||||
|
break;
|
||||||
|
case '25X25 4cross': // COMPLETE
|
||||||
|
await printDiv(
|
||||||
|
templateAvailable ? '25X254cross' : 'StickerPrints2',
|
||||||
|
style + getPageStyle(100)
|
||||||
|
);
|
||||||
|
handleModalClose();
|
||||||
|
break;
|
||||||
|
case '25X50 2cross': //Need to check
|
||||||
|
await printDiv(
|
||||||
|
templateAvailable ? '25X502cross' : 'StickerPrints3',
|
||||||
|
style + getPageStyle(100)
|
||||||
|
);
|
||||||
|
handleModalClose();
|
||||||
|
break;
|
||||||
|
case '50X30 Single': // COMPLETE
|
||||||
|
await printDiv(
|
||||||
|
templateAvailable ? '50X30Single' : 'StickerPrints4',
|
||||||
|
style
|
||||||
|
);
|
||||||
|
handleModalClose();
|
||||||
|
break;
|
||||||
|
case '50X25 Single': // COMPLETE
|
||||||
|
await printDiv(
|
||||||
|
templateAvailable ? '50X25Single' : 'StickerPrints5',
|
||||||
|
style
|
||||||
|
);
|
||||||
|
handleModalClose();
|
||||||
|
break;
|
||||||
|
case '100X13 (55MM Printable)': //Need to check
|
||||||
|
await printDiv(
|
||||||
|
templateAvailable ? '100X13(55MMPrintable)' : 'StickerPrints6',
|
||||||
|
style
|
||||||
|
);
|
||||||
|
handleModalClose();
|
||||||
|
break;
|
||||||
|
case '100X15 (70MM Printable)': // COMPLETE
|
||||||
|
await printDiv(
|
||||||
|
templateAvailable ? '100X15(70MMPrintable)' : 'StickerPrints7',
|
||||||
|
style
|
||||||
|
);
|
||||||
|
handleModalClose();
|
||||||
|
break;
|
||||||
|
case '100X150': // COMPLETE
|
||||||
|
await printDiv(
|
||||||
|
templateAvailable ? '100X150' : 'StickerPrints8',
|
||||||
|
style
|
||||||
|
);
|
||||||
|
handleModalClose();
|
||||||
|
setPrintReady(false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
alert('! Select correct cross ');
|
||||||
|
setPrintReady(false);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CoresOnChange = (params) => {
|
||||||
|
setDropdownValue(params);
|
||||||
|
formRef.current?.setFieldsValue({ Cross: params });
|
||||||
|
const template = barcodeTemplateDetails?.find(
|
||||||
|
(find) => find.SessionName === params
|
||||||
|
);
|
||||||
|
const checkComponent = (name) =>
|
||||||
|
template?.OptionDetails?.some((some) => some.OptionName === name);
|
||||||
|
const productName = checkComponent('Product Name');
|
||||||
|
setPrintProductName(productName);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CopiesOnChange = (params) => {
|
||||||
|
setCopies(params);
|
||||||
|
formRef.current?.setFieldsValue({ Copies: params });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleModalClose = () => {
|
||||||
|
setCopies(null)
|
||||||
|
setDropdownValue('')
|
||||||
|
setStickerPrintModalOpen(false);
|
||||||
|
setLabelPrintData(null);
|
||||||
|
setNickName(null);
|
||||||
|
setProdId(null)
|
||||||
|
setMultiProduct(false);
|
||||||
|
formRef?.current?.resetFields();
|
||||||
|
setPrintReady(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const imageTagBarcodeAndQR = useCallback(
|
||||||
|
(w = 'auto', fs = '6px', codeData = null) => {
|
||||||
|
const currentCode = codeData || prodId;
|
||||||
|
return (
|
||||||
|
(templateOptions.barcode || templateOptions.qrcode) && (
|
||||||
|
<>
|
||||||
|
<img
|
||||||
|
style={{ width: w }}
|
||||||
|
src={
|
||||||
|
templateOptions.codeType === 'B'
|
||||||
|
? !multiProduct
|
||||||
|
? barcodeBase64
|
||||||
|
: generateBarcode(currentCode)
|
||||||
|
: !multiProduct
|
||||||
|
? qrBase64
|
||||||
|
: generateQRCode(QrandbarcodeDatas, currentCode)
|
||||||
|
}
|
||||||
|
alt="Barcode"
|
||||||
|
/>
|
||||||
|
{templateOptions.codeType !== 'B' && (
|
||||||
|
<p style={{ margin: '0', padding: '0', fontSize: fs }}>
|
||||||
|
{currentCode}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[templateOptions, multiProduct, barcodeBase64, qrBase64, prodId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onComplete = useCallback(() => {
|
||||||
|
setMessageType(null);
|
||||||
|
setMessageData(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='direct-sale-container'>
|
||||||
|
<Messages
|
||||||
|
messageType={messageType}
|
||||||
|
messageData={messageData}
|
||||||
|
onComplete={onComplete}
|
||||||
|
/>
|
||||||
|
<div className="bsreprintmodaltable-search">
|
||||||
|
<input
|
||||||
|
ref={searchInputRef}
|
||||||
|
type="text"
|
||||||
|
placeholder="Search Product / Short Code..."
|
||||||
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ marginBottom: '10px', color: '#666', fontSize: '14px', fontStyle: 'italic' }}>
|
||||||
|
Note: Click on a row to modify the product details
|
||||||
|
</div>
|
||||||
|
<div className='DirectSaleTable' ref={tableRef}>
|
||||||
|
<Tables
|
||||||
|
rowKey="prodId"
|
||||||
|
data={(searchText != null && searchText != '') ? filteredProducts : dataSource}
|
||||||
|
dataSource={(searchText != null && searchText != '') ? filteredProducts : dataSource}
|
||||||
|
columns={columns}
|
||||||
|
onRow={(record) => ({
|
||||||
|
onClick: () => record.prodId && setEditingKey(record.key)
|
||||||
|
})}
|
||||||
|
ownPagination={true}
|
||||||
|
pagination={{
|
||||||
|
current: currentPage,
|
||||||
|
onChange: handlePageChange,
|
||||||
|
total: (searchText != null && searchText != '') ? filteredProducts?.length : ((dataSource[dataSource.length - 1]?.totalProducts || 0) + 1),
|
||||||
|
pageSize: 11,
|
||||||
|
showSizeChanger: false,
|
||||||
|
hideOnSinglePage: true,
|
||||||
|
}}
|
||||||
|
bordered
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{stickerPrintModalOpen && <StickerPrintModel
|
||||||
|
open={stickerPrintModalOpen}
|
||||||
|
handleCancel={handleModalClose}
|
||||||
|
formRef={formRef}
|
||||||
|
onFinish={onFinish}
|
||||||
|
MultiCode={multiProduct}
|
||||||
|
activeTab={null}
|
||||||
|
setActiveTab={() => { }}
|
||||||
|
selectedRecords={null}
|
||||||
|
coresdata={crossData}
|
||||||
|
dropdownValue={dropdownValue}
|
||||||
|
CoresOnChange={CoresOnChange}
|
||||||
|
barcodeTemplateDetails={barcodeTemplateDetails}
|
||||||
|
detail={labelPrintData}
|
||||||
|
proName={labelPrintData?.productName}
|
||||||
|
nickName={nickName}
|
||||||
|
ProdId={labelPrintData?.shortCode + '' + labelPrintData?.sellingPrice}
|
||||||
|
size={labelPrintData?.size}
|
||||||
|
// valueStyleColumn={valueStyleColumn}
|
||||||
|
// secondValueStyle={secondValueStyle}
|
||||||
|
// valueStyleRow={valueStyleRow}
|
||||||
|
CopiesOnChange={CopiesOnChange}
|
||||||
|
printProductName={printProductName}
|
||||||
|
totalCopies={null}
|
||||||
|
setstockWiseQrcode={null}
|
||||||
|
setProductWiseQrcode={null}
|
||||||
|
setNickName={setNickName}
|
||||||
|
templateOptions={templateOptions}
|
||||||
|
/>}
|
||||||
|
|
||||||
|
{printReady && (
|
||||||
|
<StickerPrintTemplates
|
||||||
|
selectedRecords={null}
|
||||||
|
copies={copies}
|
||||||
|
nickName={nickName}
|
||||||
|
proName={labelPrintData?.productName}
|
||||||
|
ProdId={labelPrintData?.shortCode + '' + labelPrintData?.sellingPrice}
|
||||||
|
size={labelPrintData?.size + ' ' + labelPrintData?.uomName}
|
||||||
|
detail={labelPrintData}
|
||||||
|
templateOptions={templateOptions}
|
||||||
|
base64Image={qrBase64}
|
||||||
|
QrandbarcodeDatas={null}
|
||||||
|
MultiCode={multiProduct}
|
||||||
|
generateQRCodeCopy={(code) =>
|
||||||
|
generateQRCodeCopy(null, code)
|
||||||
|
}
|
||||||
|
generateCodeImage={(code, codeType = 'Q') => generateCodeImage(code, codeType, null)}
|
||||||
|
imageTagBarcodeAndQR={imageTagBarcodeAndQR}
|
||||||
|
dropdownValue={dropdownValue}
|
||||||
|
barcodeTemplateDetails={barcodeTemplateDetails}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DirectSale;
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import QRCode from 'qrcode';
|
||||||
|
import JsBarcode from 'jsbarcode';
|
||||||
|
|
||||||
|
const useBarcodeGenerator = (code) => {
|
||||||
|
const [qrBase64, setQrBase64] = useState(null);
|
||||||
|
const [barcodeBase64, setBarcodeBase64] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!code) {
|
||||||
|
setQrBase64(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateQR = async () => {
|
||||||
|
try {
|
||||||
|
const url = await QRCode.toDataURL(code);
|
||||||
|
setQrBase64(url);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('QR generation error:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
generateQR();
|
||||||
|
}, [code]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!code) {
|
||||||
|
setBarcodeBase64(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const svg = document.createElementNS(
|
||||||
|
'http://www.w3.org/2000/svg',
|
||||||
|
'svg'
|
||||||
|
);
|
||||||
|
|
||||||
|
JsBarcode(svg, code, {
|
||||||
|
format: 'CODE128',
|
||||||
|
displayValue: true,
|
||||||
|
lineColor: '#000',
|
||||||
|
width: 2,
|
||||||
|
height: 50,
|
||||||
|
});
|
||||||
|
|
||||||
|
const svgData = new XMLSerializer().serializeToString(svg);
|
||||||
|
const svgBlob = new Blob([svgData], {
|
||||||
|
type: 'image/svg+xml;charset=utf-8',
|
||||||
|
});
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onloadend = () => {
|
||||||
|
setBarcodeBase64(reader.result);
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsDataURL(svgBlob);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Barcode generation error:', error);
|
||||||
|
}
|
||||||
|
}, [code]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
qrBase64,
|
||||||
|
barcodeBase64
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useBarcodeGenerator;
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -88,7 +88,7 @@ const StickerPrintModel = ({
|
||||||
if (coresdata?.length > 0 && open) {
|
if (coresdata?.length > 0 && open) {
|
||||||
CoresOnChange(coresdata?.[0].ConfigName);
|
CoresOnChange(coresdata?.[0].ConfigName);
|
||||||
}
|
}
|
||||||
}, [open]);
|
}, [open, coresdata]);
|
||||||
return (
|
return (
|
||||||
<DefaultModal
|
<DefaultModal
|
||||||
width={1000}
|
width={1000}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import { Tables } from '../../Components/Tables/Table.jsx';
|
||||||
import { InputField } from '../../Components/Forms/InputField.jsx';
|
import { InputField } from '../../Components/Forms/InputField.jsx';
|
||||||
import TextAreaInput from '../../Components/Forms/TextArea.jsx';
|
import TextAreaInput from '../../Components/Forms/TextArea.jsx';
|
||||||
import { uploadImage } from '../../Features/upload/upload.js';
|
import { uploadImage } from '../../Features/upload/upload.js';
|
||||||
|
import { getSession } from '../../Services/Others.js';
|
||||||
|
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
const { Column } = Table;
|
const { Column } = Table;
|
||||||
|
|
@ -490,6 +491,11 @@ const InvoiceImageExtractorModal = ({
|
||||||
uomData = [],
|
uomData = [],
|
||||||
setVisible = () => { },
|
setVisible = () => { },
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
|
const AppId = getSession('AppId');
|
||||||
|
const CompId = getSession('CompId');
|
||||||
|
const BranchId = getSession('BranchId');
|
||||||
|
|
||||||
const extractedTableRef = useRef(null);
|
const extractedTableRef = useRef(null);
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [ocrLoading, setOcrLoading] = useState(false);
|
const [ocrLoading, setOcrLoading] = useState(false);
|
||||||
|
|
@ -503,7 +509,7 @@ const InvoiceImageExtractorModal = ({
|
||||||
const [productTableData, setProductTableData] = useState([]);
|
const [productTableData, setProductTableData] = useState([]);
|
||||||
const [TotalAmtData, SetTotalAmtData] = useState(0);
|
const [TotalAmtData, SetTotalAmtData] = useState(0);
|
||||||
const [SupplierData, setSupplierData] = useState([]);
|
const [SupplierData, setSupplierData] = useState([]);
|
||||||
console.log(form?.getFieldsValue(), 'SupplierData', SupplierData);
|
console.log(form?.getFieldsValue(), 'productTableData', productTableData);
|
||||||
const [showPreviewModal, setShowPreviewModal] = useState(false);
|
const [showPreviewModal, setShowPreviewModal] = useState(false);
|
||||||
const [processedImages, setProcessedImages] = useState(new Set());
|
const [processedImages, setProcessedImages] = useState(new Set());
|
||||||
const [uploadMoreVisible, setUploadMoreVisible] = useState(false);
|
const [uploadMoreVisible, setUploadMoreVisible] = useState(false);
|
||||||
|
|
@ -716,8 +722,14 @@ const InvoiceImageExtractorModal = ({
|
||||||
setProcessingStep('Processing image…');
|
setProcessingStep('Processing image…');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const data = {
|
||||||
|
file: fileToProcess,
|
||||||
|
AppId,
|
||||||
|
CompId,
|
||||||
|
BranchId
|
||||||
|
}
|
||||||
const response = await dispatch(
|
const response = await dispatch(
|
||||||
getInvoiceImageData(fileToProcess)
|
getInvoiceImageData(data)
|
||||||
).unwrap();
|
).unwrap();
|
||||||
if (response?.data?.length > 0 && response?.statusCode === 1) {
|
if (response?.data?.length > 0 && response?.statusCode === 1) {
|
||||||
console.log('OCR API Response:', response);
|
console.log('OCR API Response:', response);
|
||||||
|
|
@ -762,19 +774,38 @@ const InvoiceImageExtractorModal = ({
|
||||||
|
|
||||||
if (data.products?.length) {
|
if (data.products?.length) {
|
||||||
const mergedProducts = [...productTableData];
|
const mergedProducts = [...productTableData];
|
||||||
|
|
||||||
data.products.forEach((p) => {
|
data.products.forEach((p) => {
|
||||||
const exists = mergedProducts.some(
|
const exists = mergedProducts.some(
|
||||||
(x) => x.description === p.description && x.rate === p.rate
|
(x) => x.description === p.description && x.rate === p.rate
|
||||||
);
|
);
|
||||||
if (!exists) mergedProducts.push(p);
|
if (!exists) mergedProducts.push(p);
|
||||||
});
|
});
|
||||||
setProductTableData(
|
|
||||||
mergedProducts?.map((item, index) => ({
|
// Sort Y first, then N
|
||||||
|
const sortedData = [...mergedProducts]
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (a?.type === b?.type) return 0;
|
||||||
|
return a?.type === 'N' ? -1 : 1;
|
||||||
|
})
|
||||||
|
.map((item, index) => ({
|
||||||
...item,
|
...item,
|
||||||
srNo: index + 1,
|
srNo: index + 1,
|
||||||
image: item?.image ? item?.image : (uploadImgData?.data?.status ? uploadImgData?.data?.image : null),
|
image: item?.image
|
||||||
}))
|
? item?.image
|
||||||
);
|
: uploadImgData?.data?.status
|
||||||
|
? uploadImgData?.data?.image
|
||||||
|
: null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
setProductTableData(sortedData);
|
||||||
|
// setProductTableData(
|
||||||
|
// mergedProducts?.map((item, index) => ({
|
||||||
|
// ...item,
|
||||||
|
// srNo: index + 1,
|
||||||
|
// image: item?.image ? item?.image : (uploadImgData?.data?.status ? uploadImgData?.data?.image : null),
|
||||||
|
// }))
|
||||||
|
// );
|
||||||
SetTotalAmtData(
|
SetTotalAmtData(
|
||||||
mergedProducts.reduce(
|
mergedProducts.reduce(
|
||||||
(sum, item) =>
|
(sum, item) =>
|
||||||
|
|
@ -1012,6 +1043,7 @@ const InvoiceImageExtractorModal = ({
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
color: '#1292ee',
|
color: '#1292ee',
|
||||||
marginBottom: '10px',
|
marginBottom: '10px',
|
||||||
|
fontFamily: 'Poppins'
|
||||||
}}
|
}}
|
||||||
onClick={() => setIsEditMode(true)}
|
onClick={() => setIsEditMode(true)}
|
||||||
>
|
>
|
||||||
|
|
@ -1151,9 +1183,21 @@ const InvoiceImageExtractorModal = ({
|
||||||
<div style={{ margin: '10px 0', fontSize: '14px', color: '#666' }}>
|
<div style={{ margin: '10px 0', fontSize: '14px', color: '#666' }}>
|
||||||
<strong>Note:</strong> Click on a row to edit the data
|
<strong>Note:</strong> Click on a row to edit the data
|
||||||
</div>
|
</div>
|
||||||
|
<div className="product-legend">
|
||||||
|
<div className="legend-item">
|
||||||
|
<span className="legend-box green"></span>
|
||||||
|
<span>Product exists in list</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="legend-item">
|
||||||
|
<span className="legend-box red"></span>
|
||||||
|
<span>Product not found in list</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="extractor-table" ref={extractedTableRef}>
|
<div className="extractor-table" ref={extractedTableRef}>
|
||||||
<Tables
|
<Tables
|
||||||
rowClassName="product-extractor-table"
|
rowClassName={(record) => `product-extractor-table ${record.type === 'N' ? 'not-added' : 'added'}`}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={productTableData}
|
data={productTableData}
|
||||||
onRow={(record) => ({
|
onRow={(record) => ({
|
||||||
|
|
|
||||||
|
|
@ -629,6 +629,7 @@ const StockForm = ({ formType }) => {
|
||||||
const unmatchedProducts = matchedProducts.filter(
|
const unmatchedProducts = matchedProducts.filter(
|
||||||
(item) => !item.matchFound
|
(item) => !item.matchFound
|
||||||
);
|
);
|
||||||
|
|
||||||
if (unmatchedProducts?.length > 0) {
|
if (unmatchedProducts?.length > 0) {
|
||||||
setLoadingText('Mapping unmatched products...');
|
setLoadingText('Mapping unmatched products...');
|
||||||
const response = await dispatch(
|
const response = await dispatch(
|
||||||
|
|
@ -705,7 +706,7 @@ const StockForm = ({ formType }) => {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const bulkResponse = await dispatch(
|
const bulkResponse = await dispatch(
|
||||||
bulkpostdata({ ProdDetails: newProductsData })
|
bulkpostdata({ UploadType: 'Purchase', ProdDetails: newProductsData })
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
if (bulkResponse?.data?.statusCode === 1) {
|
if (bulkResponse?.data?.statusCode === 1) {
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,11 @@
|
||||||
.extractor-table {
|
.extractor-table {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.extractor-table-red .ant-table-thead {
|
||||||
|
background-color: #ef4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-extractor-table {
|
.product-extractor-table {
|
||||||
|
|
@ -160,6 +165,48 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.not-added {
|
||||||
|
background-color: #ff4d4f;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #ff4d4f !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.added {
|
||||||
|
background-color: #52c41a;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-legend {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
font-family: 'Poppins';
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.legend-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-box {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
&.green {
|
||||||
|
background-color: #52c41a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.red {
|
||||||
|
background-color: #ff4d4f;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// .supplierInfo .ant-select-selector
|
// .supplierInfo .ant-select-selector
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue