feat: refactor template variable handling and add Profit Planet signature application logic
This commit is contained in:
parent
25c67783d4
commit
7b2eb4dbf0
@ -1515,7 +1515,7 @@ exports.previewLatestForUser = async (req, res) => {
|
||||
exports.previewLatestForMe = async (req, res) => {
|
||||
if (!req.user) return res.status(401).json({ error: 'Unauthorized' });
|
||||
const targetUserId = req.user.id || req.user.userId;
|
||||
const userType = req.user.user_type || req.user.userType;
|
||||
const userType = (req.user.user_type || req.user.userType || '').toString().toLowerCase();
|
||||
if (!targetUserId || !userType) return res.status(400).json({ error: 'Invalid authenticated user' });
|
||||
|
||||
const contractTypeParam = (req.query.contract_type || req.query.contractType || '').toString().toLowerCase();
|
||||
@ -1545,132 +1545,20 @@ exports.previewLatestForMe = async (req, res) => {
|
||||
if (!fileObj.Body) return res.status(404).json({ error: 'Template file not available' });
|
||||
let html = await streamToString(fileObj.Body, latest.id);
|
||||
|
||||
// Build variables from the authenticated user's DB data
|
||||
const vars = {};
|
||||
// initialize common placeholders to empty so they get stripped from template even when data is missing
|
||||
['fullName','address','zip_code','city','country','phone','fullAddress','companyFullAddress','companyName','registrationNumber','companyAddress','companyZipCode','companyCity','companyEmail','companyPhone','contactPersonName','contactPersonPhone','companyCompanyName','companyRegistrationNumber'].forEach(k => { vars[k] = ''; });
|
||||
const setIfEmpty = (key, val) => {
|
||||
if (val === undefined || val === null) return;
|
||||
const str = String(val).trim();
|
||||
if (!str) return;
|
||||
if (!vars[key] || String(vars[key]).trim() === '') {
|
||||
vars[key] = str;
|
||||
}
|
||||
};
|
||||
// base fields
|
||||
vars.email = req.user.email || '';
|
||||
const d = new Date();
|
||||
const pad = (n) => String(n).padStart(2, '0');
|
||||
vars.currentDate = `${pad(d.getDate())}.${pad(d.getMonth() + 1)}.${d.getFullYear()} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
||||
|
||||
if (userType === 'personal') {
|
||||
try {
|
||||
let p = null;
|
||||
try {
|
||||
const [rows] = await db.execute('SELECT * FROM personal_profiles WHERE user_id = ? LIMIT 1', [targetUserId]);
|
||||
p = Array.isArray(rows) ? rows[0] : rows;
|
||||
} catch (ignored) {}
|
||||
|
||||
// Fallback lookup via users.email join (personal_profiles has no email column)
|
||||
if (!p && req.user.email) {
|
||||
try {
|
||||
const [rowsEmail] = await db.execute(
|
||||
'SELECT pp.* FROM personal_profiles pp JOIN users u ON pp.user_id = u.id WHERE u.email = ? LIMIT 1',
|
||||
[req.user.email]
|
||||
);
|
||||
p = Array.isArray(rowsEmail) ? rowsEmail[0] : rowsEmail;
|
||||
} catch (ignored) {}
|
||||
}
|
||||
|
||||
if (p) {
|
||||
setIfEmpty('fullName', `${p.first_name || ''} ${p.last_name || ''}`.trim());
|
||||
setIfEmpty('address', p.address);
|
||||
setIfEmpty('zip_code', p.zip_code);
|
||||
setIfEmpty('city', p.city);
|
||||
setIfEmpty('country', p.country);
|
||||
setIfEmpty('phone', p.phone || p.phone_secondary);
|
||||
setIfEmpty('fullAddress', p.full_address);
|
||||
}
|
||||
|
||||
// Fallbacks from authenticated user payload (helps dummy/local users without profile rows)
|
||||
setIfEmpty('fullName', req.user.fullName || req.user.full_name);
|
||||
setIfEmpty('fullName', `${req.user.first_name || ''} ${req.user.last_name || ''}`);
|
||||
setIfEmpty('fullName', `${req.user.firstName || ''} ${req.user.lastName || ''}`);
|
||||
setIfEmpty('address', req.user.address || req.user.street || req.user.street_address);
|
||||
setIfEmpty('zip_code', req.user.zip_code || req.user.zip || req.user.postalCode || req.user.postal_code);
|
||||
setIfEmpty('city', req.user.city || req.user.town);
|
||||
setIfEmpty('country', req.user.country);
|
||||
setIfEmpty('phone', req.user.phone || req.user.phone_secondary || req.user.mobile);
|
||||
|
||||
if (!vars.fullAddress) {
|
||||
const parts = [];
|
||||
if (vars.address) parts.push(vars.address);
|
||||
const zipCity = [vars.zip_code, vars.city].filter(Boolean).join(' ');
|
||||
if (zipCity) parts.push(zipCity);
|
||||
vars.fullAddress = parts.join(', ');
|
||||
}
|
||||
|
||||
logger.info('[previewLatestForMe] personal vars', { userId: targetUserId, found: !!p, vars });
|
||||
} catch (e) {
|
||||
logger.warn('[previewLatestForMe] personal profile lookup failed', e && e.message);
|
||||
}
|
||||
} else if (userType === 'company') {
|
||||
try {
|
||||
const [rows] = await db.execute('SELECT * FROM company_profiles WHERE user_id = ? LIMIT 1', [targetUserId]);
|
||||
const c = rows && rows[0] ? rows[0] : null;
|
||||
if (c) {
|
||||
setIfEmpty('companyName', c.company_name);
|
||||
setIfEmpty('registrationNumber', c.registration_number);
|
||||
setIfEmpty('companyAddress', c.address);
|
||||
setIfEmpty('address', c.address);
|
||||
setIfEmpty('zip_code', c.zip_code);
|
||||
setIfEmpty('city', c.city);
|
||||
setIfEmpty('country', c.country);
|
||||
setIfEmpty('contactPersonName', c.contact_person_name);
|
||||
setIfEmpty('contactPersonPhone', c.contact_person_phone || c.phone);
|
||||
setIfEmpty('companyEmail', c.email || c.company_email || c.contact_email || req.user.email);
|
||||
setIfEmpty('companyPhone', c.phone || c.contact_person_phone);
|
||||
}
|
||||
|
||||
// Fallbacks from authenticated user payload (helps dummy/local users without profile rows)
|
||||
setIfEmpty('companyName', req.user.companyName || req.user.company_name || req.user.company);
|
||||
setIfEmpty('registrationNumber', req.user.registrationNumber || req.user.registration_number || req.user.vatNumber);
|
||||
setIfEmpty('companyAddress', req.user.address || req.user.street || req.user.street_address);
|
||||
setIfEmpty('address', req.user.address || req.user.street || req.user.street_address);
|
||||
setIfEmpty('zip_code', req.user.zip_code || req.user.zip || req.user.postalCode || req.user.postal_code);
|
||||
setIfEmpty('city', req.user.city || req.user.town);
|
||||
setIfEmpty('country', req.user.country);
|
||||
setIfEmpty('contactPersonName', req.user.contactPersonName || req.user.contact_person_name);
|
||||
setIfEmpty('contactPersonPhone', req.user.contactPersonPhone || req.user.contact_person_phone || req.user.companyPhone || req.user.phone);
|
||||
setIfEmpty('companyEmail', req.user.companyEmail || req.user.email);
|
||||
setIfEmpty('companyPhone', req.user.companyPhone || req.user.phone);
|
||||
|
||||
if (!vars.companyAddress) setIfEmpty('companyAddress', vars.address);
|
||||
if (!vars.address) setIfEmpty('address', vars.companyAddress);
|
||||
|
||||
const parts = [];
|
||||
if (vars.companyAddress) parts.push(vars.companyAddress);
|
||||
const zipCity = [vars.zip_code, vars.city].filter(Boolean).join(' ');
|
||||
if (zipCity) parts.push(zipCity);
|
||||
vars.companyFullAddress = parts.join(', ');
|
||||
|
||||
// Ensure template-prefixed company placeholders are populated
|
||||
vars.companyCompanyName = vars.companyName || '';
|
||||
vars.companyRegistrationNumber = vars.registrationNumber || '';
|
||||
vars.companyZipCode = vars.zip_code || '';
|
||||
vars.companyCity = vars.city || '';
|
||||
|
||||
// Signature/display name for company preview
|
||||
vars.fullName = vars.contactPersonName || vars.companyName || vars.fullName || '';
|
||||
logger.debug('[previewLatestForMe] company vars', { userId: targetUserId, found: !!c, vars });
|
||||
} catch (e) {
|
||||
logger.warn('[previewLatestForMe] company profile lookup failed', e && e.message);
|
||||
}
|
||||
}
|
||||
// Build variables using the same logic as contract upload
|
||||
const uow = new UnitOfWork();
|
||||
await uow.start();
|
||||
const vars = await ContractUploadService.buildTemplateVars({
|
||||
userId: targetUserId,
|
||||
user_type: userType,
|
||||
contractData: {},
|
||||
unitOfWork: uow
|
||||
});
|
||||
await uow.commit();
|
||||
|
||||
// Replace placeholders
|
||||
Object.entries(vars).forEach(([k, v]) => {
|
||||
html = html.replace(new RegExp(`{{\\s*${k}\\s*}}`, 'g'), String(v ?? ''));
|
||||
html = html.replace(new RegExp(`{{\s*${k}\s*}}`, 'g'), String(v ?? ''));
|
||||
});
|
||||
|
||||
// Show a friendly placeholder for signature in preview (not signed yet)
|
||||
|
||||
@ -50,7 +50,19 @@ async function streamToBuffer(body) {
|
||||
}
|
||||
|
||||
function fillTemplate(template, data) {
|
||||
return template.replace(/{{\s*(\w+)\s*}}/g, (_, key) => data[key] || '');
|
||||
return template.replace(/{{(\w+)}}/g, (_, key) => data[key] || '');
|
||||
}
|
||||
|
||||
async function applyProfitPlanetSignatureForHtml({ html, userId }) {
|
||||
if (!html) return html;
|
||||
if (!/{{\s*profitplanetSignature\s*}}/i.test(html)) return html;
|
||||
try {
|
||||
const { tag } = await DocumentTemplateService.getProfitPlanetSignatureTag({ maxW: 300, maxH: 300 });
|
||||
return html.replace(/{{\s*profitplanetSignature\s*}}/gi, tag || '');
|
||||
} catch (e) {
|
||||
logger.warn('ContractUploadService.uploadContract:profitplanetSignature failed', { userId, msg: e.message });
|
||||
return html.replace(/{{\s*profitplanetSignature\s*}}/gi, '');
|
||||
}
|
||||
}
|
||||
|
||||
// Build placeholder variables by combining DB profile data with any contractData overrides (for both personal/company).
|
||||
@ -311,7 +323,7 @@ class ContractUploadService {
|
||||
// Merge DB-derived vars with request data so placeholders fill like the preview endpoint
|
||||
const vars = await buildTemplateVars({ userId, user_type, contractData, unitOfWork });
|
||||
Object.entries(vars).forEach(([key, value]) => {
|
||||
const re = new RegExp(`{{\\s*${key}\\s*}}`, 'g');
|
||||
const re = new RegExp(`{{\s*${key}\s*}}`, 'g');
|
||||
htmlTemplate = htmlTemplate.replace(re, String(value ?? ''));
|
||||
});
|
||||
if (signatureImage) {
|
||||
@ -320,16 +332,8 @@ class ContractUploadService {
|
||||
htmlTemplate = htmlTemplate.replace(/{{\s*signatureImage\s*}}/g, tag);
|
||||
}
|
||||
|
||||
// Apply Profit Planet signature stamp if placeholder exists
|
||||
try {
|
||||
if (htmlTemplate.match(/{{\s*profitplanetSignature\s*}}/i)) {
|
||||
const tag = await CompanyStampService.getProfitPlanetSignatureTag({ maxW: 300, maxH: 300 });
|
||||
htmlTemplate = htmlTemplate.replace(/{{\s*profitplanetSignature\s*}}/gi, tag || '');
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn('ContractUploadService.uploadContract:profitplanetSignature failed', { userId, msg: e.message });
|
||||
htmlTemplate = htmlTemplate.replace(/{{\s*profitplanetSignature\s*}}/gi, '');
|
||||
}
|
||||
// Apply Profit Planet signature placeholder (if any)
|
||||
htmlTemplate = await applyProfitPlanetSignatureForHtml({ html: htmlTemplate, userId });
|
||||
|
||||
// Render HTML to PDF via Puppeteer (closest to preview output)
|
||||
const browser = await puppeteer.launch({ headless: 'new', args: ['--no-sandbox', '--disable-setuid-sandbox'] });
|
||||
@ -392,4 +396,7 @@ class ContractUploadService {
|
||||
}
|
||||
}
|
||||
|
||||
// Reuse helper for preview endpoints
|
||||
ContractUploadService.buildTemplateVars = buildTemplateVars;
|
||||
|
||||
module.exports = ContractUploadService;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user