first commit
This commit is contained in:
@@ -0,0 +1,345 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background: white;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.sheet {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border: 1px solid #222;
|
||||
}
|
||||
|
||||
.title-bar {
|
||||
background: #173d64;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
padding: 4px 0 5px;
|
||||
border-bottom: 1px solid #222;
|
||||
}
|
||||
|
||||
.top-area {
|
||||
display: grid;
|
||||
grid-template-columns: 1.4fr 1fr 0.65fr;
|
||||
min-height: 95px;
|
||||
border-bottom: 1px solid #444;
|
||||
}
|
||||
|
||||
.top-col {
|
||||
padding: 5px 6px;
|
||||
border-right: 1px solid #444;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.top-col:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.field-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.field-row {
|
||||
display: grid;
|
||||
grid-template-columns: 110px 1fr;
|
||||
align-items: center;
|
||||
min-height: 17px;
|
||||
border: 1px solid #444;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.field-row:last-child {
|
||||
border-bottom: 1px solid #444;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: 700;
|
||||
padding: 1px 4px;
|
||||
}
|
||||
|
||||
.value {
|
||||
padding: 1px 4px;
|
||||
}
|
||||
|
||||
.top-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.top-middle {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.top-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.meta-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.4fr 1fr 0.65fr;
|
||||
border-bottom: 1px solid #444;
|
||||
}
|
||||
|
||||
.meta-box {
|
||||
min-height: 82px;
|
||||
padding: 4px 6px;
|
||||
border-right: 1px solid #444;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.meta-box:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.meta-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.meta-table td {
|
||||
border: 1px solid #444;
|
||||
padding: 2px 4px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.meta-table td.label-cell {
|
||||
width: 105px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.details-note {
|
||||
font-size: 9px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.items-title {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
padding: 2px 0 3px;
|
||||
border-bottom: 1px solid #444;
|
||||
background: #efefef;
|
||||
}
|
||||
|
||||
.items-wrap {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.items-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.items-table th,
|
||||
.items-table td {
|
||||
border: 1px solid #666;
|
||||
font-size: 10px;
|
||||
padding: 2px 3px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.items-table th {
|
||||
background: #d9d9d9;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.no-col { width: 5%; }
|
||||
.product-col { width: 24%; }
|
||||
.qty-col { width: 5%; }
|
||||
.unit-col { width: 5%; }
|
||||
.supp-col { width: 6%; }
|
||||
.pono-col { width: 6%; }
|
||||
.rcvdqty-col { width: 7%; }
|
||||
.issuedqty-col { width: 7%; }
|
||||
.expdate-col { width: 8%; }
|
||||
.rcvdtime-col { width: 7%; }
|
||||
.date-col { width: 7%; }
|
||||
.rejects-col { width: 6%; }
|
||||
.fatcol-col { width: 6%; }
|
||||
.remark-col { width: 7%; }
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.spacer-row td {
|
||||
height: 21px;
|
||||
}
|
||||
|
||||
.signature-section {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.signature-block {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
border-top: 1px solid #333;
|
||||
padding-top: 34px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.footer-note {
|
||||
text-align: center;
|
||||
margin-top: 12px;
|
||||
border-top: 1px solid #ddd;
|
||||
padding-top: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<%
|
||||
const dispatchTitle = typeof title !== 'undefined' && title ? title : 'DISPATCH NOTE';
|
||||
const dispatchReferenceNumber = typeof referenceNumber !== 'undefined' && referenceNumber ? referenceNumber : 'N/A';
|
||||
const dispatchDate = typeof date !== 'undefined' && date ? date : 'N/A';
|
||||
const dispatchVessel = typeof vessel !== 'undefined' && vessel ? vessel : 'N/A';
|
||||
const dispatchCaptain = typeof captain !== 'undefined' && captain ? captain : 'N/A';
|
||||
const dispatchCook = typeof cook !== 'undefined' && cook ? cook : 'N/A';
|
||||
const dispatchAgent = typeof agent !== 'undefined' && agent ? agent : 'N/A';
|
||||
const dispatchDetails = typeof details !== 'undefined' && details ? details : '';
|
||||
const dispatchPlaceOfDelivery = typeof placeOfDelivery !== 'undefined' && placeOfDelivery ? placeOfDelivery : 'N/A';
|
||||
const dispatchEta = typeof eta !== 'undefined' && eta ? eta : 'N/A';
|
||||
const dispatchEtd = typeof etd !== 'undefined' && etd ? etd : 'N/A';
|
||||
const dispatchNumberOfCrew = typeof numberOfCrew !== 'undefined' && numberOfCrew ? numberOfCrew : 'N/A';
|
||||
const dispatchNextMainOrderIn = typeof nextMainOrderIn !== 'undefined' && nextMainOrderIn ? nextMainOrderIn : 'N/A';
|
||||
const dispatchPort = typeof port !== 'undefined' && port ? port : 'N/A';
|
||||
const dispatchCompany = typeof company !== 'undefined' && company ? company : 'N/A';
|
||||
const dispatchFileNo = typeof fileNo !== 'undefined' && fileNo ? fileNo : 'N/A';
|
||||
const dispatchItems = typeof items !== 'undefined' && Array.isArray(items) ? items : [];
|
||||
%>
|
||||
<div class="sheet">
|
||||
<div class="title-bar"><%= dispatchTitle %></div>
|
||||
|
||||
<div class="top-area">
|
||||
<div class="top-col">
|
||||
<div class="field-grid">
|
||||
<div class="field-row"><div class="label">Vessel:</div><div class="value"><%= dispatchVessel %></div></div>
|
||||
<div class="field-row"><div class="label">Captain:</div><div class="value"><%= dispatchCaptain %></div></div>
|
||||
<div class="field-row"><div class="label">Cook:</div><div class="value"><%= dispatchCook %></div></div>
|
||||
<div class="field-row"><div class="label">Agent:</div><div class="value"><%= dispatchAgent %></div></div>
|
||||
<div class="field-row"><div class="label">Details:</div><div class="value"><%= dispatchDetails %></div></div>
|
||||
</div>
|
||||
<div class="details-note">SC/FORM/04</div>
|
||||
</div>
|
||||
<div class="top-col">
|
||||
<div class="field-grid">
|
||||
<div class="field-row"><div class="label">Place of delivery:</div><div class="value"><%= dispatchPlaceOfDelivery %></div></div>
|
||||
<div class="field-row"><div class="label">ETA:</div><div class="value"><%= dispatchEta %></div></div>
|
||||
<div class="field-row"><div class="label">ETD:</div><div class="value"><%= dispatchEtd %></div></div>
|
||||
<div class="field-row"><div class="label">Number of crew:</div><div class="value"><%= dispatchNumberOfCrew %></div></div>
|
||||
<div class="field-row"><div class="label">Next main order in:</div><div class="value"><%= dispatchNextMainOrderIn %></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="top-col">
|
||||
<div class="field-grid">
|
||||
<div class="field-row"><div class="label">Port</div><div class="value"><%= dispatchPort %></div></div>
|
||||
<div class="field-row"><div class="label">Date</div><div class="value"><%= dispatchDate %></div></div>
|
||||
<div class="field-row"><div class="label">Company</div><div class="value"><%= dispatchCompany %></div></div>
|
||||
<div class="field-row"><div class="label">File No</div><div class="value"><%= dispatchFileNo %></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="items-title">Item Receiving Authorization and Vessel Delivery Oder details</div>
|
||||
|
||||
<div class="items-wrap">
|
||||
<table class="items-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="no-col">No</th>
|
||||
<th class="product-col">Product</th>
|
||||
<th class="qty-col">Qty</th>
|
||||
<th class="unit-col">Unit</th>
|
||||
<th class="supp-col">SUPP</th>
|
||||
<th class="pono-col">PO NO</th>
|
||||
<th class="rcvdqty-col">Rcvd Qty</th>
|
||||
<th class="issuedqty-col">Issued Qty</th>
|
||||
<th class="expdate-col">Exp Date</th>
|
||||
<th class="rcvdtime-col">Rcvd Time</th>
|
||||
<th class="date-col">Date</th>
|
||||
<th class="rejects-col">Rejects</th>
|
||||
<th class="fatcol-col">Fat Con.</th>
|
||||
<th class="remark-col">Remark</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% const rows = Math.max(5, dispatchItems.length); %>
|
||||
<% for (let index = 0; index < rows; index++) { %>
|
||||
<% const item = dispatchItems[index] || {}; %>
|
||||
<tr class="spacer-row">
|
||||
<td class="center"><%= index + 1 %></td>
|
||||
<td><%= item.product || item.description || item.item_name || item.name || '' %></td>
|
||||
<td class="center"><%= item.qty || item.quantity || '' %></td>
|
||||
<td class="center"><%= item.unit || '' %></td>
|
||||
<td class="center"><%= item.supp || item.supplier || '' %></td>
|
||||
<td class="center"><%= item.poNo || item.po_number || item.poNumber || '' %></td>
|
||||
<td class="center"><%= item.receivedQty || item.rcvdQty || '' %></td>
|
||||
<td class="center"><%= item.issuedQty || '' %></td>
|
||||
<td class="center"><%= item.expDate || item.exp_date || '' %></td>
|
||||
<td class="center"><%= item.receivedTime || item.rcvdTime || '' %></td>
|
||||
<td class="center"><%= item.date || '' %></td>
|
||||
<td class="center"><%= item.rejects || '' %></td>
|
||||
<td class="center"><%= item.fatCon || item.fat_con || '' %></td>
|
||||
<td><%= item.remark || item.remarks || '' %></td>
|
||||
</tr>
|
||||
<% } %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<% if (showSignatures) { %>
|
||||
<div class="signature-section">
|
||||
<div class="signature-block">
|
||||
<strong>Prepared By</strong>
|
||||
<div style="margin-top: 28px;">___________________</div>
|
||||
<div style="margin-top: 5px;"><%= preparedBy || 'Name' %></div>
|
||||
</div>
|
||||
<div class="signature-block">
|
||||
<strong>Approved By</strong>
|
||||
<div style="margin-top: 28px;">___________________</div>
|
||||
<div style="margin-top: 5px;"><%= approvedBy || 'Name' %></div>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="footer-note">
|
||||
<p style="font-size: 10px; color: #999;">This is an official dispatch note</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Niolla
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is proprietary and confidential.
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file, via any medium, is strictly prohibited.
|
||||
*/
|
||||
|
||||
// app/templates/documents/dispatchNote/dispatchNote.template.js
|
||||
|
||||
const { log } = require("../../../utils/consoleLog.utill");
|
||||
|
||||
module.exports = async (data) => {
|
||||
log("📋 Preparing Dispatch Note data for PDF...");
|
||||
|
||||
let payload = data;
|
||||
|
||||
if (typeof payload === "string") {
|
||||
try {
|
||||
payload = JSON.parse(payload);
|
||||
} catch (error) {
|
||||
payload = {};
|
||||
}
|
||||
}
|
||||
|
||||
const source = payload.documentData || payload.document_data || payload.data || payload;
|
||||
|
||||
const pick = (...keys) => {
|
||||
for (const key of keys) {
|
||||
const value = source?.[key];
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const templateData = {
|
||||
logoUrl:
|
||||
pick("logoUrl", "logo_url") ||
|
||||
"https://res.cloudinary.com/dtthuvvir/image/upload/v1780748661/company_logo_transperent_bg_uullzw.png",
|
||||
|
||||
title: pick("title") || "DISPATCH NOTE",
|
||||
referenceNumber: pick("referenceNumber", "reference_number", "dnNo", "dn_no") || "N/A",
|
||||
date: pick("date") || new Date().toLocaleDateString(),
|
||||
|
||||
vessel: pick("vessel", "vesselName", "vessel_name") || "",
|
||||
captain: pick("captain", "captainName", "captain_name") || "",
|
||||
cook: pick("cook", "cookName", "cook_name") || "",
|
||||
agent: pick("agent", "agentName", "agent_name") || "",
|
||||
details: pick("details", "remarks", "note") || "",
|
||||
|
||||
placeOfDelivery: pick("placeOfDelivery", "place_of_delivery") || "",
|
||||
eta: pick("eta") || "",
|
||||
etd: pick("etd") || "",
|
||||
numberOfCrew: pick("numberOfCrew", "number_of_crew", "crewCount", "crew_count") || "",
|
||||
nextMainOrderIn: pick("nextMainOrderIn", "next_main_order_in") || "",
|
||||
port: pick("port", "portName", "port_name") || "",
|
||||
company: pick("company", "companyName", "company_name") || "",
|
||||
fileNo: pick("fileNo", "file_no") || "",
|
||||
|
||||
items: Array.isArray(source.items) ? source.items : [],
|
||||
|
||||
showSignatures: pick("showSignatures") !== false,
|
||||
preparedBy: pick("preparedBy", "prepared_by") || "",
|
||||
approvedBy: pick("approvedBy", "approved_by") || "",
|
||||
};
|
||||
|
||||
log("✅ Dispatch Note data prepared");
|
||||
|
||||
return templateData;
|
||||
};
|
||||
Reference in New Issue
Block a user