@voyant-travel/mice 0.2.0 → 0.4.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 (44) hide show
  1. package/dist/booking-extension.d.ts +130 -0
  2. package/dist/booking-extension.d.ts.map +1 -0
  3. package/dist/booking-extension.js +83 -0
  4. package/dist/index.d.ts +14 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +37 -0
  7. package/dist/routes.d.ts +856 -5
  8. package/dist/routes.d.ts.map +1 -1
  9. package/dist/routes.js +167 -0
  10. package/dist/schema-delegates.d.ts +340 -0
  11. package/dist/schema-delegates.d.ts.map +1 -0
  12. package/dist/schema-delegates.js +76 -0
  13. package/dist/schema-rfp.d.ts +790 -0
  14. package/dist/schema-rfp.d.ts.map +1 -0
  15. package/dist/schema-rfp.js +104 -0
  16. package/dist/schema-rooming.d.ts +333 -0
  17. package/dist/schema-rooming.d.ts.map +1 -0
  18. package/dist/schema-rooming.js +47 -0
  19. package/dist/schema.d.ts +5 -0
  20. package/dist/schema.d.ts.map +1 -1
  21. package/dist/schema.js +4 -0
  22. package/dist/service-delegates.d.ts +40 -0
  23. package/dist/service-delegates.d.ts.map +1 -0
  24. package/dist/service-delegates.js +107 -0
  25. package/dist/service-rfp.d.ts +89 -0
  26. package/dist/service-rfp.d.ts.map +1 -0
  27. package/dist/service-rfp.js +198 -0
  28. package/dist/service-rooming.d.ts +50 -0
  29. package/dist/service-rooming.d.ts.map +1 -0
  30. package/dist/service-rooming.js +102 -0
  31. package/dist/validation-delegates.d.ts +109 -0
  32. package/dist/validation-delegates.d.ts.map +1 -0
  33. package/dist/validation-delegates.js +41 -0
  34. package/dist/validation-rfp.d.ts +110 -0
  35. package/dist/validation-rfp.d.ts.map +1 -0
  36. package/dist/validation-rfp.js +61 -0
  37. package/dist/validation-rooming.d.ts +37 -0
  38. package/dist/validation-rooming.d.ts.map +1 -0
  39. package/dist/validation-rooming.js +28 -0
  40. package/dist/validation-sessions.d.ts +1 -1
  41. package/migrations/0002_mice_baseline.sql +86 -0
  42. package/migrations/0003_mice_baseline.sql +84 -0
  43. package/migrations/meta/_journal.json +14 -0
  44. package/package.json +8 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-rfp.d.ts","sourceRoot":"","sources":["../src/schema-rfp.ts"],"names":[],"mappings":"AAcA;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,6FAMxB,CAAA;AAEF,eAAO,MAAM,uBAAuB,sFAKlC,CAAA;AAEF,eAAO,MAAM,aAAa,sGAMxB,CAAA;AAEF,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoBhB,CAAA;AAED,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgB1B,CAAA;AAED,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBhB,CAAA;AAED,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAepB,CAAA;AAED,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAe1B,CAAA;AAED,MAAM,MAAM,GAAG,GAAG,OAAO,IAAI,CAAC,YAAY,CAAA;AAC1C,MAAM,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,YAAY,CAAA;AAC7C,MAAM,MAAM,aAAa,GAAG,OAAO,cAAc,CAAC,YAAY,CAAA;AAC9D,MAAM,MAAM,gBAAgB,GAAG,OAAO,cAAc,CAAC,YAAY,CAAA;AACjE,MAAM,MAAM,GAAG,GAAG,OAAO,IAAI,CAAC,YAAY,CAAA;AAC1C,MAAM,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,YAAY,CAAA;AAC7C,MAAM,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAA;AAClD,MAAM,MAAM,UAAU,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAA;AACrD,MAAM,MAAM,aAAa,GAAG,OAAO,cAAc,CAAC,YAAY,CAAA;AAC9D,MAAM,MAAM,gBAAgB,GAAG,OAAO,cAAc,CAAC,YAAY,CAAA"}
