diff --git a/src/modules/grn/grn.service.js b/src/modules/grn/grn.service.js index 95e162b..dee3333 100644 --- a/src/modules/grn/grn.service.js +++ b/src/modules/grn/grn.service.js @@ -174,30 +174,22 @@ const buildVendorAddressLine = (address) => { const buildGrnPdfPayload = (grn, company) => { const vendorAddress = pickVendorAddress(grn.vendor?.vendor_addresses || []); - const items = (grn.items || []).map((line) => { - const acceptedQty = toNum(line.accepted_qty); - const rejectedQty = toNum(line.rejected_qty); - const rate = toNum(line.rate); - return { - line_no: line.line_no, - item_name: line.item?.item_name || '-', - item_code: line.item?.item_code || null, - remarks: line.remarks || line.rejection_reason || null, - uom: line.uom?.code || line.uom?.name || '-', - ordered_qty: toNum(line.ordered_qty), - current_qty: toNum(line.current_qty), - accepted_qty: acceptedQty, - rejected_qty: rejectedQty, - rate, - line_total: acceptedQty * rate, - }; - }); - - const acceptedValue = items.reduce((sum, line) => sum + toNum(line.line_total), 0); - const rejectedValue = items.reduce( - (sum, line) => sum + toNum(line.rejected_qty) * toNum(line.rate), - 0 - ); + const items = (grn.items || []).map((line) => ({ + line_no: line.line_no, + item_name: line.item?.item_name || '-', + item_code: line.item?.item_code || null, + uom: line.uom?.code || line.uom?.name || '-', + ordered_qty: toNum(line.ordered_qty), + current_qty: toNum(line.current_qty), + accepted_qty: toNum(line.accepted_qty), + rejected_qty: toNum(line.rejected_qty), + batch_no: line.batch_no || null, + mfg_date: line.mfg_date || null, + expiry_date: line.expiry_date || null, + rejection_reason: line.rejection_reason || null, + storage_location: line.storage_location || null, + remarks: line.remarks || null, + })); return { company: { @@ -211,8 +203,6 @@ const buildGrnPdfPayload = (grn, company) => { po_number: grn.purchase_order?.po_number || '-', vendor_invoice_no: grn.vendor_invoice_no || null, vendor_invoice_date: grn.vendor_invoice_date || null, - vendor_invoice_amount: - grn.vendor_invoice_amount != null ? toNum(grn.vendor_invoice_amount) : null, vehicle_no: grn.vehicle_no || null, lr_no: grn.lr_no || null, lr_date: grn.lr_date || null, @@ -236,11 +226,6 @@ const buildGrnPdfPayload = (grn, company) => { pincode: grn.warehouse?.pincode || '', }, items, - totals: { - accepted_value: acceptedValue, - rejected_value: rejectedValue, - grand_total: acceptedValue, - }, generated_at: `${formatDate(new Date(), { style: 'datetime' })} IST`, }; }; diff --git a/src/utils/pdf/templates/grn.template.js b/src/utils/pdf/templates/grn.template.js index 32e5e64..8b03ad2 100644 --- a/src/utils/pdf/templates/grn.template.js +++ b/src/utils/pdf/templates/grn.template.js @@ -1,6 +1,4 @@ -const { formatCurrency } = require('../helpers/formatCurrency'); const { formatDate } = require('../helpers/formatDate'); -const { numberToWords } = require('../helpers/numberToWords'); const getDummyGrnData = () => ({ company: { @@ -20,7 +18,6 @@ const getDummyGrnData = () => ({ po_number: 'PO/2026-27/00007', vendor_invoice_no: 'TSL/INV/2026/441', vendor_invoice_date: '2026-07-13', - vendor_invoice_amount: 139943.75, vehicle_no: 'TN-09-AB-4521', lr_no: 'LR-77821', lr_date: '2026-07-13', @@ -53,8 +50,12 @@ const getDummyGrnData = () => ({ current_qty: 25, accepted_qty: 25, rejected_qty: 0, - rate: 4250, - line_total: 106250, + batch_no: 'BAT-TMT-2407', + mfg_date: '2026-06-01', + expiry_date: null, + rejection_reason: null, + storage_location: 'Bay A-12', + remarks: null, }, { line_no: 2, @@ -65,28 +66,30 @@ const getDummyGrnData = () => ({ current_qty: 500, accepted_qty: 500, rejected_qty: 0, - rate: 380, - line_total: 190000, + batch_no: 'CEM-JUN-26', + mfg_date: '2026-05-15', + expiry_date: '2027-05-15', + rejection_reason: null, + storage_location: 'Shed B', + remarks: null, }, { line_no: 3, item_name: 'River sand', item_code: 'ITM-SND-01', - remarks: '2 bags damaged in transit', uom: 'CFT', ordered_qty: 1200, current_qty: 1200, accepted_qty: 1180, rejected_qty: 20, - rate: 45, - line_total: 53100, + batch_no: null, + mfg_date: null, + expiry_date: null, + rejection_reason: 'Damaged bags in transit', + storage_location: 'Yard 2', + remarks: 'Confirm bag count on delivery', }, ], - totals: { - accepted_value: 349350, - rejected_value: 900, - grand_total: 349350, - }, generated_at: '14/07/2026 09:40', }); @@ -116,19 +119,26 @@ const qtyDisplay = (value) => { return Number.isInteger(num) ? String(num) : num.toFixed(2); }; +const displayOrDash = (value) => { + if (value == null || String(value).trim() === '') return '-'; + return String(value); +}; + const generateGrnHtml = (inputData = getDummyGrnData()) => { const data = inputData || getDummyGrnData(); const company = data.company || {}; const grn = data.grn || {}; const vendor = data.vendor || {}; const warehouse = data.warehouse || {}; - const totals = data.totals || {}; const itemsRows = (data.items || []) .map((item, index) => { const code = item.item_code ? `
${escapeHtml(item.item_code)}
` : ''; + const storage = item.storage_location + ? `
Location: ${escapeHtml(item.storage_location)}
` + : ''; const remarks = item.remarks ? `
*${escapeHtml(item.remarks)}
` : ''; @@ -138,6 +148,7 @@ const generateGrnHtml = (inputData = getDummyGrnData()) => {
${escapeHtml(item.item_name || '-')}
${code} + ${storage} ${remarks} ${qtyDisplay(item.ordered_qty)} @@ -145,8 +156,10 @@ const generateGrnHtml = (inputData = getDummyGrnData()) => { ${qtyDisplay(item.accepted_qty)} ${qtyDisplay(item.rejected_qty)} ${escapeHtml(item.uom || '-')} - ${formatCurrency(item.rate)} - ${formatCurrency(item.line_total)} + ${escapeHtml(displayOrDash(item.batch_no))} + ${formatDate(item.mfg_date, { style: 'numeric' })} + ${formatDate(item.expiry_date, { style: 'numeric' })} + ${escapeHtml(displayOrDash(item.rejection_reason))} `; }) @@ -288,8 +301,8 @@ const generateGrnHtml = (inputData = getDummyGrnData()) => { padding: 8px 3px; border-bottom: 1.5px solid #111111; color: #9a9a9a; - font-size: 8.5px; - letter-spacing: 0.06em; + font-size: 8px; + letter-spacing: 0.05em; text-transform: uppercase; font-weight: 600; text-align: left; @@ -301,14 +314,15 @@ const generateGrnHtml = (inputData = getDummyGrnData()) => { padding: 9px 3px; border-bottom: 1px solid #e5e5e5; vertical-align: top; - font-size: 10.5px; + font-size: 10px; } - .col-no { width: 4%; color: #666666; } - .col-item { width: 28%; } - .col-qty { width: 8%; } - .col-uom { width: 6%; } - .col-rate { width: 11%; } - .col-amt { width: 13%; } + .col-no { width: 3%; color: #666666; } + .col-item { width: 20%; } + .col-qty { width: 7%; } + .col-uom { width: 5%; } + .col-batch { width: 10%; } + .col-date { width: 9%; } + .col-reason { width: 14%; } .item-name { font-weight: 600; } .item-code { margin-top: 1px; @@ -318,49 +332,10 @@ const generateGrnHtml = (inputData = getDummyGrnData()) => { .item-note { margin-top: 2px; color: #888888; - font-size: 9.5px; + font-size: 9px; font-style: italic; } - .summary-wrap { - margin-top: 16px; - display: flex; - justify-content: flex-end; - } - .summary { width: 280px; } - .summary-row { - display: flex; - justify-content: space-between; - gap: 24px; - padding: 3px 0; - color: #444444; - font-size: 11px; - } - .summary-row .amount { color: #111111; font-weight: 600; } - .grand-row { - display: flex; - justify-content: space-between; - gap: 24px; - margin-top: 6px; - padding: 8px 0; - border-top: 1.5px solid #111111; - border-bottom: 1.5px solid #111111; - font-size: 14px; - font-weight: 800; - } - .amount-words { - margin-top: 10px; - border: 1px solid #cfcfcf; - background: #f7f7f7; - padding: 8px 10px; - } - .amount-words .words { - margin-top: 2px; - font-weight: 700; - font-size: 11px; - line-height: 1.4; - } - .notes { margin-top: 18px; } .notes-block { margin-bottom: 12px; } .notes-block .body { @@ -482,8 +457,10 @@ const generateGrnHtml = (inputData = getDummyGrnData()) => { Accepted Rejected UOM - Rate - Amount + Batch + Mfg date + Expiry + Reason @@ -491,35 +468,6 @@ const generateGrnHtml = (inputData = getDummyGrnData()) => { -
-
-
- Accepted value - ${formatCurrency(totals.accepted_value)} -
-
- Rejected value - ${formatCurrency(totals.rejected_value)} -
- ${ - grn.vendor_invoice_amount != null && grn.vendor_invoice_amount !== '' - ? `
- Vendor invoice amount - ${formatCurrency(grn.vendor_invoice_amount)} -
` - : '' - } -
- Grand total - ${formatCurrency(totals.grand_total)} -
-
-
Amount in words
-
${escapeHtml(numberToWords(totals.grand_total))}
-
-
-
-
${ grn.remarks