feat(email): global signature footer from the business profile (#1264)
The business profile already carried the operator's full issuer block —
address, phone, email, website, VAT id — but none of it reached an email.
Those columns only fed the quote/invoice PDF renderer, so every outgoing
mail footer was the fixed logo + company name + copyright line.
The signature is rendered by wrapEmailHtml and nowhere else, so no
template, no per-type send path and no queue row needed a change. Two new
columns on business_profile (migration 198) carry the toggle and one
free-text legal line; everything else is read from the address fields the
operator already maintains.
Default off, with a test pinning that the disabled path is byte-identical
to a no-profile install.
Includes three rounds of external review fixes: the plain-text MIME part
also carries the signature; string booleans ('false'/'0') no longer
invert the toggle; the status line stays silent rather than asserting
"off" while unauthorised or loading; and the preview's Text tab mirrors
the send path's htmlToText fallback.
Manual Messages replies deliberately keep no signature — they bypass the
wrapper by design — and the UI copy names that exception.
Closes #1264 (Part A)
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* `business_profile.email_signature_enabled` + `email_signature_extra` —
|
||||
* the global email footer signature (issue #1264).
|
||||
*
|
||||
* The business profile already carries the full issuer block (address,
|
||||
* phone, email, website, VAT id) but none of it ever reached an email:
|
||||
* those columns only feed the quote/invoice PDF renderer and the public
|
||||
* quote page. Every outgoing mail footer was the fixed logo + company
|
||||
* name + copyright line built inside `wrapEmailHtml`.
|
||||
*
|
||||
* Rather than copy the address into a second place, the wrapper now reads
|
||||
* this profile row. These two columns are the only new state: a master
|
||||
* toggle and one free-text line for the legal notice (Handelsregister /
|
||||
* registration number / disclaimer) that has no dedicated column.
|
||||
*
|
||||
* Default FALSE on purpose: an upgraded install keeps a byte-identical
|
||||
* footer until an admin turns the signature on.
|
||||
*/
|
||||
|
||||
exports.up = async function (knex) {
|
||||
if (!(await knex.schema.hasTable('business_profile'))) return;
|
||||
|
||||
if (!(await knex.schema.hasColumn('business_profile', 'email_signature_enabled'))) {
|
||||
await knex.schema.alterTable('business_profile', (table) => {
|
||||
table.boolean('email_signature_enabled').notNullable().defaultTo(false);
|
||||
});
|
||||
}
|
||||
if (!(await knex.schema.hasColumn('business_profile', 'email_signature_extra'))) {
|
||||
await knex.schema.alterTable('business_profile', (table) => {
|
||||
// Plain text, rendered escaped and <br>-separated. Not HTML.
|
||||
table.text('email_signature_extra');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.down = async function (knex) {
|
||||
if (!(await knex.schema.hasTable('business_profile'))) return;
|
||||
|
||||
for (const column of ['email_signature_enabled', 'email_signature_extra']) {
|
||||
if (await knex.schema.hasColumn('business_profile', column)) {
|
||||
await knex.schema.alterTable('business_profile', (table) => {
|
||||
table.dropColumn(column);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user