feat: improve template variable handling and add Profit Planet signature stamp functionality

This commit is contained in:
seaznCode 2026-01-19 20:49:22 +01:00
parent 46b36d7edd
commit 25c67783d4
2 changed files with 56 additions and 27 deletions

View File

@ -1619,32 +1619,49 @@ exports.previewLatestForMe = async (req, res) => {
const [rows] = await db.execute('SELECT * FROM company_profiles WHERE user_id = ? LIMIT 1', [targetUserId]); const [rows] = await db.execute('SELECT * FROM company_profiles WHERE user_id = ? LIMIT 1', [targetUserId]);
const c = rows && rows[0] ? rows[0] : null; const c = rows && rows[0] ? rows[0] : null;
if (c) { if (c) {
vars.companyName = c.company_name || ''; setIfEmpty('companyName', c.company_name);
vars.registrationNumber = c.registration_number || ''; setIfEmpty('registrationNumber', c.registration_number);
vars.companyAddress = c.address || ''; setIfEmpty('companyAddress', c.address);
vars.address = vars.companyAddress; setIfEmpty('address', c.address);
vars.zip_code = c.zip_code || ''; setIfEmpty('zip_code', c.zip_code);
vars.city = c.city || ''; setIfEmpty('city', c.city);
vars.country = c.country || ''; setIfEmpty('country', c.country);
vars.contactPersonName = c.contact_person_name || ''; setIfEmpty('contactPersonName', c.contact_person_name);
vars.contactPersonPhone = c.contact_person_phone || c.phone || ''; setIfEmpty('contactPersonPhone', c.contact_person_phone || c.phone);
vars.companyEmail = c.email || c.company_email || c.contact_email || req.user.email || ''; setIfEmpty('companyEmail', c.email || c.company_email || c.contact_email || req.user.email);
vars.companyPhone = c.phone || c.contact_person_phone || ''; setIfEmpty('companyPhone', c.phone || c.contact_person_phone);
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 || '';
} }
// 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 }); logger.debug('[previewLatestForMe] company vars', { userId: targetUserId, found: !!c, vars });
} catch (e) { } catch (e) {
logger.warn('[previewLatestForMe] company profile lookup failed', e && e.message); logger.warn('[previewLatestForMe] company profile lookup failed', e && e.message);

View File

@ -3,6 +3,7 @@ const { uploadBuffer, s3: exoS3 } = require('../../utils/exoscaleUploader');
const PDFDocument = require('pdfkit'); const PDFDocument = require('pdfkit');
const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3'); const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3');
const DocumentTemplateService = require('../template/DocumentTemplateService'); const DocumentTemplateService = require('../template/DocumentTemplateService');
const CompanyStampService = require('../stamp/company/CompanyStampService');
const db = require('../../database/database'); const db = require('../../database/database');
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
@ -49,7 +50,7 @@ async function streamToBuffer(body) {
} }
function fillTemplate(template, data) { function fillTemplate(template, data) {
return template.replace(/{{(\w+)}}/g, (_, key) => data[key] || ''); return template.replace(/{{\s*(\w+)\s*}}/g, (_, key) => data[key] || '');
} }
// Build placeholder variables by combining DB profile data with any contractData overrides (for both personal/company). // Build placeholder variables by combining DB profile data with any contractData overrides (for both personal/company).
@ -310,7 +311,7 @@ class ContractUploadService {
// Merge DB-derived vars with request data so placeholders fill like the preview endpoint // Merge DB-derived vars with request data so placeholders fill like the preview endpoint
const vars = await buildTemplateVars({ userId, user_type, contractData, unitOfWork }); const vars = await buildTemplateVars({ userId, user_type, contractData, unitOfWork });
Object.entries(vars).forEach(([key, value]) => { 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 ?? '')); htmlTemplate = htmlTemplate.replace(re, String(value ?? ''));
}); });
if (signatureImage) { if (signatureImage) {
@ -319,6 +320,17 @@ class ContractUploadService {
htmlTemplate = htmlTemplate.replace(/{{\s*signatureImage\s*}}/g, tag); 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, '');
}
// Render HTML to PDF via Puppeteer (closest to preview output) // Render HTML to PDF via Puppeteer (closest to preview output)
const browser = await puppeteer.launch({ headless: 'new', args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const browser = await puppeteer.launch({ headless: 'new', args: ['--no-sandbox', '--disable-setuid-sandbox'] });
const page = await browser.newPage(); const page = await browser.newPage();