@@ -0,0 +1,104 @@
1
+ import { typeId, typeIdRef } from "@voyant-travel/db/lib/typeid-column";
2
+ import { index, integer, jsonb, pgEnum, pgTable, text, timestamp, uniqueIndex, } from "drizzle-orm/pg-core";
3
+ import { programs } from "./schema.js";
4
+ /**
5
+ * Sourcing funnel — RFP → invitations → bids → evaluation → award. CRM
6
+ * opportunity/quote model single-deal closure, not multi-supplier bid
7
+ * solicitation/comparison/scoring; this is the gap. See RFC voyant#1489 §3/§5
8
+ * (Phase 4). Award atomically accepts the winning bid, rejects the rest, and
9
+ * moves the RFP to `awarded` (downstream contract/block/booking spawn is a
10
+ * workflow subscriber on `mice.rfp.awarded`).
11
+ */
12
+ export const rfpStatusEnum = pgEnum("mice_rfp_status", [
13
+ "draft",
14
+ "issued",
15
+ "closed",
16
+ "awarded",
17
+ "cancelled",
18
+ ]);
19
+ export const rfpInvitationStatusEnum = pgEnum("mice_rfp_invitation_status", [
20
+ "invited",
21
+ "viewed",
22
+ "declined",
23
+ "responded",
24
+ ]);
25
+ export const bidStatusEnum = pgEnum("mice_bid_status", [
26
+ "draft",
27
+ "submitted",
28
+ "under_review",
29
+ "accepted",
30
+ "rejected",
31
+ ]);
32
+ export const rfps = pgTable("mice_rfps", {
33
+ id: typeId("mice_rfps"),
34
+ programId: typeIdRef("program_id")
35
+ .notNull()
36
+ .references(() => programs.id, { onDelete: "cascade" }),
37
+ title: text("title").notNull(),
38
+ requirements: jsonb("requirements").$type(),
39
+ status: rfpStatusEnum("status").notNull().default("draft"),
40
+ issuedAt: timestamp("issued_at", { withTimezone: true }),
41
+ dueAt: timestamp("due_at", { withTimezone: true }),
42
+ notes: text("notes"),
43
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
44
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
45
+ }, (table) => [
46
+ index("idx_mice_rfps_program").on(table.programId),
47
+ index("idx_mice_rfps_status").on(table.status),
48
+ ]);
49
+ export const rfpInvitations = pgTable("mice_rfp_invitations", {
50
+ id: typeId("mice_rfp_invitations"),
51
+ rfpId: typeIdRef("rfp_id")
52
+ .notNull()
53
+ .references(() => rfps.id, { onDelete: "cascade" }),
54
+ supplierId: typeIdRef("supplier_id").notNull(), // → distribution suppliers (loose)
55
+ status: rfpInvitationStatusEnum("status").notNull().default("invited"),
56
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
57
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
58
+ }, (table) => [
59
+ index("idx_mice_rfp_invitations_supplier").on(table.supplierId),
60
+ uniqueIndex("uidx_mice_rfp_invitations_rfp_supplier").on(table.rfpId, table.supplierId),
61
+ ]);
62
+ export const bids = pgTable("mice_bids", {
63
+ id: typeId("mice_bids"),
64
+ rfpId: typeIdRef("rfp_id")
65
+ .notNull()
66
+ .references(() => rfps.id, { onDelete: "cascade" }),
67
+ supplierId: typeIdRef("supplier_id").notNull(), // → distribution suppliers (loose)
68
+ status: bidStatusEnum("status").notNull().default("draft"),
69
+ totalCents: integer("total_cents"),
70
+ currency: text("currency"),
71
+ proposalDoc: text("proposal_doc"),
72
+ validUntil: timestamp("valid_until", { withTimezone: true }),
73
+ notes: text("notes"),
74
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
75
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
76
+ }, (table) => [
77
+ index("idx_mice_bids_rfp").on(table.rfpId),
78
+ index("idx_mice_bids_supplier").on(table.supplierId),
79
+ index("idx_mice_bids_status").on(table.status),
80
+ ]);
81
+ export const bidLines = pgTable("mice_bid_lines", {
82
+ id: typeId("mice_bid_lines"),
83
+ bidId: typeIdRef("bid_id")
84
+ .notNull()
85
+ .references(() => bids.id, { onDelete: "cascade" }),
86
+ requirementRef: text("requirement_ref"),
87
+ description: text("description"),
88
+ quantity: integer("quantity").notNull().default(1),
89
+ unitCents: integer("unit_cents"),
90
+ totalCents: integer("total_cents"),
91
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
92
+ }, (table) => [index("idx_mice_bid_lines_bid").on(table.bidId)]);
93
+ export const bidEvaluations = pgTable("mice_bid_evaluations", {
94
+ id: typeId("mice_bid_evaluations"),
95
+ bidId: typeIdRef("bid_id")
96
+ .notNull()
97
+ .references(() => bids.id, { onDelete: "cascade" }),
98
+ criterion: text("criterion").notNull(),
99
+ weight: integer("weight"),
100
+ score: integer("score"),
101
+ notes: text("notes"),
102
+ evaluatedBy: text("evaluated_by"),
103
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
104
+ }, (table) => [index("idx_mice_bid_evaluations_bid").on(table.bidId)]);
@@ -0,0 +1,333 @@
1
+ /**
2
+ * First-class rooming manifest (§9-Q3) — program-centric, replacing the
3
+ * fragmented roomTypeId + sharingGroupId + allocations-JSONB on booking
4
+ * traveler details. A shared room is one assignment with MANY delegates via the
5
+ * explicit join (§9-Q5). See RFC voyant#1489 (Phase 3).
6
+ */
7
+ export declare const roomingAssignments: import("drizzle-orm/pg-core").PgTableWithColumns<{
8
+ name: "mice_rooming_assignments";
9
+ schema: undefined;
10
+ columns: {
11
+ id: import("drizzle-orm/pg-core").PgColumn<{
12
+ name: string;
13
+ tableName: "mice_rooming_assignments";
14
+ dataType: "string";
15
+ columnType: "PgText";
16
+ data: string;
17
+ driverParam: string;
18
+ notNull: true;
19
+ hasDefault: true;
20
+ isPrimaryKey: true;
21
+ isAutoincrement: false;
22
+ hasRuntimeDefault: true;
23
+ enumValues: [string, ...string[]];
24
+ baseColumn: never;
25
+ identity: undefined;
26
+ generated: undefined;
27
+ }, {}, {}>;
28
+ programId: import("drizzle-orm/pg-core").PgColumn<{
29
+ name: string;
30
+ tableName: "mice_rooming_assignments";
31
+ dataType: "string";
32
+ columnType: "PgText";
33
+ data: string;
34
+ driverParam: string;
35
+ notNull: true;
36
+ hasDefault: false;
37
+ isPrimaryKey: false;
38
+ isAutoincrement: false;
39
+ hasRuntimeDefault: false;
40
+ enumValues: [string, ...string[]];
41
+ baseColumn: never;
42
+ identity: undefined;
43
+ generated: undefined;
44
+ }, {}, {}>;
45
+ roomBlockId: import("drizzle-orm/pg-core").PgColumn<{
46
+ name: string;
47
+ tableName: "mice_rooming_assignments";
48
+ dataType: "string";
49
+ columnType: "PgText";
50
+ data: string;
51
+ driverParam: string;
52
+ notNull: false;
53
+ hasDefault: false;
54
+ isPrimaryKey: false;
55
+ isAutoincrement: false;
56
+ hasRuntimeDefault: false;
57
+ enumValues: [string, ...string[]];
58
+ baseColumn: never;
59
+ identity: undefined;
60
+ generated: undefined;
61
+ }, {}, {}>;
62
+ roomTypeId: import("drizzle-orm/pg-core").PgColumn<{
63
+ name: string;
64
+ tableName: "mice_rooming_assignments";
65
+ dataType: "string";
66
+ columnType: "PgText";
67
+ data: string;
68
+ driverParam: string;
69
+ notNull: false;
70
+ hasDefault: false;
71
+ isPrimaryKey: false;
72
+ isAutoincrement: false;
73
+ hasRuntimeDefault: false;
74
+ enumValues: [string, ...string[]];
75
+ baseColumn: never;
76
+ identity: undefined;
77
+ generated: undefined;
78
+ }, {}, {}>;
79
+ bedConfig: import("drizzle-orm/pg-core").PgColumn<{
80
+ name: "bed_config";
81
+ tableName: "mice_rooming_assignments";
82
+ dataType: "string";
83
+ columnType: "PgText";
84
+ data: string;
85
+ driverParam: string;
86
+ notNull: false;
87
+ hasDefault: false;
88
+ isPrimaryKey: false;
89
+ isAutoincrement: false;
90
+ hasRuntimeDefault: false;
91
+ enumValues: [string, ...string[]];
92
+ baseColumn: never;
93
+ identity: undefined;
94
+ generated: undefined;
95
+ }, {}, {}>;
96
+ sharingGroupId: import("drizzle-orm/pg-core").PgColumn<{
97
+ name: "sharing_group_id";
98
+ tableName: "mice_rooming_assignments";
99
+ dataType: "string";
100
+ columnType: "PgText";
101
+ data: string;
102
+ driverParam: string;
103
+ notNull: false;
104
+ hasDefault: false;
105
+ isPrimaryKey: false;
106
+ isAutoincrement: false;
107
+ hasRuntimeDefault: false;
108
+ enumValues: [string, ...string[]];
109
+ baseColumn: never;
110
+ identity: undefined;
111
+ generated: undefined;
112
+ }, {}, {}>;
113
+ checkIn: import("drizzle-orm/pg-core").PgColumn<{
114
+ name: "check_in";
115
+ tableName: "mice_rooming_assignments";
116
+ dataType: "string";
117
+ columnType: "PgDateString";
118
+ data: string;
119
+ driverParam: string;
120
+ notNull: false;
121
+ hasDefault: false;
122
+ isPrimaryKey: false;
123
+ isAutoincrement: false;
124
+ hasRuntimeDefault: false;
125
+ enumValues: undefined;
126
+ baseColumn: never;
127
+ identity: undefined;
128
+ generated: undefined;
129
+ }, {}, {}>;
130
+ checkOut: import("drizzle-orm/pg-core").PgColumn<{
131
+ name: "check_out";
132
+ tableName: "mice_rooming_assignments";
133
+ dataType: "string";
134
+ columnType: "PgDateString";
135
+ data: string;
136
+ driverParam: string;
137
+ notNull: false;
138
+ hasDefault: false;
139
+ isPrimaryKey: false;
140
+ isAutoincrement: false;
141
+ hasRuntimeDefault: false;
142
+ enumValues: undefined;
143
+ baseColumn: never;
144
+ identity: undefined;
145
+ generated: undefined;
146
+ }, {}, {}>;
147
+ specialRequests: import("drizzle-orm/pg-core").PgColumn<{
148
+ name: "special_requests";
149
+ tableName: "mice_rooming_assignments";
150
+ dataType: "string";
151
+ columnType: "PgText";
152
+ data: string;
153
+ driverParam: string;
154
+ notNull: false;
155
+ hasDefault: false;
156
+ isPrimaryKey: false;
157
+ isAutoincrement: false;
158
+ hasRuntimeDefault: false;
159
+ enumValues: [string, ...string[]];
160
+ baseColumn: never;
161
+ identity: undefined;
162
+ generated: undefined;
163
+ }, {}, {}>;
164
+ metadata: import("drizzle-orm/pg-core").PgColumn<{
165
+ name: "metadata";
166
+ tableName: "mice_rooming_assignments";
167
+ dataType: "json";
168
+ columnType: "PgJsonb";
169
+ data: Record<string, unknown>;
170
+ driverParam: unknown;
171
+ notNull: false;
172
+ hasDefault: false;
173
+ isPrimaryKey: false;
174
+ isAutoincrement: false;
175
+ hasRuntimeDefault: false;
176
+ enumValues: undefined;
177
+ baseColumn: never;
178
+ identity: undefined;
179
+ generated: undefined;
180
+ }, {}, {
181
+ $type: Record<string, unknown>;
182
+ }>;
183
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
184
+ name: "created_at";
185
+ tableName: "mice_rooming_assignments";
186
+ dataType: "date";
187
+ columnType: "PgTimestamp";
188
+ data: Date;
189
+ driverParam: string;
190
+ notNull: true;
191
+ hasDefault: true;
192
+ isPrimaryKey: false;
193
+ isAutoincrement: false;
194
+ hasRuntimeDefault: false;
195
+ enumValues: undefined;
196
+ baseColumn: never;
197
+ identity: undefined;
198
+ generated: undefined;
199
+ }, {}, {}>;
200
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
201
+ name: "updated_at";
202
+ tableName: "mice_rooming_assignments";
203
+ dataType: "date";
204
+ columnType: "PgTimestamp";
205
+ data: Date;
206
+ driverParam: string;
207
+ notNull: true;
208
+ hasDefault: true;
209
+ isPrimaryKey: false;
210
+ isAutoincrement: false;
211
+ hasRuntimeDefault: false;
212
+ enumValues: undefined;
213
+ baseColumn: never;
214
+ identity: undefined;
215
+ generated: undefined;
216
+ }, {}, {}>;
217
+ };
218
+ dialect: "pg";
219
+ }>;
220
+ export declare const roomingAssignmentDelegates: import("drizzle-orm/pg-core").PgTableWithColumns<{
221
+ name: "mice_rooming_assignment_delegates";
222
+ schema: undefined;
223
+ columns: {
224
+ id: import("drizzle-orm/pg-core").PgColumn<{
225
+ name: string;
226
+ tableName: "mice_rooming_assignment_delegates";
227
+ dataType: "string";
228
+ columnType: "PgText";
229
+ data: string;
230
+ driverParam: string;
231
+ notNull: true;
232
+ hasDefault: true;
233
+ isPrimaryKey: true;
234
+ isAutoincrement: false;
235
+ hasRuntimeDefault: true;
236
+ enumValues: [string, ...string[]];
237
+ baseColumn: never;
238
+ identity: undefined;
239
+ generated: undefined;
240
+ }, {}, {}>;
241
+ roomingAssignmentId: import("drizzle-orm/pg-core").PgColumn<{
242
+ name: string;
243
+ tableName: "mice_rooming_assignment_delegates";
244
+ dataType: "string";
245
+ columnType: "PgText";
246
+ data: string;
247
+ driverParam: string;
248
+ notNull: true;
249
+ hasDefault: false;
250
+ isPrimaryKey: false;
251
+ isAutoincrement: false;
252
+ hasRuntimeDefault: false;
253
+ enumValues: [string, ...string[]];
254
+ baseColumn: never;
255
+ identity: undefined;
256
+ generated: undefined;
257
+ }, {}, {}>;
258
+ delegateId: import("drizzle-orm/pg-core").PgColumn<{
259
+ name: string;
260
+ tableName: "mice_rooming_assignment_delegates";
261
+ dataType: "string";
262
+ columnType: "PgText";
263
+ data: string;
264
+ driverParam: string;
265
+ notNull: true;
266
+ hasDefault: false;
267
+ isPrimaryKey: false;
268
+ isAutoincrement: false;
269
+ hasRuntimeDefault: false;
270
+ enumValues: [string, ...string[]];
271
+ baseColumn: never;
272
+ identity: undefined;
273
+ generated: undefined;
274
+ }, {}, {}>;
275
+ isPrimary: import("drizzle-orm/pg-core").PgColumn<{
276
+ name: "is_primary";
277
+ tableName: "mice_rooming_assignment_delegates";
278
+ dataType: "boolean";
279
+ columnType: "PgBoolean";
280
+ data: boolean;
281
+ driverParam: boolean;
282
+ notNull: true;
283
+ hasDefault: true;
284
+ isPrimaryKey: false;
285
+ isAutoincrement: false;
286
+ hasRuntimeDefault: false;
287
+ enumValues: undefined;
288
+ baseColumn: never;
289
+ identity: undefined;
290
+ generated: undefined;
291
+ }, {}, {}>;
292
+ bedLabel: import("drizzle-orm/pg-core").PgColumn<{
293
+ name: "bed_label";
294
+ tableName: "mice_rooming_assignment_delegates";
295
+ dataType: "string";
296
+ columnType: "PgText";
297
+ data: string;
298
+ driverParam: string;
299
+ notNull: false;
300
+ hasDefault: false;
301
+ isPrimaryKey: false;
302
+ isAutoincrement: false;
303
+ hasRuntimeDefault: false;
304
+ enumValues: [string, ...string[]];
305
+ baseColumn: never;
306
+ identity: undefined;
307
+ generated: undefined;
308
+ }, {}, {}>;
309
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
310
+ name: "created_at";
311
+ tableName: "mice_rooming_assignment_delegates";
312
+ dataType: "date";
313
+ columnType: "PgTimestamp";
314
+ data: Date;
315
+ driverParam: string;
316
+ notNull: true;
317
+ hasDefault: true;
318
+ isPrimaryKey: false;
319
+ isAutoincrement: false;
320
+ hasRuntimeDefault: false;
321
+ enumValues: undefined;
322
+ baseColumn: never;
323
+ identity: undefined;
324
+ generated: undefined;
325
+ }, {}, {}>;
326
+ };
327
+ dialect: "pg";
328
+ }>;
329
+ export type RoomingAssignment = typeof roomingAssignments.$inferSelect;
330
+ export type NewRoomingAssignment = typeof roomingAssignments.$inferInsert;
331
+ export type RoomingAssignmentDelegate = typeof roomingAssignmentDelegates.$inferSelect;
332
+ export type NewRoomingAssignmentDelegate = typeof roomingAssignmentDelegates.$inferInsert;
333
+ //# sourceMappingURL=schema-rooming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-rooming.d.ts","sourceRoot":"","sources":["../src/schema-rooming.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwB9B,CAAA;AAED,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBtC,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAA;AACtE,MAAM,MAAM,oBAAoB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAA;AACzE,MAAM,MAAM,yBAAyB,GAAG,OAAO,0BAA0B,CAAC,YAAY,CAAA;AACtF,MAAM,MAAM,4BAA4B,GAAG,OAAO,0BAA0B,CAAC,YAAY,CAAA"}
@@ -0,0 +1,47 @@
1
+ import { typeId, typeIdRef } from "@voyant-travel/db/lib/typeid-column";
2
+ import { boolean, date, index, jsonb, pgTable, text, timestamp, uniqueIndex, } from "drizzle-orm/pg-core";
3
+ import { programs } from "./schema.js";
4
+ import { programDelegates } from "./schema-delegates.js";
5
+ /**
6
+ * First-class rooming manifest (§9-Q3) — program-centric, replacing the
7
+ * fragmented roomTypeId + sharingGroupId + allocations-JSONB on booking
8
+ * traveler details. A shared room is one assignment with MANY delegates via the
9
+ * explicit join (§9-Q5). See RFC voyant#1489 (Phase 3).
10
+ */
11
+ export const roomingAssignments = pgTable("mice_rooming_assignments", {
12
+ id: typeId("mice_rooming_assignments"),
13
+ // Intra-package FK:
14
+ programId: typeIdRef("program_id")
15
+ .notNull()
16
+ .references(() => programs.id, { onDelete: "cascade" }),
17
+ // Cross-package → loose columns + defineLink at the deployment:
18
+ roomBlockId: typeIdRef("room_block_id"), // → accommodations.roomBlocks
19
+ roomTypeId: typeIdRef("room_type_id"), // → accommodations.roomTypes
20
+ bedConfig: text("bed_config"),
21
+ sharingGroupId: text("sharing_group_id"),
22
+ checkIn: date("check_in"),
23
+ checkOut: date("check_out"),
24
+ specialRequests: text("special_requests"),
25
+ metadata: jsonb("metadata").$type(),
26
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
27
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
28
+ }, (table) => [
29
+ index("idx_mice_rooming_assignments_program").on(table.programId),
30
+ index("idx_mice_rooming_assignments_room_block").on(table.roomBlockId),
31
+ ]);
32
+ export const roomingAssignmentDelegates = pgTable("mice_rooming_assignment_delegates", {
33
+ id: typeId("mice_rooming_assignment_delegates"),
34
+ roomingAssignmentId: typeIdRef("rooming_assignment_id")
35
+ .notNull()
36
+ .references(() => roomingAssignments.id, { onDelete: "cascade" }),
37
+ delegateId: typeIdRef("delegate_id")
38
+ .notNull()
39
+ .references(() => programDelegates.id, { onDelete: "cascade" }),
40
+ isPrimary: boolean("is_primary").notNull().default(false),
41
+ bedLabel: text("bed_label"),
42
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
43
+ }, (table) => [
44
+ index("idx_mice_rooming_assignment_delegates_assignment").on(table.roomingAssignmentId),
45
+ // A delegate appears at most once per rooming assignment.
46
+ uniqueIndex("uidx_mice_rooming_assignment_delegates_pair").on(table.roomingAssignmentId, table.delegateId),
47
+ ]);
package/dist/schema.d.ts CHANGED
@@ -326,5 +326,10 @@ export declare const programs: import("drizzle-orm/pg-core").PgTableWithColumns<
326
326
  }>;
