Snaplocal / client /src /utils /invoiceGenerator.js
Kuruva Laxmi
Deployment preparation: Configure Vercel, API URLs, and CORS
f359e2d
Raw
History Blame Contribute Delete
6.74 kB
// Invoice Generator Utility
// Generates invoice data and creates downloadable PDF
export const generateInvoiceNumber = (count) => {
return `INV-2026-${String(count + 1).padStart(6, '0')}`;
};
export const createInvoiceData = (booking, photographer, customer) => {
const subtotal = booking.totalPrice || 0;
const taxRate = 18; // 18% GST
const tax = (subtotal * taxRate) / 100;
const totalAmount = subtotal + tax;
const items = [
{
description: `${photographer.firstName} ${photographer.lastName} - Photography Session`,
quantity: 1,
unitPrice: subtotal,
total: subtotal,
},
];
return {
issueDate: new Date(),
dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
items,
subtotal,
tax,
taxRate,
totalAmount,
photographerId: photographer._id,
customerId: customer._id,
bookingId: booking._id,
};
};
export const formatInvoiceForDisplay = (invoice) => {
return {
...invoice,
issueDate: new Date(invoice.issueDate).toLocaleDateString('en-IN', {
year: 'numeric',
month: 'long',
day: 'numeric',
}),
dueDate: new Date(invoice.dueDate).toLocaleDateString('en-IN', {
year: 'numeric',
month: 'long',
day: 'numeric',
}),
};
};
// Simple text-based invoice template (can be extended with jsPDF for actual PDF)
export const generateInvoiceHTML = (invoice, photographer, customer) => {
const formatted = formatInvoiceForDisplay(invoice);
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
.container { max-width: 800px; margin: 0 auto; border: 1px solid #ddd; padding: 40px; }
.header { text-align: center; margin-bottom: 40px; border-bottom: 2px solid #2563eb; padding-bottom: 20px; }
.header h1 { margin: 0; color: #2563eb; }
.header .tagline { color: #666; font-size: 14px; }
.info-row { display: flex; justify-content: space-between; margin: 20px 0; }
.info-section { flex: 1; }
.info-section h3 { margin: 0 0 10px 0; color: #333; }
.info-section p { margin: 5px 0; color: #666; }
table { width: 100%; border-collapse: collapse; margin: 30px 0; }
table thead { background: #f3f4f6; }
table th, table td { padding: 12px; text-align: left; border-bottom: 1px solid #e5e7eb; }
.total-row { background: #f3f4f6; font-weight: bold; }
.tax-row { background: #fff; }
.grand-total { background: #2563eb; color: white; font-weight: bold; }
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #e5e7eb; text-align: center; color: #666; font-size: 12px; }
.payment-status { margin: 20px 0; padding: 15px; background: #dbeafe; border-left: 4px solid #2563eb; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>INVOICE</h1>
<p class="tagline">SnapLocal - Professional Photography Services</p>
</div>
<div class="info-row">
<div class="info-section">
<h3>Invoice Details</h3>
<p><strong>Invoice #:</strong> ${invoice.invoiceNumber}</p>
<p><strong>Issue Date:</strong> ${formatted.issueDate}</p>
<p><strong>Due Date:</strong> ${formatted.dueDate}</p>
</div>
<div class="info-section">
<h3>From</h3>
<p><strong>${photographer.firstName} ${photographer.lastName}</strong></p>
<p>${photographer.bio || 'Professional Photographer'}</p>
<p>Email: ${photographer.email || 'N/A'}</p>
</div>
</div>
<div class="info-row">
<div class="info-section">
<h3>Bill To</h3>
<p><strong>${customer.firstName} ${customer.lastName}</strong></p>
<p>Email: ${customer.email}</p>
</div>
</div>
<table>
<thead>
<tr>
<th>Description</th>
<th>Quantity</th>
<th>Unit Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
${invoice.items
.map(
item =>
`<tr>
<td>${item.description}</td>
<td>${item.quantity}</td>
<td>₹${item.unitPrice.toFixed(2)}</td>
<td>₹${item.total.toFixed(2)}</td>
</tr>`
)
.join('')}
<tr class="total-row">
<td colspan="3" style="text-align: right;">Subtotal:</td>
<td>₹${invoice.subtotal.toFixed(2)}</td>
</tr>
<tr class="tax-row">
<td colspan="3" style="text-align: right;">Tax (${invoice.taxRate}%):</td>
<td>₹${invoice.tax.toFixed(2)}</td>
</tr>
${
invoice.discount > 0
? `<tr class="tax-row">
<td colspan="3" style="text-align: right;">Discount:</td>
<td>-₹${invoice.discount.toFixed(2)}</td>
</tr>`
: ''
}
<tr class="grand-total">
<td colspan="3" style="text-align: right;">TOTAL AMOUNT DUE:</td>
<td>₹${invoice.totalAmount.toFixed(2)}</td>
</tr>
</tbody>
</table>
<div class="payment-status">
<strong>Payment Status:</strong> ${invoice.paymentStatus.toUpperCase()}
${
invoice.paidAt
? `<p>Paid on ${new Date(invoice.paidAt).toLocaleDateString()}</p>`
: ''
}
</div>
<div class="footer">
<p>Thank you for choosing SnapLocal! This is an automatically generated invoice.</p>
<p>For support, contact us at support@snaplocal.com</p>
</div>
</div>
</body>
</html>
`;
};
export const downloadInvoiceAsPDF = async (invoice, photographer, customer) => {
// Simple approach: Generate HTML and open in new window for manual PDF save
const htmlContent = generateInvoiceHTML(invoice, photographer, customer);
const newWindow = window.open('', '', 'width=900,height=600');
newWindow.document.write(htmlContent);
newWindow.document.close();
newWindow.print();
};
export const downloadInvoiceAsJSON = (invoice) => {
const dataStr = JSON.stringify(invoice, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
link.download = `${invoice.invoiceNumber}.json`;
link.click();
};