@lead-routing/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/index.js +1916 -0
  2. package/dist/prisma/migrations/20260101000000_init/migration.sql +276 -0
  3. package/dist/prisma/migrations/20260223000000_add_routing_log_dismissed/migration.sql +2 -0
  4. package/dist/prisma/migrations/20260224000000_add_org_notification_webhook/migration.sql +2 -0
  5. package/dist/prisma/migrations/20260227000000_self_hosted_schema_updates/migration.sql +68 -0
  6. package/dist/prisma/schema.prisma +315 -0
  7. package/dist/sfdc-package/force-app/main/default/applications/Lead_Router_Setup.app-meta.xml +12 -0
  8. package/dist/sfdc-package/force-app/main/default/classes/AccountTriggerTest.cls +58 -0
  9. package/dist/sfdc-package/force-app/main/default/classes/AccountTriggerTest.cls-meta.xml +5 -0
  10. package/dist/sfdc-package/force-app/main/default/classes/ContactTriggerTest.cls +62 -0
  11. package/dist/sfdc-package/force-app/main/default/classes/ContactTriggerTest.cls-meta.xml +5 -0
  12. package/dist/sfdc-package/force-app/main/default/classes/LeadTriggerTest.cls +95 -0
  13. package/dist/sfdc-package/force-app/main/default/classes/LeadTriggerTest.cls-meta.xml +5 -0
  14. package/dist/sfdc-package/force-app/main/default/classes/OnboardingController.cls +183 -0
  15. package/dist/sfdc-package/force-app/main/default/classes/OnboardingController.cls-meta.xml +5 -0
  16. package/dist/sfdc-package/force-app/main/default/classes/RoutingEngineCallout.cls +96 -0
  17. package/dist/sfdc-package/force-app/main/default/classes/RoutingEngineCallout.cls-meta.xml +5 -0
  18. package/dist/sfdc-package/force-app/main/default/classes/RoutingEngineMock.cls +28 -0
  19. package/dist/sfdc-package/force-app/main/default/classes/RoutingEngineMock.cls-meta.xml +5 -0
  20. package/dist/sfdc-package/force-app/main/default/classes/RoutingPayloadBuilder.cls +50 -0
  21. package/dist/sfdc-package/force-app/main/default/classes/RoutingPayloadBuilder.cls-meta.xml +5 -0
  22. package/dist/sfdc-package/force-app/main/default/lwc/onboardingWizard/onboardingWizard.html +230 -0
  23. package/dist/sfdc-package/force-app/main/default/lwc/onboardingWizard/onboardingWizard.js +222 -0
  24. package/dist/sfdc-package/force-app/main/default/lwc/onboardingWizard/onboardingWizard.js-meta.xml +11 -0
  25. package/dist/sfdc-package/force-app/main/default/namedCredentials/RoutingEngine.namedCredential-meta.xml +10 -0
  26. package/dist/sfdc-package/force-app/main/default/objects/Routing_Error_Log__c/Routing_Error_Log__c.object-meta.xml +21 -0
  27. package/dist/sfdc-package/force-app/main/default/objects/Routing_Error_Log__c/fields/Created_At__c.field-meta.xml +6 -0
  28. package/dist/sfdc-package/force-app/main/default/objects/Routing_Error_Log__c/fields/Payload__c.field-meta.xml +8 -0
  29. package/dist/sfdc-package/force-app/main/default/objects/Routing_Error_Log__c/fields/Response_Body__c.field-meta.xml +8 -0
  30. package/dist/sfdc-package/force-app/main/default/objects/Routing_Error_Log__c/fields/Status_Code__c.field-meta.xml +9 -0
  31. package/dist/sfdc-package/force-app/main/default/objects/Routing_Settings__c/Routing_Settings__c.object-meta.xml +8 -0
  32. package/dist/sfdc-package/force-app/main/default/objects/Routing_Settings__c/fields/Account_Insert_Enabled__c.field-meta.xml +7 -0
  33. package/dist/sfdc-package/force-app/main/default/objects/Routing_Settings__c/fields/Account_Routing_Enabled__c.field-meta.xml +7 -0
  34. package/dist/sfdc-package/force-app/main/default/objects/Routing_Settings__c/fields/Account_Update_Enabled__c.field-meta.xml +7 -0
  35. package/dist/sfdc-package/force-app/main/default/objects/Routing_Settings__c/fields/App_Url__c.field-meta.xml +8 -0
  36. package/dist/sfdc-package/force-app/main/default/objects/Routing_Settings__c/fields/Contact_Insert_Enabled__c.field-meta.xml +7 -0
  37. package/dist/sfdc-package/force-app/main/default/objects/Routing_Settings__c/fields/Contact_Routing_Enabled__c.field-meta.xml +7 -0
  38. package/dist/sfdc-package/force-app/main/default/objects/Routing_Settings__c/fields/Contact_Update_Enabled__c.field-meta.xml +7 -0
  39. package/dist/sfdc-package/force-app/main/default/objects/Routing_Settings__c/fields/Engine_Endpoint__c.field-meta.xml +8 -0
  40. package/dist/sfdc-package/force-app/main/default/objects/Routing_Settings__c/fields/Lead_Insert_Enabled__c.field-meta.xml +7 -0
  41. package/dist/sfdc-package/force-app/main/default/objects/Routing_Settings__c/fields/Lead_Routing_Enabled__c.field-meta.xml +7 -0
  42. package/dist/sfdc-package/force-app/main/default/objects/Routing_Settings__c/fields/Lead_Update_Enabled__c.field-meta.xml +7 -0
  43. package/dist/sfdc-package/force-app/main/default/objects/Routing_Settings__c/fields/Webhook_Secret__c.field-meta.xml +8 -0
  44. package/dist/sfdc-package/force-app/main/default/permissionsets/LeadRouterAdmin.permissionset-meta.xml +14 -0
  45. package/dist/sfdc-package/force-app/main/default/remoteSiteSettings/LeadRouterEngine.remoteSite-meta.xml +7 -0
  46. package/dist/sfdc-package/force-app/main/default/tabs/Lead_Router_Setup.tab-meta.xml +7 -0
  47. package/dist/sfdc-package/force-app/main/default/triggers/AccountTrigger.trigger +28 -0
  48. package/dist/sfdc-package/force-app/main/default/triggers/AccountTrigger.trigger-meta.xml +5 -0
  49. package/dist/sfdc-package/force-app/main/default/triggers/ContactTrigger.trigger +28 -0
  50. package/dist/sfdc-package/force-app/main/default/triggers/ContactTrigger.trigger-meta.xml +5 -0
  51. package/dist/sfdc-package/force-app/main/default/triggers/LeadTrigger.trigger +28 -0
  52. package/dist/sfdc-package/force-app/main/default/triggers/LeadTrigger.trigger-meta.xml +5 -0
  53. package/dist/sfdc-package/sfdx-project.json +14 -0
  54. package/package.json +41 -0
