feat: enhance user data handling in PDF generation with improved fallback logic for company and personal profiles
This commit is contained in:
parent
dfbd731f53
commit
1fc9af3be9
@ -730,6 +730,27 @@ exports.generatePdfWithSignature = async (req, res) => {
|
|||||||
companyPhone: ''
|
companyPhone: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Map company-prefixed placeholders from raw request early (helps when no company_profile row exists)
|
||||||
|
const rawCompanyName = pick(rawUserData, ['companyCompanyName','companyName','company_name','company']);
|
||||||
|
const rawCompanyReg = pick(rawUserData, ['companyRegistrationNumber','registrationNumber','registration_number','registrationNo']);
|
||||||
|
const rawCompanyAddr = pick(rawUserData, ['companyAddress','company_address','address','streetAddress']);
|
||||||
|
const rawCompanyZip = pick(rawUserData, ['companyZipCode','company_zip','zip','zip_code','postalCode','postal_code']);
|
||||||
|
const rawCompanyCity = pick(rawUserData, ['companyCity','city','town']);
|
||||||
|
const rawCompanyEmail = pick(rawUserData, ['companyEmail','company_email','contact_email','email']);
|
||||||
|
const rawCompanyPhone = pick(rawUserData, ['companyPhone','company_phone','contactPhone','contact_phone','phone','telephone','mobile']);
|
||||||
|
const rawCompanyContactName = pick(rawUserData, ['companyContactPersonName','contactPersonName','contact_person_name','contactPerson','contactName','contact']);
|
||||||
|
const rawCompanyContactPhone = pick(rawUserData, ['companyContactPersonPhone','contactPersonPhone','contact_person_phone','contactPhone','contact_phone','phone','telephone','mobile']);
|
||||||
|
|
||||||
|
userData.companyCompanyName = userData.companyCompanyName || rawCompanyName || userData.companyName || '';
|
||||||
|
userData.companyRegistrationNumber = userData.companyRegistrationNumber || rawCompanyReg || userData.registrationNumber || '';
|
||||||
|
userData.companyAddress = userData.companyAddress || rawCompanyAddr || userData.address || '';
|
||||||
|
userData.companyZipCode = userData.companyZipCode || rawCompanyZip || userData.zip_code || '';
|
||||||
|
userData.companyCity = userData.companyCity || rawCompanyCity || userData.city || '';
|
||||||
|
userData.companyEmail = userData.companyEmail || rawCompanyEmail || userData.email || '';
|
||||||
|
userData.companyPhone = userData.companyPhone || rawCompanyPhone || userData.contactPersonPhone || userData.phone || '';
|
||||||
|
userData.companyContactPersonName = userData.companyContactPersonName || rawCompanyContactName || userData.contactPersonName || '';
|
||||||
|
userData.companyContactPersonPhone = userData.companyContactPersonPhone || rawCompanyContactPhone || userData.contactPersonPhone || '';
|
||||||
|
|
||||||
// Add personal-user specific fallbacks: fullName and personalAddress and phone
|
// Add personal-user specific fallbacks: fullName and personalAddress and phone
|
||||||
const rawFull = pick(rawUserData, ['fullName','full_name','name']) ||
|
const rawFull = pick(rawUserData, ['fullName','full_name','name']) ||
|
||||||
((pick(rawUserData, ['firstName','first_name']) && pick(rawUserData, ['lastName','last_name'])) ? `${pick(rawUserData, ['firstName','first_name'])} ${pick(rawUserData, ['lastName','last_name'])}` : undefined);
|
((pick(rawUserData, ['firstName','first_name']) && pick(rawUserData, ['lastName','last_name'])) ? `${pick(rawUserData, ['firstName','first_name'])} ${pick(rawUserData, ['lastName','last_name'])}` : undefined);
|
||||||
@ -759,6 +780,12 @@ exports.generatePdfWithSignature = async (req, res) => {
|
|||||||
// add formatted currentDate into normalized map
|
// add formatted currentDate into normalized map
|
||||||
userData.currentDate = formattedCurrentDate;
|
userData.currentDate = formattedCurrentDate;
|
||||||
|
|
||||||
|
// Derive fullName from company contact if still missing (helps company contracts)
|
||||||
|
if (!userData.fullName) {
|
||||||
|
const contactName = rawCompanyContactName || rawCompanyName || userData.contactPersonName || userData.companyContactPersonName;
|
||||||
|
if (contactName) userData.fullName = contactName;
|
||||||
|
}
|
||||||
|
|
||||||
// If frontend didn't send personalAddress/phone, try to fetch from personal_profiles in DB (authenticated user)
|
// If frontend didn't send personalAddress/phone, try to fetch from personal_profiles in DB (authenticated user)
|
||||||
try {
|
try {
|
||||||
// Build candidate identifiers: various authUser/rawUserData id/email shapes
|
// Build candidate identifiers: various authUser/rawUserData id/email shapes
|
||||||
@ -798,34 +825,25 @@ exports.generatePdfWithSignature = async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (prof) {
|
if (prof) {
|
||||||
if (!userData.address) {
|
// Use DB values as canonical when present
|
||||||
userData.address = pick(prof, ['address','street','street_address','streetAddress','address_line1','addressLine1','addr','personal_address']) || userData.address || '';
|
userData.address = pick(prof, ['address','street','street_address','streetAddress','address_line1','addressLine1','addr','personal_address']) || userData.address || '';
|
||||||
}
|
userData.zip_code = pick(prof, ['zip','zip_code','postal_code','postalCode']) || userData.zip_code || '';
|
||||||
if (!userData.zip_code) {
|
userData.city = pick(prof, ['city','town','municipality']) || userData.city || '';
|
||||||
userData.zip_code = pick(prof, ['zip','zip_code','postal_code','postalCode']) || userData.zip_code || '';
|
userData.personalAddress = pick(prof, ['personal_address','personalAddress']) || userData.personalAddress || '';
|
||||||
}
|
userData.phone = pick(prof, ['phone','telephone','mobile','contact_phone','contactPhone','phone_secondary']) || userData.phone || '';
|
||||||
if (!userData.city) {
|
const fn = pick(prof, ['full_name','fullName','name','first_name','firstName']);
|
||||||
userData.city = pick(prof, ['city','town','municipality']) || userData.city || '';
|
const ln = pick(prof, ['last_name','lastName']);
|
||||||
}
|
if (fn || ln) userData.fullName = `${fn || ''} ${ln || ''}`.trim();
|
||||||
if (!userData.personalAddress) {
|
if (!userData.fullName) userData.fullName = pick(prof, ['full_name','fullName','name']) || userData.fullName || '';
|
||||||
userData.personalAddress = pick(prof, ['personal_address','personalAddress']) || userData.personalAddress || '';
|
|
||||||
}
|
// Compose full address primarily from DB columns
|
||||||
if (!userData.phone) {
|
const addrParts = [];
|
||||||
userData.phone = pick(prof, ['phone','telephone','mobile','contact_phone','contactPhone']) || userData.phone || '';
|
if (userData.address) addrParts.push(userData.address);
|
||||||
}
|
const zipCity = [userData.zip_code, userData.city].filter(Boolean).join(' ');
|
||||||
if (!userData.fullName) {
|
if (zipCity) addrParts.push(zipCity);
|
||||||
userData.fullName = pick(prof, ['full_name','fullName','name']) || userData.fullName || '';
|
userData.fullAddress = addrParts.join(', ') || userData.fullAddress || userData.personalAddress || '';
|
||||||
if (!userData.fullName) {
|
|
||||||
const fn = pick(prof, ['first_name','firstName']);
|
logger.debug(`[generatePdfWithSignature] Applied personal_profiles data id=${prof.id || prof.user_id || 'unknown'}`);
|
||||||
const ln = pick(prof, ['last_name','lastName']);
|
|
||||||
if (fn || ln) userData.fullName = `${fn || ''} ${ln || ''}`.trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
userData.fullAddress = pick(rawUserData, ['fullAddress','full_address']) ||
|
|
||||||
pick(authUser, ['fullAddress','full_address']) ||
|
|
||||||
pick(prof, ['full_address','fullAddress']) ||
|
|
||||||
(userData.address ? `${userData.address}${userData.zip_code || userData.city ? ', ' : ''}${userData.zip_code ? userData.zip_code + ' ' : ''}${userData.city || ''}`.trim() : (userData.personalAddress || '')) || '';
|
|
||||||
logger.debug(`[generatePdfWithSignature] Filled missing userData from personal_profiles for profile id=${prof.id || prof.user_id || 'unknown'}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- company_profiles lookup (UnitOfWork) for company-related placeholders if still missing ---
|
// --- company_profiles lookup (UnitOfWork) for company-related placeholders if still missing ---
|
||||||
@ -915,80 +933,38 @@ exports.generatePdfWithSignature = async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (comp) {
|
if (comp) {
|
||||||
// Primary company fields
|
// Prefer DB values directly
|
||||||
const compName = pick(comp, ['company_name','companyName','name','company']);
|
const compName = pick(comp, ['company_name']);
|
||||||
const compReg = pick(comp, ['registration_number','registrationNumber','registrationNo']);
|
const compReg = pick(comp, ['registration_number']);
|
||||||
const compAddr = pick(comp, ['address','company_address','street','streetAddress']);
|
const compAddr = pick(comp, ['address']);
|
||||||
const compZip = pick(comp, ['zip','zip_code','postal_code','postcode']);
|
const compZip = pick(comp, ['zip_code']);
|
||||||
const compCity = pick(comp, ['city','town']);
|
const compCity = pick(comp, ['city']);
|
||||||
const compCountry = pick(comp, ['country','country_code','countryName']);
|
const compCountry = pick(comp, ['country']);
|
||||||
const compContactName = pick(comp, ['contact_person_name','contactPersonName','contactName','contact']);
|
const compContactName = pick(comp, ['contact_person_name']);
|
||||||
const compContactPhone = pick(comp, ['contact_person_phone','contactPersonPhone','phone','telephone','mobile']);
|
const compContactPhone = pick(comp, ['contact_person_phone']);
|
||||||
const compEmail = pick(comp, ['email','company_email','contact_email']);
|
const compPhone = pick(comp, ['phone']);
|
||||||
// ADDED: possible generic phone field
|
|
||||||
const compGenericPhone = pick(comp, ['phone','telephone','mobile']);
|
|
||||||
|
|
||||||
if (!userData.companyName && compName) userData.companyName = compName;
|
if (compName) { userData.companyName = compName; userData.companyCompanyName = compName; }
|
||||||
if (!userData.registrationNumber && compReg) userData.registrationNumber = compReg;
|
if (compReg) { userData.registrationNumber = compReg; userData.companyRegistrationNumber = compReg; }
|
||||||
if (!userData.companyAddress && compAddr) userData.companyAddress = compAddr;
|
if (compAddr) { userData.companyAddress = compAddr; userData.address = userData.address || compAddr; }
|
||||||
// Also populate the generic address key used in many templates
|
if (compZip) { userData.companyZipCode = compZip; userData.zip_code = userData.zip_code || compZip; }
|
||||||
if ((!userData.address || String(userData.address).trim() === '') && compAddr) userData.address = compAddr;
|
if (compCity) { userData.companyCity = compCity; userData.city = userData.city || compCity; }
|
||||||
if (!userData.zip_code && compZip) userData.zip_code = compZip;
|
if (compCountry) { userData.companyCountry = compCountry; userData.country = userData.country || compCountry; }
|
||||||
if (!userData.city && compCity) userData.city = compCity;
|
if (compContactName) { userData.companyContactPersonName = compContactName; userData.contactPersonName = userData.contactPersonName || compContactName; }
|
||||||
// populate country & contact phone (generic)
|
if (compContactPhone) { userData.companyContactPersonPhone = compContactPhone; userData.contactPersonPhone = userData.contactPersonPhone || compContactPhone; }
|
||||||
if (!userData.country && compCountry) userData.country = compCountry;
|
if (compPhone) { userData.companyPhone = compPhone; userData.phone = userData.phone || compPhone; }
|
||||||
if (!userData.contactPersonName && compContactName) userData.contactPersonName = compContactName;
|
|
||||||
if (!userData.contactPersonPhone && compContactPhone) userData.contactPersonPhone = compContactPhone;
|
|
||||||
// Mirror contact phone into generic phone too (helps templates using phone)
|
|
||||||
if ((!userData.phone || String(userData.phone).trim() === '') && compContactPhone) userData.phone = compContactPhone;
|
|
||||||
// Fill email if missing
|
|
||||||
if ((!userData.email || String(userData.email).trim() === '') && compEmail) userData.email = compEmail;
|
|
||||||
|
|
||||||
// ALSO populate company-prefixed keys so templates using companyXXX placeholders are satisfied
|
// Compose company full address from DB fields
|
||||||
if (compName) userData.companyCompanyName = compName;
|
const addrParts = [];
|
||||||
if (compReg) userData.companyRegistrationNumber = compReg;
|
if (compAddr) addrParts.push(compAddr);
|
||||||
if (compAddr) userData.companyAddress = compAddr; // already set but ensure presence
|
const zipCity = [compZip, compCity].filter(Boolean).join(' ');
|
||||||
if (compZip) userData.companyZipCode = compZip;
|
if (zipCity) addrParts.push(zipCity);
|
||||||
if (compCity) userData.companyCity = compCity;
|
const composed = addrParts.join(', ');
|
||||||
if (compCountry) userData.companyCountry = compCountry;
|
if (composed) userData.companyFullAddress = composed;
|
||||||
if (compContactName) userData.companyContactPersonName = compContactName;
|
|
||||||
if (compContactPhone) userData.companyContactPersonPhone = compContactPhone;
|
|
||||||
|
|
||||||
// ADDED: fill required placeholders
|
// Keep email fallback behavior (no email column in company_profiles)
|
||||||
if (!userData.companyEmail && compEmail) userData.companyEmail = compEmail;
|
|
||||||
if (!userData.companyPhone && (compContactPhone || compGenericPhone))
|
|
||||||
userData.companyPhone = compContactPhone || compGenericPhone;
|
|
||||||
|
|
||||||
// ADDED: compute companyFullAddress if not provided
|
logger.debug(`[generatePdfWithSignature] Applied company_profiles data id=${comp.id || comp.company_id || 'unknown'}`);
|
||||||
if (!userData.companyFullAddress) {
|
|
||||||
const addrParts = [];
|
|
||||||
if (compAddr) addrParts.push(compAddr);
|
|
||||||
const zipCity = [compZip, compCity].filter(Boolean).join(' ');
|
|
||||||
if (zipCity) addrParts.push(zipCity);
|
|
||||||
userData.companyFullAddress = addrParts.join(', ');
|
|
||||||
}
|
|
||||||
// Fallback: if still empty try any supplied raw/full address fields
|
|
||||||
if (!userData.companyFullAddress) {
|
|
||||||
userData.companyFullAddress =
|
|
||||||
pick(rawUserData, ['companyFullAddress','company_full_address','fullAddress','full_address']) ||
|
|
||||||
pick(authUser, ['companyFullAddress','company_full_address']) || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safe debug log of which company-derived keys we populated
|
|
||||||
const populated = [];
|
|
||||||
if (compName) populated.push('companyName');
|
|
||||||
if (compReg) populated.push('registrationNumber');
|
|
||||||
if (compAddr) populated.push('companyAddress/address');
|
|
||||||
if (compZip) populated.push('zip_code/companyZipCode');
|
|
||||||
if (compCity) populated.push('city/companyCity');
|
|
||||||
if (compCountry) populated.push('country/companyCountry');
|
|
||||||
if (compContactName) populated.push('contactPersonName/companyContactPersonName');
|
|
||||||
if (compContactPhone) populated.push('contactPersonPhone/companyContactPersonPhone');
|
|
||||||
if (compEmail) populated.push('email/companyEmail');
|
|
||||||
if (userData.companyFullAddress) populated.push('companyFullAddress'); // ADDED
|
|
||||||
if (userData.companyEmail) populated.push('companyEmail'); // ADDED
|
|
||||||
if (userData.companyPhone) populated.push('companyPhone'); // ADDED
|
|
||||||
logger.debug(`[generatePdfWithSignature] Filled missing company userData from company_profiles id=${comp.id || comp.company_id || 'unknown'} - populated: ${populated.join(',')}`);
|
|
||||||
}
|
}
|
||||||
await uow.commit();
|
await uow.commit();
|
||||||
} catch (errU) {
|
} catch (errU) {
|
||||||
@ -1062,16 +1038,36 @@ exports.generatePdfWithSignature = async (req, res) => {
|
|||||||
} catch (e) { /* ignore */ }
|
} catch (e) { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Final fallbacks for composed address fields (still prefer DB values set above)
|
||||||
|
if (!userData.fullAddress) {
|
||||||
|
const addrParts = [];
|
||||||
|
if (userData.address) addrParts.push(userData.address);
|
||||||
|
const zipCity = [userData.zip_code, userData.city].filter(Boolean).join(' ');
|
||||||
|
if (zipCity) addrParts.push(zipCity);
|
||||||
|
userData.fullAddress = addrParts.join(', ');
|
||||||
|
}
|
||||||
|
if (!userData.companyFullAddress) {
|
||||||
|
const addrParts = [];
|
||||||
|
if (userData.companyAddress) addrParts.push(userData.companyAddress);
|
||||||
|
const zipCity = [userData.companyZipCode, userData.companyCity].filter(Boolean).join(' ');
|
||||||
|
if (zipCity) addrParts.push(zipCity);
|
||||||
|
userData.companyFullAddress = addrParts.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
// Insert userData into HTML (simple replace, adjust as needed)
|
// Insert userData into HTML (simple replace, adjust as needed)
|
||||||
if (userData && typeof userData === 'object') {
|
if (userData && typeof userData === 'object') {
|
||||||
Object.entries(userData).forEach(([key, value]) => {
|
Object.entries(userData).forEach(([key, value]) => {
|
||||||
const beforeCount = (html.match(new RegExp(`{{\\s*${key}\\s*}}`, 'g')) || []).length;
|
const beforeCount = (html.match(new RegExp(`{{\s*${key}\s*}}`, 'g')) || []).length;
|
||||||
html = html.replace(new RegExp(`{{\\s*${key}\\s*}}`, 'g'), value);
|
html = html.replace(new RegExp(`{{\s*${key}\s*}}`, 'g'), value);
|
||||||
const afterCount = (html.match(new RegExp(`{{\\s*${key}\\s*}}`, 'g')) || []).length;
|
const afterCount = (html.match(new RegExp(`{{\s*${key}\s*}}`, 'g')) || []).length;
|
||||||
if (beforeCount || afterCount) {
|
if (beforeCount || afterCount) {
|
||||||
logger.debug(`[generatePdfWithSignature] replaced ${key}: before=${beforeCount} after=${afterCount}`);
|
logger.debug(`[generatePdfWithSignature] replaced ${key}: before=${beforeCount} after=${afterCount}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const remainingPlaceholders = html.match(/{{\s*([^}\s]+)\s*}}/g) || [];
|
||||||
|
if (remainingPlaceholders.length) {
|
||||||
|
logger.debug('[generatePdfWithSignature] Unreplaced placeholders detected', { count: remainingPlaceholders.length, samples: remainingPlaceholders.slice(0, 5) });
|
||||||
|
}
|
||||||
logger.debug(`[generatePdfWithSignature] User data replaced in HTML`);
|
logger.debug(`[generatePdfWithSignature] User data replaced in HTML`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1482,6 +1478,16 @@ exports.previewLatestForMe = async (req, res) => {
|
|||||||
|
|
||||||
// Build variables from the authenticated user's DB data
|
// Build variables from the authenticated user's DB data
|
||||||
const vars = {};
|
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
|
// base fields
|
||||||
vars.email = req.user.email || '';
|
vars.email = req.user.email || '';
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
@ -1490,22 +1496,52 @@ exports.previewLatestForMe = async (req, res) => {
|
|||||||
|
|
||||||
if (userType === 'personal') {
|
if (userType === 'personal') {
|
||||||
try {
|
try {
|
||||||
const [rows] = await db.execute('SELECT * FROM personal_profiles WHERE user_id = ? LIMIT 1', [targetUserId]);
|
let p = null;
|
||||||
const p = rows && rows[0] ? rows[0] : 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) {
|
if (p) {
|
||||||
const fullName = `${p.first_name || ''} ${p.last_name || ''}`.trim();
|
setIfEmpty('fullName', `${p.first_name || ''} ${p.last_name || ''}`.trim());
|
||||||
vars.fullName = fullName;
|
setIfEmpty('address', p.address);
|
||||||
vars.address = p.address || '';
|
setIfEmpty('zip_code', p.zip_code);
|
||||||
vars.zip_code = p.zip_code || '';
|
setIfEmpty('city', p.city);
|
||||||
vars.city = p.city || '';
|
setIfEmpty('country', p.country);
|
||||||
vars.country = p.country || '';
|
setIfEmpty('phone', p.phone || p.phone_secondary);
|
||||||
vars.phone = p.phone || '';
|
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 = [];
|
const parts = [];
|
||||||
if (vars.address) parts.push(vars.address);
|
if (vars.address) parts.push(vars.address);
|
||||||
const zipCity = [vars.zip_code, vars.city].filter(Boolean).join(' ');
|
const zipCity = [vars.zip_code, vars.city].filter(Boolean).join(' ');
|
||||||
if (zipCity) parts.push(zipCity);
|
if (zipCity) parts.push(zipCity);
|
||||||
vars.fullAddress = parts.join(', ');
|
vars.fullAddress = parts.join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.info('[previewLatestForMe] personal vars', { userId: targetUserId, found: !!p, vars });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.warn('[previewLatestForMe] personal profile lookup failed', e && e.message);
|
logger.warn('[previewLatestForMe] personal profile lookup failed', e && e.message);
|
||||||
}
|
}
|
||||||
@ -1536,7 +1572,11 @@ exports.previewLatestForMe = async (req, res) => {
|
|||||||
vars.companyRegistrationNumber = vars.registrationNumber;
|
vars.companyRegistrationNumber = vars.registrationNumber;
|
||||||
vars.companyZipCode = vars.zip_code;
|
vars.companyZipCode = vars.zip_code;
|
||||||
vars.companyCity = vars.city;
|
vars.companyCity = vars.city;
|
||||||
|
|
||||||
|
// Signature/display name for company preview
|
||||||
|
vars.fullName = vars.contactPersonName || vars.companyName || '';
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
@ -1544,9 +1584,21 @@ exports.previewLatestForMe = async (req, res) => {
|
|||||||
|
|
||||||
// Replace placeholders
|
// Replace placeholders
|
||||||
Object.entries(vars).forEach(([k, v]) => {
|
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 ?? ''));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Strip signature placeholder in preview (not signed yet)
|
||||||
|
html = html.replace(/{{\s*signatureImage\s*}}/g, '');
|
||||||
|
|
||||||
|
// Remove any remaining placeholders except stamp/signature markers
|
||||||
|
html = sanitizePlaceholders(html, ['companyStamp','companyStampInline','companyStampSmall','profitplanetSignature']);
|
||||||
|
|
||||||
|
// Log any remaining placeholders to aid debugging
|
||||||
|
const remaining = html.match(/{{\s*([^}\s]+)\s*}}/g) || [];
|
||||||
|
if (remaining.length) {
|
||||||
|
logger.debug('[previewLatestForMe] unreplaced placeholders', { count: remaining.length, samples: remaining.slice(0, 5) });
|
||||||
|
}
|
||||||
|
|
||||||
// Apply company stamp and signature where applicable
|
// Apply company stamp and signature where applicable
|
||||||
try { html = await applyCompanyStampPlaceholders(html, req); } catch (e) {}
|
try { html = await applyCompanyStampPlaceholders(html, req); } catch (e) {}
|
||||||
try { html = await applyProfitPlanetSignature(html); } catch (e) {}
|
try { html = await applyProfitPlanetSignature(html); } catch (e) {}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user