@foundrynorth/compass-schema 1.0.20 → 1.0.22
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/LICENSE +4 -3
- package/dist/schema.d.ts +5819 -1953
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +318 -8
- package/dist/schema.js.map +1 -1
- package/dist/types/activity-feed.d.ts +37 -0
- package/dist/types/activity-feed.d.ts.map +1 -0
- package/dist/types/activity-feed.js +63 -0
- package/dist/types/activity-feed.js.map +1 -0
- package/package.json +2 -2
- package/src/schema.ts +491 -9
- package/src/types/activity-feed.ts +97 -0
package/dist/schema.js
CHANGED
|
@@ -86,6 +86,7 @@ import { sql } from "drizzle-orm";
|
|
|
86
86
|
import { pgTable, text, varchar, integer, jsonb, timestamp, doublePrecision, serial, numeric, uuid, date, index, boolean, pgEnum, unique, uniqueIndex, } from "drizzle-orm/pg-core";
|
|
87
87
|
import { createInsertSchema } from "drizzle-zod";
|
|
88
88
|
import { z } from "zod";
|
|
89
|
+
import { ACTIVITY_EVENT_TYPES, ACTIVITY_SEVERITIES, ACTIVITY_SOURCE_SYSTEMS, PENDING_ACTION_TYPES, } from "./types/activity-feed.js";
|
|
89
90
|
export const users = pgTable("users", {
|
|
90
91
|
id: varchar("id")
|
|
91
92
|
.primaryKey()
|
|
@@ -98,6 +99,21 @@ export const insertUserSchema = createInsertSchema(users).pick({
|
|
|
98
99
|
password: true,
|
|
99
100
|
});
|
|
100
101
|
export const avatarRequestStatusEnum = pgEnum("avatar_request_status", ["pending", "processed", "rejected"]);
|
|
102
|
+
// Activity feed enums (order activity feed + pending actions)
|
|
103
|
+
export const activityEventTypeEnum = pgEnum("activity_event_type", ACTIVITY_EVENT_TYPES);
|
|
104
|
+
export const activitySeverityEnum = pgEnum("activity_severity", ACTIVITY_SEVERITIES);
|
|
105
|
+
export const activitySourceSystemEnum = pgEnum("activity_source_system", ACTIVITY_SOURCE_SYSTEMS);
|
|
106
|
+
export const pendingActionTypeEnum = pgEnum("pending_action_type", PENDING_ACTION_TYPES);
|
|
107
|
+
export const amendmentReasonEnum = pgEnum("amendment_reason", [
|
|
108
|
+
"budget_adjustment",
|
|
109
|
+
"flight_date_change",
|
|
110
|
+
"product_swap",
|
|
111
|
+
"add_line_items",
|
|
112
|
+
"remove_line_items",
|
|
113
|
+
"rate_renegotiation",
|
|
114
|
+
"client_request",
|
|
115
|
+
"other",
|
|
116
|
+
]);
|
|
101
117
|
// Vendor enums (must be declared before clerkUsers which references them)
|
|
102
118
|
export const vendorFulfillmentStatusEnum = pgEnum("vendor_fulfillment_status", [
|
|
103
119
|
"assigned",
|
|
@@ -167,6 +183,19 @@ export const userProfiles = pgTable("user_profiles", {
|
|
|
167
183
|
photoUrl: text("photo_url"), // Profile photo (can sync from Clerk or custom upload)
|
|
168
184
|
agencyAvatarUrl: text("agency_avatar_url"), // Hand-sketched avatar for proposals (Fal.ai)
|
|
169
185
|
lastAvatarGeneratedAt: timestamp("last_avatar_generated_at"), // Rate limiting: non-admins can only generate 1/day
|
|
186
|
+
proposalPortraitRenderUrl: text("proposal_portrait_render_url"),
|
|
187
|
+
proposalPortraitCutoutUrl: text("proposal_portrait_cutout_url"),
|
|
188
|
+
proposalPortraitStatus: text("proposal_portrait_status"),
|
|
189
|
+
proposalPortraitPromptVersion: text("proposal_portrait_prompt_version"),
|
|
190
|
+
proposalPortraitSeed: integer("proposal_portrait_seed"),
|
|
191
|
+
proposalPortraitGeneratedAt: timestamp("proposal_portrait_generated_at"),
|
|
192
|
+
proposalPortraitSourcePhotoHash: text("proposal_portrait_source_photo_hash"),
|
|
193
|
+
proposalPortraitPreviewRenderUrl: text("proposal_portrait_preview_render_url"),
|
|
194
|
+
proposalPortraitPreviewCutoutUrl: text("proposal_portrait_preview_cutout_url"),
|
|
195
|
+
proposalPortraitPreviewPromptVersion: text("proposal_portrait_preview_prompt_version"),
|
|
196
|
+
proposalPortraitPreviewSeed: integer("proposal_portrait_preview_seed"),
|
|
197
|
+
proposalPortraitPreviewGeneratedAt: timestamp("proposal_portrait_preview_generated_at"),
|
|
198
|
+
proposalPortraitPreviewSourcePhotoHash: text("proposal_portrait_preview_source_photo_hash"),
|
|
170
199
|
// Active Market Preference (overrides partner default)
|
|
171
200
|
preferredMarketId: varchar("preferred_market_id"), // User's preferred market for planning
|
|
172
201
|
// Digital Business Card
|
|
@@ -195,6 +224,19 @@ export const externalTeamMembers = pgTable("external_team_members", {
|
|
|
195
224
|
title: text("title"),
|
|
196
225
|
photoUrl: text("photo_url"),
|
|
197
226
|
agencyAvatarUrl: text("agency_avatar_url"),
|
|
227
|
+
proposalPortraitRenderUrl: text("proposal_portrait_render_url"),
|
|
228
|
+
proposalPortraitCutoutUrl: text("proposal_portrait_cutout_url"),
|
|
229
|
+
proposalPortraitStatus: text("proposal_portrait_status"),
|
|
230
|
+
proposalPortraitPromptVersion: text("proposal_portrait_prompt_version"),
|
|
231
|
+
proposalPortraitSeed: integer("proposal_portrait_seed"),
|
|
232
|
+
proposalPortraitGeneratedAt: timestamp("proposal_portrait_generated_at"),
|
|
233
|
+
proposalPortraitSourcePhotoHash: text("proposal_portrait_source_photo_hash"),
|
|
234
|
+
proposalPortraitPreviewRenderUrl: text("proposal_portrait_preview_render_url"),
|
|
235
|
+
proposalPortraitPreviewCutoutUrl: text("proposal_portrait_preview_cutout_url"),
|
|
236
|
+
proposalPortraitPreviewPromptVersion: text("proposal_portrait_preview_prompt_version"),
|
|
237
|
+
proposalPortraitPreviewSeed: integer("proposal_portrait_preview_seed"),
|
|
238
|
+
proposalPortraitPreviewGeneratedAt: timestamp("proposal_portrait_preview_generated_at"),
|
|
239
|
+
proposalPortraitPreviewSourcePhotoHash: text("proposal_portrait_preview_source_photo_hash"),
|
|
198
240
|
linkedinUrl: text("linkedin_url"),
|
|
199
241
|
displayOrder: integer("display_order").default(0),
|
|
200
242
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
@@ -433,6 +475,30 @@ export const competitorSchema = z.object({
|
|
|
433
475
|
lng: z.number(),
|
|
434
476
|
})
|
|
435
477
|
.optional(), // Detailed location information
|
|
478
|
+
// Ad library data from Apify deep enrichment
|
|
479
|
+
ads: z
|
|
480
|
+
.object({
|
|
481
|
+
facebook: z
|
|
482
|
+
.array(z.object({
|
|
483
|
+
adImage: z.string().optional(),
|
|
484
|
+
headline: z.string().optional(),
|
|
485
|
+
adCopy: z.string().optional(),
|
|
486
|
+
callToAction: z.string().optional(),
|
|
487
|
+
publisherPlatforms: z.array(z.string()).optional(),
|
|
488
|
+
startDate: z.string().optional(),
|
|
489
|
+
}))
|
|
490
|
+
.optional(),
|
|
491
|
+
google: z
|
|
492
|
+
.array(z.object({
|
|
493
|
+
headline: z.string().optional(),
|
|
494
|
+
description: z.string().optional(),
|
|
495
|
+
landingPage: z.string().optional(),
|
|
496
|
+
adType: z.string().optional(),
|
|
497
|
+
imageUrl: z.string().optional(),
|
|
498
|
+
}))
|
|
499
|
+
.optional(),
|
|
500
|
+
})
|
|
501
|
+
.optional(),
|
|
436
502
|
});
|
|
437
503
|
export const extractionResultSchema = z.object({
|
|
438
504
|
brand: z.string(),
|
|
@@ -855,7 +921,7 @@ export const plans = pgTable("plans", {
|
|
|
855
921
|
selectedOptionId: varchar("selected_option_id"), // FK → plan_options.id (nullable during migration)
|
|
856
922
|
contractId: uuid("contract_id"), // FK → contracts.id (nullable — links to annual contract)
|
|
857
923
|
// Workflow mode: determines which UI sections and features are available
|
|
858
|
-
// 'research_backed' =
|
|
924
|
+
// 'research_backed' = discovery mode — creates plan, runs analysis in background
|
|
859
925
|
// 'direct_config' = configure products directly without research
|
|
860
926
|
// 'aor_pitch' = agency-of-record pitch with proposal focus
|
|
861
927
|
// 'io_entry' = direct insertion order entry from client PDF/XLSX
|
|
@@ -974,6 +1040,7 @@ export const plans = pgTable("plans", {
|
|
|
974
1040
|
defaultTargeting: text("default_targeting"),
|
|
975
1041
|
defaultAudience: text("default_audience"),
|
|
976
1042
|
defaultPromoting: text("default_promoting"),
|
|
1043
|
+
defaultCreativeSource: varchar("default_creative_source"),
|
|
977
1044
|
defaultFlightStart: date("default_flight_start"),
|
|
978
1045
|
defaultFlightEnd: date("default_flight_end"),
|
|
979
1046
|
// Slack integration — per-plan client channel for alert routing
|
|
@@ -1027,8 +1094,12 @@ export const proposals = pgTable("proposals", {
|
|
|
1027
1094
|
.primaryKey()
|
|
1028
1095
|
.default(sql `gen_random_uuid()`),
|
|
1029
1096
|
// Optional plan reference (nullable — can be standalone)
|
|
1030
|
-
planId: varchar("plan_id"),
|
|
1031
|
-
|
|
1097
|
+
planId: varchar("plan_id").references(() => plans.id, {
|
|
1098
|
+
onDelete: "set null",
|
|
1099
|
+
}),
|
|
1100
|
+
engagementId: varchar("engagement_id").references(() => engagements.id, {
|
|
1101
|
+
onDelete: "set null",
|
|
1102
|
+
}), // nullable during transition
|
|
1032
1103
|
// Configuration references (which plan_configurations to include)
|
|
1033
1104
|
configurationIds: jsonb("configuration_ids").$type(),
|
|
1034
1105
|
optionIds: jsonb("option_ids").$type(),
|
|
@@ -1080,6 +1151,35 @@ export const insertPlanSchema = createInsertSchema(plans).omit({
|
|
|
1080
1151
|
id: true,
|
|
1081
1152
|
createdAt: true,
|
|
1082
1153
|
});
|
|
1154
|
+
// ============================================================================
|
|
1155
|
+
// Proposal Engagement Tracking
|
|
1156
|
+
// ============================================================================
|
|
1157
|
+
export const proposalEngagement = pgTable("proposal_engagement", {
|
|
1158
|
+
id: varchar("id")
|
|
1159
|
+
.primaryKey()
|
|
1160
|
+
.default(sql `gen_random_uuid()`),
|
|
1161
|
+
proposalId: varchar("proposal_id"), // FK → proposals.id (nullable for legacy plan-based shares)
|
|
1162
|
+
planId: varchar("plan_id"), // FK → plans.id (fallback for legacy shares)
|
|
1163
|
+
shareToken: text("share_token").notNull(), // The share token used to access
|
|
1164
|
+
sessionId: text("session_id").notNull(), // Anonymous session ID (crypto.randomUUID on client)
|
|
1165
|
+
// Engagement data
|
|
1166
|
+
events: jsonb("events").notNull().default(sql `'[]'::jsonb`), // Array of engagement events
|
|
1167
|
+
engagementScore: integer("engagement_score").default(0), // Computed 0-100 score
|
|
1168
|
+
summary: text("summary"), // AI-generated follow-up brief
|
|
1169
|
+
// Device context
|
|
1170
|
+
device: text("device"), // "desktop" | "mobile" | "tablet"
|
|
1171
|
+
referrer: text("referrer"), // HTTP referrer
|
|
1172
|
+
userAgent: text("user_agent"),
|
|
1173
|
+
// Timestamps
|
|
1174
|
+
firstSeenAt: timestamp("first_seen_at").notNull().defaultNow(),
|
|
1175
|
+
lastSeenAt: timestamp("last_seen_at").notNull().defaultNow(),
|
|
1176
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
1177
|
+
}, (table) => [
|
|
1178
|
+
index("proposal_engagement_proposal_id_idx").on(table.proposalId),
|
|
1179
|
+
index("proposal_engagement_plan_id_idx").on(table.planId),
|
|
1180
|
+
index("proposal_engagement_share_token_idx").on(table.shareToken),
|
|
1181
|
+
index("proposal_engagement_token_session_idx").on(table.shareToken, table.sessionId),
|
|
1182
|
+
]);
|
|
1083
1183
|
// Plan Configurations — Named product mixes (replaces monolithic planAllocations)
|
|
1084
1184
|
// A plan can have multiple configurations (e.g., "Digital Focus", "Full Mix", "Print Heavy")
|
|
1085
1185
|
export const planConfigurations = pgTable("plan_configurations", {
|
|
@@ -1414,6 +1514,7 @@ export const clerkUsers = pgTable("clerk_users", {
|
|
|
1414
1514
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
1415
1515
|
approvedAt: timestamp("approved_at"),
|
|
1416
1516
|
approvedBy: text("approved_by"), // Admin who approved
|
|
1517
|
+
compassVoiceWarmth: numeric("compass_voice_warmth").default("0.6"),
|
|
1417
1518
|
});
|
|
1418
1519
|
export const insertClerkUserSchema = createInsertSchema(clerkUsers).omit({
|
|
1419
1520
|
createdAt: true,
|
|
@@ -3268,19 +3369,106 @@ export const mediaOrderStatusEnum = pgEnum("media_order_status", [
|
|
|
3268
3369
|
"active",
|
|
3269
3370
|
"completed",
|
|
3270
3371
|
]);
|
|
3372
|
+
export const salesInitiatives = pgTable("sales_initiatives", {
|
|
3373
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
3374
|
+
code: varchar("code").notNull().unique(),
|
|
3375
|
+
name: varchar("name").notNull(),
|
|
3376
|
+
description: text("description"),
|
|
3377
|
+
hubspotValue: varchar("hubspot_value").notNull().unique(),
|
|
3378
|
+
displayOrder: integer("display_order").notNull().default(0),
|
|
3379
|
+
startsAt: date("starts_at"),
|
|
3380
|
+
endsAt: date("ends_at"),
|
|
3381
|
+
isActive: boolean("is_active").notNull().default(true),
|
|
3382
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
3383
|
+
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
|
3384
|
+
}, (table) => [
|
|
3385
|
+
index("sales_initiatives_active_idx").on(table.isActive),
|
|
3386
|
+
index("sales_initiatives_display_order_idx").on(table.displayOrder),
|
|
3387
|
+
]);
|
|
3388
|
+
export const insertSalesInitiativeSchema = createInsertSchema(salesInitiatives).omit({
|
|
3389
|
+
id: true,
|
|
3390
|
+
createdAt: true,
|
|
3391
|
+
updatedAt: true,
|
|
3392
|
+
});
|
|
3393
|
+
export const updateSalesInitiativeSchema = insertSalesInitiativeSchema.partial();
|
|
3394
|
+
export const salesPackages = pgTable("sales_packages", {
|
|
3395
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
3396
|
+
code: varchar("code").notNull().unique(),
|
|
3397
|
+
name: varchar("name").notNull(),
|
|
3398
|
+
description: text("description"),
|
|
3399
|
+
initiativeCode: varchar("initiative_code"),
|
|
3400
|
+
sellerSummary: text("seller_summary"),
|
|
3401
|
+
packagePrice: numeric("package_price", { precision: 12, scale: 2 }),
|
|
3402
|
+
discountPercent: numeric("discount_percent", { precision: 5, scale: 2 }),
|
|
3403
|
+
addedValueAmount: numeric("added_value_amount", { precision: 12, scale: 2 }),
|
|
3404
|
+
addedValueDescription: varchar("added_value_description", { length: 255 }),
|
|
3405
|
+
hubspotPackageValue: varchar("hubspot_package_value").notNull().unique(),
|
|
3406
|
+
startsAt: date("starts_at"),
|
|
3407
|
+
endsAt: date("ends_at"),
|
|
3408
|
+
status: varchar("status").notNull().default("draft"),
|
|
3409
|
+
recommendationMode: varchar("recommendation_mode").notNull().default("both"),
|
|
3410
|
+
recommendationPriority: integer("recommendation_priority").notNull().default(0),
|
|
3411
|
+
eligibilityRules: jsonb("eligibility_rules").$type(),
|
|
3412
|
+
assistantHints: jsonb("assistant_hints").$type(),
|
|
3413
|
+
taskTemplateMode: varchar("task_template_mode").notNull().default("standard"),
|
|
3414
|
+
displayOrder: integer("display_order").notNull().default(0),
|
|
3415
|
+
isActive: boolean("is_active").notNull().default(true),
|
|
3416
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
3417
|
+
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
|
3418
|
+
}, (table) => [
|
|
3419
|
+
index("sales_packages_active_idx").on(table.isActive),
|
|
3420
|
+
index("sales_packages_status_idx").on(table.status),
|
|
3421
|
+
index("sales_packages_display_order_idx").on(table.displayOrder),
|
|
3422
|
+
index("sales_packages_initiative_code_idx").on(table.initiativeCode),
|
|
3423
|
+
]);
|
|
3424
|
+
export const insertSalesPackageSchema = createInsertSchema(salesPackages).omit({
|
|
3425
|
+
id: true,
|
|
3426
|
+
createdAt: true,
|
|
3427
|
+
updatedAt: true,
|
|
3428
|
+
});
|
|
3429
|
+
export const updateSalesPackageSchema = insertSalesPackageSchema.partial();
|
|
3430
|
+
export const salesPackageProducts = pgTable("sales_package_products", {
|
|
3431
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
3432
|
+
salesPackageCode: varchar("sales_package_code").notNull(),
|
|
3433
|
+
stribProductId: uuid("strib_product_id")
|
|
3434
|
+
.notNull()
|
|
3435
|
+
.references(() => stribProducts.id, { onDelete: "cascade" }),
|
|
3436
|
+
displayOrder: integer("display_order").notNull().default(0),
|
|
3437
|
+
approvedUnitRate: numeric("approved_unit_rate", { precision: 12, scale: 4 }),
|
|
3438
|
+
approvedRateType: varchar("approved_rate_type"),
|
|
3439
|
+
allocationMode: varchar("allocation_mode").notNull().default("percent"),
|
|
3440
|
+
allocationValue: numeric("allocation_value", { precision: 12, scale: 4 }),
|
|
3441
|
+
required: boolean("required").notNull().default(true),
|
|
3442
|
+
defaultFulfillmentDetails: jsonb("default_fulfillment_details"),
|
|
3443
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
3444
|
+
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
|
3445
|
+
}, (table) => [
|
|
3446
|
+
uniqueIndex("sales_package_products_unique_idx").on(table.salesPackageCode, table.stribProductId),
|
|
3447
|
+
index("sales_package_products_code_idx").on(table.salesPackageCode),
|
|
3448
|
+
index("sales_package_products_product_idx").on(table.stribProductId),
|
|
3449
|
+
]);
|
|
3271
3450
|
export const mediaOrders = pgTable("media_orders", {
|
|
3272
3451
|
id: varchar("id")
|
|
3273
3452
|
.primaryKey()
|
|
3274
3453
|
.default(sql `gen_random_uuid()`),
|
|
3275
|
-
planId: varchar("plan_id")
|
|
3276
|
-
|
|
3454
|
+
planId: varchar("plan_id").references(() => plans.id, {
|
|
3455
|
+
onDelete: "set null",
|
|
3456
|
+
}), // nullable — can exist without a plan
|
|
3457
|
+
engagementId: varchar("engagement_id").references(() => engagements.id, {
|
|
3458
|
+
onDelete: "set null",
|
|
3459
|
+
}), // nullable during transition
|
|
3277
3460
|
configurationId: varchar("configuration_id"), // FK → plan_configurations.id (named product mix)
|
|
3278
3461
|
optionId: varchar("option_id"), // FK → plan_options.id (canonical selected option)
|
|
3279
|
-
contractId: uuid("contract_id")
|
|
3462
|
+
contractId: uuid("contract_id").references(() => contracts.id, {
|
|
3463
|
+
onDelete: "set null",
|
|
3464
|
+
}), // nullable — links to annual contract
|
|
3280
3465
|
partnerId: varchar("partner_id"),
|
|
3281
3466
|
hubspotDealId: text("hubspot_deal_id"),
|
|
3282
3467
|
hubspotCompanyId: text("hubspot_company_id"),
|
|
3283
3468
|
parentHubspotCompanyId: text("parent_hubspot_company_id"),
|
|
3469
|
+
initiativeCode: varchar("initiative_code"),
|
|
3470
|
+
salesPackageCode: varchar("sales_package_code"),
|
|
3471
|
+
salesPackageSnapshot: jsonb("sales_package_snapshot").$type(),
|
|
3284
3472
|
// Client header
|
|
3285
3473
|
clientName: text("client_name").notNull(),
|
|
3286
3474
|
clientContactName: text("client_contact_name"),
|
|
@@ -3304,6 +3492,8 @@ export const mediaOrders = pgTable("media_orders", {
|
|
|
3304
3492
|
activatedAt: timestamp("activated_at"),
|
|
3305
3493
|
completedAt: timestamp("completed_at"),
|
|
3306
3494
|
returnReason: text("return_reason"),
|
|
3495
|
+
amendmentReason: amendmentReasonEnum("amendment_reason"),
|
|
3496
|
+
amendmentNotes: text("amendment_notes"),
|
|
3307
3497
|
slaHold: boolean("sla_hold").default(false),
|
|
3308
3498
|
clientTier: text("client_tier"),
|
|
3309
3499
|
// Share link
|
|
@@ -3329,8 +3519,6 @@ export const mediaOrders = pgTable("media_orders", {
|
|
|
3329
3519
|
defaultPromoting: text("default_promoting"),
|
|
3330
3520
|
// Order-level notes
|
|
3331
3521
|
orderNotes: text("order_notes"),
|
|
3332
|
-
// Sales initiative (links to sales_initiatives table)
|
|
3333
|
-
initiativeCode: varchar("initiative_code"),
|
|
3334
3522
|
// Billing fields
|
|
3335
3523
|
navigaAdvertiserId: text("naviga_advertiser_id"),
|
|
3336
3524
|
purchaseOrderNumber: text("purchase_order_number"),
|
|
@@ -3378,12 +3566,14 @@ export const mediaOrders = pgTable("media_orders", {
|
|
|
3378
3566
|
index("media_orders_engagement_id_idx").on(table.engagementId),
|
|
3379
3567
|
index("media_orders_partner_id_idx").on(table.partnerId),
|
|
3380
3568
|
index("media_orders_hubspot_company_id_idx").on(table.hubspotCompanyId),
|
|
3569
|
+
index("media_orders_hubspot_deal_id_idx").on(table.hubspotDealId),
|
|
3381
3570
|
index("media_orders_status_idx").on(table.status),
|
|
3382
3571
|
index("media_orders_vendor_id_idx").on(table.vendorId),
|
|
3383
3572
|
index("media_orders_vendor_fulfillment_status_idx").on(table.vendorFulfillmentStatus),
|
|
3384
3573
|
index("media_orders_parent_company_idx").on(table.parentHubspotCompanyId),
|
|
3385
3574
|
index("media_orders_contract_id_idx").on(table.contractId),
|
|
3386
3575
|
index("media_orders_initiative_code_idx").on(table.initiativeCode),
|
|
3576
|
+
index("media_orders_sales_package_code_idx").on(table.salesPackageCode),
|
|
3387
3577
|
]);
|
|
3388
3578
|
export const insertMediaOrderSchema = createInsertSchema(mediaOrders).omit({
|
|
3389
3579
|
id: true,
|
|
@@ -3476,6 +3666,11 @@ export const mediaOrderLineItems = pgTable("media_order_line_items", {
|
|
|
3476
3666
|
// Fulfillment details (product-specific fields for HubSpot sync)
|
|
3477
3667
|
fulfillmentDetails: jsonb("fulfillment_details"),
|
|
3478
3668
|
creativeSource: varchar("creative_source"),
|
|
3669
|
+
initiativeCode: varchar("initiative_code"),
|
|
3670
|
+
salesPackageCode: varchar("sales_package_code"),
|
|
3671
|
+
salesPackageComponentRef: varchar("sales_package_component_ref"),
|
|
3672
|
+
isPackageDerived: boolean("is_package_derived").notNull().default(false),
|
|
3673
|
+
packagePricingSource: varchar("package_pricing_source"),
|
|
3479
3674
|
// Ad trafficking reference key (auto-generated naming convention)
|
|
3480
3675
|
trafficKey: text("traffic_key"),
|
|
3481
3676
|
// Canonical tracking fields for DSP/reporting consistency
|
|
@@ -3485,9 +3680,12 @@ export const mediaOrderLineItems = pgTable("media_order_line_items", {
|
|
|
3485
3680
|
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
|
3486
3681
|
}, (table) => [
|
|
3487
3682
|
index("media_order_line_items_section_id_idx").on(table.sectionId),
|
|
3683
|
+
index("media_order_line_items_sync_status_idx").on(table.syncStatus),
|
|
3488
3684
|
index("idx_line_items_parent").on(table.parentLineItemId),
|
|
3489
3685
|
index("media_order_line_items_location_id_idx").on(table.locationId),
|
|
3490
3686
|
index("media_order_line_items_tracking_key_idx").on(table.trackingKey),
|
|
3687
|
+
index("media_order_line_items_initiative_code_idx").on(table.initiativeCode),
|
|
3688
|
+
index("media_order_line_items_sales_package_code_idx").on(table.salesPackageCode),
|
|
3491
3689
|
]);
|
|
3492
3690
|
export const insertMediaOrderLineItemSchema = createInsertSchema(mediaOrderLineItems).omit({
|
|
3493
3691
|
id: true,
|
|
@@ -3652,6 +3850,20 @@ export const insertStribProductSchema = createInsertSchema(stribProducts).omit({
|
|
|
3652
3850
|
updatedAt: true,
|
|
3653
3851
|
});
|
|
3654
3852
|
export const updateStribProductSchema = insertStribProductSchema.partial();
|
|
3853
|
+
export const salesInitiativeProducts = pgTable("sales_initiative_products", {
|
|
3854
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
3855
|
+
initiativeCode: varchar("initiative_code").notNull(),
|
|
3856
|
+
stribProductId: uuid("strib_product_id")
|
|
3857
|
+
.notNull()
|
|
3858
|
+
.references(() => stribProducts.id, { onDelete: "cascade" }),
|
|
3859
|
+
displayOrder: integer("display_order").notNull().default(0),
|
|
3860
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
3861
|
+
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
|
3862
|
+
}, (table) => [
|
|
3863
|
+
uniqueIndex("sales_initiative_products_unique_idx").on(table.initiativeCode, table.stribProductId),
|
|
3864
|
+
index("sales_initiative_products_code_idx").on(table.initiativeCode),
|
|
3865
|
+
index("sales_initiative_products_product_idx").on(table.stribProductId),
|
|
3866
|
+
]);
|
|
3655
3867
|
/**
|
|
3656
3868
|
* strib_product_rates — tier-based pricing per product.
|
|
3657
3869
|
* Different tiers depending on family: Digital uses open/advocacy/tier_1/tier_2/tier_3;
|
|
@@ -3915,6 +4127,7 @@ export const rateExceptionRequests = pgTable("rate_exception_requests", {
|
|
|
3915
4127
|
escalationHistory: jsonb("escalation_history"), // Array of {level, at, by, reason}
|
|
3916
4128
|
expiresAt: timestamp("expires_at"),
|
|
3917
4129
|
approvalScope: varchar("approval_scope").notNull().default("request"), // "request" = affects this exception only; "catalog" = also updates global product rate
|
|
4130
|
+
batchId: uuid("batch_id"), // Groups bulk rate exception requests submitted together
|
|
3918
4131
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
3919
4132
|
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
|
3920
4133
|
}, (table) => [
|
|
@@ -3922,6 +4135,8 @@ export const rateExceptionRequests = pgTable("rate_exception_requests", {
|
|
|
3922
4135
|
index("rate_exception_status_idx").on(table.status),
|
|
3923
4136
|
index("rate_exception_requester_idx").on(table.requestedByUserId),
|
|
3924
4137
|
index("rate_exception_plan_idx").on(table.planId),
|
|
4138
|
+
index("rate_exception_batch_idx").on(table.batchId),
|
|
4139
|
+
index("rate_exception_order_status_idx").on(table.mediaOrderId, table.status),
|
|
3925
4140
|
]);
|
|
3926
4141
|
// ─── Lead Time Rules ──────────────────────────────────────────────────────────
|
|
3927
4142
|
export const leadTimeRules = pgTable("lead_time_rules", {
|
|
@@ -3968,6 +4183,31 @@ export const slaConfig = pgTable("sla_config", {
|
|
|
3968
4183
|
createdAt: timestamp("created_at").defaultNow(),
|
|
3969
4184
|
updatedAt: timestamp("updated_at").defaultNow(),
|
|
3970
4185
|
});
|
|
4186
|
+
// ─── SLA Snapshots ───────────────────────────────────────────────────────────
|
|
4187
|
+
/**
|
|
4188
|
+
* sla_snapshots — Point-in-time SLA state from fn-flux evaluations.
|
|
4189
|
+
* Each row captures the hold status, deadline, milestones, and profile
|
|
4190
|
+
* for a given media order + fulfillment ticket pair.
|
|
4191
|
+
*/
|
|
4192
|
+
export const slaHoldStatusEnum = pgEnum("sla_hold_status", ["clear", "hold", "override"]);
|
|
4193
|
+
export const slaSnapshots = pgTable("sla_snapshots", {
|
|
4194
|
+
id: serial("id").primaryKey(),
|
|
4195
|
+
mediaOrderId: text("media_order_id").notNull().references(() => mediaOrders.id),
|
|
4196
|
+
ticketId: text("ticket_id").notNull(),
|
|
4197
|
+
holdStatus: slaHoldStatusEnum("hold_status").notNull().default("clear"),
|
|
4198
|
+
holdReason: text("hold_reason"),
|
|
4199
|
+
slaDays: integer("sla_days").notNull(),
|
|
4200
|
+
availableDays: numeric("available_days"),
|
|
4201
|
+
slaDeadline: timestamp("sla_deadline"),
|
|
4202
|
+
campaignStartDate: date("campaign_start_date"),
|
|
4203
|
+
milestones: jsonb("milestones"),
|
|
4204
|
+
profileName: text("profile_name"),
|
|
4205
|
+
lastUpdatedAt: timestamp("last_updated_at").defaultNow(),
|
|
4206
|
+
}, (table) => [
|
|
4207
|
+
index("sla_snapshots_media_order_id_idx").on(table.mediaOrderId),
|
|
4208
|
+
index("sla_snapshots_ticket_id_idx").on(table.ticketId),
|
|
4209
|
+
uniqueIndex("sla_snapshots_order_ticket_uniq").on(table.mediaOrderId, table.ticketId),
|
|
4210
|
+
]);
|
|
3971
4211
|
// ─── Inventory Sync Audit Trail ─────────────────────────────────────────────
|
|
3972
4212
|
/**
|
|
3973
4213
|
* inventory_sync_log — Audit trail for external inventory syncs (GAM, Sailthru, Naviga).
|
|
@@ -5932,6 +6172,7 @@ export const semCampaigns = pgTable("sem_campaigns", {
|
|
|
5932
6172
|
// =============================================================================
|
|
5933
6173
|
export const compassEventOutboxStatusEnum = pgEnum("compass_event_outbox_status", [
|
|
5934
6174
|
"pending",
|
|
6175
|
+
"processing",
|
|
5935
6176
|
"failed",
|
|
5936
6177
|
"delivered",
|
|
5937
6178
|
"dead_letter",
|
|
@@ -5967,4 +6208,73 @@ export const creativeDeliveries = pgTable("creative_deliveries", {
|
|
|
5967
6208
|
assetCount: integer("asset_count").notNull().default(0),
|
|
5968
6209
|
receivedAt: timestamp("received_at").defaultNow().notNull(),
|
|
5969
6210
|
});
|
|
6211
|
+
// ─── Order Activity Feed ────────────────────────────────────────────────────
|
|
6212
|
+
export const orderActivityFeed = pgTable("order_activity_feed", {
|
|
6213
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
6214
|
+
mediaOrderId: varchar("media_order_id")
|
|
6215
|
+
.notNull()
|
|
6216
|
+
.references(() => mediaOrders.id),
|
|
6217
|
+
lineItemId: varchar("line_item_id").references(() => mediaOrderLineItems.id),
|
|
6218
|
+
eventType: activityEventTypeEnum("event_type").notNull(),
|
|
6219
|
+
title: text("title").notNull(),
|
|
6220
|
+
detail: text("detail"),
|
|
6221
|
+
severity: activitySeverityEnum("severity").notNull(),
|
|
6222
|
+
sourceSystem: activitySourceSystemEnum("source_system").notNull(),
|
|
6223
|
+
externalId: text("external_id").notNull().unique(), // idempotency key — prevents duplicate event ingestion
|
|
6224
|
+
status: text("status"), // intentionally untyped — values vary by eventType (creative vs fulfillment vs contract)
|
|
6225
|
+
occurredAt: timestamp("occurred_at").notNull(),
|
|
6226
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
6227
|
+
metadata: jsonb("metadata").$type(),
|
|
6228
|
+
}, (table) => [
|
|
6229
|
+
index("order_activity_feed_order_occurred_idx").on(table.mediaOrderId, table.occurredAt),
|
|
6230
|
+
index("order_activity_feed_line_item_idx").on(table.lineItemId),
|
|
6231
|
+
]);
|
|
6232
|
+
// ─── Pending Actions ────────────────────────────────────────────────────────
|
|
6233
|
+
export const pendingActions = pgTable("pending_actions", {
|
|
6234
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
6235
|
+
mediaOrderId: varchar("media_order_id")
|
|
6236
|
+
.notNull()
|
|
6237
|
+
.references(() => mediaOrders.id),
|
|
6238
|
+
lineItemId: varchar("line_item_id").references(() => mediaOrderLineItems.id),
|
|
6239
|
+
actionType: pendingActionTypeEnum("action_type").notNull(),
|
|
6240
|
+
message: text("message").notNull(),
|
|
6241
|
+
activityFeedId: uuid("activity_feed_id")
|
|
6242
|
+
.notNull()
|
|
6243
|
+
.references(() => orderActivityFeed.id),
|
|
6244
|
+
resolvedAt: timestamp("resolved_at"),
|
|
6245
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
6246
|
+
}, (table) => [
|
|
6247
|
+
index("pending_actions_order_idx").on(table.mediaOrderId),
|
|
6248
|
+
index("pending_actions_line_item_idx").on(table.lineItemId),
|
|
6249
|
+
index("pending_actions_activity_feed_idx").on(table.activityFeedId),
|
|
6250
|
+
index("pending_actions_unresolved_idx").on(table.mediaOrderId).where(sql `resolved_at IS NULL`),
|
|
6251
|
+
]);
|
|
6252
|
+
// ─── Order Version Snapshots ────────────────────────────────────────────────
|
|
6253
|
+
export const orderVersionSnapshots = pgTable("order_version_snapshots", {
|
|
6254
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
6255
|
+
mediaOrderId: varchar("media_order_id")
|
|
6256
|
+
.notNull()
|
|
6257
|
+
.references(() => mediaOrders.id),
|
|
6258
|
+
version: integer("version").notNull(),
|
|
6259
|
+
amendmentReason: text("amendment_reason"),
|
|
6260
|
+
amendmentNotes: text("amendment_notes"),
|
|
6261
|
+
amendedBy: varchar("amended_by"),
|
|
6262
|
+
snapshot: jsonb("snapshot").notNull().$type(),
|
|
6263
|
+
netInvestment: numeric("net_investment", { precision: 12, scale: 2 }),
|
|
6264
|
+
lineItemCount: integer("line_item_count"),
|
|
6265
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
6266
|
+
}, (table) => [
|
|
6267
|
+
index("order_version_snapshots_order_version_idx").on(table.mediaOrderId, table.version),
|
|
6268
|
+
]);
|
|
6269
|
+
// ─── User Milestones ────────────────────────────────────────────────────────
|
|
6270
|
+
export const userMilestones = pgTable("user_milestones", {
|
|
6271
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
6272
|
+
userId: varchar("user_id").notNull(),
|
|
6273
|
+
milestoneKey: text("milestone_key").notNull(),
|
|
6274
|
+
shownAt: timestamp("shown_at").notNull().defaultNow(),
|
|
6275
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
6276
|
+
}, (table) => [
|
|
6277
|
+
index("user_milestones_user_id_idx").on(table.userId),
|
|
6278
|
+
unique("user_milestones_user_key_unique").on(table.userId, table.milestoneKey),
|
|
6279
|
+
]);
|
|
5970
6280
|
//# sourceMappingURL=schema.js.map
|