feat(crm): event-type dropdown on quotes; quote→event uses it (no more hardcoded 'wedding')
Quotes now carry an event type (migration 146: quotes.event_type, the
event_types.slug_prefix), chosen from the active event-types catalog in the
quote editor's Event section. convertToEvent reads it instead of the
unconditional hardcoded 'wedding': quote.event_type → crm_default_event_type
setting → 'wedding' as last-resort seeded fallback. When the booking flow's
prepare_event is wired, it reads the same field.
Backend: createQuote/updateQuote persist event_type (hasColumn-guarded);
adminQuotes route accepts + returns eventType. Frontend: FormState + payload +
load + a catalog-sourced dropdown ("— Use default —"); EN/DE strings.
This commit is contained in:
@@ -86,6 +86,7 @@ function transformQuote(q) {
|
||||
validUntil: q.valid_until,
|
||||
eventName: q.event_name,
|
||||
eventDate: q.event_date,
|
||||
eventType: q.event_type ?? null,
|
||||
eventTimeStart: q.event_time_start,
|
||||
eventTimeEnd: q.event_time_end,
|
||||
expectedDurationHours: q.expected_duration_hours == null ? null : Number(q.expected_duration_hours),
|
||||
@@ -212,7 +213,7 @@ function mapPayloadToService(body) {
|
||||
customerAccountId: 'customerAccountId',
|
||||
language: 'language', currency: 'currency',
|
||||
issueDate: 'issueDate', validUntil: 'validUntil',
|
||||
eventName: 'eventName', eventDate: 'eventDate',
|
||||
eventName: 'eventName', eventDate: 'eventDate', eventType: 'eventType',
|
||||
eventTimeStart: 'eventTimeStart', eventTimeEnd: 'eventTimeEnd',
|
||||
expectedDurationHours: 'expectedDurationHours',
|
||||
paymentTermTemplateId: 'paymentTermTemplateId',
|
||||
|
||||
@@ -563,6 +563,11 @@ async function createQuote(payload, adminId) {
|
||||
if (payload.vatCode !== undefined && await hasColumnCached('quotes', 'vat_code')) {
|
||||
row.vat_code = payload.vatCode ? String(payload.vatCode).slice(0, 16) : null;
|
||||
}
|
||||
// Migration 146 — event type (event_types.slug_prefix). Drives the type of
|
||||
// the event the quote converts into, instead of the old hardcoded 'wedding'.
|
||||
if (payload.eventType !== undefined && await hasColumnCached('quotes', 'event_type')) {
|
||||
row.event_type = payload.eventType ? String(payload.eventType).slice(0, 64) : null;
|
||||
}
|
||||
const inserted = await trx('quotes').insert(row).returning('id');
|
||||
const quoteId = typeof inserted[0] === 'object' ? inserted[0].id : inserted[0];
|
||||
|
||||
@@ -691,6 +696,10 @@ async function updateQuote(id, payload, adminId) {
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'vatCode') && await hasColumnCached('quotes', 'vat_code')) {
|
||||
updates.vat_code = payload.vatCode ? String(payload.vatCode).slice(0, 16) : null;
|
||||
}
|
||||
// Migration 146 — event type.
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'eventType') && await hasColumnCached('quotes', 'event_type')) {
|
||||
updates.event_type = payload.eventType ? String(payload.eventType).slice(0, 64) : null;
|
||||
}
|
||||
await trx('quotes').where({ id }).update(updates);
|
||||
|
||||
// When linked to a project, cascade across the deal lineage so the linked
|
||||
@@ -1482,6 +1491,13 @@ async function convertToEvent(quoteId, adminId, options = {}) {
|
||||
const customerEmail = customer.email || `${quote.quote_number.toLowerCase()}@picpeak.local`;
|
||||
const adminEmail = adminRow?.email || customer.email || '[email protected]';
|
||||
|
||||
// Event type for the new event: the type chosen on the quote (migration 146),
|
||||
// else a configurable org default, else 'wedding' as the last-resort seeded
|
||||
// type. Replaces the old unconditional hardcoded 'wedding'.
|
||||
const eventType = (quote.event_type && String(quote.event_type).trim())
|
||||
|| (await getAppSetting('crm_default_event_type'))
|
||||
|| 'wedding';
|
||||
|
||||
// Each candidate column is paired with the value we'd write. We
|
||||
// ask the DB which columns exist and only keep the matching pairs
|
||||
// — bullet-proof against schema drift in either direction.
|
||||
@@ -1496,7 +1512,7 @@ async function convertToEvent(quoteId, adminId, options = {}) {
|
||||
customer_email: customerEmail,
|
||||
customer_phone: customer.phone,
|
||||
admin_email: adminEmail,
|
||||
event_type: 'wedding',
|
||||
event_type: eventType,
|
||||
password_hash: placeholder,
|
||||
share_link: shareLink,
|
||||
share_token: shareLink,
|
||||
|
||||
Reference in New Issue
Block a user