@@ -0,0 +1,276 @@
1
+ -- CreateEnum
2
+ CREATE TYPE "SfdcObjectType" AS ENUM ('LEAD', 'CONTACT', 'ACCOUNT');
3
+
4
+ -- CreateEnum
5
+ CREATE TYPE "TriggerEvent" AS ENUM ('INSERT', 'UPDATE', 'BOTH');
6
+
7
+ -- CreateEnum
8
+ CREATE TYPE "RuleStatus" AS ENUM ('ACTIVE', 'INACTIVE');
9
+
10
+ -- CreateEnum
11
+ CREATE TYPE "AssignmentType" AS ENUM ('USER', 'ROUND_ROBIN', 'QUEUE');
12
+
13
+ -- CreateEnum
14
+ CREATE TYPE "TeamMemberStatus" AS ENUM ('ACTIVE', 'PAUSED');
15
+
16
+ -- CreateEnum
17
+ CREATE TYPE "RoutingStatus" AS ENUM ('SUCCESS', 'FAILED', 'UNMATCHED', 'RETRY');
18
+
19
+ -- CreateTable
20
+ CREATE TABLE "organizations" (
21
+ "id" TEXT NOT NULL,
22
+ "sfdcOrgId" TEXT NOT NULL,
23
+ "sfdcInstanceUrl" TEXT NOT NULL,
24
+ "oauthAccessToken" TEXT NOT NULL,
25
+ "oauthRefreshToken" TEXT NOT NULL,
26
+ "webhookSecret" TEXT NOT NULL,
27
+ "seatsPurchased" INTEGER NOT NULL DEFAULT 5,
28
+ "seatsUsed" INTEGER NOT NULL DEFAULT 0,
29
+ "onboardingDone" BOOLEAN NOT NULL DEFAULT false,
30
+ "notificationWebhookUrl" TEXT,
31
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
32
+ "updatedAt" TIMESTAMP(3) NOT NULL,
33
+
34
+ CONSTRAINT "organizations_pkey" PRIMARY KEY ("id")
35
+ );
36
+
37
+ -- CreateTable
38
+ CREATE TABLE "users" (
39
+ "id" TEXT NOT NULL,
40
+ "orgId" TEXT NOT NULL,
41
+ "sfdcUserId" TEXT NOT NULL,
42
+ "name" TEXT NOT NULL,
43
+ "email" TEXT NOT NULL,
44
+ "role" TEXT,
45
+ "profile" TEXT,
46
+ "department" TEXT,
47
+ "isLicensed" BOOLEAN NOT NULL DEFAULT false,
48
+ "isActive" BOOLEAN NOT NULL DEFAULT true,
49
+ "lastRoutedAt" TIMESTAMP(3),
50
+ "syncedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
51
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
52
+ "updatedAt" TIMESTAMP(3) NOT NULL,
53
+
54
+ CONSTRAINT "users_pkey" PRIMARY KEY ("id")
55
+ );
56
+
57
+ -- CreateTable
58
+ CREATE TABLE "round_robin_teams" (
59
+ "id" TEXT NOT NULL,
60
+ "orgId" TEXT NOT NULL,
61
+ "name" TEXT NOT NULL,
62
+ "description" TEXT,
63
+ "pointerIndex" INTEGER NOT NULL DEFAULT 0,
64
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
65
+ "updatedAt" TIMESTAMP(3) NOT NULL,
66
+
67
+ CONSTRAINT "round_robin_teams_pkey" PRIMARY KEY ("id")
68
+ );
69
+
70
+ -- CreateTable
71
+ CREATE TABLE "team_members" (
72
+ "id" TEXT NOT NULL,
73
+ "teamId" TEXT NOT NULL,
74
+ "userId" TEXT NOT NULL,
75
+ "status" "TeamMemberStatus" NOT NULL DEFAULT 'ACTIVE',
76
+ "weight" INTEGER NOT NULL DEFAULT 1,
77
+ "assignmentCount" INTEGER NOT NULL DEFAULT 0,
78
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
79
+ "updatedAt" TIMESTAMP(3) NOT NULL,
80
+
81
+ CONSTRAINT "team_members_pkey" PRIMARY KEY ("id")
82
+ );
83
+
84
+ -- CreateTable
85
+ CREATE TABLE "routing_rules" (
86
+ "id" TEXT NOT NULL,
87
+ "orgId" TEXT NOT NULL,
88
+ "objectType" "SfdcObjectType" NOT NULL,
89
+ "triggerEvent" "TriggerEvent" NOT NULL,
90
+ "name" TEXT NOT NULL,
91
+ "priority" INTEGER NOT NULL,
92
+ "status" "RuleStatus" NOT NULL DEFAULT 'ACTIVE',
93
+ "assignmentType" "AssignmentType" NOT NULL,
94
+ "assigneeUserId" TEXT,
95
+ "assigneeTeamId" TEXT,
96
+ "assigneeQueueId" TEXT,
97
+ "isDryRun" BOOLEAN NOT NULL DEFAULT false,
98
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
99
+ "updatedAt" TIMESTAMP(3) NOT NULL,
100
+
101
+ CONSTRAINT "routing_rules_pkey" PRIMARY KEY ("id")
102
+ );
103
+
104
+ -- CreateTable
105
+ CREATE TABLE "rule_conditions" (
106
+ "id" TEXT NOT NULL,
107
+ "ruleId" TEXT NOT NULL,
108
+ "groupId" TEXT NOT NULL,
109
+ "fieldName" TEXT NOT NULL,
110
+ "operator" TEXT NOT NULL,
111
+ "value" TEXT,
112
+ "sortOrder" INTEGER NOT NULL DEFAULT 0,
113
+
114
+ CONSTRAINT "rule_conditions_pkey" PRIMARY KEY ("id")
115
+ );
116
+
117
+ -- CreateTable
118
+ CREATE TABLE "routing_logs" (
119
+ "id" TEXT NOT NULL,
120
+ "orgId" TEXT NOT NULL,
121
+ "sfdcRecordId" TEXT NOT NULL,
122
+ "objectType" "SfdcObjectType" NOT NULL,
123
+ "eventType" "TriggerEvent" NOT NULL,
124
+ "ruleId" TEXT,
125
+ "ruleName" TEXT,
126
+ "assigneeId" TEXT,
127
+ "assigneeName" TEXT,
128
+ "assignmentType" "AssignmentType",
129
+ "isDryRun" BOOLEAN NOT NULL DEFAULT false,
130
+ "status" "RoutingStatus" NOT NULL,
131
+ "errorMessage" TEXT,
132
+ "retryCount" INTEGER NOT NULL DEFAULT 0,
133
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
134
+
135
+ CONSTRAINT "routing_logs_pkey" PRIMARY KEY ("id")
136
+ );
137
+
138
+ -- CreateTable
139
+ CREATE TABLE "audit_logs" (
140
+ "id" TEXT NOT NULL,
141
+ "orgId" TEXT NOT NULL,
142
+ "actorId" TEXT NOT NULL,
143
+ "actorName" TEXT NOT NULL,
144
+ "action" TEXT NOT NULL,
145
+ "entityType" TEXT NOT NULL,
146
+ "entityId" TEXT NOT NULL,
147
+ "beforeState" JSONB,
148
+ "afterState" JSONB,
149
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
150
+
151
+ CONSTRAINT "audit_logs_pkey" PRIMARY KEY ("id")
152
+ );
153
+
154
+ -- CreateTable
155
+ CREATE TABLE "sfdc_queues" (
156
+ "id" TEXT NOT NULL,
157
+ "orgId" TEXT NOT NULL,
158
+ "sfdcQueueId" TEXT NOT NULL,
159
+ "name" TEXT NOT NULL,
160
+ "syncedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
161
+
162
+ CONSTRAINT "sfdc_queues_pkey" PRIMARY KEY ("id")
163
+ );
164
+
165
+ -- CreateTable
166
+ CREATE TABLE "field_schemas" (
167
+ "id" TEXT NOT NULL,
168
+ "orgId" TEXT NOT NULL,
169
+ "objectType" "SfdcObjectType" NOT NULL,
170
+ "fieldApiName" TEXT NOT NULL,
171
+ "fieldLabel" TEXT NOT NULL,
172
+ "fieldType" TEXT NOT NULL,
173
+ "picklistValues" JSONB,
174
+ "syncedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
175
+
176
+ CONSTRAINT "field_schemas_pkey" PRIMARY KEY ("id")
177
+ );
178
+
179
+ -- CreateTable
180
+ CREATE TABLE "billing_info" (
181
+ "id" TEXT NOT NULL,
182
+ "orgId" TEXT NOT NULL,
183
+ "entityName" TEXT,
184
+ "gstin" TEXT,
185
+ "addressLine1" TEXT,
186
+ "addressLine2" TEXT,
187
+ "city" TEXT,
188
+ "state" TEXT,
189
+ "pinCode" TEXT,
190
+ "invoiceEmail" TEXT,
191
+ "updatedAt" TIMESTAMP(3) NOT NULL,
192
+
193
+ CONSTRAINT "billing_info_pkey" PRIMARY KEY ("id")
194
+ );
195
+
196
+ -- CreateTable
197
+ CREATE TABLE "sessions" (
198
+ "id" TEXT NOT NULL,
199
+ "orgId" TEXT NOT NULL,
200
+ "userId" TEXT NOT NULL,
201
+ "userName" TEXT NOT NULL,
202
+ "userEmail" TEXT NOT NULL,
203
+ "expiresAt" TIMESTAMP(3) NOT NULL,
204
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
205
+
206
+ CONSTRAINT "sessions_pkey" PRIMARY KEY ("id")
207
+ );
208
+
209
+ -- CreateIndex
210
+ CREATE UNIQUE INDEX "organizations_sfdcOrgId_key" ON "organizations"("sfdcOrgId");
211
+
212
+ -- CreateIndex
213
+ CREATE UNIQUE INDEX "users_orgId_sfdcUserId_key" ON "users"("orgId", "sfdcUserId");
214
+
215
+ -- CreateIndex
216
+ CREATE UNIQUE INDEX "team_members_teamId_userId_key" ON "team_members"("teamId", "userId");
217
+
218
+ -- CreateIndex
219
+ CREATE INDEX "routing_logs_orgId_createdAt_idx" ON "routing_logs"("orgId", "createdAt");
220
+
221
+ -- CreateIndex
222
+ CREATE INDEX "routing_logs_orgId_status_idx" ON "routing_logs"("orgId", "status");
223
+
224
+ -- CreateIndex
225
+ CREATE INDEX "audit_logs_orgId_createdAt_idx" ON "audit_logs"("orgId", "createdAt");
226
+
227
+ -- CreateIndex
228
+ CREATE UNIQUE INDEX "sfdc_queues_orgId_sfdcQueueId_key" ON "sfdc_queues"("orgId", "sfdcQueueId");
229
+
230
+ -- CreateIndex
231
+ CREATE UNIQUE INDEX "field_schemas_orgId_objectType_fieldApiName_key" ON "field_schemas"("orgId", "objectType", "fieldApiName");
232
+
233
+ -- CreateIndex
234
+ CREATE UNIQUE INDEX "billing_info_orgId_key" ON "billing_info"("orgId");
235
+
236
+ -- CreateIndex
237
+ CREATE INDEX "sessions_orgId_idx" ON "sessions"("orgId");
238
+
239
+ -- AddForeignKey
240
+ ALTER TABLE "users" ADD CONSTRAINT "users_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
241
+
242
+ -- AddForeignKey
243
+ ALTER TABLE "round_robin_teams" ADD CONSTRAINT "round_robin_teams_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
244
+
245
+ -- AddForeignKey
246
+ ALTER TABLE "team_members" ADD CONSTRAINT "team_members_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "round_robin_teams"("id") ON DELETE CASCADE ON UPDATE CASCADE;
247
+
248
+ -- AddForeignKey
249
+ ALTER TABLE "team_members" ADD CONSTRAINT "team_members_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
250
+
251
+ -- AddForeignKey
252
+ ALTER TABLE "routing_rules" ADD CONSTRAINT "routing_rules_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
253
+
254
+ -- AddForeignKey
255
+ ALTER TABLE "routing_rules" ADD CONSTRAINT "routing_rules_assigneeTeamId_fkey" FOREIGN KEY ("assigneeTeamId") REFERENCES "round_robin_teams"("id") ON DELETE SET NULL ON UPDATE CASCADE;
256
+
257
+ -- AddForeignKey
258
+ ALTER TABLE "routing_rules" ADD CONSTRAINT "routing_rules_assigneeQueueId_fkey" FOREIGN KEY ("assigneeQueueId") REFERENCES "sfdc_queues"("id") ON DELETE SET NULL ON UPDATE CASCADE;
259
+
260
+ -- AddForeignKey
261
+ ALTER TABLE "rule_conditions" ADD CONSTRAINT "rule_conditions_ruleId_fkey" FOREIGN KEY ("ruleId") REFERENCES "routing_rules"("id") ON DELETE CASCADE ON UPDATE CASCADE;
262
+
263
+ -- AddForeignKey
264
+ ALTER TABLE "routing_logs" ADD CONSTRAINT "routing_logs_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
265
+
266
+ -- AddForeignKey
267
+ ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
268
+
269
+ -- AddForeignKey
270
+ ALTER TABLE "sfdc_queues" ADD CONSTRAINT "sfdc_queues_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
271
+
272
+ -- AddForeignKey
273
+ ALTER TABLE "field_schemas" ADD CONSTRAINT "field_schemas_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
274
+
275
+ -- AddForeignKey
276
+ ALTER TABLE "billing_info" ADD CONSTRAINT "billing_info_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,2 @@
1
+ -- AlterTable
2
+ ALTER TABLE "routing_logs" ADD COLUMN "dismissed" BOOLEAN NOT NULL DEFAULT false;
@@ -0,0 +1,2 @@
1
+ -- AlterTable (column already added in init migration if starting fresh)
2
+ ALTER TABLE "organizations" ADD COLUMN IF NOT EXISTS "notificationWebhookUrl" TEXT;
@@ -0,0 +1,68 @@
1
+ -- ============================================================
2
+ -- Self-hosted schema updates
3
+ -- Brings the DB in sync with the current Prisma schema:
4
+ -- 1. Make SFDC credential columns nullable (populated during onboarding)
5
+ -- 2. Add Plan enum + new org columns (plan, isActive, quotas)
6
+ -- 3. Create app_users table (CLI-managed admin users)
7
+ -- 4. Create invites table
8
+ -- 5. Add recordSnapshot to routing_logs
9
+ -- ============================================================
10
+
11
+ -- 1. Make SFDC credential columns nullable
12
+ ALTER TABLE "organizations" ALTER COLUMN "sfdcOrgId" DROP NOT NULL;
13
+ ALTER TABLE "organizations" ALTER COLUMN "sfdcInstanceUrl" DROP NOT NULL;
14
+ ALTER TABLE "organizations" ALTER COLUMN "oauthAccessToken" DROP NOT NULL;
15
+ ALTER TABLE "organizations" ALTER COLUMN "oauthRefreshToken" DROP NOT NULL;
16
+
17
+ -- 2. Plan enum + new org columns
18
+ CREATE TYPE "Plan" AS ENUM ('FREE', 'PAID');
19
+
20
+ ALTER TABLE "organizations"
21
+ ADD COLUMN IF NOT EXISTS "plan" "Plan" NOT NULL DEFAULT 'FREE',
22
+ ADD COLUMN IF NOT EXISTS "isActive" BOOLEAN NOT NULL DEFAULT true,
23
+ ADD COLUMN IF NOT EXISTS "routingQuotaUsed" INTEGER NOT NULL DEFAULT 0,
24
+ ADD COLUMN IF NOT EXISTS "quotaResetAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
25
+
26
+ -- 3. app_users table
27
+ CREATE TABLE "app_users" (
28
+ "id" TEXT NOT NULL,
29
+ "orgId" TEXT NOT NULL,
30
+ "email" TEXT NOT NULL,
31
+ "name" TEXT NOT NULL,
32
+ "passwordHash" TEXT NOT NULL,
33
+ "role" TEXT NOT NULL DEFAULT 'ADMIN',
34
+ "isActive" BOOLEAN NOT NULL DEFAULT true,
35
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
36
+ "updatedAt" TIMESTAMP(3) NOT NULL,
37
+
38
+ CONSTRAINT "app_users_pkey" PRIMARY KEY ("id")
39
+ );
40
+
41
+ CREATE UNIQUE INDEX "app_users_orgId_email_key" ON "app_users"("orgId", "email");
42
+
43
+ ALTER TABLE "app_users"
44
+ ADD CONSTRAINT "app_users_orgId_fkey"
45
+ FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
46
+
47
+ -- 4. invites table
48
+ CREATE TABLE "invites" (
49
+ "id" TEXT NOT NULL,
50
+ "orgId" TEXT NOT NULL,
51
+ "email" TEXT NOT NULL,
52
+ "token" TEXT NOT NULL,
53
+ "expiresAt" TIMESTAMP(3) NOT NULL,
54
+ "acceptedAt" TIMESTAMP(3),
55
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
56
+
57
+ CONSTRAINT "invites_pkey" PRIMARY KEY ("id")
58
+ );
59
+
60
+ CREATE UNIQUE INDEX "invites_token_key" ON "invites"("token");
61
+
62
+ ALTER TABLE "invites"
63
+ ADD CONSTRAINT "invites_orgId_fkey"
64
+ FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
65
+
66
+ -- 5. recordSnapshot column in routing_logs
67
+ ALTER TABLE "routing_logs"
68
+ ADD COLUMN IF NOT EXISTS "recordSnapshot" JSONB;
@@ -0,0 +1,315 @@
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ output = "../../../node_modules/.prisma/client"
4
+ }
5
+
6
+ datasource db {
7
+ provider = "postgresql"
8
+ url = env("DATABASE_URL")
9
+ }
10
+
11
+ // ─── Enums ────────────────────────────────────────────────────────────────────
12
+
13
+ enum SfdcObjectType {
14
+ LEAD
15
+ CONTACT
16
+ ACCOUNT
17
+ }
18
+
19
+ enum TriggerEvent {
20
+ INSERT
21
+ UPDATE
22
+ BOTH
23
+ }
24
+
25
+ enum RuleStatus {
26
+ ACTIVE
27
+ INACTIVE
28
+ }
29
+
30
+ enum AssignmentType {
31
+ USER
32
+ ROUND_ROBIN
33
+ QUEUE
34
+ }
35
+
36
+ enum TeamMemberStatus {
37
+ ACTIVE
38
+ PAUSED
39
+ }
40
+
41
+ enum RoutingStatus {
42
+ SUCCESS
43
+ FAILED
44
+ UNMATCHED
45
+ RETRY
46
+ }
47
+
48
+ enum Plan {
49
+ FREE
50
+ PAID
51
+ }
52
+
53
+ // ─── Models ───────────────────────────────────────────────────────────────────
54
+
55
+ model Organization {
56
+ id String @id @default(cuid())
57
+ sfdcOrgId String? @unique // null until CRM is connected in onboarding
58
+ sfdcInstanceUrl String? // null until CRM is connected
59
+ oauthAccessToken String? // null until CRM is connected
60
+ oauthRefreshToken String? // null until CRM is connected
61
+ webhookSecret String // HMAC secret for verifying Apex trigger payloads
62
+ plan Plan @default(FREE)
63
+ isActive Boolean @default(true)
64
+ seatsPurchased Int @default(5)
65
+ seatsUsed Int @default(0)
66
+ routingQuotaUsed Int @default(0)
67
+ quotaResetAt DateTime @default(now())
68
+ onboardingDone Boolean @default(false)
69
+ notificationWebhookUrl String?
70
+ createdAt DateTime @default(now())
71
+ updatedAt DateTime @updatedAt
72
+
73
+ users User[]
74
+ appUsers AppUser[]
75
+ invites Invite[]
76
+ teams RoundRobinTeam[]
77
+ routingRules RoutingRule[]
78
+ routingLogs RoutingLog[]
79
+ auditLogs AuditLog[]
80
+ sfdcQueues SfdcQueue[]
81
+ fieldSchemas FieldSchema[]
82
+ billingInfo BillingInfo?
83
+
84
+ @@map("organizations")
85
+ }
86
+
87
+ model AppUser {
88
+ id String @id @default(cuid())
89
+ orgId String
90
+ email String
91
+ name String
92
+ passwordHash String // PBKDF2: "salt:hash" (salt=16-byte hex, 310000 iterations, sha256)
93
+ role String @default("ADMIN") // ADMIN | MEMBER
94
+ isActive Boolean @default(true)
95
+ createdAt DateTime @default(now())
96
+ updatedAt DateTime @updatedAt
97
+
98
+ org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
99
+
100
+ @@unique([orgId, email])
101
+ @@map("app_users")
102
+ }
103
+
104
+ model Invite {
105
+ id String @id @default(cuid())
106
+ orgId String
107
+ email String
108
+ token String @unique // 48-char hex (crypto.randomBytes(24))
109
+ expiresAt DateTime // 72 hours from creation
110
+ acceptedAt DateTime? // null until invite is used
111
+ createdAt DateTime @default(now())
112
+
113
+ org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
114
+
115
+ @@map("invites")
116
+ }
117
+
118
+ model User {
119
+ id String @id @default(cuid())
120
+ orgId String
121
+ sfdcUserId String
122
+ name String
123
+ email String
124
+ role String?
125
+ profile String?
126
+ department String?
127
+ isLicensed Boolean @default(false)
128
+ isActive Boolean @default(true) // false if no longer in SFDC
129
+ lastRoutedAt DateTime?
130
+ syncedAt DateTime @default(now())
131
+ createdAt DateTime @default(now())
132
+ updatedAt DateTime @updatedAt
133
+
134
+ org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
135
+ teamMemberships TeamMember[]
136
+
137
+ @@unique([orgId, sfdcUserId])
138
+ @@map("users")
139
+ }
140
+
141
+ model RoundRobinTeam {
142
+ id String @id @default(cuid())
143
+ orgId String
144
+ name String
145
+ description String?
146
+ pointerIndex Int @default(0) // kept in sync with Redis; Redis is authoritative
147
+ createdAt DateTime @default(now())
148
+ updatedAt DateTime @updatedAt
149
+
150
+ org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
151
+ members TeamMember[]
152
+ routingRules RoutingRule[] @relation("TeamRules")
153
+
154
+ @@map("round_robin_teams")
155
+ }
156
+
157
+ model TeamMember {
158
+ id String @id @default(cuid())
159
+ teamId String
160
+ userId String
161
+ status TeamMemberStatus @default(ACTIVE)
162
+ weight Int @default(1) // P2 weighted round robin
163
+ assignmentCount Int @default(0)
164
+ createdAt DateTime @default(now())
165
+ updatedAt DateTime @updatedAt
166
+
167
+ team RoundRobinTeam @relation(fields: [teamId], references: [id], onDelete: Cascade)
168
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
169
+
170
+ @@unique([teamId, userId])
171
+ @@map("team_members")
172
+ }
173
+
174
+ model RoutingRule {
175
+ id String @id @default(cuid())
176
+ orgId String
177
+ objectType SfdcObjectType
178
+ triggerEvent TriggerEvent
179
+ name String
180
+ priority Int
181
+ status RuleStatus @default(ACTIVE)
182
+ assignmentType AssignmentType
183
+ assigneeUserId String? // references User.id (individual user)
184
+ assigneeTeamId String? // references RoundRobinTeam.id
185
+ assigneeQueueId String? // references SfdcQueue.id
186
+ isDryRun Boolean @default(false)
187
+ createdAt DateTime @default(now())
188
+ updatedAt DateTime @updatedAt
189
+
190
+ org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
191
+ team RoundRobinTeam? @relation("TeamRules", fields: [assigneeTeamId], references: [id])
192
+ queue SfdcQueue? @relation(fields: [assigneeQueueId], references: [id])
193
+ conditions RuleCondition[]
194
+
195
+ @@map("routing_rules")
196
+ }
197
+
198
+ model RuleCondition {
199
+ id String @id @default(cuid())
200
+ ruleId String
201
+ groupId String // conditions in same group share AND logic; groups are joined with OR
202
+ fieldName String // SFDC field API name (e.g., "LeadSource")
203
+ operator String // e.g., "equals", "contains", "gt"
204
+ value String? // null for is_blank / is_not_blank / is_true / is_false
205
+ sortOrder Int @default(0)
206
+
207
+ rule RoutingRule @relation(fields: [ruleId], references: [id], onDelete: Cascade)
208
+
209
+ @@map("rule_conditions")
210
+ }
211
+
212
+ model RoutingLog {
213
+ id String @id @default(cuid())
214
+ orgId String
215
+ sfdcRecordId String
216
+ objectType SfdcObjectType
217
+ eventType TriggerEvent
218
+ ruleId String?
219
+ ruleName String?
220
+ assigneeId String? // SFDC user/queue ID
221
+ assigneeName String?
222
+ assignmentType AssignmentType?
223
+ isDryRun Boolean @default(false)
224
+ status RoutingStatus
225
+ errorMessage String?
226
+ retryCount Int @default(0)
227
+ dismissed Boolean @default(false)
228
+ recordSnapshot Json? // full field payload from SFDC at time of routing
229
+ createdAt DateTime @default(now())
230
+
231
+ org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
232
+
233
+ @@index([orgId, createdAt])
234
+ @@index([orgId, status])
235
+ @@map("routing_logs")
236
+ }
237
+
238
+ model AuditLog {
239
+ id String @id @default(cuid())
240
+ orgId String
241
+ actorId String // User.id
242
+ actorName String
243
+ action String // e.g., "USER_LICENSED", "RULE_CREATED"
244
+ entityType String // e.g., "User", "RoutingRule"
245
+ entityId String
246
+ beforeState Json?
247
+ afterState Json?
248
+ createdAt DateTime @default(now())
249
+
250
+ org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
251
+
252
+ @@index([orgId, createdAt])
253
+ @@map("audit_logs")
254
+ }
255
+
256
+ model SfdcQueue {
257
+ id String @id @default(cuid())
258
+ orgId String
259
+ sfdcQueueId String // SFDC Queue ID (00G...)
260
+ name String
261
+ syncedAt DateTime @default(now())
262
+
263
+ org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
264
+ routingRules RoutingRule[]
265
+
266
+ @@unique([orgId, sfdcQueueId])
267
+ @@map("sfdc_queues")
268
+ }
269
+
270
+ model FieldSchema {
271
+ id String @id @default(cuid())
272
+ orgId String
273
+ objectType SfdcObjectType
274
+ fieldApiName String
275
+ fieldLabel String
276
+ fieldType String // TEXT | NUMBER | DATE | DATETIME | BOOLEAN | PICKLIST | MULTI_PICKLIST | LOOKUP
277
+ picklistValues Json? // string[] for picklist/multi-picklist fields
278
+ syncedAt DateTime @default(now())
279
+
280
+ org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
281
+
282
+ @@unique([orgId, objectType, fieldApiName])
283
+ @@map("field_schemas")
284
+ }
285
+
286
+ model BillingInfo {
287
+ id String @id @default(cuid())
288
+ orgId String @unique
289
+ entityName String?
290
+ gstin String?
291
+ addressLine1 String?
292
+ addressLine2 String?
293
+ city String?
294
+ state String?
295
+ pinCode String?
296
+ invoiceEmail String?
297
+ updatedAt DateTime @updatedAt
298
+
299
+ org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
300
+
301
+ @@map("billing_info")
302
+ }
303
+
304
+ model Session {
305
+ id String @id @default(cuid())
306
+ orgId String
307
+ userId String // SFDC User ID of the logged-in admin
308
+ userName String
309
+ userEmail String
310
+ expiresAt DateTime
311
+ createdAt DateTime @default(now())
312
+
313
+ @@index([orgId])
314
+ @@map("sessions")
315
+ }
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <CustomApplication xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <defaultLandingTab>Lead_Router_Setup</defaultLandingTab>
4
+ <description>Lead Router Setup Wizard</description>
5
+ <formFactors>Large</formFactors>
6
+ <isNavAutoTempTabsDisabled>false</isNavAutoTempTabsDisabled>
7
+ <isNavPersonalizationDisabled>true</isNavPersonalizationDisabled>
8
+ <label>Lead Router Setup</label>
9
+ <navType>Standard</navType>
10
+ <tabs>Lead_Router_Setup</tabs>
11
+ <uiType>Lightning</uiType>
12
+ </CustomApplication>