pattanshetti / src /lib /invoiceUtils.ts
triflix's picture
Upload 99 files
4be2b2b verified
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
/**
* Export invoice element as high-quality image
*/
export async function exportInvoiceAsImage(
element: HTMLElement,
options?: { scale?: number; format?: 'png' | 'jpeg'; quality?: number }
): Promise<string> {
const { scale = 3, format = 'png', quality = 0.95 } = options || {};
const canvas = await html2canvas(element, {
scale, // Higher quality for better readability
useCORS: true,
backgroundColor: '#ffffff',
logging: false,
windowWidth: element.scrollWidth,
windowHeight: element.scrollHeight,
});
const imageFormat = format === 'jpeg' ? 'image/jpeg' : 'image/png';
return canvas.toDataURL(imageFormat, quality);
}
/**
* Downloads the invoice as an image file
* @param base64Image - Base64 encoded image string
* @param filename - Name of the file to download
*/
export function downloadInvoiceImage(base64Image: string, filename: string = 'invoice.png') {
const link = document.createElement('a');
link.href = base64Image;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
/**
* Shares invoice via WhatsApp Web
* @param phoneNumber - Customer's phone number (with country code, e.g., 919876543210)
* @param billNumber - Invoice/Bill number
* @param amount - Total amount
* @param base64Image - Base64 encoded image (optional, for future file sharing)
*/
export function shareInvoiceViaWhatsApp(
phoneNumber: string,
billNumber: string,
amount: number,
language: 'en' | 'mr' = 'mr'
) {
// Clean phone number (remove spaces, hyphens, etc.)
const cleanedNumber = phoneNumber.replace(/[^\d+]/g, '');
// Ensure country code is present
const formattedNumber = cleanedNumber.startsWith('+') || cleanedNumber.startsWith('91')
? cleanedNumber
: `91${cleanedNumber}`;
// Create message text based on language
const message = language === 'mr'
? `नमस्कार,\n\nतुमचे बिल नंबर *${billNumber}* तयार झाले आहे.\n\nएकूण रक्कम: ₹${amount.toLocaleString('en-IN')}\n\nकृपया रक्कम वेळेवर भरावी.\n\nधन्यवाद!`
: `Hello,\n\nYour bill number *${billNumber}* is ready.\n\nTotal Amount: ₹${amount.toLocaleString('en-IN')}\n\nPlease pay on time.\n\nThank you!`;
// WhatsApp Web/API URL
const whatsappUrl = `https://wa.me/${formattedNumber}?text=${encodeURIComponent(message)}`;
// Open WhatsApp
window.open(whatsappUrl, '_blank');
}
/**
* Shares invoice image via WhatsApp using the device's native share (mobile)
* @param base64Image - Base64 encoded image
* @param billNumber - Invoice number
* @param partyName - Customer name
*/
export async function shareInvoiceImageNative(
base64Image: string,
billNumber: string,
partyName: string
) {
try {
// Convert base64 to blob
const response = await fetch(base64Image);
const blob = await response.blob();
// Create a file from blob
const file = new File([blob], `Invoice_${billNumber}.png`, { type: 'image/png' });
// Check if Web Share API is supported
if (navigator.share && navigator.canShare({ files: [file] })) {
await navigator.share({
title: `Invoice ${billNumber}`,
text: `Invoice for ${partyName}`,
files: [file],
});
return true;
} else {
// Fallback: Download the image
downloadInvoiceImage(base64Image, `Invoice_${billNumber}_${partyName}.png`);
return false;
}
} catch (error) {
console.error('Error sharing invoice:', error);
// Fallback: Download the image
downloadInvoiceImage(base64Image, `Invoice_${billNumber}_${partyName}.png`);
return false;
}
}
/**
* Formats phone number for WhatsApp
* @param phone - Phone number in any format
* @returns Formatted phone number with country code
*/
export function formatPhoneForWhatsApp(phone: string): string {
const cleaned = phone.replace(/[^\d+]/g, '');
if (cleaned.startsWith('+91')) {
return cleaned;
} else if (cleaned.startsWith('91') && cleaned.length === 12) {
return `+${cleaned}`;
} else if (cleaned.length === 10) {
return `+91${cleaned}`;
}
return cleaned;
}
/**
* Validates Indian phone number
* @param phone - Phone number to validate
* @returns true if valid
*/
export function isValidIndianPhoneNumber(phone: string): boolean {
const cleaned = phone.replace(/[^\d]/g, '');
// Check if it's a 10-digit number starting with 6-9
if (cleaned.length === 10 && /^[6-9]\d{9}$/.test(cleaned)) {
return true;
}
// Check if it's a 12-digit number starting with 91
if (cleaned.length === 12 && cleaned.startsWith('91') && /^91[6-9]\d{9}$/.test(cleaned)) {
return true;
}
return false;
}
/**
* Export invoice as PDF
*/
export async function exportInvoiceAsPDF(
element: HTMLElement,
filename: string = 'invoice.pdf',
options?: { orientation?: 'portrait' | 'landscape'; format?: 'a4' | 'letter' }
): Promise<void> {
const { orientation = 'portrait', format = 'a4' } = options || {};
// Generate high-quality image first
const imageData = await exportInvoiceAsImage(element, { scale: 3, format: 'jpeg', quality: 0.95 });
// Create PDF
const pdf = new jsPDF({
orientation,
unit: 'mm',
format,
});
// Calculate dimensions to fit page
const imgProps = pdf.getImageProperties(imageData);
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
// Add image to PDF
pdf.addImage(imageData, 'JPEG', 0, 0, pdfWidth, pdfHeight);
// Save PDF
pdf.save(filename);
}
/**
* Generate invoice text for WhatsApp or SMS
*/
export function generateInvoiceText(
billNumber: string,
partyName: string,
items: Array<{ name: string; quantity: number; rate: number; amount: number }>,
charges: { bardhana?: number; hamali?: number; adhath?: number; cess?: number; gaadiBharni?: number },
total: number,
language: 'en' | 'mr' = 'mr'
): string {
if (language === 'mr') {
let text = `🧾 *बिल नंबर:* ${billNumber}\n`;
text += `👤 *पार्टी:* ${partyName}\n`;
text += `📅 *तारीख:* ${new Date().toLocaleDateString('mr-IN')}\n\n`;
text += `*📦 माल तपशील:*\n`;
text += `${'─'.repeat(35)}\n`;
items.forEach((item, i) => {
text += `${i + 1}. ${item.name}\n`;
text += ` ${item.quantity} पोत्या × ₹${item.rate} = ₹${item.amount.toLocaleString('en-IN')}\n`;
});
text += `${'─'.repeat(35)}\n`;
// Add charges if any
const totalCharges = (charges.bardhana || 0) + (charges.hamali || 0) +
(charges.adhath || 0) + (charges.cess || 0) + (charges.gaadiBharni || 0);
if (totalCharges > 0) {
text += `\n*💰 खर्च:*\n`;
if (charges.bardhana) text += ` बर्धाना: ₹${charges.bardhana.toLocaleString('en-IN')}\n`;
if (charges.hamali) text += ` हमाली: ₹${charges.hamali.toLocaleString('en-IN')}\n`;
if (charges.adhath) text += ` अडत: ₹${charges.adhath.toLocaleString('en-IN')}\n`;
if (charges.cess) text += ` सेस: ₹${charges.cess.toLocaleString('en-IN')}\n`;
if (charges.gaadiBharni) text += ` गाडी भरणी: ₹${charges.gaadiBharni.toLocaleString('en-IN')}\n`;
text += `${'─'.repeat(35)}\n`;
}
text += `\n*💵 एकूण रक्कम: ₹${total.toLocaleString('en-IN')}*\n\n`;
text += `कृपया रक्कम वेळेवर भरावी.\n\n`;
text += `धन्यवाद! 🙏`;
return text;
} else {
let text = `🧾 *Bill No:* ${billNumber}\n`;
text += `👤 *Party:* ${partyName}\n`;
text += `📅 *Date:* ${new Date().toLocaleDateString('en-IN')}\n\n`;
text += `*📦 Items:*\n`;
text += `${'─'.repeat(35)}\n`;
items.forEach((item, i) => {
text += `${i + 1}. ${item.name}\n`;
text += ` ${item.quantity} bags × ₹${item.rate} = ₹${item.amount.toLocaleString('en-IN')}\n`;
});
text += `${'─'.repeat(35)}\n`;
const totalCharges = (charges.bardhana || 0) + (charges.hamali || 0) +
(charges.adhath || 0) + (charges.cess || 0) + (charges.gaadiBharni || 0);
if (totalCharges > 0) {
text += `\n*💰 Charges:*\n`;
if (charges.bardhana) text += ` Bardhana: ₹${charges.bardhana.toLocaleString('en-IN')}\n`;
if (charges.hamali) text += ` Hamali: ₹${charges.hamali.toLocaleString('en-IN')}\n`;
if (charges.adhath) text += ` Commission: ₹${charges.adhath.toLocaleString('en-IN')}\n`;
if (charges.cess) text += ` Cess: ₹${charges.cess.toLocaleString('en-IN')}\n`;
if (charges.gaadiBharni) text += ` Loading: ₹${charges.gaadiBharni.toLocaleString('en-IN')}\n`;
text += `${'─'.repeat(35)}\n`;
}
text += `\n*💵 Total Amount: ₹${total.toLocaleString('en-IN')}*\n\n`;
text += `Please pay on time.\n\n`;
text += `Thank you! 🙏`;
return text;
}
}
/**
* Share detailed invoice text via WhatsApp
*/
export function shareDetailedInvoiceText(
phoneNumber: string,
invoiceText: string
): void {
const formattedPhone = formatPhoneForWhatsApp(phoneNumber);
const encodedText = encodeURIComponent(invoiceText);
const whatsappUrl = `https://wa.me/${formattedPhone}?text=${encodedText}`;
window.open(whatsappUrl, '_blank');
}
/**
* Prints the invoice
* @param element - The invoice element to print
*/
export function printInvoice(element?: HTMLElement) {
if (element) {
// Create a new window with just the invoice content
const printWindow = window.open('', '_blank');
if (printWindow) {
printWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<title>Print Invoice</title>
<style>
body { margin: 0; padding: 20px; font-family: Arial, sans-serif; }
@media print {
body { margin: 0; }
@page { margin: 10mm; }
}
</style>
</head>
<body>
${element.innerHTML}
</body>
</html>
`);
printWindow.document.close();
setTimeout(() => {
printWindow.print();
printWindow.close();
}, 250);
}
} else {
// Default browser print
window.print();
}
}