From 0c28735c90e999d983585f021f38be3925a82489 Mon Sep 17 00:00:00 2001 From: phaichayon Date: Wed, 24 Jun 2026 14:23:15 +0700 Subject: [PATCH] task-d.5.3 --- ...ad-assignment-create-enquiry-foundation.md | 108 + drizzle/0000_swift_prism.sql | 657 +++ drizzle/meta/0000_snapshot.json | 3938 ++++++++++++++- drizzle/meta/_journal.json | 151 +- {drizzle => drizzle_bk}/0000_icy_lizard.sql | 0 .../0001_orange_mandarin.sql | 0 {drizzle => drizzle_bk}/0002_plain_anthem.sql | 0 .../0003_blushing_bruce_banner.sql | 0 .../0004_worthless_ender_wiggin.sql | 0 .../0005_numerous_blob.sql | 0 .../0006_worried_mordo.sql | 0 .../0007_luxuriant_malice.sql | 0 .../0008_clean_justin_hammer.sql | 0 {drizzle => drizzle_bk}/0009_lazy_shard.sql | 0 .../0010_calm_wendell_rand.sql | 0 .../0011_brief_hobgoblin.sql | 0 .../0011_goofy_the_hood.sql | 0 .../0012_odd_fat_cobra.sql | 0 {drizzle => drizzle_bk}/0012_simple_bucky.sql | 0 .../0013_great_nicolaos.sql | 0 .../0014_bored_valkyrie.sql | 0 .../0014_crm_user_role_assignments.sql | 0 ...0015_customer_ownership_contact_shares.sql | 0 .../0016_won_lost_lifecycle_governance.sql | 0 .../0017_short_swordsman.sql | 0 .../0018_report_foundation.sql | 0 .../0019_sleepy_ultron.sql | 0 .../0020_lead_foundation_schema.sql | 0 .../0021_worried_nebula.sql | 0 .../0022_lead_assignment_foundation.sql | 9 + drizzle_bk/0023_yellow_carlie_cooper.sql | 8 + drizzle_bk/meta/0000_snapshot.json | 331 ++ .../meta/0001_snapshot.json | 0 .../meta/0002_snapshot.json | 0 .../meta/0003_snapshot.json | 0 .../meta/0004_snapshot.json | 0 .../meta/0005_snapshot.json | 0 .../meta/0006_snapshot.json | 0 .../meta/0007_snapshot.json | 0 .../meta/0008_snapshot.json | 0 .../meta/0009_snapshot.json | 0 .../meta/0010_snapshot.json | 0 .../meta/0011_snapshot.json | 0 .../meta/0012_snapshot.json | 0 .../meta/0013_snapshot.json | 0 .../meta/0014_snapshot.json | 0 .../meta/0017_snapshot.json | 0 .../meta/0019_snapshot.json | 0 .../meta/0021_snapshot.json | 0 drizzle_bk/meta/0023_snapshot.json | 4263 +++++++++++++++++ drizzle_bk/meta/_journal.json | 174 + plans/task-d.5.3.md | 537 +++ plans/task-d.5.4.md | 506 ++ src/app/api/crm/leads/[id]/assign/route.ts | 62 + src/db/schema.ts | 8 + src/db/seeds/foundation.seed.ts | 5 +- src/features/crm/enquiries/server/service.ts | 8 +- src/features/crm/leads/schemas/lead.schema.ts | 15 +- .../crm/leads/server/assignment.service.ts | 300 ++ src/features/crm/leads/server/service.ts | 80 +- src/features/crm/leads/types.ts | 32 + 61 files changed, 11019 insertions(+), 173 deletions(-) create mode 100644 docs/implementation/task-d53-lead-assignment-create-enquiry-foundation.md create mode 100644 drizzle/0000_swift_prism.sql rename {drizzle => drizzle_bk}/0000_icy_lizard.sql (100%) rename {drizzle => drizzle_bk}/0001_orange_mandarin.sql (100%) rename {drizzle => drizzle_bk}/0002_plain_anthem.sql (100%) rename {drizzle => drizzle_bk}/0003_blushing_bruce_banner.sql (100%) rename {drizzle => drizzle_bk}/0004_worthless_ender_wiggin.sql (100%) rename {drizzle => drizzle_bk}/0005_numerous_blob.sql (100%) rename {drizzle => drizzle_bk}/0006_worried_mordo.sql (100%) rename {drizzle => drizzle_bk}/0007_luxuriant_malice.sql (100%) rename {drizzle => drizzle_bk}/0008_clean_justin_hammer.sql (100%) rename {drizzle => drizzle_bk}/0009_lazy_shard.sql (100%) rename {drizzle => drizzle_bk}/0010_calm_wendell_rand.sql (100%) rename {drizzle => drizzle_bk}/0011_brief_hobgoblin.sql (100%) rename {drizzle => drizzle_bk}/0011_goofy_the_hood.sql (100%) rename {drizzle => drizzle_bk}/0012_odd_fat_cobra.sql (100%) rename {drizzle => drizzle_bk}/0012_simple_bucky.sql (100%) rename {drizzle => drizzle_bk}/0013_great_nicolaos.sql (100%) rename {drizzle => drizzle_bk}/0014_bored_valkyrie.sql (100%) rename {drizzle => drizzle_bk}/0014_crm_user_role_assignments.sql (100%) rename {drizzle => drizzle_bk}/0015_customer_ownership_contact_shares.sql (100%) rename {drizzle => drizzle_bk}/0016_won_lost_lifecycle_governance.sql (100%) rename {drizzle => drizzle_bk}/0017_short_swordsman.sql (100%) rename {drizzle => drizzle_bk}/0018_report_foundation.sql (100%) rename {drizzle => drizzle_bk}/0019_sleepy_ultron.sql (100%) rename {drizzle => drizzle_bk}/0020_lead_foundation_schema.sql (100%) rename {drizzle => drizzle_bk}/0021_worried_nebula.sql (100%) create mode 100644 drizzle_bk/0022_lead_assignment_foundation.sql create mode 100644 drizzle_bk/0023_yellow_carlie_cooper.sql create mode 100644 drizzle_bk/meta/0000_snapshot.json rename {drizzle => drizzle_bk}/meta/0001_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0002_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0003_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0004_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0005_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0006_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0007_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0008_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0009_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0010_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0011_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0012_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0013_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0014_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0017_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0019_snapshot.json (100%) rename {drizzle => drizzle_bk}/meta/0021_snapshot.json (100%) create mode 100644 drizzle_bk/meta/0023_snapshot.json create mode 100644 drizzle_bk/meta/_journal.json create mode 100644 plans/task-d.5.3.md create mode 100644 plans/task-d.5.4.md create mode 100644 src/app/api/crm/leads/[id]/assign/route.ts create mode 100644 src/features/crm/leads/server/assignment.service.ts diff --git a/docs/implementation/task-d53-lead-assignment-create-enquiry-foundation.md b/docs/implementation/task-d53-lead-assignment-create-enquiry-foundation.md new file mode 100644 index 0000000..3f8d4fa --- /dev/null +++ b/docs/implementation/task-d53-lead-assignment-create-enquiry-foundation.md @@ -0,0 +1,108 @@ +# Task D.5.3: Lead Assignment -> Create Enquiry Foundation + +## Objective + +Establish the first production lead-assignment flow that preserves `crm_leads` as the +marketing-owned source record while creating or reusing a linked `crm_enquiries` record for the +sales execution workspace. + +## Review Completed + +- `AGENTS.md` +- `docs/standards/project-foundations.md` +- `docs/standards/task-catalog.md` +- `docs/adr/0018-lead-enquiry-domain-separation.md` +- `docs/implementation/task-d5-lead-enquiry-domain-separation.md` +- `docs/implementation/task-d52-lead-domain-service-api-foundation.md` +- `docs/security/crm-authorization-boundaries.md` +- `src/features/crm/leads/server/service.ts` +- `src/features/crm/enquiries/server/service.ts` +- `src/features/crm/customers/server/service.ts` +- `src/features/foundation/audit-log/service.ts` +- `src/features/foundation/document-sequence/service.ts` + +## What Changed + +- Added lead bootstrap and assignment fields to `crm_leads`: + - `description` + - `product_type` + - `priority` + - `estimated_value` + - `assigned_sales_owner_id` + - `assigned_at` + - `assigned_by` + - `assignment_remark` +- Added [assignment.service.ts](C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/features/crm/leads/server/assignment.service.ts) + with: + - `assignLead()` + - internal `createEnquiryFromLead()` +- Added [route.ts](C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/app/api/crm/leads/[id]/assign/route.ts) + for `POST /api/crm/leads/[id]/assign` +- Extended lead DTOs and schemas so new lead records can carry the data needed to bootstrap an + enquiry +- Added `assigned` to seeded `crm_lead_status` +- Tightened enquiry-owner validation so assignees must be sales-assignable members, not merely any + organization member + +## Assignment Behavior + +- route permission is `crm.lead.assign` +- service validates: + - lead exists and is active + - actor can access the lead through resolved CRM scope + - assignee belongs to the same organization and is a sales-assignable member +- if an active enquiry already exists for the lead: + - no second enquiry is created + - the existing enquiry is returned + - lead assignment metadata is synchronized +- if no active enquiry exists: + - enquiry code generation reuses `generateNextDocumentCode()` with `documentType = crm_enquiry` + - enquiry is created with `lead_id = crm_leads.id` + - enquiry owner compatibility fields use the existing enquiry model: + - `assigned_to_user_id` + - `assigned_at` + - `assigned_by` + - `assignment_remark` + +## Lead -> Enquiry Bootstrap Mapping + +When creating the enquiry from a lead, the flow copies: + +- `customerId` +- `contactId` +- `description` +- `projectName` +- `projectLocation` +- `productType` +- `estimatedValue` +- `priority` + +The enquiry title is derived from `lead.projectName` and falls back to `Lead ` when the +project name is empty. + +## Audit Actions + +This task records: + +- `assign_lead` on `crm_lead` +- `create_enquiry_from_lead` on `crm_enquiry` + +Captured linkage includes: + +- `leadId` +- `enquiryId` +- `salesOwnerId` + +## Compatibility Notes + +This task intentionally does not change: + +- quotation flows +- approval flows +- dashboard datasets +- reporting datasets +- won/lost lifecycle flows +- legacy enquiry list/detail APIs outside the stricter assignee validation + +`crm_enquiries.pipeline_stage` remains in place for compatibility, but lead assignment now creates a +dedicated enquiry record instead of mutating a lead record in place. diff --git a/drizzle/0000_swift_prism.sql b/drizzle/0000_swift_prism.sql new file mode 100644 index 0000000..16af9c4 --- /dev/null +++ b/drizzle/0000_swift_prism.sql @@ -0,0 +1,657 @@ +CREATE TABLE "crm_approval_actions" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "approval_request_id" text NOT NULL, + "step_number" integer NOT NULL, + "action" text NOT NULL, + "remark" text, + "acted_by" text NOT NULL, + "acted_at" timestamp with time zone DEFAULT now() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_approval_requests" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "workflow_id" text NOT NULL, + "entity_type" text NOT NULL, + "entity_id" text NOT NULL, + "current_step" integer DEFAULT 1 NOT NULL, + "status" text NOT NULL, + "requested_by" text NOT NULL, + "requested_at" timestamp with time zone DEFAULT now() NOT NULL, + "completed_at" timestamp with time zone, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_approval_steps" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "workflow_id" text NOT NULL, + "step_number" integer NOT NULL, + "role_code" text NOT NULL, + "role_name" text NOT NULL, + "is_required" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_approval_workflows" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "code" text NOT NULL, + "name" text NOT NULL, + "entity_type" text NOT NULL, + "is_active" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_contact_shares" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "contact_id" text NOT NULL, + "shared_to_user_id" text NOT NULL, + "shared_by_user_id" text NOT NULL, + "shared_at" timestamp with time zone DEFAULT now() NOT NULL, + "is_active" boolean DEFAULT true NOT NULL, + "remark" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_customer_contacts" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "customer_id" text NOT NULL, + "name" text NOT NULL, + "position" text, + "department" text, + "phone" text, + "mobile" text, + "email" text, + "is_primary" boolean DEFAULT false NOT NULL, + "notes" text, + "is_active" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone, + "created_by" text NOT NULL, + "updated_by" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "crm_customer_owner_history" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "customer_id" text NOT NULL, + "old_owner_user_id" text, + "new_owner_user_id" text, + "changed_by" text NOT NULL, + "changed_at" timestamp with time zone DEFAULT now() NOT NULL, + "remark" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_customers" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "branch_id" text, + "code" text NOT NULL, + "name" text NOT NULL, + "abbr" text, + "tax_id" text, + "customer_type" text NOT NULL, + "customer_status" text NOT NULL, + "address" text, + "province" text, + "district" text, + "sub_district" text, + "postal_code" text, + "country" text, + "phone" text, + "fax" text, + "email" text, + "website" text, + "lead_channel" text, + "customer_group" text, + "customer_sub_group" text, + "owner_user_id" text, + "owner_assigned_at" timestamp with time zone, + "owner_assigned_by" text, + "notes" text, + "is_active" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone, + "created_by" text NOT NULL, + "updated_by" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "crm_document_artifacts" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "entity_type" text NOT NULL, + "entity_id" text NOT NULL, + "document_type" text NOT NULL, + "artifact_type" text NOT NULL, + "template_version_id" text, + "storage_provider" text NOT NULL, + "storage_key" text NOT NULL, + "file_name" text NOT NULL, + "content_type" text NOT NULL, + "file_size" integer, + "checksum" text, + "status" text DEFAULT 'active' NOT NULL, + "generated_by" text NOT NULL, + "generated_at" timestamp with time zone DEFAULT now() NOT NULL, + "locked_at" timestamp with time zone, + "locked_by" text, + "voided_at" timestamp with time zone, + "voided_by" text, + "void_reason" text, + "metadata" jsonb, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_document_template_mappings" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "template_version_id" text NOT NULL, + "placeholder_key" text NOT NULL, + "source_path" text NOT NULL, + "data_type" text NOT NULL, + "sheet_name" text, + "default_value" text, + "format_mask" text, + "sort_order" integer DEFAULT 0 NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_document_template_table_columns" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "mapping_id" text NOT NULL, + "column_name" text NOT NULL, + "source_field" text NOT NULL, + "column_letter" text, + "sort_order" integer DEFAULT 0 NOT NULL, + "format_mask" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_document_template_versions" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "template_id" text NOT NULL, + "version" text NOT NULL, + "file_path" text, + "schema_json" jsonb NOT NULL, + "preview_image_url" text, + "is_active" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone, + "created_by" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "crm_document_templates" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "document_type" text NOT NULL, + "product_type" text NOT NULL, + "file_type" text NOT NULL, + "template_name" text NOT NULL, + "description" text, + "is_default" boolean DEFAULT false NOT NULL, + "is_active" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone, + "created_by" text NOT NULL, + "updated_by" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "crm_enquiries" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "branch_id" text, + "lead_id" text, + "code" text NOT NULL, + "customer_id" text NOT NULL, + "contact_id" text, + "title" text NOT NULL, + "description" text, + "requirement" text, + "project_name" text, + "project_location" text, + "product_type" text NOT NULL, + "status" text NOT NULL, + "priority" text NOT NULL, + "lead_channel" text, + "estimated_value" double precision, + "chance_percent" integer, + "expected_close_date" timestamp with time zone, + "competitor" text, + "source" text, + "notes" text, + "is_hot_project" boolean DEFAULT false NOT NULL, + "is_active" boolean DEFAULT true NOT NULL, + "pipeline_stage" text DEFAULT 'lead' NOT NULL, + "closed_won_at" timestamp with time zone, + "closed_lost_at" timestamp with time zone, + "closed_by_user_id" text, + "po_number" text, + "po_date" timestamp with time zone, + "po_amount" double precision, + "po_currency" text, + "lost_reason" text, + "lost_competitor" text, + "lost_remark" text, + "assigned_to_user_id" text, + "assigned_at" timestamp with time zone, + "assigned_by" text, + "assignment_remark" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone, + "created_by" text NOT NULL, + "updated_by" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "crm_enquiry_attachments" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "enquiry_id" text NOT NULL, + "category" text NOT NULL, + "file_name" text NOT NULL, + "original_file_name" text NOT NULL, + "storage_provider" text NOT NULL, + "storage_key" text NOT NULL, + "file_size" integer, + "file_type" text, + "description" text, + "uploaded_at" timestamp with time zone DEFAULT now() NOT NULL, + "uploaded_by" text NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_enquiry_customers" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "enquiry_id" text NOT NULL, + "customer_id" text NOT NULL, + "role" text NOT NULL, + "remark" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_enquiry_followups" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "enquiry_id" text NOT NULL, + "followup_date" timestamp with time zone NOT NULL, + "followup_type" text NOT NULL, + "contact_id" text, + "outcome" text, + "notes" text, + "next_followup_date" timestamp with time zone, + "next_action" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone, + "created_by" text NOT NULL, + "updated_by" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "crm_leads" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "branch_id" text, + "code" text NOT NULL, + "customer_id" text, + "contact_id" text, + "description" text, + "project_name" text, + "project_location" text, + "product_type" text, + "priority" text, + "estimated_value" double precision, + "awareness_id" text, + "status" text DEFAULT 'new_job' NOT NULL, + "followup_status" text, + "lost_reason" text, + "outcome" text DEFAULT 'open' NOT NULL, + "owner_marketing_user_id" text, + "assigned_sales_owner_id" text, + "assigned_at" timestamp with time zone, + "assigned_by" text, + "assignment_remark" text, + "created_by" text NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_quotation_attachments" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "quotation_id" text NOT NULL, + "file_name" text NOT NULL, + "original_file_name" text NOT NULL, + "file_path" text NOT NULL, + "file_size" integer, + "file_type" text, + "description" text, + "uploaded_at" timestamp with time zone DEFAULT now() NOT NULL, + "uploaded_by" text NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_quotation_customers" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "quotation_id" text NOT NULL, + "customer_id" text NOT NULL, + "role" text NOT NULL, + "is_primary" boolean DEFAULT false NOT NULL, + "remark" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_quotation_followups" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "quotation_id" text NOT NULL, + "followup_date" timestamp with time zone NOT NULL, + "followup_type" text NOT NULL, + "contact_id" text, + "outcome" text, + "notes" text, + "next_followup_date" timestamp with time zone, + "next_action" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone, + "created_by" text NOT NULL, + "updated_by" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "crm_quotation_items" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "quotation_id" text NOT NULL, + "item_number" integer NOT NULL, + "product_type" text NOT NULL, + "description" text NOT NULL, + "quantity" double precision DEFAULT 0 NOT NULL, + "unit" text, + "unit_price" double precision DEFAULT 0 NOT NULL, + "discount" double precision DEFAULT 0 NOT NULL, + "discount_type" text, + "tax_rate" double precision DEFAULT 0 NOT NULL, + "total_price" double precision DEFAULT 0 NOT NULL, + "notes" text, + "sort_order" integer DEFAULT 0 NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_quotation_topic_items" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "topic_id" text NOT NULL, + "content" text NOT NULL, + "sort_order" integer DEFAULT 0 NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_quotation_topics" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "quotation_id" text NOT NULL, + "topic_type" text NOT NULL, + "title" text NOT NULL, + "sort_order" integer DEFAULT 0 NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "crm_quotations" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "branch_id" text, + "code" text NOT NULL, + "enquiry_id" text, + "customer_id" text NOT NULL, + "contact_id" text, + "quotation_date" timestamp with time zone NOT NULL, + "valid_until" timestamp with time zone, + "quotation_type" text NOT NULL, + "project_name" text, + "project_location" text, + "attention" text, + "reference" text, + "notes" text, + "status" text NOT NULL, + "revision" integer DEFAULT 0 NOT NULL, + "parent_quotation_id" text, + "revision_remark" text, + "currency" text NOT NULL, + "exchange_rate" double precision DEFAULT 1 NOT NULL, + "subtotal" double precision DEFAULT 0 NOT NULL, + "discount" double precision DEFAULT 0 NOT NULL, + "discount_type" text, + "tax_rate" double precision DEFAULT 0 NOT NULL, + "tax_amount" double precision DEFAULT 0 NOT NULL, + "total_amount" double precision DEFAULT 0 NOT NULL, + "chance_percent" integer, + "is_hot_project" boolean DEFAULT false NOT NULL, + "competitor" text, + "salesman_id" text, + "is_sent" boolean DEFAULT false NOT NULL, + "sent_at" timestamp with time zone, + "sent_via" text, + "approved_at" timestamp with time zone, + "approved_artifact_id" text, + "approved_pdf_url" text, + "approved_snapshot" jsonb, + "approved_template_version_id" text, + "accepted_at" timestamp with time zone, + "rejected_at" timestamp with time zone, + "rejection_reason" text, + "is_active" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone, + "created_by" text NOT NULL, + "updated_by" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "crm_report_definitions" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "code" text NOT NULL, + "name" text NOT NULL, + "description" text, + "category" text NOT NULL, + "is_system" boolean DEFAULT true NOT NULL, + "is_active" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "crm_role_profiles" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "code" text NOT NULL, + "name" text NOT NULL, + "description" text, + "permissions" text[] DEFAULT '{}' NOT NULL, + "branch_scope_mode" text DEFAULT 'assigned' NOT NULL, + "product_scope_mode" text DEFAULT 'assigned' NOT NULL, + "ownership_scope" text DEFAULT 'own' NOT NULL, + "approval_authority" text DEFAULT 'none' NOT NULL, + "is_system" boolean DEFAULT false NOT NULL, + "is_active" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone, + "created_by" text NOT NULL, + "updated_by" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "crm_user_role_assignments" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "user_id" text NOT NULL, + "role_profile_id" text NOT NULL, + "branch_scope_mode" text DEFAULT 'inherit' NOT NULL, + "branch_scope_ids" text[] DEFAULT '{}' NOT NULL, + "product_type_scope_mode" text DEFAULT 'inherit' NOT NULL, + "product_type_scope_ids" text[] DEFAULT '{}' NOT NULL, + "is_primary" boolean DEFAULT false NOT NULL, + "is_active" boolean DEFAULT true NOT NULL, + "assigned_by" text NOT NULL, + "assigned_at" timestamp with time zone DEFAULT now() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "deleted_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "document_sequences" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "branch_id" text DEFAULT '' NOT NULL, + "document_type" text NOT NULL, + "prefix" text NOT NULL, + "period" text NOT NULL, + "current_number" integer DEFAULT 0 NOT NULL, + "padding_length" integer DEFAULT 3 NOT NULL, + "is_active" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "memberships" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "organization_id" text NOT NULL, + "role" text DEFAULT 'user' NOT NULL, + "business_role" text DEFAULT 'sales_support' NOT NULL, + "permissions" text[] DEFAULT '{}' NOT NULL, + "branch_scope_ids" text[] DEFAULT '{}' NOT NULL, + "product_type_scope_ids" text[] DEFAULT '{}' NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "ms_options" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "category" text NOT NULL, + "code" text NOT NULL, + "label" text NOT NULL, + "value" text, + "parent_id" text, + "sort_order" integer DEFAULT 0 NOT NULL, + "is_active" boolean DEFAULT true NOT NULL, + "metadata" jsonb, + "deleted_at" timestamp with time zone, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "organizations" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "slug" text NOT NULL, + "image_url" text, + "plan" text DEFAULT 'free' NOT NULL, + "created_by" text NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "products" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "products_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "organization_id" text NOT NULL, + "name" text NOT NULL, + "category" text NOT NULL, + "description" text NOT NULL, + "photo_url" text NOT NULL, + "price" double precision NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "tr_audit_logs" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "branch_id" text, + "user_id" text NOT NULL, + "entity_type" text NOT NULL, + "entity_id" text NOT NULL, + "action" text NOT NULL, + "before_data" jsonb, + "after_data" jsonb, + "request_id" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "users" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "email" text NOT NULL, + "password_hash" text NOT NULL, + "system_role" text DEFAULT 'user' NOT NULL, + "image" text, + "active_organization_id" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX "crm_approval_steps_workflow_step_idx" ON "crm_approval_steps" USING btree ("workflow_id","step_number");--> statement-breakpoint +CREATE UNIQUE INDEX "crm_approval_workflows_org_code_idx" ON "crm_approval_workflows" USING btree ("organization_id","code");--> statement-breakpoint +CREATE UNIQUE INDEX "crm_contact_shares_org_contact_shared_user_idx" ON "crm_contact_shares" USING btree ("organization_id","contact_id","shared_to_user_id");--> statement-breakpoint +CREATE UNIQUE INDEX "crm_customers_org_code_idx" ON "crm_customers" USING btree ("organization_id","code");--> statement-breakpoint +CREATE UNIQUE INDEX "crm_document_template_mappings_version_placeholder_idx" ON "crm_document_template_mappings" USING btree ("template_version_id","placeholder_key");--> statement-breakpoint +CREATE UNIQUE INDEX "crm_document_template_table_columns_mapping_name_idx" ON "crm_document_template_table_columns" USING btree ("mapping_id","column_name");--> statement-breakpoint +CREATE UNIQUE INDEX "crm_document_template_versions_template_version_idx" ON "crm_document_template_versions" USING btree ("template_id","version");--> statement-breakpoint +CREATE UNIQUE INDEX "crm_document_templates_org_doc_product_file_name_idx" ON "crm_document_templates" USING btree ("organization_id","document_type","product_type","file_type","template_name");--> statement-breakpoint +CREATE UNIQUE INDEX "crm_enquiries_org_code_idx" ON "crm_enquiries" USING btree ("organization_id","code");--> statement-breakpoint +CREATE UNIQUE INDEX "crm_leads_org_code_idx" ON "crm_leads" USING btree ("organization_id","code");--> statement-breakpoint +CREATE UNIQUE INDEX "crm_quotations_org_code_idx" ON "crm_quotations" USING btree ("organization_id","code");--> statement-breakpoint +CREATE UNIQUE INDEX "crm_report_definitions_org_code_idx" ON "crm_report_definitions" USING btree ("organization_id","code");--> statement-breakpoint +CREATE UNIQUE INDEX "crm_role_profiles_org_code_idx" ON "crm_role_profiles" USING btree ("organization_id","code");--> statement-breakpoint +CREATE UNIQUE INDEX "crm_user_role_assignments_org_user_role_idx" ON "crm_user_role_assignments" USING btree ("organization_id","user_id","role_profile_id");--> statement-breakpoint +CREATE UNIQUE INDEX "document_sequences_org_doc_period_branch_idx" ON "document_sequences" USING btree ("organization_id","document_type","period","branch_id");--> statement-breakpoint +CREATE UNIQUE INDEX "ms_options_org_category_code_idx" ON "ms_options" USING btree ("organization_id","category","code");--> statement-breakpoint +CREATE UNIQUE INDEX "organizations_slug_idx" ON "organizations" USING btree ("slug");--> statement-breakpoint +CREATE UNIQUE INDEX "users_email_idx" ON "users" USING btree ("email"); \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json index 0aeadcc..eb35bd1 100644 --- a/drizzle/meta/0000_snapshot.json +++ b/drizzle/meta/0000_snapshot.json @@ -1,9 +1,3724 @@ { - "id": "1e236d6a-ca42-4830-b230-67a7c233d87b", + "id": "5b317a18-e2f9-48c7-a371-c6a972e13344", "prevId": "00000000-0000-0000-0000-000000000000", "version": "7", "dialect": "postgresql", "tables": { + "public.crm_approval_actions": { + "name": "crm_approval_actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "approval_request_id": { + "name": "approval_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "step_number": { + "name": "step_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acted_by": { + "name": "acted_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "acted_at": { + "name": "acted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_approval_requests": { + "name": "crm_approval_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_step": { + "name": "current_step", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_by": { + "name": "requested_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_approval_steps": { + "name": "crm_approval_steps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "step_number": { + "name": "step_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "role_code": { + "name": "role_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role_name": { + "name": "role_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_required": { + "name": "is_required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_approval_steps_workflow_step_idx": { + "name": "crm_approval_steps_workflow_step_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "step_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_approval_workflows": { + "name": "crm_approval_workflows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_approval_workflows_org_code_idx": { + "name": "crm_approval_workflows_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_contact_shares": { + "name": "crm_contact_shares", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "shared_to_user_id": { + "name": "shared_to_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "shared_by_user_id": { + "name": "shared_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "shared_at": { + "name": "shared_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_contact_shares_org_contact_shared_user_idx": { + "name": "crm_contact_shares_org_contact_shared_user_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "contact_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "shared_to_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_customer_contacts": { + "name": "crm_customer_contacts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "department": { + "name": "department", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mobile": { + "name": "mobile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_primary": { + "name": "is_primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_customer_owner_history": { + "name": "crm_customer_owner_history", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "old_owner_user_id": { + "name": "old_owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "new_owner_user_id": { + "name": "new_owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "changed_by": { + "name": "changed_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "changed_at": { + "name": "changed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_customers": { + "name": "crm_customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_id": { + "name": "branch_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "abbr": { + "name": "abbr", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tax_id": { + "name": "tax_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_type": { + "name": "customer_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_status": { + "name": "customer_status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "province": { + "name": "province", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "district": { + "name": "district", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sub_district": { + "name": "sub_district", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postal_code": { + "name": "postal_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country": { + "name": "country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fax": { + "name": "fax", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lead_channel": { + "name": "lead_channel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_group": { + "name": "customer_group", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_sub_group": { + "name": "customer_sub_group", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_assigned_at": { + "name": "owner_assigned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "owner_assigned_by": { + "name": "owner_assigned_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "crm_customers_org_code_idx": { + "name": "crm_customers_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_document_artifacts": { + "name": "crm_document_artifacts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_type": { + "name": "document_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "artifact_type": { + "name": "artifact_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_version_id": { + "name": "template_version_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "storage_provider": { + "name": "storage_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "checksum": { + "name": "checksum", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "generated_by": { + "name": "generated_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "generated_at": { + "name": "generated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "locked_by": { + "name": "locked_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "voided_at": { + "name": "voided_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "voided_by": { + "name": "voided_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "void_reason": { + "name": "void_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_document_template_mappings": { + "name": "crm_document_template_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_version_id": { + "name": "template_version_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "placeholder_key": { + "name": "placeholder_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_path": { + "name": "source_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data_type": { + "name": "data_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sheet_name": { + "name": "sheet_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "default_value": { + "name": "default_value", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "format_mask": { + "name": "format_mask", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_document_template_mappings_version_placeholder_idx": { + "name": "crm_document_template_mappings_version_placeholder_idx", + "columns": [ + { + "expression": "template_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "placeholder_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_document_template_table_columns": { + "name": "crm_document_template_table_columns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mapping_id": { + "name": "mapping_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "column_name": { + "name": "column_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_field": { + "name": "source_field", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "column_letter": { + "name": "column_letter", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "format_mask": { + "name": "format_mask", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_document_template_table_columns_mapping_name_idx": { + "name": "crm_document_template_table_columns_mapping_name_idx", + "columns": [ + { + "expression": "mapping_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "column_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_document_template_versions": { + "name": "crm_document_template_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "schema_json": { + "name": "schema_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "preview_image_url": { + "name": "preview_image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "crm_document_template_versions_template_version_idx": { + "name": "crm_document_template_versions_template_version_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_document_templates": { + "name": "crm_document_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_type": { + "name": "document_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "product_type": { + "name": "product_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_type": { + "name": "file_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_name": { + "name": "template_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "crm_document_templates_org_doc_product_file_name_idx": { + "name": "crm_document_templates_org_doc_product_file_name_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "file_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "template_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_enquiries": { + "name": "crm_enquiries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_id": { + "name": "branch_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lead_id": { + "name": "lead_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requirement": { + "name": "requirement", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_name": { + "name": "project_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_location": { + "name": "project_location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "product_type": { + "name": "product_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lead_channel": { + "name": "lead_channel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estimated_value": { + "name": "estimated_value", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "chance_percent": { + "name": "chance_percent", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "expected_close_date": { + "name": "expected_close_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "competitor": { + "name": "competitor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_hot_project": { + "name": "is_hot_project", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "pipeline_stage": { + "name": "pipeline_stage", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'lead'" + }, + "closed_won_at": { + "name": "closed_won_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "closed_lost_at": { + "name": "closed_lost_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "closed_by_user_id": { + "name": "closed_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "po_number": { + "name": "po_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "po_date": { + "name": "po_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "po_amount": { + "name": "po_amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "po_currency": { + "name": "po_currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lost_reason": { + "name": "lost_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lost_competitor": { + "name": "lost_competitor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lost_remark": { + "name": "lost_remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assigned_to_user_id": { + "name": "assigned_to_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assigned_at": { + "name": "assigned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "assigned_by": { + "name": "assigned_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assignment_remark": { + "name": "assignment_remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "crm_enquiries_org_code_idx": { + "name": "crm_enquiries_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_enquiry_attachments": { + "name": "crm_enquiry_attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enquiry_id": { + "name": "enquiry_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_file_name": { + "name": "original_file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_provider": { + "name": "storage_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "file_type": { + "name": "file_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "uploaded_by": { + "name": "uploaded_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_enquiry_customers": { + "name": "crm_enquiry_customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enquiry_id": { + "name": "enquiry_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_enquiry_followups": { + "name": "crm_enquiry_followups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enquiry_id": { + "name": "enquiry_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "followup_date": { + "name": "followup_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "followup_type": { + "name": "followup_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_followup_date": { + "name": "next_followup_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_action": { + "name": "next_action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_leads": { + "name": "crm_leads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_id": { + "name": "branch_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_name": { + "name": "project_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_location": { + "name": "project_location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "product_type": { + "name": "product_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estimated_value": { + "name": "estimated_value", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "awareness_id": { + "name": "awareness_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'new_job'" + }, + "followup_status": { + "name": "followup_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lost_reason": { + "name": "lost_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "owner_marketing_user_id": { + "name": "owner_marketing_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assigned_sales_owner_id": { + "name": "assigned_sales_owner_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assigned_at": { + "name": "assigned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "assigned_by": { + "name": "assigned_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assignment_remark": { + "name": "assignment_remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_leads_org_code_idx": { + "name": "crm_leads_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotation_attachments": { + "name": "crm_quotation_attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quotation_id": { + "name": "quotation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_file_name": { + "name": "original_file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "file_type": { + "name": "file_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "uploaded_by": { + "name": "uploaded_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotation_customers": { + "name": "crm_quotation_customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quotation_id": { + "name": "quotation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_primary": { + "name": "is_primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotation_followups": { + "name": "crm_quotation_followups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quotation_id": { + "name": "quotation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "followup_date": { + "name": "followup_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "followup_type": { + "name": "followup_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_followup_date": { + "name": "next_followup_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_action": { + "name": "next_action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotation_items": { + "name": "crm_quotation_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quotation_id": { + "name": "quotation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "item_number": { + "name": "item_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "product_type": { + "name": "product_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "unit": { + "name": "unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "unit_price": { + "name": "unit_price", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "discount": { + "name": "discount", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "discount_type": { + "name": "discount_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tax_rate": { + "name": "tax_rate", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_price": { + "name": "total_price", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotation_topic_items": { + "name": "crm_quotation_topic_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic_id": { + "name": "topic_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotation_topics": { + "name": "crm_quotation_topics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quotation_id": { + "name": "quotation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic_type": { + "name": "topic_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotations": { + "name": "crm_quotations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_id": { + "name": "branch_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enquiry_id": { + "name": "enquiry_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "quotation_date": { + "name": "quotation_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "valid_until": { + "name": "valid_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "quotation_type": { + "name": "quotation_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "project_name": { + "name": "project_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_location": { + "name": "project_location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attention": { + "name": "attention", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference": { + "name": "reference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "parent_quotation_id": { + "name": "parent_quotation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revision_remark": { + "name": "revision_remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "exchange_rate": { + "name": "exchange_rate", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "subtotal": { + "name": "subtotal", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "discount": { + "name": "discount", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "discount_type": { + "name": "discount_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tax_rate": { + "name": "tax_rate", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "tax_amount": { + "name": "tax_amount", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_amount": { + "name": "total_amount", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "chance_percent": { + "name": "chance_percent", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_hot_project": { + "name": "is_hot_project", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "competitor": { + "name": "competitor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "salesman_id": { + "name": "salesman_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_sent": { + "name": "is_sent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sent_via": { + "name": "sent_via", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "approved_artifact_id": { + "name": "approved_artifact_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approved_pdf_url": { + "name": "approved_pdf_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approved_snapshot": { + "name": "approved_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "approved_template_version_id": { + "name": "approved_template_version_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accepted_at": { + "name": "accepted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rejected_at": { + "name": "rejected_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rejection_reason": { + "name": "rejection_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "crm_quotations_org_code_idx": { + "name": "crm_quotations_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_report_definitions": { + "name": "crm_report_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_system": { + "name": "is_system", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "crm_report_definitions_org_code_idx": { + "name": "crm_report_definitions_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_role_profiles": { + "name": "crm_role_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permissions": { + "name": "permissions", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "branch_scope_mode": { + "name": "branch_scope_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'assigned'" + }, + "product_scope_mode": { + "name": "product_scope_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'assigned'" + }, + "ownership_scope": { + "name": "ownership_scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'own'" + }, + "approval_authority": { + "name": "approval_authority", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "is_system": { + "name": "is_system", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "crm_role_profiles_org_code_idx": { + "name": "crm_role_profiles_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_user_role_assignments": { + "name": "crm_user_role_assignments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role_profile_id": { + "name": "role_profile_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_scope_mode": { + "name": "branch_scope_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'inherit'" + }, + "branch_scope_ids": { + "name": "branch_scope_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "product_type_scope_mode": { + "name": "product_type_scope_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'inherit'" + }, + "product_type_scope_ids": { + "name": "product_type_scope_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "is_primary": { + "name": "is_primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "assigned_by": { + "name": "assigned_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assigned_at": { + "name": "assigned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_user_role_assignments_org_user_role_idx": { + "name": "crm_user_role_assignments_org_user_role_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "role_profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.document_sequences": { + "name": "document_sequences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_id": { + "name": "branch_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "document_type": { + "name": "document_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "period": { + "name": "period", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_number": { + "name": "current_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "padding_length": { + "name": "padding_length", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "document_sequences_org_doc_period_branch_idx": { + "name": "document_sequences_org_doc_period_branch_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "branch_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.memberships": { "name": "memberships", "schema": "", @@ -38,7 +3753,7 @@ "type": "text", "primaryKey": false, "notNull": true, - "default": "'viewer'" + "default": "'sales_support'" }, "permissions": { "name": "permissions", @@ -47,6 +3762,20 @@ "notNull": true, "default": "'{}'" }, + "branch_scope_ids": { + "name": "branch_scope_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "product_type_scope_ids": { + "name": "product_type_scope_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, "created_at": { "name": "created_at", "type": "timestamp with time zone", @@ -70,6 +3799,129 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.ms_options": { + "name": "ms_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ms_options_org_category_code_idx": { + "name": "ms_options_org_category_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.organizations": { "name": "organizations", "schema": "", @@ -230,6 +4082,86 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.tr_audit_logs": { + "name": "tr_audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_id": { + "name": "branch_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "before_data": { + "name": "before_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "after_data": { + "name": "after_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.users": { "name": "users", "schema": "", @@ -328,4 +4260,4 @@ "schemas": {}, "tables": {} } -} +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 2faf618..daa9086 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -5,155 +5,8 @@ { "idx": 0, "version": "7", - "when": 1781148450992, - "tag": "0000_icy_lizard", - "breakpoints": true - }, - { - "idx": 1, - "version": "7", - "when": 1781171200406, - "tag": "0001_orange_mandarin", - "breakpoints": true - }, - { - "idx": 2, - "version": "7", - "when": 1781498575433, - "tag": "0002_plain_anthem", - "breakpoints": true - }, - { - "idx": 3, - "version": "7", - "when": 1781505431077, - "tag": "0003_blushing_bruce_banner", - "breakpoints": true - }, - { - "idx": 4, - "version": "7", - "when": 1781511534267, - "tag": "0004_worthless_ender_wiggin", - "breakpoints": true - }, - { - "idx": 5, - "version": "7", - "when": 1781575976324, - "tag": "0005_numerous_blob", - "breakpoints": true - }, - { - "idx": 6, - "version": "7", - "when": 1781579746622, - "tag": "0006_worried_mordo", - "breakpoints": true - }, - { - "idx": 7, - "version": "7", - "when": 1781589458455, - "tag": "0007_luxuriant_malice", - "breakpoints": true - }, - { - "idx": 8, - "version": "7", - "when": 1781595962545, - "tag": "0008_clean_justin_hammer", - "breakpoints": true - }, - { - "idx": 9, - "version": "7", - "when": 1781603421415, - "tag": "0009_lazy_shard", - "breakpoints": true - }, - { - "idx": 10, - "version": "7", - "when": 1781675409847, - "tag": "0010_calm_wendell_rand", - "breakpoints": true - }, - { - "idx": 11, - "version": "7", - "when": 1781747812297, - "tag": "0011_goofy_the_hood", - "breakpoints": true - }, - { - "idx": 12, - "version": "7", - "when": 1781836261952, - "tag": "0012_simple_bucky", - "breakpoints": true - }, - { - "idx": 13, - "version": "7", - "when": 1782097963376, - "tag": "0013_great_nicolaos", - "breakpoints": true - }, - { - "idx": 14, - "version": "7", - "when": 1782099743528, - "tag": "0014_bored_valkyrie", - "breakpoints": true - }, - { - "idx": 15, - "version": "7", - "when": 1782549600000, - "tag": "0015_customer_ownership_contact_shares", - "breakpoints": true - }, - { - "idx": 16, - "version": "7", - "when": 1782795600000, - "tag": "0016_won_lost_lifecycle_governance", - "breakpoints": true - }, - { - "idx": 17, - "version": "7", - "when": 1782121929212, - "tag": "0017_short_swordsman", - "breakpoints": true - }, - { - "idx": 18, - "version": "7", - "when": 1782799200000, - "tag": "0018_report_foundation", - "breakpoints": true - }, - { - "idx": 19, - "version": "7", - "when": 1782137095907, - "tag": "0019_sleepy_ultron", - "breakpoints": true - }, - { - "idx": 20, - "version": "7", - "when": 1782262800000, - "tag": "0020_lead_foundation_schema", - "breakpoints": true - }, - { - "idx": 21, - "version": "7", - "when": 1782279100730, - "tag": "0021_worried_nebula", + "when": 1782285058938, + "tag": "0000_swift_prism", "breakpoints": true } ] diff --git a/drizzle/0000_icy_lizard.sql b/drizzle_bk/0000_icy_lizard.sql similarity index 100% rename from drizzle/0000_icy_lizard.sql rename to drizzle_bk/0000_icy_lizard.sql diff --git a/drizzle/0001_orange_mandarin.sql b/drizzle_bk/0001_orange_mandarin.sql similarity index 100% rename from drizzle/0001_orange_mandarin.sql rename to drizzle_bk/0001_orange_mandarin.sql diff --git a/drizzle/0002_plain_anthem.sql b/drizzle_bk/0002_plain_anthem.sql similarity index 100% rename from drizzle/0002_plain_anthem.sql rename to drizzle_bk/0002_plain_anthem.sql diff --git a/drizzle/0003_blushing_bruce_banner.sql b/drizzle_bk/0003_blushing_bruce_banner.sql similarity index 100% rename from drizzle/0003_blushing_bruce_banner.sql rename to drizzle_bk/0003_blushing_bruce_banner.sql diff --git a/drizzle/0004_worthless_ender_wiggin.sql b/drizzle_bk/0004_worthless_ender_wiggin.sql similarity index 100% rename from drizzle/0004_worthless_ender_wiggin.sql rename to drizzle_bk/0004_worthless_ender_wiggin.sql diff --git a/drizzle/0005_numerous_blob.sql b/drizzle_bk/0005_numerous_blob.sql similarity index 100% rename from drizzle/0005_numerous_blob.sql rename to drizzle_bk/0005_numerous_blob.sql diff --git a/drizzle/0006_worried_mordo.sql b/drizzle_bk/0006_worried_mordo.sql similarity index 100% rename from drizzle/0006_worried_mordo.sql rename to drizzle_bk/0006_worried_mordo.sql diff --git a/drizzle/0007_luxuriant_malice.sql b/drizzle_bk/0007_luxuriant_malice.sql similarity index 100% rename from drizzle/0007_luxuriant_malice.sql rename to drizzle_bk/0007_luxuriant_malice.sql diff --git a/drizzle/0008_clean_justin_hammer.sql b/drizzle_bk/0008_clean_justin_hammer.sql similarity index 100% rename from drizzle/0008_clean_justin_hammer.sql rename to drizzle_bk/0008_clean_justin_hammer.sql diff --git a/drizzle/0009_lazy_shard.sql b/drizzle_bk/0009_lazy_shard.sql similarity index 100% rename from drizzle/0009_lazy_shard.sql rename to drizzle_bk/0009_lazy_shard.sql diff --git a/drizzle/0010_calm_wendell_rand.sql b/drizzle_bk/0010_calm_wendell_rand.sql similarity index 100% rename from drizzle/0010_calm_wendell_rand.sql rename to drizzle_bk/0010_calm_wendell_rand.sql diff --git a/drizzle/0011_brief_hobgoblin.sql b/drizzle_bk/0011_brief_hobgoblin.sql similarity index 100% rename from drizzle/0011_brief_hobgoblin.sql rename to drizzle_bk/0011_brief_hobgoblin.sql diff --git a/drizzle/0011_goofy_the_hood.sql b/drizzle_bk/0011_goofy_the_hood.sql similarity index 100% rename from drizzle/0011_goofy_the_hood.sql rename to drizzle_bk/0011_goofy_the_hood.sql diff --git a/drizzle/0012_odd_fat_cobra.sql b/drizzle_bk/0012_odd_fat_cobra.sql similarity index 100% rename from drizzle/0012_odd_fat_cobra.sql rename to drizzle_bk/0012_odd_fat_cobra.sql diff --git a/drizzle/0012_simple_bucky.sql b/drizzle_bk/0012_simple_bucky.sql similarity index 100% rename from drizzle/0012_simple_bucky.sql rename to drizzle_bk/0012_simple_bucky.sql diff --git a/drizzle/0013_great_nicolaos.sql b/drizzle_bk/0013_great_nicolaos.sql similarity index 100% rename from drizzle/0013_great_nicolaos.sql rename to drizzle_bk/0013_great_nicolaos.sql diff --git a/drizzle/0014_bored_valkyrie.sql b/drizzle_bk/0014_bored_valkyrie.sql similarity index 100% rename from drizzle/0014_bored_valkyrie.sql rename to drizzle_bk/0014_bored_valkyrie.sql diff --git a/drizzle/0014_crm_user_role_assignments.sql b/drizzle_bk/0014_crm_user_role_assignments.sql similarity index 100% rename from drizzle/0014_crm_user_role_assignments.sql rename to drizzle_bk/0014_crm_user_role_assignments.sql diff --git a/drizzle/0015_customer_ownership_contact_shares.sql b/drizzle_bk/0015_customer_ownership_contact_shares.sql similarity index 100% rename from drizzle/0015_customer_ownership_contact_shares.sql rename to drizzle_bk/0015_customer_ownership_contact_shares.sql diff --git a/drizzle/0016_won_lost_lifecycle_governance.sql b/drizzle_bk/0016_won_lost_lifecycle_governance.sql similarity index 100% rename from drizzle/0016_won_lost_lifecycle_governance.sql rename to drizzle_bk/0016_won_lost_lifecycle_governance.sql diff --git a/drizzle/0017_short_swordsman.sql b/drizzle_bk/0017_short_swordsman.sql similarity index 100% rename from drizzle/0017_short_swordsman.sql rename to drizzle_bk/0017_short_swordsman.sql diff --git a/drizzle/0018_report_foundation.sql b/drizzle_bk/0018_report_foundation.sql similarity index 100% rename from drizzle/0018_report_foundation.sql rename to drizzle_bk/0018_report_foundation.sql diff --git a/drizzle/0019_sleepy_ultron.sql b/drizzle_bk/0019_sleepy_ultron.sql similarity index 100% rename from drizzle/0019_sleepy_ultron.sql rename to drizzle_bk/0019_sleepy_ultron.sql diff --git a/drizzle/0020_lead_foundation_schema.sql b/drizzle_bk/0020_lead_foundation_schema.sql similarity index 100% rename from drizzle/0020_lead_foundation_schema.sql rename to drizzle_bk/0020_lead_foundation_schema.sql diff --git a/drizzle/0021_worried_nebula.sql b/drizzle_bk/0021_worried_nebula.sql similarity index 100% rename from drizzle/0021_worried_nebula.sql rename to drizzle_bk/0021_worried_nebula.sql diff --git a/drizzle_bk/0022_lead_assignment_foundation.sql b/drizzle_bk/0022_lead_assignment_foundation.sql new file mode 100644 index 0000000..0a1779c --- /dev/null +++ b/drizzle_bk/0022_lead_assignment_foundation.sql @@ -0,0 +1,9 @@ +ALTER TABLE "crm_leads" + ADD COLUMN IF NOT EXISTS "description" text, + ADD COLUMN IF NOT EXISTS "product_type" text, + ADD COLUMN IF NOT EXISTS "priority" text, + ADD COLUMN IF NOT EXISTS "estimated_value" double precision, + ADD COLUMN IF NOT EXISTS "assigned_sales_owner_id" text, + ADD COLUMN IF NOT EXISTS "assigned_at" timestamp with time zone, + ADD COLUMN IF NOT EXISTS "assigned_by" text, + ADD COLUMN IF NOT EXISTS "assignment_remark" text; diff --git a/drizzle_bk/0023_yellow_carlie_cooper.sql b/drizzle_bk/0023_yellow_carlie_cooper.sql new file mode 100644 index 0000000..570df70 --- /dev/null +++ b/drizzle_bk/0023_yellow_carlie_cooper.sql @@ -0,0 +1,8 @@ +ALTER TABLE "crm_leads" ADD COLUMN "description" text;--> statement-breakpoint +ALTER TABLE "crm_leads" ADD COLUMN "product_type" text;--> statement-breakpoint +ALTER TABLE "crm_leads" ADD COLUMN "priority" text;--> statement-breakpoint +ALTER TABLE "crm_leads" ADD COLUMN "estimated_value" double precision;--> statement-breakpoint +ALTER TABLE "crm_leads" ADD COLUMN "assigned_sales_owner_id" text;--> statement-breakpoint +ALTER TABLE "crm_leads" ADD COLUMN "assigned_at" timestamp with time zone;--> statement-breakpoint +ALTER TABLE "crm_leads" ADD COLUMN "assigned_by" text;--> statement-breakpoint +ALTER TABLE "crm_leads" ADD COLUMN "assignment_remark" text; \ No newline at end of file diff --git a/drizzle_bk/meta/0000_snapshot.json b/drizzle_bk/meta/0000_snapshot.json new file mode 100644 index 0000000..0aeadcc --- /dev/null +++ b/drizzle_bk/meta/0000_snapshot.json @@ -0,0 +1,331 @@ +{ + "id": "1e236d6a-ca42-4830-b230-67a7c233d87b", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.memberships": { + "name": "memberships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "business_role": { + "name": "business_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'viewer'" + }, + "permissions": { + "name": "permissions", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organizations": { + "name": "organizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'free'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organizations_slug_idx": { + "name": "organizations_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "products_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "photo_url": { + "name": "photo_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "price": { + "name": "price", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "system_role": { + "name": "system_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/drizzle/meta/0001_snapshot.json b/drizzle_bk/meta/0001_snapshot.json similarity index 100% rename from drizzle/meta/0001_snapshot.json rename to drizzle_bk/meta/0001_snapshot.json diff --git a/drizzle/meta/0002_snapshot.json b/drizzle_bk/meta/0002_snapshot.json similarity index 100% rename from drizzle/meta/0002_snapshot.json rename to drizzle_bk/meta/0002_snapshot.json diff --git a/drizzle/meta/0003_snapshot.json b/drizzle_bk/meta/0003_snapshot.json similarity index 100% rename from drizzle/meta/0003_snapshot.json rename to drizzle_bk/meta/0003_snapshot.json diff --git a/drizzle/meta/0004_snapshot.json b/drizzle_bk/meta/0004_snapshot.json similarity index 100% rename from drizzle/meta/0004_snapshot.json rename to drizzle_bk/meta/0004_snapshot.json diff --git a/drizzle/meta/0005_snapshot.json b/drizzle_bk/meta/0005_snapshot.json similarity index 100% rename from drizzle/meta/0005_snapshot.json rename to drizzle_bk/meta/0005_snapshot.json diff --git a/drizzle/meta/0006_snapshot.json b/drizzle_bk/meta/0006_snapshot.json similarity index 100% rename from drizzle/meta/0006_snapshot.json rename to drizzle_bk/meta/0006_snapshot.json diff --git a/drizzle/meta/0007_snapshot.json b/drizzle_bk/meta/0007_snapshot.json similarity index 100% rename from drizzle/meta/0007_snapshot.json rename to drizzle_bk/meta/0007_snapshot.json diff --git a/drizzle/meta/0008_snapshot.json b/drizzle_bk/meta/0008_snapshot.json similarity index 100% rename from drizzle/meta/0008_snapshot.json rename to drizzle_bk/meta/0008_snapshot.json diff --git a/drizzle/meta/0009_snapshot.json b/drizzle_bk/meta/0009_snapshot.json similarity index 100% rename from drizzle/meta/0009_snapshot.json rename to drizzle_bk/meta/0009_snapshot.json diff --git a/drizzle/meta/0010_snapshot.json b/drizzle_bk/meta/0010_snapshot.json similarity index 100% rename from drizzle/meta/0010_snapshot.json rename to drizzle_bk/meta/0010_snapshot.json diff --git a/drizzle/meta/0011_snapshot.json b/drizzle_bk/meta/0011_snapshot.json similarity index 100% rename from drizzle/meta/0011_snapshot.json rename to drizzle_bk/meta/0011_snapshot.json diff --git a/drizzle/meta/0012_snapshot.json b/drizzle_bk/meta/0012_snapshot.json similarity index 100% rename from drizzle/meta/0012_snapshot.json rename to drizzle_bk/meta/0012_snapshot.json diff --git a/drizzle/meta/0013_snapshot.json b/drizzle_bk/meta/0013_snapshot.json similarity index 100% rename from drizzle/meta/0013_snapshot.json rename to drizzle_bk/meta/0013_snapshot.json diff --git a/drizzle/meta/0014_snapshot.json b/drizzle_bk/meta/0014_snapshot.json similarity index 100% rename from drizzle/meta/0014_snapshot.json rename to drizzle_bk/meta/0014_snapshot.json diff --git a/drizzle/meta/0017_snapshot.json b/drizzle_bk/meta/0017_snapshot.json similarity index 100% rename from drizzle/meta/0017_snapshot.json rename to drizzle_bk/meta/0017_snapshot.json diff --git a/drizzle/meta/0019_snapshot.json b/drizzle_bk/meta/0019_snapshot.json similarity index 100% rename from drizzle/meta/0019_snapshot.json rename to drizzle_bk/meta/0019_snapshot.json diff --git a/drizzle/meta/0021_snapshot.json b/drizzle_bk/meta/0021_snapshot.json similarity index 100% rename from drizzle/meta/0021_snapshot.json rename to drizzle_bk/meta/0021_snapshot.json diff --git a/drizzle_bk/meta/0023_snapshot.json b/drizzle_bk/meta/0023_snapshot.json new file mode 100644 index 0000000..775487b --- /dev/null +++ b/drizzle_bk/meta/0023_snapshot.json @@ -0,0 +1,4263 @@ +{ + "id": "beb57e07-da33-448f-8336-d2fb88fbdf1a", + "prevId": "cf5d9384-b358-4115-8397-7d499981456f", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.crm_approval_actions": { + "name": "crm_approval_actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "approval_request_id": { + "name": "approval_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "step_number": { + "name": "step_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acted_by": { + "name": "acted_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "acted_at": { + "name": "acted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_approval_requests": { + "name": "crm_approval_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_step": { + "name": "current_step", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_by": { + "name": "requested_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_approval_steps": { + "name": "crm_approval_steps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "step_number": { + "name": "step_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "role_code": { + "name": "role_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role_name": { + "name": "role_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_required": { + "name": "is_required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_approval_steps_workflow_step_idx": { + "name": "crm_approval_steps_workflow_step_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "step_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_approval_workflows": { + "name": "crm_approval_workflows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_approval_workflows_org_code_idx": { + "name": "crm_approval_workflows_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_contact_shares": { + "name": "crm_contact_shares", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "shared_to_user_id": { + "name": "shared_to_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "shared_by_user_id": { + "name": "shared_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "shared_at": { + "name": "shared_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_contact_shares_org_contact_shared_user_idx": { + "name": "crm_contact_shares_org_contact_shared_user_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "contact_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "shared_to_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_customer_contacts": { + "name": "crm_customer_contacts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "department": { + "name": "department", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mobile": { + "name": "mobile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_primary": { + "name": "is_primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_customer_owner_history": { + "name": "crm_customer_owner_history", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "old_owner_user_id": { + "name": "old_owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "new_owner_user_id": { + "name": "new_owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "changed_by": { + "name": "changed_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "changed_at": { + "name": "changed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_customers": { + "name": "crm_customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_id": { + "name": "branch_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "abbr": { + "name": "abbr", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tax_id": { + "name": "tax_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_type": { + "name": "customer_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_status": { + "name": "customer_status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "province": { + "name": "province", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "district": { + "name": "district", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sub_district": { + "name": "sub_district", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postal_code": { + "name": "postal_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country": { + "name": "country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fax": { + "name": "fax", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lead_channel": { + "name": "lead_channel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_group": { + "name": "customer_group", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_sub_group": { + "name": "customer_sub_group", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_assigned_at": { + "name": "owner_assigned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "owner_assigned_by": { + "name": "owner_assigned_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "crm_customers_org_code_idx": { + "name": "crm_customers_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_document_artifacts": { + "name": "crm_document_artifacts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_type": { + "name": "document_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "artifact_type": { + "name": "artifact_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_version_id": { + "name": "template_version_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "storage_provider": { + "name": "storage_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "checksum": { + "name": "checksum", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "generated_by": { + "name": "generated_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "generated_at": { + "name": "generated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "locked_by": { + "name": "locked_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "voided_at": { + "name": "voided_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "voided_by": { + "name": "voided_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "void_reason": { + "name": "void_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_document_template_mappings": { + "name": "crm_document_template_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_version_id": { + "name": "template_version_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "placeholder_key": { + "name": "placeholder_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_path": { + "name": "source_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data_type": { + "name": "data_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sheet_name": { + "name": "sheet_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "default_value": { + "name": "default_value", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "format_mask": { + "name": "format_mask", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_document_template_mappings_version_placeholder_idx": { + "name": "crm_document_template_mappings_version_placeholder_idx", + "columns": [ + { + "expression": "template_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "placeholder_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_document_template_table_columns": { + "name": "crm_document_template_table_columns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mapping_id": { + "name": "mapping_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "column_name": { + "name": "column_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_field": { + "name": "source_field", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "column_letter": { + "name": "column_letter", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "format_mask": { + "name": "format_mask", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_document_template_table_columns_mapping_name_idx": { + "name": "crm_document_template_table_columns_mapping_name_idx", + "columns": [ + { + "expression": "mapping_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "column_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_document_template_versions": { + "name": "crm_document_template_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "schema_json": { + "name": "schema_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "preview_image_url": { + "name": "preview_image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "crm_document_template_versions_template_version_idx": { + "name": "crm_document_template_versions_template_version_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_document_templates": { + "name": "crm_document_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_type": { + "name": "document_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "product_type": { + "name": "product_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_type": { + "name": "file_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_name": { + "name": "template_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "crm_document_templates_org_doc_product_file_name_idx": { + "name": "crm_document_templates_org_doc_product_file_name_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "file_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "template_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_enquiries": { + "name": "crm_enquiries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_id": { + "name": "branch_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lead_id": { + "name": "lead_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requirement": { + "name": "requirement", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_name": { + "name": "project_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_location": { + "name": "project_location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "product_type": { + "name": "product_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lead_channel": { + "name": "lead_channel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estimated_value": { + "name": "estimated_value", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "chance_percent": { + "name": "chance_percent", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "expected_close_date": { + "name": "expected_close_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "competitor": { + "name": "competitor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_hot_project": { + "name": "is_hot_project", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "pipeline_stage": { + "name": "pipeline_stage", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'lead'" + }, + "closed_won_at": { + "name": "closed_won_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "closed_lost_at": { + "name": "closed_lost_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "closed_by_user_id": { + "name": "closed_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "po_number": { + "name": "po_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "po_date": { + "name": "po_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "po_amount": { + "name": "po_amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "po_currency": { + "name": "po_currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lost_reason": { + "name": "lost_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lost_competitor": { + "name": "lost_competitor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lost_remark": { + "name": "lost_remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assigned_to_user_id": { + "name": "assigned_to_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assigned_at": { + "name": "assigned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "assigned_by": { + "name": "assigned_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assignment_remark": { + "name": "assignment_remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "crm_enquiries_org_code_idx": { + "name": "crm_enquiries_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_enquiry_attachments": { + "name": "crm_enquiry_attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enquiry_id": { + "name": "enquiry_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_file_name": { + "name": "original_file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_provider": { + "name": "storage_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "file_type": { + "name": "file_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "uploaded_by": { + "name": "uploaded_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_enquiry_customers": { + "name": "crm_enquiry_customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enquiry_id": { + "name": "enquiry_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_enquiry_followups": { + "name": "crm_enquiry_followups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enquiry_id": { + "name": "enquiry_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "followup_date": { + "name": "followup_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "followup_type": { + "name": "followup_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_followup_date": { + "name": "next_followup_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_action": { + "name": "next_action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_leads": { + "name": "crm_leads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_id": { + "name": "branch_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_name": { + "name": "project_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_location": { + "name": "project_location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "product_type": { + "name": "product_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estimated_value": { + "name": "estimated_value", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "awareness_id": { + "name": "awareness_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'new_job'" + }, + "followup_status": { + "name": "followup_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lost_reason": { + "name": "lost_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "owner_marketing_user_id": { + "name": "owner_marketing_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assigned_sales_owner_id": { + "name": "assigned_sales_owner_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assigned_at": { + "name": "assigned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "assigned_by": { + "name": "assigned_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assignment_remark": { + "name": "assignment_remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_leads_org_code_idx": { + "name": "crm_leads_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotation_attachments": { + "name": "crm_quotation_attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quotation_id": { + "name": "quotation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_file_name": { + "name": "original_file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "file_type": { + "name": "file_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "uploaded_by": { + "name": "uploaded_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotation_customers": { + "name": "crm_quotation_customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quotation_id": { + "name": "quotation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_primary": { + "name": "is_primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotation_followups": { + "name": "crm_quotation_followups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quotation_id": { + "name": "quotation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "followup_date": { + "name": "followup_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "followup_type": { + "name": "followup_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_followup_date": { + "name": "next_followup_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_action": { + "name": "next_action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotation_items": { + "name": "crm_quotation_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quotation_id": { + "name": "quotation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "item_number": { + "name": "item_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "product_type": { + "name": "product_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "unit": { + "name": "unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "unit_price": { + "name": "unit_price", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "discount": { + "name": "discount", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "discount_type": { + "name": "discount_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tax_rate": { + "name": "tax_rate", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_price": { + "name": "total_price", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotation_topic_items": { + "name": "crm_quotation_topic_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic_id": { + "name": "topic_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotation_topics": { + "name": "crm_quotation_topics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quotation_id": { + "name": "quotation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic_type": { + "name": "topic_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_quotations": { + "name": "crm_quotations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_id": { + "name": "branch_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enquiry_id": { + "name": "enquiry_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "quotation_date": { + "name": "quotation_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "valid_until": { + "name": "valid_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "quotation_type": { + "name": "quotation_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "project_name": { + "name": "project_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_location": { + "name": "project_location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attention": { + "name": "attention", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference": { + "name": "reference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "parent_quotation_id": { + "name": "parent_quotation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revision_remark": { + "name": "revision_remark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "exchange_rate": { + "name": "exchange_rate", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "subtotal": { + "name": "subtotal", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "discount": { + "name": "discount", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "discount_type": { + "name": "discount_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tax_rate": { + "name": "tax_rate", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "tax_amount": { + "name": "tax_amount", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_amount": { + "name": "total_amount", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "chance_percent": { + "name": "chance_percent", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_hot_project": { + "name": "is_hot_project", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "competitor": { + "name": "competitor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "salesman_id": { + "name": "salesman_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_sent": { + "name": "is_sent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sent_via": { + "name": "sent_via", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "approved_artifact_id": { + "name": "approved_artifact_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approved_pdf_url": { + "name": "approved_pdf_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approved_snapshot": { + "name": "approved_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "approved_template_version_id": { + "name": "approved_template_version_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accepted_at": { + "name": "accepted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rejected_at": { + "name": "rejected_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rejection_reason": { + "name": "rejection_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "crm_quotations_org_code_idx": { + "name": "crm_quotations_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_report_definitions": { + "name": "crm_report_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_system": { + "name": "is_system", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "crm_report_definitions_org_code_idx": { + "name": "crm_report_definitions_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_role_profiles": { + "name": "crm_role_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permissions": { + "name": "permissions", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "branch_scope_mode": { + "name": "branch_scope_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'assigned'" + }, + "product_scope_mode": { + "name": "product_scope_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'assigned'" + }, + "ownership_scope": { + "name": "ownership_scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'own'" + }, + "approval_authority": { + "name": "approval_authority", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "is_system": { + "name": "is_system", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "crm_role_profiles_org_code_idx": { + "name": "crm_role_profiles_org_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.crm_user_role_assignments": { + "name": "crm_user_role_assignments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role_profile_id": { + "name": "role_profile_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_scope_mode": { + "name": "branch_scope_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'inherit'" + }, + "branch_scope_ids": { + "name": "branch_scope_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "product_type_scope_mode": { + "name": "product_type_scope_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'inherit'" + }, + "product_type_scope_ids": { + "name": "product_type_scope_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "is_primary": { + "name": "is_primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "assigned_by": { + "name": "assigned_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assigned_at": { + "name": "assigned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "crm_user_role_assignments_org_user_role_idx": { + "name": "crm_user_role_assignments_org_user_role_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "role_profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.document_sequences": { + "name": "document_sequences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_id": { + "name": "branch_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "document_type": { + "name": "document_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "period": { + "name": "period", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_number": { + "name": "current_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "padding_length": { + "name": "padding_length", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "document_sequences_org_doc_period_branch_idx": { + "name": "document_sequences_org_doc_period_branch_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "branch_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memberships": { + "name": "memberships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "business_role": { + "name": "business_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'sales_support'" + }, + "permissions": { + "name": "permissions", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "branch_scope_ids": { + "name": "branch_scope_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "product_type_scope_ids": { + "name": "product_type_scope_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ms_options": { + "name": "ms_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ms_options_org_category_code_idx": { + "name": "ms_options_org_category_code_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organizations": { + "name": "organizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'free'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organizations_slug_idx": { + "name": "organizations_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "products_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "photo_url": { + "name": "photo_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "price": { + "name": "price", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tr_audit_logs": { + "name": "tr_audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_id": { + "name": "branch_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "before_data": { + "name": "before_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "after_data": { + "name": "after_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "system_role": { + "name": "system_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle_bk/meta/_journal.json b/drizzle_bk/meta/_journal.json new file mode 100644 index 0000000..1298c4a --- /dev/null +++ b/drizzle_bk/meta/_journal.json @@ -0,0 +1,174 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1781148450992, + "tag": "0000_icy_lizard", + "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1781171200406, + "tag": "0001_orange_mandarin", + "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1781498575433, + "tag": "0002_plain_anthem", + "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1781505431077, + "tag": "0003_blushing_bruce_banner", + "breakpoints": true + }, + { + "idx": 4, + "version": "7", + "when": 1781511534267, + "tag": "0004_worthless_ender_wiggin", + "breakpoints": true + }, + { + "idx": 5, + "version": "7", + "when": 1781575976324, + "tag": "0005_numerous_blob", + "breakpoints": true + }, + { + "idx": 6, + "version": "7", + "when": 1781579746622, + "tag": "0006_worried_mordo", + "breakpoints": true + }, + { + "idx": 7, + "version": "7", + "when": 1781589458455, + "tag": "0007_luxuriant_malice", + "breakpoints": true + }, + { + "idx": 8, + "version": "7", + "when": 1781595962545, + "tag": "0008_clean_justin_hammer", + "breakpoints": true + }, + { + "idx": 9, + "version": "7", + "when": 1781603421415, + "tag": "0009_lazy_shard", + "breakpoints": true + }, + { + "idx": 10, + "version": "7", + "when": 1781675409847, + "tag": "0010_calm_wendell_rand", + "breakpoints": true + }, + { + "idx": 11, + "version": "7", + "when": 1781747812297, + "tag": "0011_goofy_the_hood", + "breakpoints": true + }, + { + "idx": 12, + "version": "7", + "when": 1781836261952, + "tag": "0012_simple_bucky", + "breakpoints": true + }, + { + "idx": 13, + "version": "7", + "when": 1782097963376, + "tag": "0013_great_nicolaos", + "breakpoints": true + }, + { + "idx": 14, + "version": "7", + "when": 1782099743528, + "tag": "0014_bored_valkyrie", + "breakpoints": true + }, + { + "idx": 15, + "version": "7", + "when": 1782549600000, + "tag": "0015_customer_ownership_contact_shares", + "breakpoints": true + }, + { + "idx": 16, + "version": "7", + "when": 1782795600000, + "tag": "0016_won_lost_lifecycle_governance", + "breakpoints": true + }, + { + "idx": 17, + "version": "7", + "when": 1782121929212, + "tag": "0017_short_swordsman", + "breakpoints": true + }, + { + "idx": 18, + "version": "7", + "when": 1782799200000, + "tag": "0018_report_foundation", + "breakpoints": true + }, + { + "idx": 19, + "version": "7", + "when": 1782137095907, + "tag": "0019_sleepy_ultron", + "breakpoints": true + }, + { + "idx": 20, + "version": "7", + "when": 1782262800000, + "tag": "0020_lead_foundation_schema", + "breakpoints": true + }, + { + "idx": 21, + "version": "7", + "when": 1782279100730, + "tag": "0021_worried_nebula", + "breakpoints": true + }, + { + "idx": 22, + "version": "7", + "when": 1782286200000, + "tag": "0022_lead_assignment_foundation", + "breakpoints": true + }, + { + "idx": 23, + "version": "7", + "when": 1782284237696, + "tag": "0023_yellow_carlie_cooper", + "breakpoints": true + } + ] +} diff --git a/plans/task-d.5.3.md b/plans/task-d.5.3.md new file mode 100644 index 0000000..9b7b2a7 --- /dev/null +++ b/plans/task-d.5.3.md @@ -0,0 +1,537 @@ +# Task D.5.3: Lead Assignment → Create Enquiry Foundation + +## Status + +Planned + +## Objective + +Implement the first production transition from Lead into Enquiry. + +This task establishes the business boundary between: + +```txt +Marketing Domain + ↓ +Lead + +Sales Domain + ↓ +Enquiry +``` + +A Lead remains the marketing-owned source record. + +An Enquiry becomes the sales execution workspace. + +This task introduces: + +* Lead assignment workflow +* Enquiry creation from Lead +* Lead ↔ Enquiry relationship +* Initial ownership transfer +* Enquiry bootstrap generation + +without changing quotation, dashboard, reporting, or approval behavior. + +--- + +# Mandatory Review + +Review: + +* AGENTS.md +* Project Foundations Registry +* Task Catalog +* ADR-0018 Lead / Enquiry Domain Separation +* Task D.5 +* Task D.5.1 +* Task D.5.2 +* Customer Ownership Foundation +* CRM Security Foundation +* Audit Foundation +* Document Sequence Foundation + +--- + +# Existing Foundation + +Verified available: + +```txt +crm_leads +crm_enquiries +crm_enquiries.lead_id + +crm_lead sequence +crm_enquiry sequence + +Lead Service +Lead API +``` + +Do not recreate: + +```txt +tables +migrations +seed data +master-option categories +``` + +--- + +# Business Model + +## Before Assignment + +Owner: + +```txt +Marketing +``` + +Workspace: + +```txt +Lead +``` + +--- + +## After Assignment + +Owner: + +```txt +Sales +``` + +Workspace: + +```txt +Enquiry +``` + +--- + +## Relationship + +```txt +1 Lead + ↓ +0..N Enquiries +``` + +Lead remains immutable historical source. + +Enquiries become sales execution records. + +--- + +# Scope D5.3.1 Lead Assignment Service + +Create: + +```txt +src/features/crm/leads/server/assignment.service.ts +``` + +Required operation: + +```txt +assignLead() +``` + +--- + +Input: + +```ts +AssignLeadInput +``` + +Fields: + +```ts +leadId +salesOwnerId +assignmentRemark +``` + +--- + +Validation: + +```txt +lead exists +lead active +salesOwnerId valid +same organization +``` + +--- + +# Scope D5.3.2 Create Enquiry From Lead + +Create: + +```txt +createEnquiryFromLead() +``` + +inside assignment service. + +--- + +Rules + +Assignment automatically creates: + +```txt +crm_enquiry +``` + +linked to: + +```txt +crm_leads.id +``` + +through: + +```txt +crm_enquiries.lead_id +``` + +--- + +Reuse: + +```txt +generateNextDocumentCode() +``` + +with: + +```txt +crm_enquiry +``` + +--- + +Expected: + +```txt +EN2606-001 +``` + +or existing enquiry format. + +No custom numbering logic. + +--- + +# Scope D5.3.3 Enquiry Bootstrap Mapping + +When enquiry is created: + +Copy: + +```txt +customerId +contactId +projectName +projectLocation +productType +description +estimatedValue +priority +``` + +from Lead. + +--- + +Do NOT copy: + +```txt +marketing-only notes +internal marketing activity history +audit events +``` + +--- + +Lead remains unchanged. + +--- + +# Scope D5.3.4 Lead Assignment API + +Create: + +```txt +POST /api/crm/leads/[id]/assign +``` + +--- + +Handler must call: + +```txt +Lead Assignment Service +``` + +No direct DB access. + +--- + +Response: + +```ts +{ + leadId: string; + enquiryId: string; + enquiryCode: string; +} +``` + +--- + +# Scope D5.3.5 Ownership Transfer + +After assignment: + +Lead: + +```txt +status = assigned +``` + +--- + +Lead: + +```txt +assigned_sales_owner_id +assigned_at +assigned_by +``` + +updated. + +--- + +Enquiry: + +```txt +owner_user_id +``` + +set to assigned salesperson. + +--- + +Marketing remains able to: + +```txt +view lead +view assignment result +``` + +but not manage enquiry. + +--- + +# Scope D5.3.6 CRM Security + +Reuse: + +```txt +resolveCrmMembershipAccess() +``` + +Assignment permission: + +```txt +lead.assign +``` + +Only: + +```txt +Marketing Manager +CRM Manager +Admin +``` + +or equivalent resolved permission. + +--- + +Do not check role strings directly. + +--- + +# Scope D5.3.7 Duplicate Protection + +Prevent: + +```txt +multiple enquiry creation +``` + +for the same assignment action. + +--- + +Rules: + +If assignment already produced an active enquiry: + +```txt +return existing enquiry +``` + +instead of creating a second one. + +--- + +# Scope D5.3.8 Audit Logging + +Reuse audit foundation. + +Required actions: + +```txt +assign_lead +create_enquiry_from_lead +``` + +--- + +Audit must capture: + +```txt +leadId +enquiryId +salesOwnerId +``` + +--- + +# Scope D5.3.9 Legacy Compatibility + +Do not modify: + +```txt +quotation flow +approval flow +dashboard +reports +won/lost process +``` + +--- + +Current enquiry APIs must continue functioning. + +--- + +# Deliverables + +New: + +```txt +src/features/crm/leads/server/assignment.service.ts +``` + +--- + +New API: + +```txt +POST /api/crm/leads/[id]/assign +``` + +--- + +Documentation: + +```txt +docs/implementation/task-d53-lead-assignment-create-enquiry-foundation.md +``` + +--- + +# Verification + +Run: + +```txt +npm exec tsc --noEmit +``` + +--- + +Verify: + +```txt +Assign Lead +Create Enquiry +Reuse Existing Enquiry +Lead Status Update +Audit Logging +Security Enforcement +``` + +--- + +Verify: + +```txt +Quotation APIs still work +Dashboard still works +Reports still work +``` + +--- + +# Explicit Non-Scope + +Do not: + +* create Lead UI +* create Opportunity UI +* create quotation conversion +* change approval workflow +* change reports +* change dashboard +* migrate historical data +* implement won/lost synchronization + +These belong to later D.5 phases. + +--- + +# Definition of Done + +Task is complete when: + +* Lead assignment API exists +* Assignment creates or reuses enquiry +* crm_enquiries.lead_id is populated +* Enquiry owner is assigned salesperson +* Lead assignment is audited +* Existing CRM functionality remains unchanged + +Result: + +```txt +Lead Assignment = Established + +Lead → Enquiry Transition = Established + +Ready for Opportunity Workspace Separation +``` diff --git a/plans/task-d.5.4.md b/plans/task-d.5.4.md new file mode 100644 index 0000000..22061fc --- /dev/null +++ b/plans/task-d.5.4.md @@ -0,0 +1,506 @@ +# Task D.5.4: Lead / Enquiry Workspace Separation UI Foundation + +## Status + +Planned + +## Objective + +Separate the user-facing CRM workspace between Lead and Enquiry. + +This task makes Lead and Enquiry visible as different working areas while preserving existing enquiry, quotation, dashboard, and reporting behavior. + +Business boundary: + +```txt +Lead = Marketing-owned source record +Enquiry = Sales execution workspace +``` + +This task focuses on UI routing, navigation, data-source separation, and safe compatibility wiring. + +--- + +## Mandatory Review + +Review before implementation: + +* `AGENTS.md` +* `docs/standards/project-foundations.md` +* `docs/standards/task-catalog.md` +* `docs/adr/0018-lead-enquiry-domain-separation.md` +* `docs/implementation/task-d5-lead-enquiry-domain-separation.md` +* `docs/implementation/task-d51-lead-foundation-stabilization.md` +* `docs/implementation/task-d52-lead-domain-service-api-foundation.md` +* `docs/implementation/task-d53-lead-assignment-create-enquiry-foundation.md` +* `docs/security/crm-authorization-boundaries.md` +* Existing CRM navigation/sidebar configuration +* Existing enquiry list/detail/form UI +* Existing lead API routes +* Existing enquiry API routes + +--- + +## Existing Foundation + +Already available: + +```txt +crm_leads +crm_enquiries +crm_enquiries.lead_id + +/api/crm/leads +/api/crm/leads/[id] +/api/crm/leads/[id]/followups +/api/crm/leads/[id]/assign + +existing enquiry APIs +existing quotation APIs +``` + +Do not recreate: + +```txt +tables +migrations +seed data +service foundation +assignment service +``` + +--- + +## Workspace Rules + +### Lead Workspace + +Audience: + +```txt +Marketing +Marketing Manager +CRM Manager +Admin +``` + +Purpose: + +```txt +capture lead +qualify lead +record awareness/status/lost reason +assign lead to sales +view linked enquiry result +``` + +Source: + +```txt +crm_leads +/api/crm/leads +``` + +--- + +### Enquiry Workspace + +Audience: + +```txt +Sales +Sales Manager +CRM Manager +Admin +``` + +Purpose: + +```txt +work assigned opportunity +prepare for quotation +track sales execution +continue existing enquiry lifecycle +``` + +Source: + +```txt +crm_enquiries +existing /api/crm/enquiries +``` + +--- + +## Scope D5.4.1 Navigation Separation + +Update CRM navigation so Lead and Enquiry are separate menu items. + +Add: + +```txt +CRM > Leads +CRM > Enquiries +``` + +Rules: + +* Lead menu uses lead permission/scope +* Enquiry menu keeps existing permission/scope +* Do not rename Enquiry menu globally if it breaks existing routes +* Do not remove old enquiry route yet + +Suggested routes: + +```txt +/dashboard/crm/leads +/dashboard/crm/leads/[id] + +/dashboard/crm/enquiries +/dashboard/crm/enquiries/[id] +``` + +If project uses another route prefix, follow existing app route convention. + +--- + +## Scope D5.4.2 Lead List Page + +Create Lead list page using `/api/crm/leads`. + +Required columns: + +```txt +Lead Code +Customer +Project Name +Status +Awareness +Estimated Value +Suggested Sales Owner +Assigned Sales Owner +Created Date +``` + +Required actions: + +```txt +View +Edit +Assign +Delete +``` + +Rules: + +* Assign action appears only when user has assignment permission +* Delete action appears only when user has delete permission +* No direct enquiry mutation from Lead list +* Use existing shadcn/table/list conventions +* Use design tokens only +* Follow existing CRM page layout pattern + +--- + +## Scope D5.4.3 Lead Detail Page + +Create Lead detail page using `/api/crm/leads/[id]`. + +Sections: + +```txt +Lead Summary +Customer Information +Qualification Information +Follow-up History +Assignment Information +Linked Enquiries +Audit-safe Activity View +``` + +Actions: + +```txt +Edit Lead +Assign Lead +Create Follow-up +Delete Lead +Open Linked Enquiry +``` + +Rules: + +* Assignment creates or reuses enquiry through `/api/crm/leads/[id]/assign` +* After assignment, show linked enquiry +* Marketing can see assignment result but must not edit enquiry unless permitted +* Follow-up history uses D.5.2 audit-backed implementation until future ADR decides final persistence + +--- + +## Scope D5.4.4 Lead Create / Edit Form + +Create or adapt Lead form components. + +Required fields: + +```txt +Customer +Contact +Project Name +Project Location +Description +Product Type +Priority +Estimated Value +Awareness +Status +Lost Reason +Notes +``` + +Rules: + +* Use master options for awareness, status, lost reason +* No hardcoded labels +* Product type must use existing option/source convention +* Validate with D.5.2 lead schemas +* On create, call `POST /api/crm/leads` +* On edit, call `PATCH /api/crm/leads/[id]` + +--- + +## Scope D5.4.5 Lead Assignment UI + +Create assignment dialog or sheet. + +Input: + +```txt +Sales Owner +Assignment Remark +``` + +Behavior: + +```txt +POST /api/crm/leads/[id]/assign +``` + +After success: + +```txt +show enquiry code +show Open Enquiry action +refresh lead detail +refresh lead list +``` + +Rules: + +* Sales owner list must use assignable sales members only +* If API returns existing enquiry, show reuse message instead of duplicate-created message +* Do not create enquiry client-side +* Do not mutate `crm_enquiries` directly from UI + +--- + +## Scope D5.4.6 Enquiry UI Compatibility + +Keep current enquiry UI operational. + +Add optional linked-lead display if `lead_id` exists: + +```txt +Source Lead +Lead Code +Open Lead +``` + +Rules: + +* Do not rewrite enquiry workflow +* Do not change quotation conversion behavior +* Do not change dashboard/report queries +* Do not remove `pipeline_stage` +* Existing enquiry list/detail must still work + +--- + +## Scope D5.4.7 Permission / Visibility Handling + +UI must respect backend permissions but not replace backend enforcement. + +Lead UI: + +```txt +crm.lead.read +crm.lead.create +crm.lead.update +crm.lead.delete +crm.lead.assign +``` + +Enquiry UI: + +```txt +existing enquiry permissions +``` + +Rules: + +* Hide unauthorized actions +* Handle 403 gracefully +* Do not check role strings directly if access helpers exist +* Follow existing CRM access resolver/client conventions + +--- + +## Scope D5.4.8 Empty / Loading / Error States + +Implement consistent states: + +```txt +loading +empty +not authorized +not found +server error +validation error +``` + +Messaging: + +* Lead empty state should guide Marketing to create first Lead +* Enquiry empty state should remain sales-oriented +* Assignment success should clearly identify created/reused enquiry + +--- + +## Scope D5.4.9 Documentation + +Create: + +```txt +docs/implementation/task-d54-lead-enquiry-workspace-separation-ui-foundation.md +``` + +Document: + +```txt +routes added +components added +APIs consumed +permission mapping +compatibility notes +known limitations +``` + +--- + +## Deliverables + +New or updated pages: + +```txt +src/app/**/crm/leads/page.tsx +src/app/**/crm/leads/[id]/page.tsx +``` + +New or updated components: + +```txt +src/features/crm/leads/components/lead-list.tsx +src/features/crm/leads/components/lead-form.tsx +src/features/crm/leads/components/lead-detail.tsx +src/features/crm/leads/components/lead-assignment-dialog.tsx +src/features/crm/leads/components/lead-followup-panel.tsx +``` + +Updated: + +```txt +CRM navigation/sidebar +Enquiry detail optional Source Lead display +``` + +Documentation: + +```txt +docs/implementation/task-d54-lead-enquiry-workspace-separation-ui-foundation.md +``` + +--- + +## Explicit Non-Scope + +Do not: + +* create new tables +* create migrations +* change lead assignment service behavior +* change enquiry service behavior beyond read-only linked lead display +* change quotation flow +* change approval flow +* change dashboard datasets +* change reports +* implement won/lost synchronization +* replace audit-backed lead follow-up persistence +* migrate historical enquiry records into leads + +These belong to later D.5 phases. + +--- + +## Verification + +Run: + +```txt +npm exec tsc --noEmit +``` + +Optional if project supports: + +```txt +npm run lint +npm run build +``` + +Manual verification: + +```txt +Lead menu appears for permitted users +Lead menu hidden or blocked for unauthorized users +Lead list loads from /api/crm/leads +Lead create works +Lead edit works +Lead detail works +Lead assign creates or reuses enquiry +Linked enquiry opens from lead detail +Existing enquiry page still works +Existing quotation flow still works +Dashboard still works +Reports still work +``` + +--- + +## Definition of Done + +Task is complete when: + +* Lead and Enquiry appear as separate workspaces +* Lead UI uses Lead APIs +* Enquiry UI remains compatible +* Lead assignment UI creates or reuses enquiry through API +* Linked enquiry is visible from Lead detail +* Existing enquiry, quotation, dashboard, and reporting flows remain unchanged + +Result: + +```txt +Lead Workspace = Established +Enquiry Workspace = Preserved +Lead / Enquiry UI Boundary = Established +Ready for D.5.5 Opportunity / Sales Workspace Refinement +``` diff --git a/src/app/api/crm/leads/[id]/assign/route.ts b/src/app/api/crm/leads/[id]/assign/route.ts new file mode 100644 index 0000000..2868bac --- /dev/null +++ b/src/app/api/crm/leads/[id]/assign/route.ts @@ -0,0 +1,62 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { z } from 'zod'; +import { assignLeadSchema } from '@/features/crm/leads/schemas/lead.schema'; +import { assignLead } from '@/features/crm/leads/server/assignment.service'; +import { buildLeadAccessContext } from '@/features/crm/leads/server/service'; +import { PERMISSIONS } from '@/lib/auth/rbac'; +import { AuthError, requireOrganizationAccess } from '@/lib/auth/session'; + +type Params = { + params: Promise<{ id: string }>; +}; + +export async function POST(request: NextRequest, { params }: Params) { + try { + const { id } = await params; + const { organization, session, membership } = await requireOrganizationAccess({ + permission: PERMISSIONS.crmLeadAssign + }); + const payload = assignLeadSchema.parse(await request.json()); + const accessContext = buildLeadAccessContext({ + organizationId: organization.id, + userId: session.user.id, + membership + }); + + const result = await assignLead( + organization.id, + session.user.id, + { + leadId: id, + salesOwnerId: payload.salesOwnerId, + assignmentRemark: payload.assignmentRemark + }, + accessContext + ); + + return NextResponse.json({ + success: true, + message: 'Lead assigned successfully', + leadId: result.leadId, + enquiryId: result.enquiryId, + enquiryCode: result.enquiryCode + }); + } catch (error) { + if (error instanceof AuthError) { + return NextResponse.json({ message: error.message }, { status: error.status }); + } + + if (error instanceof z.ZodError) { + return NextResponse.json( + { message: error.issues[0]?.message ?? 'Invalid payload' }, + { status: 400 } + ); + } + + if (error instanceof Error) { + return NextResponse.json({ message: error.message }, { status: 400 }); + } + + return NextResponse.json({ message: 'Unable to assign lead' }, { status: 500 }); + } +} diff --git a/src/db/schema.ts b/src/db/schema.ts index a1847aa..2a40a16 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -275,14 +275,22 @@ export const crmLeads = pgTable( code: text('code').notNull(), customerId: text('customer_id'), contactId: text('contact_id'), + description: text('description'), projectName: text('project_name'), projectLocation: text('project_location'), + productType: text('product_type'), + priority: text('priority'), + estimatedValue: doublePrecision('estimated_value'), 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'), + assignedSalesOwnerId: text('assigned_sales_owner_id'), + assignedAt: timestamp('assigned_at', { withTimezone: true }), + assignedBy: text('assigned_by'), + assignmentRemark: text('assignment_remark'), createdBy: text('created_by').notNull(), createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(), diff --git a/src/db/seeds/foundation.seed.ts b/src/db/seeds/foundation.seed.ts index 4c2aef7..bf83cda 100644 --- a/src/db/seeds/foundation.seed.ts +++ b/src/db/seeds/foundation.seed.ts @@ -384,8 +384,9 @@ const FOUNDATION_OPTIONS = { 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 } + { code: 'assigned', label: 'Assigned', value: 'assigned', sortOrder: 3 }, + { code: 'closed_lost', label: 'Closed Lost', value: 'closed_lost', sortOrder: 4 }, + { code: 'cancel', label: 'Cancel', value: 'cancel', sortOrder: 5 } ], crm_lead_followup_status: [ { diff --git a/src/features/crm/enquiries/server/service.ts b/src/features/crm/enquiries/server/service.ts index 28c8935..c214024 100644 --- a/src/features/crm/enquiries/server/service.ts +++ b/src/features/crm/enquiries/server/service.ts @@ -448,7 +448,9 @@ export async function assertAssignableUserBelongsToOrganization( ) { const [membership] = await db .select({ - userId: memberships.userId + userId: memberships.userId, + role: memberships.role, + businessRole: memberships.businessRole }) .from(memberships) .where(and(eq(memberships.organizationId, organizationId), eq(memberships.userId, userId))) @@ -458,6 +460,10 @@ export async function assertAssignableUserBelongsToOrganization( throw new AuthError('Assignee not found in organization', 404); } + if (membership.role !== 'admin' && !ASSIGNABLE_BUSINESS_ROLES.has(membership.businessRole)) { + throw new AuthError('Assignee must be a sales owner', 400); + } + return membership; } diff --git a/src/features/crm/leads/schemas/lead.schema.ts b/src/features/crm/leads/schemas/lead.schema.ts index 496386f..dc2c328 100644 --- a/src/features/crm/leads/schemas/lead.schema.ts +++ b/src/features/crm/leads/schemas/lead.schema.ts @@ -4,10 +4,14 @@ export const leadSchema = z.object({ branchId: z.string().nullable().optional(), customerId: z.string().nullable().optional(), contactId: z.string().nullable().optional(), + description: z.string().nullable().optional(), projectName: z.string().nullable().optional(), projectLocation: z.string().nullable().optional(), + productType: z.string().nullable().optional(), + priority: z.string().nullable().optional(), + estimatedValue: z.number().nullable().optional(), awarenessId: z.string().nullable().optional(), - status: z.string().min(1, 'Please select a lead status'), + status: z.string().min(1, 'Please select lead status'), followupStatus: z.string().nullable().optional(), lostReason: z.string().nullable().optional(), outcome: z.enum(['open', 'won', 'lost']).default('open'), @@ -16,9 +20,14 @@ export const leadSchema = z.object({ export const updateLeadSchema = leadSchema.partial(); +export const assignLeadSchema = z.object({ + salesOwnerId: z.string().min(1, 'Sales owner is required'), + assignmentRemark: z.string().nullable().optional() +}); + export const leadFollowupSchema = z.object({ - followupDate: z.string().min(1, 'Follow-up date is required'), - followupStatus: z.string().min(1, 'Please select a follow-up status'), + followupDate: z.string().min(1, 'Follow-up date required'), + followupStatus: z.string().min(1, 'Please select follow-up status'), note: z.string().nullable().optional(), nextFollowupDate: z.string().nullable().optional() }); diff --git a/src/features/crm/leads/server/assignment.service.ts b/src/features/crm/leads/server/assignment.service.ts new file mode 100644 index 0000000..4a7ec06 --- /dev/null +++ b/src/features/crm/leads/server/assignment.service.ts @@ -0,0 +1,300 @@ +import { and, asc, eq, inArray, isNull } from 'drizzle-orm'; +import { crmEnquiries, crmLeads } from '@/db/schema'; +import { db } from '@/lib/db'; +import { AuthError } from '@/lib/auth/session'; +import { generateNextDocumentCode } from '@/features/foundation/document-sequence/service'; +import { auditAction } from '@/features/foundation/audit-log/service'; +import { getActiveOptionsByCategory } from '@/features/foundation/master-options/service'; +import { + canAccessScopedCrmRecord, + type CrmSecurityContext +} from '@/features/crm/security/server/service'; +import { + assertAssignableUserBelongsToOrganization, + assertContactBelongsToOrganization, + assertCustomerBelongsToOrganization +} from '@/features/crm/enquiries/server/service'; +import type { AssignLeadInput, AssignLeadResult } from '../types'; + +export type LeadAssignmentAccessContext = CrmSecurityContext; + +type LeadRow = typeof crmLeads.$inferSelect; +type EnquiryRow = typeof crmEnquiries.$inferSelect; + +function canAccessLeadRow(row: LeadRow, accessContext: LeadAssignmentAccessContext) { + return canAccessScopedCrmRecord(accessContext, { + branchId: row.branchId, + createdBy: row.createdBy, + ownerUserId: row.ownerMarketingUserId + }); +} + +async function assertLeadBelongsToOrganization( + leadId: string, + organizationId: string, + accessContext?: LeadAssignmentAccessContext +) { + const [lead] = await db + .select() + .from(crmLeads) + .where( + and( + eq(crmLeads.id, leadId), + eq(crmLeads.organizationId, organizationId), + isNull(crmLeads.deletedAt) + ) + ) + .limit(1); + + if (!lead) { + throw new AuthError('Lead not found', 404); + } + + if (accessContext && !canAccessLeadRow(lead, accessContext)) { + throw new AuthError('Forbidden', 403); + } + + return lead; +} + +async function resolveDefaultEnquiryStatusId(organizationId: string) { + const options = await getActiveOptionsByCategory('crm_enquiry_status', { organizationId }); + const preferred = options.find((option) => option.code === 'new'); + const resolved = preferred ?? options[0]; + + if (!resolved) { + throw new AuthError('Enquiry status options are not configured', 400); + } + + return resolved.id; +} + +async function findActiveEnquiryByLead(leadId: string, organizationId: string) { + const [enquiry] = await db + .select() + .from(crmEnquiries) + .where( + and( + eq(crmEnquiries.leadId, leadId), + eq(crmEnquiries.organizationId, organizationId), + inArray(crmEnquiries.pipelineStage, ['lead', 'enquiry']), + isNull(crmEnquiries.deletedAt) + ) + ) + .orderBy(asc(crmEnquiries.createdAt)) + .limit(1); + + return enquiry ?? null; +} + +async function syncLeadAssignment(input: { + leadId: string; + status: string; + salesOwnerId: string; + assignedBy: string; + assignmentRemark?: string | null; +}) { + const now = new Date(); + + await db + .update(crmLeads) + .set({ + status: input.status, + assignedSalesOwnerId: input.salesOwnerId, + assignedAt: now, + assignedBy: input.assignedBy, + assignmentRemark: input.assignmentRemark?.trim() || null, + updatedAt: now + }) + .where(eq(crmLeads.id, input.leadId)); +} + +async function createEnquiryFromLead(input: { + lead: LeadRow; + organizationId: string; + actorUserId: string; + salesOwnerId: string; + assignmentRemark?: string | null; +}) { + if (!input.lead.customerId) { + throw new AuthError('Lead requires a customer before assignment', 400); + } + + if (!input.lead.productType) { + throw new AuthError('Lead requires a product type before assignment', 400); + } + + if (!input.lead.priority) { + throw new AuthError('Lead requires a priority before assignment', 400); + } + + await assertCustomerBelongsToOrganization(input.lead.customerId, input.organizationId); + + if (input.lead.contactId) { + await assertContactBelongsToOrganization( + input.lead.contactId, + input.organizationId, + input.lead.customerId + ); + } + + const [statusId, documentCode] = await Promise.all([ + resolveDefaultEnquiryStatusId(input.organizationId), + generateNextDocumentCode({ + organizationId: input.organizationId, + documentType: 'crm_enquiry', + branchId: input.lead.branchId ?? null + }) + ]); + + const now = new Date(); + const [created] = await db + .insert(crmEnquiries) + .values({ + id: crypto.randomUUID(), + organizationId: input.organizationId, + branchId: input.lead.branchId ?? null, + leadId: input.lead.id, + code: documentCode.code, + customerId: input.lead.customerId, + contactId: input.lead.contactId ?? null, + title: input.lead.projectName?.trim() || `Lead ${input.lead.code}`, + description: input.lead.description?.trim() || null, + requirement: null, + projectName: input.lead.projectName?.trim() || null, + projectLocation: input.lead.projectLocation?.trim() || null, + productType: input.lead.productType, + status: statusId, + priority: input.lead.priority, + leadChannel: null, + estimatedValue: input.lead.estimatedValue ?? null, + chancePercent: null, + expectedCloseDate: null, + competitor: null, + source: `lead:${input.lead.code}`, + notes: null, + isHotProject: false, + isActive: true, + pipelineStage: 'enquiry', + assignedToUserId: input.salesOwnerId, + assignedAt: now, + assignedBy: input.actorUserId, + assignmentRemark: input.assignmentRemark?.trim() || null, + createdBy: input.actorUserId, + updatedBy: input.actorUserId + }) + .returning(); + + return created; +} + +export async function assignLead( + organizationId: string, + actorUserId: string, + input: AssignLeadInput, + accessContext?: LeadAssignmentAccessContext +): Promise { + const lead = await assertLeadBelongsToOrganization(input.leadId, organizationId, accessContext); + await assertAssignableUserBelongsToOrganization(input.salesOwnerId, organizationId); + + const existingEnquiry = await findActiveEnquiryByLead(input.leadId, organizationId); + + if (existingEnquiry) { + const resolvedSalesOwnerId = existingEnquiry.assignedToUserId ?? input.salesOwnerId; + + if (!existingEnquiry.assignedToUserId) { + await db + .update(crmEnquiries) + .set({ + pipelineStage: 'enquiry', + assignedToUserId: input.salesOwnerId, + assignedAt: new Date(), + assignedBy: actorUserId, + assignmentRemark: input.assignmentRemark?.trim() || null, + updatedAt: new Date(), + updatedBy: actorUserId + }) + .where(eq(crmEnquiries.id, existingEnquiry.id)); + } + + await syncLeadAssignment({ + leadId: input.leadId, + status: 'assigned', + salesOwnerId: resolvedSalesOwnerId, + assignedBy: actorUserId, + assignmentRemark: input.assignmentRemark + }); + + await auditAction({ + organizationId, + branchId: lead.branchId, + userId: actorUserId, + entityType: 'crm_lead', + entityId: input.leadId, + action: 'assign_lead', + afterData: { + leadId: input.leadId, + enquiryId: existingEnquiry.id, + salesOwnerId: resolvedSalesOwnerId, + reusedExistingEnquiry: true + } + }); + + return { + leadId: input.leadId, + enquiryId: existingEnquiry.id, + enquiryCode: existingEnquiry.code + }; + } + + const createdEnquiry = await createEnquiryFromLead({ + lead, + organizationId, + actorUserId, + salesOwnerId: input.salesOwnerId, + assignmentRemark: input.assignmentRemark + }); + + await syncLeadAssignment({ + leadId: input.leadId, + status: 'assigned', + salesOwnerId: input.salesOwnerId, + assignedBy: actorUserId, + assignmentRemark: input.assignmentRemark + }); + + await auditAction({ + organizationId, + branchId: lead.branchId, + userId: actorUserId, + entityType: 'crm_enquiry', + entityId: createdEnquiry.id, + action: 'create_enquiry_from_lead', + afterData: { + leadId: input.leadId, + enquiryId: createdEnquiry.id, + salesOwnerId: input.salesOwnerId + } + }); + + await auditAction({ + organizationId, + branchId: lead.branchId, + userId: actorUserId, + entityType: 'crm_lead', + entityId: input.leadId, + action: 'assign_lead', + afterData: { + leadId: input.leadId, + enquiryId: createdEnquiry.id, + salesOwnerId: input.salesOwnerId, + reusedExistingEnquiry: false + } + }); + + return { + leadId: input.leadId, + enquiryId: createdEnquiry.id, + enquiryCode: createdEnquiry.code + }; +} diff --git a/src/features/crm/leads/server/service.ts b/src/features/crm/leads/server/service.ts index b5e15f7..df75737 100644 --- a/src/features/crm/leads/server/service.ts +++ b/src/features/crm/leads/server/service.ts @@ -49,7 +49,9 @@ const LEAD_OPTION_CATEGORIES = { awareness: 'crm_lead_awareness', status: 'crm_lead_status', followupStatus: 'crm_lead_followup_status', - lostReason: 'crm_lead_lost_reason' + lostReason: 'crm_lead_lost_reason', + productType: 'crm_product_type', + priority: 'crm_priority' } as const; export type LeadAccessContext = CrmSecurityContext; @@ -61,6 +63,8 @@ type LeadLookupMaps = { statusLabels: Map; followupStatusLabels: Map; lostReasonLabels: Map; + productTypeLabels: Map; + priorityLabels: Map; customerNames: Map; contactNames: Map; userNames: Map; @@ -121,8 +125,12 @@ function mapLeadSummary(row: LeadRow, lookups: LeadLookupMaps): LeadSummary { customerName: row.customerId ? (lookups.customerNames.get(row.customerId) ?? null) : null, contactId: row.contactId ?? null, contactName: row.contactId ? (lookups.contactNames.get(row.contactId) ?? null) : null, + description: row.description ?? null, projectName: row.projectName ?? null, projectLocation: row.projectLocation ?? null, + productType: row.productType ?? null, + priority: row.priority ?? null, + estimatedValue: row.estimatedValue ?? null, awarenessId: row.awarenessId ?? null, awarenessLabel: row.awarenessId ? (lookups.awarenessLabels.get(row.awarenessId) ?? null) : null, status: row.status, @@ -138,6 +146,14 @@ function mapLeadSummary(row: LeadRow, lookups: LeadLookupMaps): LeadSummary { ownerMarketingName: row.ownerMarketingUserId ? (lookups.userNames.get(row.ownerMarketingUserId) ?? null) : null, + assignedSalesOwnerId: row.assignedSalesOwnerId ?? null, + assignedSalesOwnerName: row.assignedSalesOwnerId + ? (lookups.userNames.get(row.assignedSalesOwnerId) ?? null) + : null, + assignedAt: toIsoDateTime(row.assignedAt), + assignedBy: row.assignedBy ?? null, + assignedByName: row.assignedBy ? (lookups.userNames.get(row.assignedBy) ?? null) : null, + assignmentRemark: row.assignmentRemark ?? null, createdBy: row.createdBy, createdAt: row.createdAt.toISOString(), updatedAt: row.updatedAt.toISOString(), @@ -339,12 +355,15 @@ async function assertLeadBelongsToOrganization( } async function buildLookupMaps(organizationId: string, rows: LeadRow[]): Promise { - const [awarenesses, statuses, followupStatuses, lostReasons] = await Promise.all([ - getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.awareness, { organizationId }), - getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.status, { organizationId }), - getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.followupStatus, { organizationId }), - getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.lostReason, { organizationId }) - ]); + const [awarenesses, statuses, followupStatuses, lostReasons, productTypes, priorities] = + await Promise.all([ + getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.awareness, { organizationId }), + getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.status, { organizationId }), + getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.followupStatus, { organizationId }), + getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.lostReason, { organizationId }), + getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.productType, { organizationId }), + getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.priority, { organizationId }) + ]); const customerIds = [ ...new Set(rows.map((row) => row.customerId).filter((value): value is string => Boolean(value))) ]; @@ -354,7 +373,12 @@ async function buildLookupMaps(organizationId: string, rows: LeadRow[]): Promise const userIds = [ ...new Set( rows - .flatMap((row) => [row.ownerMarketingUserId, row.createdBy]) + .flatMap((row) => [ + row.ownerMarketingUserId, + row.assignedSalesOwnerId, + row.assignedBy, + row.createdBy + ]) .filter((value): value is string => Boolean(value)) ) ]; @@ -385,6 +409,8 @@ async function buildLookupMaps(organizationId: string, rows: LeadRow[]): Promise statusLabels: new Map(statuses.map((item) => [item.id, item.label])), followupStatusLabels: new Map(followupStatuses.map((item) => [item.id, item.label])), lostReasonLabels: new Map(lostReasons.map((item) => [item.id, item.label])), + productTypeLabels: new Map(productTypes.map((item) => [item.id, item.label])), + priorityLabels: new Map(priorities.map((item) => [item.id, item.label])), customerNames: new Map(customerRows.map((item) => [item.id, item.name])), contactNames: new Map(contactRows.map((item) => [item.id, item.name])), userNames: new Map(userRows.map((item) => [item.id, item.name])) @@ -399,6 +425,12 @@ async function validateLeadPayload( ) { await Promise.all([ assertMasterOptionValue(organizationId, LEAD_OPTION_CATEGORIES.awareness, payload.awarenessId), + assertMasterOptionValue( + organizationId, + LEAD_OPTION_CATEGORIES.productType, + payload.productType + ), + assertMasterOptionValue(organizationId, LEAD_OPTION_CATEGORIES.priority, payload.priority), payload.status ? assertMasterOptionValue(organizationId, LEAD_OPTION_CATEGORIES.status, payload.status) : Promise.resolve(), @@ -454,19 +486,24 @@ async function validateLeadPayload( } export async function getLeadReferenceData(organizationId: string): Promise { - const [awarenesses, statuses, followupStatuses, lostReasons, branches] = await Promise.all([ - getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.awareness, { organizationId }), - getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.status, { organizationId }), - getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.followupStatus, { organizationId }), - getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.lostReason, { organizationId }), - getUserBranches() - ]); + const [awarenesses, statuses, followupStatuses, lostReasons, productTypes, priorities, branches] = + await Promise.all([ + getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.awareness, { organizationId }), + getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.status, { organizationId }), + getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.followupStatus, { organizationId }), + getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.lostReason, { organizationId }), + getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.productType, { organizationId }), + getActiveOptionsByCategory(LEAD_OPTION_CATEGORIES.priority, { organizationId }), + getUserBranches() + ]); return { awarenesses: awarenesses.map(mapOption), statuses: statuses.map(mapOption), followupStatuses: followupStatuses.map(mapOption), lostReasons: lostReasons.map(mapOption), + productTypes: productTypes.map(mapOption), + priorities: priorities.map(mapOption), branches: branches.map(mapBranch) }; } @@ -570,8 +607,12 @@ export async function createLead( code: codeResult.code, customerId: payload.customerId ?? null, contactId: payload.contactId ?? null, + description: payload.description?.trim() || null, projectName: payload.projectName?.trim() || null, projectLocation: payload.projectLocation?.trim() || null, + productType: payload.productType ?? null, + priority: payload.priority ?? null, + estimatedValue: payload.estimatedValue ?? null, awarenessId: payload.awarenessId ?? null, status: payload.status, followupStatus: payload.followupStatus ?? null, @@ -600,9 +641,14 @@ export async function updateLead( payload.contactId === undefined ? current.contactId : payload.contactId, + description: payload.description === undefined ? current.description : payload.description, projectName: payload.projectName === undefined ? current.projectName : payload.projectName, projectLocation: payload.projectLocation === undefined ? current.projectLocation : payload.projectLocation, + productType: payload.productType === undefined ? current.productType : payload.productType, + priority: payload.priority === undefined ? current.priority : payload.priority, + estimatedValue: + payload.estimatedValue === undefined ? current.estimatedValue : payload.estimatedValue, awarenessId: payload.awarenessId === undefined ? current.awarenessId : payload.awarenessId, status: payload.status ?? current.status, followupStatus: @@ -623,8 +669,12 @@ export async function updateLead( branchId: nextPayload.branchId ?? null, customerId: nextPayload.customerId ?? null, contactId: nextPayload.contactId ?? null, + description: nextPayload.description?.trim() || null, projectName: nextPayload.projectName?.trim() || null, projectLocation: nextPayload.projectLocation?.trim() || null, + productType: nextPayload.productType ?? null, + priority: nextPayload.priority ?? null, + estimatedValue: nextPayload.estimatedValue ?? null, awarenessId: nextPayload.awarenessId ?? null, status: nextPayload.status, followupStatus: nextPayload.followupStatus ?? null, diff --git a/src/features/crm/leads/types.ts b/src/features/crm/leads/types.ts index 7c9829b..2a7390f 100644 --- a/src/features/crm/leads/types.ts +++ b/src/features/crm/leads/types.ts @@ -17,6 +17,8 @@ export interface LeadReferenceData { statuses: LeadOption[]; followupStatuses: LeadOption[]; lostReasons: LeadOption[]; + productTypes: LeadOption[]; + priorities: LeadOption[]; branches: LeadBranchOption[]; } @@ -24,8 +26,12 @@ export interface CreateLeadInput { branchId?: string | null; customerId?: string | null; contactId?: string | null; + description?: string | null; projectName?: string | null; projectLocation?: string | null; + productType?: string | null; + priority?: string | null; + estimatedValue?: number | null; awarenessId?: string | null; status: string; followupStatus?: string | null; @@ -38,8 +44,12 @@ export interface UpdateLeadInput { branchId?: string | null; customerId?: string | null; contactId?: string | null; + description?: string | null; projectName?: string | null; projectLocation?: string | null; + productType?: string | null; + priority?: string | null; + estimatedValue?: number | null; awarenessId?: string | null; status?: string; followupStatus?: string | null; @@ -69,6 +79,12 @@ export interface CreateLeadFollowupInput { nextFollowupDate?: string | null; } +export interface AssignLeadInput { + leadId: string; + salesOwnerId: string; + assignmentRemark?: string | null; +} + export interface LeadSummary { id: string; organizationId: string; @@ -78,8 +94,12 @@ export interface LeadSummary { customerName: string | null; contactId: string | null; contactName: string | null; + description: string | null; projectName: string | null; projectLocation: string | null; + productType: string | null; + priority: string | null; + estimatedValue: number | null; awarenessId: string | null; awarenessLabel: string | null; status: string; @@ -91,6 +111,12 @@ export interface LeadSummary { outcome: string; ownerMarketingUserId: string | null; ownerMarketingName: string | null; + assignedSalesOwnerId: string | null; + assignedSalesOwnerName: string | null; + assignedAt: string | null; + assignedBy: string | null; + assignedByName: string | null; + assignmentRemark: string | null; createdBy: string; createdAt: string; updatedAt: string; @@ -119,3 +145,9 @@ export interface LeadFollowupSummary { createdBy: string; createdByName: string | null; } + +export interface AssignLeadResult { + leadId: string; + enquiryId: string; + enquiryCode: string; +}