327
327
  export type Program = typeof programs.$inferSelect;
328
328
  export type NewProgram = typeof programs.$inferInsert;
329
+ export type { BookingMiceDetail, NewBookingMiceDetail } from "./booking-extension.js";
330
+ export { bookingMiceDetails } from "./booking-extension.js";
331
+ export * from "./schema-delegates.js";
332
+ export * from "./schema-rfp.js";
333
+ export * from "./schema-rooming.js";
329
334
  export * from "./schema-sessions.js";
330
335
  //# sourceMappingURL=schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,qGAM1B,CAAA;AAEF,eAAO,MAAM,iBAAiB,iHAO5B,CAAA;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BpB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAA;AAClD,MAAM,MAAM,UAAU,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAA;AAErD,cAAc,sBAAsB,CAAA"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,qGAM1B,CAAA;AAEF,eAAO,MAAM,iBAAiB,iHAO5B,CAAA;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BpB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAA;AAClD,MAAM,MAAM,UAAU,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAA;AAErD,YAAY,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,cAAc,uBAAuB,CAAA;AACrC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA"}
package/dist/schema.js CHANGED
@@ -48,4 +48,8 @@ export const programs = pgTable("mice_programs", {
48
48
  index("idx_mice_programs_status").on(table.status),
49
49
  index("idx_mice_programs_dates").on(table.startDate, table.endDate),
50
50
  ]);
