task-d.5.1
This commit is contained in:
phaichayon
2026-06-24 12:28:04 +07:00
parent c1ecd5ea50
commit 9c75788ba7
10 changed files with 794 additions and 1 deletions

View File

@@ -266,6 +266,33 @@ export const crmCustomerContacts = pgTable('crm_customer_contacts', {
updatedBy: text('updated_by').notNull()
});
export const crmLeads = pgTable(
'crm_leads',
{
id: text('id').primaryKey(),
organizationId: text('organization_id').notNull(),
branchId: text('branch_id'),
code: text('code').notNull(),
customerId: text('customer_id'),
contactId: text('contact_id'),
projectName: text('project_name'),
projectLocation: text('project_location'),
awarenessId: text('awareness_id'),
status: text('status').default('new_job').notNull(),
followupStatus: text('followup_status'),
lostReason: text('lost_reason'),
outcome: text('outcome').default('open').notNull(),
ownerMarketingUserId: text('owner_marketing_user_id'),
createdBy: text('created_by').notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
deletedAt: timestamp('deleted_at', { withTimezone: true })
},
(table) => ({
organizationCodeIdx: uniqueIndex('crm_leads_org_code_idx').on(table.organizationId, table.code)
})
);
export const crmContactShares = pgTable(
'crm_contact_shares',
{
@@ -296,6 +323,7 @@ export const crmEnquiries = pgTable(
id: text('id').primaryKey(),
organizationId: text('organization_id').notNull(),
branchId: text('branch_id'),
leadId: text('lead_id'),
code: text('code').notNull(),
customerId: text('customer_id').notNull(),
contactId: text('contact_id'),

View File

@@ -342,6 +342,105 @@ const FOUNDATION_OPTIONS = {
{ code: 'line', label: 'LINE', value: 'line', sortOrder: 5 },
{ code: 'other', label: 'Other', value: 'other', sortOrder: 6 }
],
crm_lead_awareness: [
{
code: 'google_search_website',
label: 'Google Search / Website',
value: 'google_search_website',
sortOrder: 1
},
{ code: 'social_media', label: 'Social Media', value: 'social_media', sortOrder: 2 },
{
code: 'email_marketing',
label: 'Email Marketing',
value: 'email_marketing',
sortOrder: 3
},
{ code: 'visit', label: 'Visit', value: 'visit', sortOrder: 4 },
{
code: 'exhibition_event',
label: 'Exhibition / Event',
value: 'exhibition_event',
sortOrder: 5
},
{
code: 'brochure_catalog',
label: 'Brochure / Catalog',
value: 'brochure_catalog',
sortOrder: 6
},
{
code: 'word_of_mouth',
label: 'Word of Mouth',
value: 'word_of_mouth',
sortOrder: 7
},
{ code: 'corporate_car', label: 'Corporate Car', value: 'corporate_car', sortOrder: 8 },
{ code: 'product_label', label: 'Product Label', value: 'product_label', sortOrder: 9 },
{ code: 'repurchasing', label: 'Re-purchasing', value: 'repurchasing', sortOrder: 10 },
{ code: 'introducing', label: 'Introducing', value: 'introducing', sortOrder: 11 },
{ code: 'bci', label: 'BCI', value: 'bci', sortOrder: 12 }
],
crm_lead_status: [
{ code: 'new_job', label: 'New Job', value: 'new_job', sortOrder: 1 },
{ code: 'follow_up', label: 'Follow Up', value: 'follow_up', sortOrder: 2 },
{ code: 'closed_lost', label: 'Closed Lost', value: 'closed_lost', sortOrder: 3 },
{ code: 'cancel', label: 'Cancel', value: 'cancel', sortOrder: 4 }
],
crm_lead_followup_status: [
{
code: 'budget_pending',
label: 'Budget Pending',
value: 'budget_pending',
sortOrder: 1
},
{
code: 'project_under_review',
label: 'Project Under Review',
value: 'project_under_review',
sortOrder: 2
},
{
code: 'spec_clarification',
label: 'Spec Clarification',
value: 'spec_clarification',
sortOrder: 3
},
{
code: 'site_survey_waiting',
label: 'Site Survey Waiting',
value: 'site_survey_waiting',
sortOrder: 4
},
{
code: 'drawing_followup',
label: 'Drawing Follow-up',
value: 'drawing_followup',
sortOrder: 5
},
{
code: 'quotation_preparation',
label: 'Quotation Preparation',
value: 'quotation_preparation',
sortOrder: 6
},
{ code: 'waiting_po', label: 'Waiting PO', value: 'waiting_po', sortOrder: 7 }
],
crm_lead_lost_reason: [
{ code: 'price', label: 'Price', value: 'price', sortOrder: 1 },
{ code: 'delivery_time', label: 'Delivery Time', value: 'delivery_time', sortOrder: 2 },
{ code: 'sales_person', label: 'Sales Person', value: 'sales_person', sortOrder: 3 },
{ code: 'service', label: 'Service', value: 'service', sortOrder: 4 },
{ code: 'customer', label: 'Customer', value: 'customer', sortOrder: 5 },
{
code: 'product_quality',
label: 'Product Quality',
value: 'product_quality',
sortOrder: 6
},
{ code: 'other', label: 'Other', value: 'other', sortOrder: 7 },
{ code: 'transfer', label: 'Transfer', value: 'transfer', sortOrder: 8 }
],
crm_report_category: [
{ code: 'pipeline', label: 'Pipeline', value: 'pipeline', sortOrder: 1 },
{ code: 'sales', label: 'Sales', value: 'sales', sortOrder: 2 },
@@ -421,6 +520,8 @@ const DOCUMENT_SEQUENCES = [
{ documentType: 'customer', prefix: 'CUS' },
{ documentType: 'contact', prefix: 'CON' },
{ documentType: 'enquiry', prefix: 'ENQ' },
{ documentType: 'crm_lead', prefix: 'LD' },
{ documentType: 'crm_enquiry', prefix: 'EN' },
{ documentType: 'quotation', prefix: 'QT' },
{ documentType: 'approval', prefix: 'APV' }
];

View File

@@ -16,6 +16,8 @@ const DEFAULT_DOCUMENT_PREFIXES: Record<string, string> = {
customer: 'CUS',
contact: 'CON',
enquiry: 'ENQ',
crm_lead: 'LD',
crm_enquiry: 'EN',
quotation: 'QT',
approval: 'APV'
};