@lead-routing/cli 0.5.0 → 0.6.1
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.
- package/dist/index.js +352 -124
- package/dist/prisma/migrations/20260315000000_fix_aggregate_upsert_race/migration.sql +36 -0
- package/dist/prisma/migrations/20260316000000_add_license_activation_columns/migration.sql +5 -0
- package/dist/prisma/migrations/20260316100000_add_bulk_search_runs/migration.sql +36 -0
- package/dist/prisma/migrations/20260316203552_add_ai_agent_models/migration.sql +97 -0
- package/dist/prisma/migrations/20260317000000_add_routing_flows/migration.sql +76 -0
- package/dist/prisma/migrations/20260318000000_add_flow_edge_handles/migration.sql +3 -0
- package/dist/prisma/migrations/20260318100000_add_branch_steps/migration.sql +2 -0
- package/dist/prisma/migrations/20260318200000_add_api_tokens/migration.sql +24 -0
- package/dist/prisma/migrations/migration_lock.toml +2 -2
- package/dist/prisma/schema.prisma +195 -0
- package/package.json +1 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- Fix: the existing @@unique constraint does not prevent duplicate rows
|
|
2
|
+
-- because PostgreSQL treats NULLs as distinct in unique indexes.
|
|
3
|
+
|
|
4
|
+
-- Step 1: Clean up any existing duplicate rows (keep the one with highest totalCount)
|
|
5
|
+
DELETE FROM "routing_daily_aggregates" a
|
|
6
|
+
USING "routing_daily_aggregates" b
|
|
7
|
+
WHERE a."orgId" = b."orgId"
|
|
8
|
+
AND a."date" = b."date"
|
|
9
|
+
AND a."objectType" IS NOT DISTINCT FROM b."objectType"
|
|
10
|
+
AND a."ruleId" IS NOT DISTINCT FROM b."ruleId"
|
|
11
|
+
AND a."pathLabel" IS NOT DISTINCT FROM b."pathLabel"
|
|
12
|
+
AND a."branchId" IS NOT DISTINCT FROM b."branchId"
|
|
13
|
+
AND a."teamId" IS NOT DISTINCT FROM b."teamId"
|
|
14
|
+
AND a."assigneeId" IS NOT DISTINCT FROM b."assigneeId"
|
|
15
|
+
AND a."id" < b."id";
|
|
16
|
+
|
|
17
|
+
-- Step 2: Create an immutable cast helper for the enum column.
|
|
18
|
+
-- PostgreSQL's built-in enum::text cast is only STABLE, not IMMUTABLE,
|
|
19
|
+
-- so it cannot be used directly in an index expression.
|
|
20
|
+
CREATE OR REPLACE FUNCTION immutable_object_type_text(val "SfdcObjectType")
|
|
21
|
+
RETURNS text LANGUAGE sql IMMUTABLE PARALLEL SAFE AS $$
|
|
22
|
+
SELECT val::text;
|
|
23
|
+
$$;
|
|
24
|
+
|
|
25
|
+
-- Step 3: Create a functional unique index using COALESCE to handle NULLs
|
|
26
|
+
CREATE UNIQUE INDEX "routing_daily_aggregates_dimension_key"
|
|
27
|
+
ON "routing_daily_aggregates" (
|
|
28
|
+
"orgId",
|
|
29
|
+
"date",
|
|
30
|
+
COALESCE("ruleId", ''),
|
|
31
|
+
COALESCE("pathLabel", ''),
|
|
32
|
+
COALESCE("branchId", ''),
|
|
33
|
+
COALESCE("teamId", ''),
|
|
34
|
+
COALESCE("assigneeId", ''),
|
|
35
|
+
COALESCE(immutable_object_type_text("objectType"), '')
|
|
36
|
+
);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- AlterTable
|
|
2
|
+
ALTER TABLE "organizations" ADD COLUMN IF NOT EXISTS "licenseKey" TEXT;
|
|
3
|
+
ALTER TABLE "organizations" ADD COLUMN IF NOT EXISTS "licenseTier" TEXT;
|
|
4
|
+
ALTER TABLE "organizations" ADD COLUMN IF NOT EXISTS "licenseValidUntil" TIMESTAMP(3);
|
|
5
|
+
ALTER TABLE "organizations" ADD COLUMN IF NOT EXISTS "licenseActivatedAt" TIMESTAMP(3);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- AlterTable
|
|
2
|
+
ALTER TABLE "routing_rules" ADD COLUMN "searchMaxRecords" INTEGER,
|
|
3
|
+
ADD COLUMN "searchBatchSize" INTEGER;
|
|
4
|
+
|
|
5
|
+
-- CreateTable
|
|
6
|
+
CREATE TABLE "bulk_search_runs" (
|
|
7
|
+
"id" TEXT NOT NULL,
|
|
8
|
+
"orgId" TEXT NOT NULL,
|
|
9
|
+
"ruleId" TEXT NOT NULL,
|
|
10
|
+
"status" TEXT NOT NULL DEFAULT 'RUNNING',
|
|
11
|
+
"recordsFound" INTEGER NOT NULL DEFAULT 0,
|
|
12
|
+
"recordsProcessed" INTEGER NOT NULL DEFAULT 0,
|
|
13
|
+
"recordsRouted" INTEGER NOT NULL DEFAULT 0,
|
|
14
|
+
"recordsFailed" INTEGER NOT NULL DEFAULT 0,
|
|
15
|
+
"recordsSkipped" INTEGER NOT NULL DEFAULT 0,
|
|
16
|
+
"error" TEXT,
|
|
17
|
+
"startedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
18
|
+
"completedAt" TIMESTAMP(3),
|
|
19
|
+
"durationMs" INTEGER,
|
|
20
|
+
"maxRecords" INTEGER,
|
|
21
|
+
"batchSize" INTEGER,
|
|
22
|
+
|
|
23
|
+
CONSTRAINT "bulk_search_runs_pkey" PRIMARY KEY ("id")
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
-- CreateIndex
|
|
27
|
+
CREATE INDEX "bulk_search_runs_orgId_ruleId_idx" ON "bulk_search_runs"("orgId", "ruleId");
|
|
28
|
+
|
|
29
|
+
-- CreateIndex
|
|
30
|
+
CREATE INDEX "bulk_search_runs_ruleId_status_idx" ON "bulk_search_runs"("ruleId", "status");
|
|
31
|
+
|
|
32
|
+
-- AddForeignKey
|
|
33
|
+
ALTER TABLE "bulk_search_runs" ADD CONSTRAINT "bulk_search_runs_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
34
|
+
|
|
35
|
+
-- AddForeignKey
|
|
36
|
+
ALTER TABLE "bulk_search_runs" ADD CONSTRAINT "bulk_search_runs_ruleId_fkey" FOREIGN KEY ("ruleId") REFERENCES "routing_rules"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
-- AlterTable
|
|
2
|
+
ALTER TABLE "route_match_configs" ALTER COLUMN "updatedAt" DROP DEFAULT;
|
|
3
|
+
|
|
4
|
+
-- AlterTable
|
|
5
|
+
ALTER TABLE "routing_branches" ALTER COLUMN "updatedAt" DROP DEFAULT;
|
|
6
|
+
|
|
7
|
+
-- CreateTable
|
|
8
|
+
CREATE TABLE "ai_custom_prompts" (
|
|
9
|
+
"id" TEXT NOT NULL,
|
|
10
|
+
"orgId" TEXT NOT NULL,
|
|
11
|
+
"type" TEXT NOT NULL,
|
|
12
|
+
"name" TEXT NOT NULL,
|
|
13
|
+
"content" TEXT NOT NULL,
|
|
14
|
+
"context" TEXT,
|
|
15
|
+
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
|
16
|
+
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
|
17
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
18
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
19
|
+
|
|
20
|
+
CONSTRAINT "ai_custom_prompts_pkey" PRIMARY KEY ("id")
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
-- CreateTable
|
|
24
|
+
CREATE TABLE "ai_agent_logs" (
|
|
25
|
+
"id" TEXT NOT NULL,
|
|
26
|
+
"orgId" TEXT NOT NULL,
|
|
27
|
+
"context" TEXT NOT NULL,
|
|
28
|
+
"toolName" TEXT NOT NULL,
|
|
29
|
+
"action" TEXT NOT NULL,
|
|
30
|
+
"entityType" TEXT NOT NULL,
|
|
31
|
+
"entityId" TEXT,
|
|
32
|
+
"entityName" TEXT,
|
|
33
|
+
"input" JSONB NOT NULL,
|
|
34
|
+
"output" JSONB,
|
|
35
|
+
"status" TEXT NOT NULL,
|
|
36
|
+
"error" TEXT,
|
|
37
|
+
"actorId" TEXT NOT NULL,
|
|
38
|
+
"actorName" TEXT NOT NULL,
|
|
39
|
+
"durationMs" INTEGER,
|
|
40
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
41
|
+
|
|
42
|
+
CONSTRAINT "ai_agent_logs_pkey" PRIMARY KEY ("id")
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
-- CreateTable
|
|
46
|
+
CREATE TABLE "ai_chat_feedback" (
|
|
47
|
+
"id" TEXT NOT NULL,
|
|
48
|
+
"orgId" TEXT NOT NULL,
|
|
49
|
+
"rating" TEXT NOT NULL,
|
|
50
|
+
"userMessage" TEXT NOT NULL,
|
|
51
|
+
"aiResponse" TEXT NOT NULL,
|
|
52
|
+
"toolsUsed" JSONB,
|
|
53
|
+
"context" TEXT,
|
|
54
|
+
"feedback" TEXT,
|
|
55
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
56
|
+
|
|
57
|
+
CONSTRAINT "ai_chat_feedback_pkey" PRIMARY KEY ("id")
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
-- CreateIndex
|
|
61
|
+
CREATE INDEX "ai_custom_prompts_orgId_type_isActive_idx" ON "ai_custom_prompts"("orgId", "type", "isActive");
|
|
62
|
+
|
|
63
|
+
-- CreateIndex
|
|
64
|
+
CREATE INDEX "ai_agent_logs_orgId_createdAt_idx" ON "ai_agent_logs"("orgId", "createdAt");
|
|
65
|
+
|
|
66
|
+
-- CreateIndex
|
|
67
|
+
CREATE INDEX "ai_agent_logs_orgId_context_idx" ON "ai_agent_logs"("orgId", "context");
|
|
68
|
+
|
|
69
|
+
-- CreateIndex
|
|
70
|
+
CREATE INDEX "ai_chat_feedback_orgId_rating_idx" ON "ai_chat_feedback"("orgId", "rating");
|
|
71
|
+
|
|
72
|
+
-- AddForeignKey
|
|
73
|
+
ALTER TABLE "routing_rules" ADD CONSTRAINT "routing_rules_defaultOwnerUserId_fkey" FOREIGN KEY ("defaultOwnerUserId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
74
|
+
|
|
75
|
+
-- AddForeignKey
|
|
76
|
+
ALTER TABLE "routing_rules" ADD CONSTRAINT "routing_rules_defaultOwnerTeamId_fkey" FOREIGN KEY ("defaultOwnerTeamId") REFERENCES "round_robin_teams"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
77
|
+
|
|
78
|
+
-- AddForeignKey
|
|
79
|
+
ALTER TABLE "routing_rules" ADD CONSTRAINT "routing_rules_defaultOwnerQueueId_fkey" FOREIGN KEY ("defaultOwnerQueueId") REFERENCES "sfdc_queues"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
80
|
+
|
|
81
|
+
-- AddForeignKey
|
|
82
|
+
ALTER TABLE "routing_branches" ADD CONSTRAINT "routing_branches_assigneeUserId_fkey" FOREIGN KEY ("assigneeUserId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
83
|
+
|
|
84
|
+
-- AddForeignKey
|
|
85
|
+
ALTER TABLE "routing_branches" ADD CONSTRAINT "routing_branches_assigneeTeamId_fkey" FOREIGN KEY ("assigneeTeamId") REFERENCES "round_robin_teams"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
86
|
+
|
|
87
|
+
-- AddForeignKey
|
|
88
|
+
ALTER TABLE "routing_branches" ADD CONSTRAINT "routing_branches_assigneeQueueId_fkey" FOREIGN KEY ("assigneeQueueId") REFERENCES "sfdc_queues"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
89
|
+
|
|
90
|
+
-- AddForeignKey
|
|
91
|
+
ALTER TABLE "ai_custom_prompts" ADD CONSTRAINT "ai_custom_prompts_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
92
|
+
|
|
93
|
+
-- AddForeignKey
|
|
94
|
+
ALTER TABLE "ai_agent_logs" ADD CONSTRAINT "ai_agent_logs_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
95
|
+
|
|
96
|
+
-- AddForeignKey
|
|
97
|
+
ALTER TABLE "ai_chat_feedback" ADD CONSTRAINT "ai_chat_feedback_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
-- CreateEnum
|
|
2
|
+
CREATE TYPE "FlowStatus" AS ENUM ('DRAFT', 'ACTIVE', 'INACTIVE');
|
|
3
|
+
|
|
4
|
+
-- CreateEnum
|
|
5
|
+
CREATE TYPE "FlowNodeType" AS ENUM ('ENTRY', 'DECISION', 'BRANCH_DECISION', 'MATCH', 'ASSIGNMENT', 'UPDATE_FIELD', 'CREATE_TASK', 'FILTER', 'DEFAULT');
|
|
6
|
+
|
|
7
|
+
-- AlterTable
|
|
8
|
+
ALTER TABLE "organizations" ADD COLUMN "routingMode" JSONB;
|
|
9
|
+
|
|
10
|
+
-- AlterTable
|
|
11
|
+
ALTER TABLE "routing_logs" ADD COLUMN "flowId" TEXT,
|
|
12
|
+
ADD COLUMN "flowNodePath" JSONB;
|
|
13
|
+
|
|
14
|
+
-- CreateTable
|
|
15
|
+
CREATE TABLE "routing_flows" (
|
|
16
|
+
"id" TEXT NOT NULL,
|
|
17
|
+
"orgId" TEXT NOT NULL,
|
|
18
|
+
"objectType" "SfdcObjectType" NOT NULL,
|
|
19
|
+
"name" TEXT NOT NULL DEFAULT 'Untitled Flow',
|
|
20
|
+
"status" "FlowStatus" NOT NULL DEFAULT 'DRAFT',
|
|
21
|
+
"triggerEvent" "TriggerEvent" NOT NULL DEFAULT 'BOTH',
|
|
22
|
+
"isDryRun" BOOLEAN NOT NULL DEFAULT false,
|
|
23
|
+
"version" INTEGER NOT NULL DEFAULT 1,
|
|
24
|
+
"publishedAt" TIMESTAMP(3),
|
|
25
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
26
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
27
|
+
|
|
28
|
+
CONSTRAINT "routing_flows_pkey" PRIMARY KEY ("id")
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
-- CreateTable
|
|
32
|
+
CREATE TABLE "flow_nodes" (
|
|
33
|
+
"id" TEXT NOT NULL,
|
|
34
|
+
"flowId" TEXT NOT NULL,
|
|
35
|
+
"type" "FlowNodeType" NOT NULL,
|
|
36
|
+
"label" TEXT,
|
|
37
|
+
"positionX" DOUBLE PRECISION NOT NULL DEFAULT 0,
|
|
38
|
+
"positionY" DOUBLE PRECISION NOT NULL DEFAULT 0,
|
|
39
|
+
"config" JSONB,
|
|
40
|
+
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
|
41
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
42
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
43
|
+
|
|
44
|
+
CONSTRAINT "flow_nodes_pkey" PRIMARY KEY ("id")
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
-- CreateTable
|
|
48
|
+
CREATE TABLE "flow_edges" (
|
|
49
|
+
"id" TEXT NOT NULL,
|
|
50
|
+
"flowId" TEXT NOT NULL,
|
|
51
|
+
"fromId" TEXT NOT NULL,
|
|
52
|
+
"toId" TEXT NOT NULL,
|
|
53
|
+
"label" TEXT,
|
|
54
|
+
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
|
55
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
56
|
+
|
|
57
|
+
CONSTRAINT "flow_edges_pkey" PRIMARY KEY ("id")
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
-- CreateIndex
|
|
61
|
+
CREATE UNIQUE INDEX "routing_flows_orgId_objectType_key" ON "routing_flows"("orgId", "objectType");
|
|
62
|
+
|
|
63
|
+
-- AddForeignKey
|
|
64
|
+
ALTER TABLE "routing_flows" ADD CONSTRAINT "routing_flows_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
65
|
+
|
|
66
|
+
-- AddForeignKey
|
|
67
|
+
ALTER TABLE "flow_nodes" ADD CONSTRAINT "flow_nodes_flowId_fkey" FOREIGN KEY ("flowId") REFERENCES "routing_flows"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
68
|
+
|
|
69
|
+
-- AddForeignKey
|
|
70
|
+
ALTER TABLE "flow_edges" ADD CONSTRAINT "flow_edges_flowId_fkey" FOREIGN KEY ("flowId") REFERENCES "routing_flows"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
71
|
+
|
|
72
|
+
-- AddForeignKey
|
|
73
|
+
ALTER TABLE "flow_edges" ADD CONSTRAINT "flow_edges_fromId_fkey" FOREIGN KEY ("fromId") REFERENCES "flow_nodes"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
74
|
+
|
|
75
|
+
-- AddForeignKey
|
|
76
|
+
ALTER TABLE "flow_edges" ADD CONSTRAINT "flow_edges_toId_fkey" FOREIGN KEY ("toId") REFERENCES "flow_nodes"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
CREATE TABLE "api_tokens" (
|
|
3
|
+
"id" TEXT NOT NULL,
|
|
4
|
+
"orgId" TEXT NOT NULL,
|
|
5
|
+
"name" TEXT NOT NULL,
|
|
6
|
+
"tokenHash" TEXT NOT NULL,
|
|
7
|
+
"prefix" TEXT NOT NULL,
|
|
8
|
+
"scopes" TEXT[] DEFAULT ARRAY['read', 'route']::TEXT[],
|
|
9
|
+
"lastUsedAt" TIMESTAMP(3),
|
|
10
|
+
"expiresAt" TIMESTAMP(3),
|
|
11
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
12
|
+
"revokedAt" TIMESTAMP(3),
|
|
13
|
+
|
|
14
|
+
CONSTRAINT "api_tokens_pkey" PRIMARY KEY ("id")
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
-- CreateIndex
|
|
18
|
+
CREATE UNIQUE INDEX "api_tokens_tokenHash_key" ON "api_tokens"("tokenHash");
|
|
19
|
+
|
|
20
|
+
-- CreateIndex
|
|
21
|
+
CREATE INDEX "api_tokens_orgId_idx" ON "api_tokens"("orgId");
|
|
22
|
+
|
|
23
|
+
-- AddForeignKey
|
|
24
|
+
ALTER TABLE "api_tokens" ADD CONSTRAINT "api_tokens_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
# Please do not edit this file manually
|
|
2
|
-
# It should be added in your version-control system (
|
|
3
|
-
provider = "postgresql"
|
|
2
|
+
# It should be added in your version-control system (e.g., Git)
|
|
3
|
+
provider = "postgresql"
|
|
@@ -77,6 +77,24 @@ enum Plan {
|
|
|
77
77
|
PAID
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
enum FlowStatus {
|
|
81
|
+
DRAFT
|
|
82
|
+
ACTIVE
|
|
83
|
+
INACTIVE
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
enum FlowNodeType {
|
|
87
|
+
ENTRY
|
|
88
|
+
DECISION
|
|
89
|
+
BRANCH_DECISION
|
|
90
|
+
MATCH
|
|
91
|
+
ASSIGNMENT
|
|
92
|
+
UPDATE_FIELD
|
|
93
|
+
CREATE_TASK
|
|
94
|
+
FILTER
|
|
95
|
+
DEFAULT
|
|
96
|
+
}
|
|
97
|
+
|
|
80
98
|
// ─── Models ───────────────────────────────────────────────────────────────────
|
|
81
99
|
|
|
82
100
|
model Organization {
|
|
@@ -107,6 +125,11 @@ model Organization {
|
|
|
107
125
|
aiBaseUrl String? // custom endpoint URL (null = default)
|
|
108
126
|
aiCustomHeaders Json? // custom headers for self-hosted proxies
|
|
109
127
|
aiChatCount Int @default(0)
|
|
128
|
+
// License activation (from web UI)
|
|
129
|
+
licenseKey String?
|
|
130
|
+
licenseTier String? // "free" | "pro"
|
|
131
|
+
licenseValidUntil DateTime?
|
|
132
|
+
licenseActivatedAt DateTime?
|
|
110
133
|
createdAt DateTime @default(now())
|
|
111
134
|
updatedAt DateTime @updatedAt
|
|
112
135
|
|
|
@@ -123,6 +146,13 @@ model Organization {
|
|
|
123
146
|
routingDailyAggregates RoutingDailyAggregate[]
|
|
124
147
|
conversionTracking ConversionTracking[]
|
|
125
148
|
companyAliases CompanyAlias[]
|
|
149
|
+
bulkSearchRuns BulkSearchRun[]
|
|
150
|
+
aiCustomPrompts AiCustomPrompt[]
|
|
151
|
+
aiAgentLogs AiAgentLog[]
|
|
152
|
+
aiChatFeedback AiChatFeedback[]
|
|
153
|
+
routingMode Json?
|
|
154
|
+
routingFlows RoutingFlow[]
|
|
155
|
+
apiTokens ApiToken[]
|
|
126
156
|
|
|
127
157
|
@@map("organizations")
|
|
128
158
|
}
|
|
@@ -250,6 +280,9 @@ model RoutingRule {
|
|
|
250
280
|
lastRunDurationMs Int?
|
|
251
281
|
totalRuns Int @default(0)
|
|
252
282
|
totalRecordsRouted Int @default(0)
|
|
283
|
+
// Bulk search configuration
|
|
284
|
+
searchMaxRecords Int? // user-configurable max records per run
|
|
285
|
+
searchBatchSize Int? // micro-batch size (default 500)
|
|
253
286
|
// Default owner: absolute catch-all for new Route Builder routes
|
|
254
287
|
defaultOwnerType AssignmentType?
|
|
255
288
|
defaultOwnerUserId String?
|
|
@@ -268,6 +301,7 @@ model RoutingRule {
|
|
|
268
301
|
branches RoutingBranch[] // new Route Builder paths
|
|
269
302
|
matchConfig RouteMatchConfig?
|
|
270
303
|
triggerConditions TriggerCondition[]
|
|
304
|
+
bulkSearchRuns BulkSearchRun[]
|
|
271
305
|
|
|
272
306
|
@@map("routing_rules")
|
|
273
307
|
}
|
|
@@ -285,6 +319,7 @@ model RoutingBranch {
|
|
|
285
319
|
assigneeTeam RoundRobinTeam? @relation("BranchAssigneeTeam", fields: [assigneeTeamId], references: [id])
|
|
286
320
|
assigneeQueueId String?
|
|
287
321
|
assigneeQueue SfdcQueue? @relation("BranchAssigneeQueue", fields: [assigneeQueueId], references: [id])
|
|
322
|
+
steps Json?
|
|
288
323
|
createdAt DateTime @default(now())
|
|
289
324
|
updatedAt DateTime @updatedAt
|
|
290
325
|
|
|
@@ -405,6 +440,8 @@ model RoutingLog {
|
|
|
405
440
|
branchId String? // FK → RoutingBranch for path-level analytics
|
|
406
441
|
createdAt DateTime @default(now())
|
|
407
442
|
|
|
443
|
+
flowId String?
|
|
444
|
+
flowNodePath Json?
|
|
408
445
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
409
446
|
branch RoutingBranch? @relation(fields: [branchId], references: [id])
|
|
410
447
|
conversionTracking ConversionTracking?
|
|
@@ -435,6 +472,64 @@ model AuditLog {
|
|
|
435
472
|
@@map("audit_logs")
|
|
436
473
|
}
|
|
437
474
|
|
|
475
|
+
model RoutingFlow {
|
|
476
|
+
id String @id @default(cuid())
|
|
477
|
+
orgId String
|
|
478
|
+
objectType SfdcObjectType
|
|
479
|
+
name String @default("Untitled Flow")
|
|
480
|
+
status FlowStatus @default(DRAFT)
|
|
481
|
+
triggerEvent TriggerEvent @default(BOTH)
|
|
482
|
+
isDryRun Boolean @default(false)
|
|
483
|
+
version Int @default(1)
|
|
484
|
+
publishedAt DateTime?
|
|
485
|
+
createdAt DateTime @default(now())
|
|
486
|
+
updatedAt DateTime @updatedAt
|
|
487
|
+
|
|
488
|
+
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
489
|
+
nodes FlowNode[]
|
|
490
|
+
edges FlowEdge[]
|
|
491
|
+
|
|
492
|
+
@@unique([orgId, objectType])
|
|
493
|
+
@@map("routing_flows")
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
model FlowNode {
|
|
497
|
+
id String @id @default(cuid())
|
|
498
|
+
flowId String
|
|
499
|
+
type FlowNodeType
|
|
500
|
+
label String?
|
|
501
|
+
positionX Float @default(0)
|
|
502
|
+
positionY Float @default(0)
|
|
503
|
+
config Json?
|
|
504
|
+
sortOrder Int @default(0)
|
|
505
|
+
createdAt DateTime @default(now())
|
|
506
|
+
updatedAt DateTime @updatedAt
|
|
507
|
+
|
|
508
|
+
flow RoutingFlow @relation(fields: [flowId], references: [id], onDelete: Cascade)
|
|
509
|
+
outEdges FlowEdge[] @relation("FromNode")
|
|
510
|
+
inEdges FlowEdge[] @relation("ToNode")
|
|
511
|
+
|
|
512
|
+
@@map("flow_nodes")
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
model FlowEdge {
|
|
516
|
+
id String @id @default(cuid())
|
|
517
|
+
flowId String
|
|
518
|
+
fromId String
|
|
519
|
+
toId String
|
|
520
|
+
label String?
|
|
521
|
+
sourceHandle String?
|
|
522
|
+
targetHandle String?
|
|
523
|
+
sortOrder Int @default(0)
|
|
524
|
+
createdAt DateTime @default(now())
|
|
525
|
+
|
|
526
|
+
flow RoutingFlow @relation(fields: [flowId], references: [id], onDelete: Cascade)
|
|
527
|
+
from FlowNode @relation("FromNode", fields: [fromId], references: [id], onDelete: Cascade)
|
|
528
|
+
to FlowNode @relation("ToNode", fields: [toId], references: [id], onDelete: Cascade)
|
|
529
|
+
|
|
530
|
+
@@map("flow_edges")
|
|
531
|
+
}
|
|
532
|
+
|
|
438
533
|
model SfdcQueue {
|
|
439
534
|
id String @id @default(cuid())
|
|
440
535
|
orgId String
|
|
@@ -576,3 +671,103 @@ model CompanyAlias {
|
|
|
576
671
|
@@index([orgId, nameB])
|
|
577
672
|
@@map("company_aliases")
|
|
578
673
|
}
|
|
674
|
+
|
|
675
|
+
model BulkSearchRun {
|
|
676
|
+
id String @id @default(cuid())
|
|
677
|
+
orgId String
|
|
678
|
+
ruleId String
|
|
679
|
+
status String @default("RUNNING") // RUNNING | COMPLETE | FAILED | CANCELLED
|
|
680
|
+
recordsFound Int @default(0)
|
|
681
|
+
recordsProcessed Int @default(0)
|
|
682
|
+
recordsRouted Int @default(0)
|
|
683
|
+
recordsFailed Int @default(0)
|
|
684
|
+
recordsSkipped Int @default(0)
|
|
685
|
+
error String?
|
|
686
|
+
startedAt DateTime @default(now())
|
|
687
|
+
completedAt DateTime?
|
|
688
|
+
durationMs Int?
|
|
689
|
+
maxRecords Int?
|
|
690
|
+
batchSize Int?
|
|
691
|
+
|
|
692
|
+
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
693
|
+
rule RoutingRule @relation(fields: [ruleId], references: [id], onDelete: Cascade)
|
|
694
|
+
|
|
695
|
+
@@index([orgId, ruleId])
|
|
696
|
+
@@index([ruleId, status])
|
|
697
|
+
@@map("bulk_search_runs")
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
model ApiToken {
|
|
701
|
+
id String @id @default(cuid())
|
|
702
|
+
orgId String
|
|
703
|
+
name String
|
|
704
|
+
tokenHash String @unique
|
|
705
|
+
prefix String
|
|
706
|
+
scopes String[] @default(["read", "route"])
|
|
707
|
+
lastUsedAt DateTime?
|
|
708
|
+
expiresAt DateTime?
|
|
709
|
+
createdAt DateTime @default(now())
|
|
710
|
+
revokedAt DateTime?
|
|
711
|
+
|
|
712
|
+
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
713
|
+
|
|
714
|
+
@@index([orgId])
|
|
715
|
+
@@map("api_tokens")
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
model AiCustomPrompt {
|
|
719
|
+
id String @id @default(cuid())
|
|
720
|
+
orgId String
|
|
721
|
+
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
722
|
+
type String // "instruction" | "template" | "alias" | "memory"
|
|
723
|
+
name String
|
|
724
|
+
content String
|
|
725
|
+
context String? // null = global, "license-users" | "teams" | "routing-rules"
|
|
726
|
+
isActive Boolean @default(true)
|
|
727
|
+
sortOrder Int @default(0)
|
|
728
|
+
createdAt DateTime @default(now())
|
|
729
|
+
updatedAt DateTime @updatedAt
|
|
730
|
+
|
|
731
|
+
@@index([orgId, type, isActive])
|
|
732
|
+
@@map("ai_custom_prompts")
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
model AiAgentLog {
|
|
736
|
+
id String @id @default(cuid())
|
|
737
|
+
orgId String
|
|
738
|
+
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
739
|
+
context String
|
|
740
|
+
toolName String
|
|
741
|
+
action String
|
|
742
|
+
entityType String
|
|
743
|
+
entityId String?
|
|
744
|
+
entityName String?
|
|
745
|
+
input Json
|
|
746
|
+
output Json?
|
|
747
|
+
status String
|
|
748
|
+
error String?
|
|
749
|
+
actorId String
|
|
750
|
+
actorName String
|
|
751
|
+
durationMs Int?
|
|
752
|
+
createdAt DateTime @default(now())
|
|
753
|
+
|
|
754
|
+
@@index([orgId, createdAt])
|
|
755
|
+
@@index([orgId, context])
|
|
756
|
+
@@map("ai_agent_logs")
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
model AiChatFeedback {
|
|
760
|
+
id String @id @default(cuid())
|
|
761
|
+
orgId String
|
|
762
|
+
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
763
|
+
rating String
|
|
764
|
+
userMessage String
|
|
765
|
+
aiResponse String
|
|
766
|
+
toolsUsed Json?
|
|
767
|
+
context String?
|
|
768
|
+
feedback String?
|
|
769
|
+
createdAt DateTime @default(now())
|
|
770
|
+
|
|
771
|
+
@@index([orgId, rating])
|
|
772
|
+
@@map("ai_chat_feedback")
|
|
773
|
+
}
|