51
+ export { bookingMiceDetails } from "./booking-extension.js";
52
+ export * from "./schema-delegates.js";
53
+ export * from "./schema-rfp.js";
54
+ export * from "./schema-rooming.js";
51
55
  export * from "./schema-sessions.js";
@@ -0,0 +1,40 @@
1
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
+ import { type DelegateSessionEnrollment, type ProgramDelegate } from "./schema-delegates.js";
3
+ import type { CreateDelegateBody, DelegateListQuery, EnrollDelegateBody, UpdateDelegateBody } from "./validation-delegates.js";
4
+ export type CreateDelegateOutcome = {
5
+ status: "ok";
6
+ delegate: ProgramDelegate;
7
+ } | {
8
+ status: "program_not_found";
9
+ };
10
+ export declare function createDelegate(db: PostgresJsDatabase, input: CreateDelegateBody): Promise<CreateDelegateOutcome>;
11
+ export declare function getDelegate(db: PostgresJsDatabase, id: string): Promise<(ProgramDelegate & {
12
+ enrollments: DelegateSessionEnrollment[];
13
+ }) | null>;
14
+ export declare function listDelegates(db: PostgresJsDatabase, query: DelegateListQuery): Promise<{
15
+ data: ProgramDelegate[];
16
+ limit: number;
17
+ offset: number;
18
+ }>;
19
+ export declare function updateDelegate(db: PostgresJsDatabase, id: string, input: UpdateDelegateBody): Promise<ProgramDelegate | null>;
20
+ export type EnrollDelegateOutcome = {
21
+ status: "ok";
22
+ enrollment: DelegateSessionEnrollment;
23
+ idempotent: boolean;
24
+ } | {
25
+ status: "delegate_not_found";
26
+ } | {
27
+ status: "session_not_found";
28
+ } | {
29
+ status: "program_mismatch";
30
+ };
31
+ /** Enroll a delegate in a session. Idempotent on (delegate, session). */
32
+ export declare function enrollDelegate(db: PostgresJsDatabase, delegateId: string, input: EnrollDelegateBody): Promise<EnrollDelegateOutcome>;
33
+ export declare const delegateService: {
34
+ createDelegate: typeof createDelegate;
35
+ getDelegate: typeof getDelegate;
36
+ listDelegates: typeof listDelegates;
37
+ updateDelegate: typeof updateDelegate;
38
+ enrollDelegate: typeof enrollDelegate;
39
+ };
40
+ //# sourceMappingURL=service-delegates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-delegates.d.ts","sourceRoot":"","sources":["../src/service-delegates.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,OAAO,EACL,KAAK,yBAAyB,EAE9B,KAAK,eAAe,EAErB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,2BAA2B,CAAA;AAYlC,MAAM,MAAM,qBAAqB,GAC7B;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,eAAe,CAAA;CAAE,GAC3C;IAAE,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAA;AAEnC,wBAAsB,cAAc,CAClC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAUhC;AAED,wBAAsB,WAAW,CAC/B,EAAE,EAAE,kBAAkB,EACtB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,CAAC,eAAe,GAAG;IAAE,WAAW,EAAE,yBAAyB,EAAE,CAAA;CAAE,CAAC,GAAG,IAAI,CAAC,CAYlF;AAED,wBAAsB,aAAa,CACjC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC;IAAE,IAAI,EAAE,eAAe,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAYrE;AAED,wBAAsB,cAAc,CAClC,EAAE,EAAE,kBAAkB,EACtB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAOjC;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,yBAAyB,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAC5E;IAAE,MAAM,EAAE,oBAAoB,CAAA;CAAE,GAChC;IAAE,MAAM,EAAE,mBAAmB,CAAA;CAAE,GAC/B;IAAE,MAAM,EAAE,kBAAkB,CAAA;CAAE,CAAA;AAElC,yEAAyE;AACzE,wBAAsB,cAAc,CAClC,EAAE,EAAE,kBAAkB,EACtB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAqChC;AAED,eAAO,MAAM,eAAe;;;;;;CAM3B,CAAA"}
@@ -0,0 +1,107 @@
1
+ import { and, asc, eq } from "drizzle-orm";
2
+ import { programs } from "./schema.js";
3
+ import { delegateSessionEnrollments, programDelegates, } from "./schema-delegates.js";
4
+ import { programSessions } from "./schema-sessions.js";
5
+ /** Coerce ISO datetime strings (from validation) into Date for timestamp columns. */
6
+ function withTimestamps(input) {
7
+ const { arrivalAt, departureAt, ...rest } = input;
8
+ return {
9
+ ...rest,
10
+ ...(arrivalAt !== undefined ? { arrivalAt: new Date(arrivalAt) } : {}),
11
+ ...(departureAt !== undefined ? { departureAt: new Date(departureAt) } : {}),
12
+ };
13
+ }
14
+ export async function createDelegate(db, input) {
15
+ const [program] = await db
16
+ .select({ id: programs.id })
17
+ .from(programs)
18
+ .where(eq(programs.id, input.programId))
19
+ .limit(1);
20
+ if (!program)
21
+ return { status: "program_not_found" };
22
+ const [delegate] = await db.insert(programDelegates).values(withTimestamps(input)).returning();
23
+ if (!delegate)
24
+ throw new Error("createDelegate: insert returned no rows");
25
+ return { status: "ok", delegate };
26
+ }
27
+ export async function getDelegate(db, id) {
28
+ const [delegate] = await db
29
+ .select()
30
+ .from(programDelegates)
31
+ .where(eq(programDelegates.id, id))
32
+ .limit(1);
33
+ if (!delegate)
34
+ return null;
35
+ const enrollments = await db
36
+ .select()
37
+ .from(delegateSessionEnrollments)
38
+ .where(eq(delegateSessionEnrollments.delegateId, id));
39
+ return { ...delegate, enrollments };
40
+ }
41
+ export async function listDelegates(db, query) {
42
+ const conditions = [eq(programDelegates.programId, query.programId)];
43
+ if (query.status)
44
+ conditions.push(eq(programDelegates.status, query.status));
45
+ if (query.role)
46
+ conditions.push(eq(programDelegates.role, query.role));
47
+ const data = await db
48
+ .select()
49
+ .from(programDelegates)
50
+ .where(and(...conditions))
51
+ .orderBy(asc(programDelegates.createdAt))
52
+ .limit(query.limit)
53
+ .offset(query.offset);
54
+ return { data, limit: query.limit, offset: query.offset };
55
+ }
56
+ export async function updateDelegate(db, id, input) {
57
+ const [delegate] = await db
58
+ .update(programDelegates)
59
+ .set({ ...withTimestamps(input), updatedAt: new Date() })
60
+ .where(eq(programDelegates.id, id))
61
+ .returning();
62
+ return delegate ?? null;
63
+ }
64
+ /** Enroll a delegate in a session. Idempotent on (delegate, session). */
65
+ export async function enrollDelegate(db, delegateId, input) {
66
+ return db.transaction(async (tx) => {
67
+ const [delegate] = await tx
68
+ .select({ id: programDelegates.id, programId: programDelegates.programId })
69
+ .from(programDelegates)
70
+ .where(eq(programDelegates.id, delegateId))
71
+ .limit(1);
72
+ if (!delegate)
73
+ return { status: "delegate_not_found" };
74
+ const [session] = await tx
75
+ .select({ id: programSessions.id, programId: programSessions.programId })
76
+ .from(programSessions)
77
+ .where(eq(programSessions.id, input.sessionId))
78
+ .limit(1);
79
+ if (!session)
80
+ return { status: "session_not_found" };
81
+ // The session must belong to the delegate's program — neither the FK nor the
82
+ // unique index includes program_id, so guard against cross-program enrollment.
83
+ if (session.programId !== delegate.programId)
84
+ return { status: "program_mismatch" };
85
+ const [existing] = await tx
86
+ .select()
87
+ .from(delegateSessionEnrollments)
88
+ .where(and(eq(delegateSessionEnrollments.delegateId, delegateId), eq(delegateSessionEnrollments.sessionId, input.sessionId)))
89
+ .limit(1);
90
+ if (existing)
91
+ return { status: "ok", enrollment: existing, idempotent: true };
92
+ const [enrollment] = await tx
93
+ .insert(delegateSessionEnrollments)
94
+ .values({ delegateId, sessionId: input.sessionId, status: input.status })
95
+ .returning();
96
+ if (!enrollment)
97
+ throw new Error("enrollDelegate: insert returned no rows");
98
+ return { status: "ok", enrollment, idempotent: false };
99
+ });
100
+ }
101
+ export const delegateService = {
102
+ createDelegate,
103
+ getDelegate,
104
+ listDelegates,
105
+ updateDelegate,
106
+ enrollDelegate,
107
+ };