fetch product images from Shopify API and add to order data for invoice generation
This commit is contained in:
parent
46713fc66b
commit
8281f95c71
@ -222,9 +222,9 @@ const generateInvoicePDF = async (orderData) => {
|
|||||||
for (let i = 0; i < orderData.line_items.length; i++) {
|
for (let i = 0; i < orderData.line_items.length; i++) {
|
||||||
const item = orderData.line_items[i];
|
const item = orderData.line_items[i];
|
||||||
|
|
||||||
// Background color for alternating rows
|
// Background color for alternating rows (slightly taller: 46 height)
|
||||||
if (i % 2 === 1) {
|
if (i % 2 === 1) {
|
||||||
doc.rect(40, tableY - 4, 515, 38).fill(rowAltColor);
|
doc.rect(40, tableY - 6, 515, 46).fill(rowAltColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.fontSize(9)
|
doc.fontSize(9)
|
||||||
@ -232,41 +232,57 @@ const generateInvoicePDF = async (orderData) => {
|
|||||||
.fillColor(textColor);
|
.fillColor(textColor);
|
||||||
|
|
||||||
// S.No
|
// S.No
|
||||||
doc.text((i + 1).toString(), 40, tableY);
|
doc.text((i + 1).toString(), 40, tableY + 10);
|
||||||
|
|
||||||
|
// Handle product image
|
||||||
|
let textX = 85;
|
||||||
|
let textWidth = 270;
|
||||||
|
if (item.image_url) {
|
||||||
|
const imgBuffer = await fetchImageBuffer(item.image_url);
|
||||||
|
if (imgBuffer) {
|
||||||
|
try {
|
||||||
|
doc.image(imgBuffer, 85, tableY - 2, { fit: [35, 35] });
|
||||||
|
textX = 130;
|
||||||
|
textWidth = 225;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error rendering line item image:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Product Name and Variant/Quantity details
|
// Product Name and Variant/Quantity details
|
||||||
const productText = item.title || item.name;
|
const productText = item.title || item.name;
|
||||||
doc.text(productText, 85, tableY, { width: 270, height: 15, ellipsis: true });
|
doc.text(productText, textX, tableY, { width: textWidth, height: 15, ellipsis: true });
|
||||||
|
|
||||||
// Display quantity/variant info if available
|
// Display quantity/variant info if available
|
||||||
if (item.variant_title) {
|
if (item.variant_title) {
|
||||||
doc.fontSize(8)
|
doc.fontSize(8)
|
||||||
.fillColor('#64748b')
|
.fillColor('#64748b')
|
||||||
.text(item.variant_title, 85, tableY + 12, { width: 270 });
|
.text(item.variant_title, textX, tableY + 14, { width: textWidth });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset font size
|
// Reset font size
|
||||||
doc.fontSize(9).fillColor(textColor);
|
doc.fontSize(9).fillColor(textColor);
|
||||||
|
|
||||||
// Quantity
|
// Quantity
|
||||||
doc.text(item.quantity.toString(), 365, tableY, { width: 50, align: 'right' });
|
doc.text(item.quantity.toString(), 365, tableY + 10, { width: 50, align: 'right' });
|
||||||
|
|
||||||
// Unit Price
|
// Unit Price
|
||||||
const unitPrice = parseFloat(item.price).toFixed(2);
|
const unitPrice = parseFloat(item.price).toFixed(2);
|
||||||
doc.text(`${currencySymbol}${unitPrice}`, 425, tableY, { width: 60, align: 'right' });
|
doc.text(`${currencySymbol}${unitPrice}`, 425, tableY + 10, { width: 60, align: 'right' });
|
||||||
|
|
||||||
// Total Price
|
// Total Price
|
||||||
const totalPrice = (parseFloat(item.price) * item.quantity).toFixed(2);
|
const totalPrice = (parseFloat(item.price) * item.quantity).toFixed(2);
|
||||||
doc.text(`${currencySymbol}${totalPrice}`, 495, tableY, { width: 60, align: 'right' });
|
doc.text(`${currencySymbol}${totalPrice}`, 495, tableY + 10, { width: 60, align: 'right' });
|
||||||
|
|
||||||
// Divider Line
|
// Divider Line
|
||||||
doc.moveTo(40, tableY + 28)
|
doc.moveTo(40, tableY + 38)
|
||||||
.lineTo(555, tableY + 28)
|
.lineTo(555, tableY + 38)
|
||||||
.lineWidth(0.5)
|
.lineWidth(0.5)
|
||||||
.strokeColor('#f1f5f9')
|
.strokeColor('#f1f5f9')
|
||||||
.stroke();
|
.stroke();
|
||||||
|
|
||||||
tableY += 34;
|
tableY += 44;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
37
server.js
37
server.js
@ -52,6 +52,43 @@ app.post('/webhooks/orders/create', verifyShopifyWebhook, async (req, res) => {
|
|||||||
const processMessage = `[${new Date().toISOString()}] Processing Order ${orderData.order_number}\n`;
|
const processMessage = `[${new Date().toISOString()}] Processing Order ${orderData.order_number}\n`;
|
||||||
fs.appendFileSync('webhook.log', processMessage);
|
fs.appendFileSync('webhook.log', processMessage);
|
||||||
|
|
||||||
|
// Fetch product images from Shopify Admin API (requires read_products scope)
|
||||||
|
if (orderData.line_items && orderData.line_items.length > 0) {
|
||||||
|
await Promise.all(orderData.line_items.map(async (item) => {
|
||||||
|
if (!item.product_id) return;
|
||||||
|
try {
|
||||||
|
const shopifyUrl = `https://${process.env.SHOPIFY_STORE_DOMAIN}/admin/api/2024-01/products/${item.product_id}.json`;
|
||||||
|
const response = await fetch(shopifyUrl, {
|
||||||
|
headers: {
|
||||||
|
'X-Shopify-Access-Token': process.env.SHOPIFY_ADMIN_API_TOKEN
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
const product = data.product;
|
||||||
|
if (product) {
|
||||||
|
// Find variant-specific image
|
||||||
|
let imgUrl = null;
|
||||||
|
if (item.variant_id && product.variants) {
|
||||||
|
const variant = product.variants.find(v => v.id === item.variant_id);
|
||||||
|
if (variant && variant.image_id && product.images) {
|
||||||
|
const variantImg = product.images.find(img => img.id === variant.image_id);
|
||||||
|
if (variantImg) imgUrl = variantImg.src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback to main product image
|
||||||
|
item.image_url = imgUrl || product.image?.src || (product.images && product.images[0]?.src) || null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const errText = await response.text();
|
||||||
|
console.error(`Shopify API error fetching product ${item.product_id}: Status ${response.status} - ${errText}`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to fetch image for product ${item.product_id}:`, err);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Generate the PDF invoice
|
// 1. Generate the PDF invoice
|
||||||
const pdfBuffer = await generateInvoicePDF(orderData);
|
const pdfBuffer = await generateInvoicePDF(orderData);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user