Merge pull request 'product-receipt' (#148) from product-receipt into main

Reviewed-on: Pozomind/pozo-retail-app#148
This commit is contained in:
karthikalakshmi 2026-02-16 14:36:27 +05:30
commit 6f7e2f6cdd
5 changed files with 328 additions and 16 deletions

View File

@ -79,17 +79,23 @@ export const getRePrintReason = createAsyncThunk(
export const ReprintDetails = createAsyncThunk(
"addCustomer/ReprintDetails",
async (data) => {
const { AppId, CompId, BranchId, OrderFromDate, OrderToDate, OrderId } = data || {};
const { AppId, CompId, BranchId, OrderFromDate, OrderToDate, OrderId,PageNumber } = data || {};
if (OrderId && !OrderFromDate && !OrderToDate) {
return await axiosRetailInstanceData.get(
`/reprint?CompId=${CompId}&BranchId=${BranchId}&AppId=${AppId}&OrderId=${OrderId}&pageNumber=${data.PageNumber}&Type=${data.Type}`
);
}
if (OrderFromDate && OrderToDate) {
if (OrderFromDate && OrderToDate && PageNumber) {
return await axiosRetailInstanceData.get(
`/reprint?CompId=${CompId}&BranchId=${BranchId}&AppId=${AppId}&orderFromDate=${OrderFromDate}&orderToDate=${OrderToDate} &pageNumber=${data.PageNumber}&Type=${data.Type}`
);
} else {
}
else if(OrderFromDate && OrderToDate ) {
return await axiosRetailInstanceData.get(
`/reprint?CompId=${CompId}&BranchId=${BranchId}&AppId=${AppId}&orderFromDate=${OrderFromDate}&orderToDate=${OrderToDate}&Type=${data.Type}`
);
}
else {
return await axiosRetailInstanceData.get(
`/reprint?CompId=${CompId}&BranchId=${BranchId}&AppId=${AppId}&pageNumber=${data.PageNumber}&Type=${data.Type}`
);

View File

@ -105,11 +105,12 @@ export const postBulkConfiguration = createAsyncThunk(
export const SelfBookingEmailSendLink = createAsyncThunk(
'email/sendEmailLink',
async (EmailData) => {
const { toEmail, type, fileUrl} = EmailData;
const { toEmail, type, fileUrl,HeaderType} = EmailData;
const data = {
toEmail:toEmail,
Type: type,
fileUrl:fileUrl,
HeaderType:HeaderType
};
return await axiosEmail_SMSData.post('/SendUrlEmail', data);
}

View File

@ -11,6 +11,7 @@ import { PublicQrCodepost } from '../../../../Features/ConfigMasterPage/ConfigMa
import { renderToStaticMarkup } from 'react-dom/server';
const MainHomeUrl = import.meta.env.ENV_MAIN_BASE_URL;
const uploadApiUrl = import.meta.env.ENV_IMAGE_UPLOAD_API_URL;
// Create a secure blob URL creator that works in all environments
const createSecureBlobUrl = (blob) => {
@ -60,10 +61,10 @@ export const MobileMultiPdfPrint = async ({
dispatch,
UserId = '',
onProgress = () => {},
returnBlob = false,
}) => {
try {
const style = await PrintStyleFunction(printerTemplateStyle, 'Sales');
const stylesMap = {
'Style 1': 'PrintStyle1',
'Style 2': 'PrintStyle2',
@ -171,6 +172,15 @@ export const MobileMultiPdfPrint = async ({
}, 100);
}
// Return blob if requested (after download completes)
if (returnBlob && PrintComing === 'download') {
setPrintOrderDetails([]);
if (container) {
document.body.removeChild(container);
}
return { success: true, pdfBlob: combinedPdfBlob };
}
// if (PrintComing === "whatsapp") {
// const orderIds = PrintOrderDetails.map((row) => row?.OrderId).filter(Boolean);
// const documentUrl = `${MainHomeUrl}viewbill?code=${orderIds}`;

View File

@ -88,6 +88,8 @@ import { changeOrderOfferDetail } from '../../../../../Features/Offer/Offer.js';
import { ChangeFullFreeProductList, changeFullOfferAppliedProducts, changeLoyaltyConsumedQuantities } from '../../../../../Features/Offer/Offernew/BookingOffernew.js';
import { ChangeTotalAmount } from '../../../../../Features/ExteraCharges/ExtraCharges.js';
import { Radio } from 'antd';
import ReprintPDFDataShare from './ReprintPDFDataShare.jsx';
import { ImShare } from 'react-icons/im';
const RetailApiurl = import.meta.env.ENV_API_URL;
@ -116,7 +118,6 @@ function BSReprint({ setOpenModel = () => { } }) {
const [bulkMode, setBulkMode] = useState(false);
const [loading, setLoading] = useState(false);
const [reprintData, setReprintData] = useState([]);
console.log(reprintData, 'reprintData');
const [reprintReasonList, setReprintReasonList] = useState([]);
const [isDuplicate, setIsDuplicate] = useState('Original');
const [BillSearch, setBillSearch] = useState('');
@ -138,7 +139,7 @@ function BSReprint({ setOpenModel = () => { } }) {
SettingDataSelector?.[0]?.['SettingDtlDetails']?.find(
(item) => item.SettingIdName === 'Estimation'
)?.SettingValue === 'Y';
console.log(preferenceEstimate, 'preferenceEstimate');
console.log(preferenceEstimate, 'preferenceEstimate');
const editBill = SettingDataSelector?.[0]?.SettingDtlDetails?.find(
(setting) =>
setting?.SettingIdName?.toLowerCase() === 'editbill' &&
@ -167,9 +168,10 @@ function BSReprint({ setOpenModel = () => { } }) {
const SessionMobileNo = getSession('MobileNo');
const [dateStrings, setDateStrings] = useState([]);
const [type, setType] = useState("Sales");
const [showShareModal, setShowShareModal] = useState(false);
// ---- 1. Fetch Function ----
const fetchData = async ({ queryKey }) => {
const [_, dates, page,type] = queryKey;
const [_, dates, page, type] = queryKey;
const Response = await dispatch(
ReprintDetails({
CompId,
@ -178,7 +180,7 @@ function BSReprint({ setOpenModel = () => { } }) {
OrderFromDate: dates?.[0] ?? '',
OrderToDate: dates?.[1] ?? '',
PageNumber: page,
Type:type
Type: type
})
).unwrap();
if (Response?.data?.statusCode === 1) {
@ -189,7 +191,7 @@ function BSReprint({ setOpenModel = () => { } }) {
};
const { data, isLoading, error } = useQuery({
queryKey: ['reprint', dateStrings, page,type],
queryKey: ['reprint', dateStrings, page, type],
queryFn: fetchData,
enabled: true,
staleTime: 1000 * 60 * 5,
@ -250,7 +252,7 @@ function BSReprint({ setOpenModel = () => { } }) {
};
commonMasterApiCall();
}, []);
useEffect(() => {
useEffect(() => {
DefaultReprintReason();
}, []);
useEffect(() => {
@ -1193,7 +1195,6 @@ function BSReprint({ setOpenModel = () => { } }) {
const mutiplePrint = async (printType) => {
setIsProcessing(true);
setProcessingProgress(0);
const result = await MobileMultiPdfPrint({
printerTemplateStyle,
printDatas,
@ -1406,7 +1407,7 @@ function BSReprint({ setOpenModel = () => { } }) {
}
}
const typeOptions = [
const typeOptions = [
{ label: "Sales", value: "Sales" },
{ label: "Estimate", value: "Est" },
];
@ -1490,7 +1491,9 @@ function BSReprint({ setOpenModel = () => { } }) {
Reprintformdata();
}}
/>
<div>
<ImShare fontSize={22} onClick={() => setShowShareModal(true)} style={{ cursor: 'pointer' }} />
</div>
<div>
{selectedRows.length > 0 && (
<div className="action-icons-reprint">
@ -1503,6 +1506,9 @@ function BSReprint({ setOpenModel = () => { } }) {
onClick={() => mutiplePrint('whatsapp')}
className="icon-whatsapp"
/>
{/* <ReprintPDFDataShare
printData={selectedRows}
/> */}
</div>
)}
</div>
@ -1848,6 +1854,14 @@ function BSReprint({ setOpenModel = () => { } }) {
/>
</div>
))}
{ showShareModal && <ReprintPDFDataShare
printerTemplateStyle={printerTemplateStyle}
printDatas={printDatas}
SettingDataSelector={SettingDataSelector}
open={showShareModal}
onClose={() => setShowShareModal(false)}
/>}
</>
);
}

View File

@ -0,0 +1,281 @@
import { FaDownload, FaWhatsapp } from "react-icons/fa";
import { extractLastNumberOrderId, getSession } from "../../../../../Services/Others";
import { useDispatch } from "react-redux";
import { useCallback, useState } from "react";
import { DatePicker, Modal } from "antd";
import { ReprintDetails } from "../../../../../Features/BookingScreen/Customer/addCustomer";
import Buttons from "../../../../../Components/Forms/Buttons";
import { MobileMultiPdfPrint, PdfProcessingProgress } from "../../BookingFunctionality/MobileMultiPdfPrint";
import PaymentPdfBooking from "../../../../paymentpdfPage/PaymentPdfBooking";
import { MdEmail } from "react-icons/md";
import { uploadImage } from "../../../../../Features/upload/upload";
import { SelfBookingEmailSendLink } from "../../../../../Features/ConfigMasterPage/ConfigMasterPage";
import { Messages } from "../../../../../Components/Notifications/Messages";
const { RangePicker } = DatePicker;
const uploadApiUrl = import.meta.env.ENV_IMAGE_UPLOAD_API_URL;
const ReprintPDFDataShare = ({ printDatas, printerTemplateStyle, SettingDataSelector, open = false, onClose = () => { } }) => {
const dispatch = useDispatch();
const CompId = getSession('CompId');
const BranchId = getSession('BranchId');
const AppId = getSession('AppId');
const UserId = getSession('UserId');
const [dateStrings, setDateStrings] = useState([]);
const [type, setType] = useState("Sales");
const [reprintData, setReprintData] = useState();
const [isProcessing, setIsProcessing] = useState(false);
const [processingProgress, setProcessingProgress] = useState(0);
const [isDuplicate, setIsDuplicate] = useState('Original');
const [emailId, setEmailId] = useState('');
const [isEmail, setIsEmail] = useState(false);
const [pdfUrl, setPdfUrl] = useState(false);
const [messageType, setMessageType] = useState(null);
const [messageData, setMessageData] = useState(null);
const [isFetching, setIsFetching] = useState(false);
const handleDateChange = (dateStrings) => {
if (dateStrings[0] && dateStrings[1]) {
setDateStrings(dateStrings);
fetchData(dateStrings);
}
};
const fetchData = async (dates = dateStrings) => {
setIsFetching(true);
try {
const Response = await dispatch(ReprintDetails({
CompId,
BranchId,
AppId,
OrderFromDate: dates?.[0] ?? '',
OrderToDate: dates?.[1] ?? '',
Type: type
})).unwrap();
if (Response?.data?.statusCode === 1) {
setReprintData(Response?.data?.data);
} else {
setReprintData(null);
}
} finally {
setIsFetching(false);
}
};
const pdfPast = async () => {
if (printerTemplateStyle !== "TaxInvoice") {
setMessageType("error");
setMessageData("Please select invoice print style");
return;
}
if (!reprintData || reprintData.length === 0) {
return;
}
try {
setIsProcessing(true);
setProcessingProgress(0);
const result = await MobileMultiPdfPrint({
printerTemplateStyle,
printDatas,
PrintOrderDetails: reprintData,
setPrintOrderDetails: setReprintData,
PrintComing: 'download',
UserId: UserId,
dispatch,
onProgress: (progress) => setProcessingProgress(progress),
returnBlob: true
});
if (result?.pdfBlob) {
const file = new File([result.pdfBlob], 'invoices.pdf', { type: 'application/pdf' });
const uploadImgData = await dispatch(uploadImage(file)).unwrap();
if (uploadImgData?.data?.status) {
const imageUrl = uploadImgData?.data?.image;
const urlObj = new URL(imageUrl);
const fileName = urlObj.searchParams.get("fileId");
const downloadUrl = `${uploadApiUrl}/upload/DownloadFile?fileId=${fileName}`;
setPdfUrl(downloadUrl);
} else {
setMessageType("error");
setMessageData("Failed to upload PDF");
}
}
} catch (error) {
setMessageType("error");
setMessageData("Error processing PDF");
} finally {
setIsProcessing(false);
}
};
const handleDownload = () => {
if (pdfUrl) {
window.open(pdfUrl, '_blank');
}
};
const SendToEmailLink = async () => {
if (!emailId) {
setMessageType("error");
setMessageData("Please enter a recipient email.");
return;
}
const data = {
toEmail: emailId,
type: 'M',
HeaderType:'invoice attached',
fileUrl: pdfUrl,
};
try {
const res = await dispatch(SelfBookingEmailSendLink(data)).unwrap();
setMessageType("success");
setMessageData(res.data?.response);
clearAndClose();
} catch (err) {
console.error("Error sending Email:", err);
setMessageType("error");
setMessageData("Failed to send email. Please try again.");
}
};
const handleWhatsAppClick = () => {
window.open(whatsappShareUrl, '_blank');
clearAndClose();
};
const clearAndClose = () => {
setDateStrings([]);
setReprintData(null);
setPdfUrl(false);
setEmailId('');
setIsEmail(false);
onClose();
setDateStrings([]);
};
const message = `
Please find your complete bill invoice attached in the link below:
${pdfUrl}
Kindly review the details and keep this invoice for your records.
Thank you for choosing us!`;
const whatsappShareUrl = `https://wa.me/?text=${encodeURIComponent(message)}`;
const onComplete = useCallback(() => {
setMessageData(null);
setMessageType(null);
}, []);
return (
<div>
<Messages
messageType={messageType}
messageData={messageData}
onComplete={onComplete}
/>
<Modal
title="Invoice Send"
open={open}
onCancel={onClose}
footer={null}
>
<>
<div style={{ display: 'flex', gap: '1rem', alignItems: "center" }}>
<div>
<RangePicker
placeholder={['Start Date', 'End Date']}
format="YYYY-MM-DD"
onChange={handleDateChange}
/>
</div>
<div>
<Buttons
buttonText="Invoice Pdf"
color="901D77"
handleSubmit={pdfPast}
disabled={!reprintData || reprintData.length === 0 || isFetching}
/>
</div>
</div>
{isEmail && <div style={{ display: 'flex', gap: '1rem', alignItems: "center", marginTop: '1rem' }}>
<input
type="email"
placeholder="Enter email address"
value={emailId}
onChange={(e) => setEmailId(e.target.value)}
style={{ flex: 1, padding: '8px', borderRadius: '4px', border: '1px solid #d9d9d9' }}
/>
<Buttons
buttonText="Send Email"
color="901D77"
handleSubmit={() => SendToEmailLink()}
/>
</div>}
{pdfUrl && (
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '10px',
margin: '20px 0px'
}}
>
<div className="action-icons-reprint">
<FaDownload
onClick={handleDownload}
className="icon-download"
style={{ cursor: 'pointer' }}
/>
<MdEmail onClick={() => setIsEmail(prev => !prev)} className="icon-email" style={{ cursor: 'pointer' }} />
<FaWhatsapp
onClick={handleWhatsAppClick}
className="icon-whatsapp"
style={{ cursor: 'pointer' }}
/>
</div>
</div>
)}
<PdfProcessingProgress
progress={processingProgress}
isProcessing={isProcessing}
/>
</>
</Modal>
<div style={{ display: 'none' }}>
{reprintData?.map((orderDetail, index) => (
<div key={index} id={`RePrint-${index}`}>
<PaymentPdfBooking
index={index}
table2Data={orderDetail}
singleData2={orderDetail?.productDetails}
ReprintType={isDuplicate}
orderId={
orderDetail?.OrderId &&
extractLastNumberOrderId(
orderDetail?.OrderId,
orderDetail?.FYStatus
)
}
CreatedDate={orderDetail?.CreatedDate}
PaymentStatus={orderDetail?.OrderPaymentDtl}
Preference={SettingDataSelector}
printDatas={printDatas}
/>
</div>
))}
</div>
</div>
)
}
export default ReprintPDFDataShare;