@voyant-travel/quotes 0.119.2

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 (51) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +39 -0
  3. package/dist/booking-extension.d.ts +123 -0
  4. package/dist/booking-extension.d.ts.map +1 -0
  5. package/dist/booking-extension.js +87 -0
  6. package/dist/index.d.ts +16 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +32 -0
  9. package/dist/routes/index.d.ts +1410 -0
  10. package/dist/routes/index.d.ts.map +1 -0
  11. package/dist/routes/index.js +8 -0
  12. package/dist/routes/pipelines.d.ts +292 -0
  13. package/dist/routes/pipelines.d.ts.map +1 -0
  14. package/dist/routes/pipelines.js +59 -0
  15. package/dist/routes/quote-versions.d.ts +746 -0
  16. package/dist/routes/quote-versions.d.ts.map +1 -0
  17. package/dist/routes/quote-versions.js +175 -0
  18. package/dist/routes/quotes.d.ts +391 -0
  19. package/dist/routes/quotes.d.ts.map +1 -0
  20. package/dist/routes/quotes.js +70 -0
  21. package/dist/schema-relations.d.ts +30 -0
  22. package/dist/schema-relations.d.ts.map +1 -0
  23. package/dist/schema-relations.js +49 -0
  24. package/dist/schema-sales.d.ts +1511 -0
  25. package/dist/schema-sales.d.ts.map +1 -0
  26. package/dist/schema-sales.js +164 -0
  27. package/dist/schema-shared.d.ts +5 -0
  28. package/dist/schema-shared.d.ts.map +1 -0
  29. package/dist/schema-shared.js +18 -0
  30. package/dist/schema.d.ts +5 -0
  31. package/dist/schema.d.ts.map +1 -0
  32. package/dist/schema.js +4 -0
  33. package/dist/service/helpers.d.ts +22 -0
  34. package/dist/service/helpers.d.ts.map +1 -0
  35. package/dist/service/helpers.js +39 -0
  36. package/dist/service/index.d.ts +1734 -0
  37. package/dist/service/index.d.ts.map +1 -0
  38. package/dist/service/index.js +11 -0
  39. package/dist/service/pipelines.d.ts +113 -0
  40. package/dist/service/pipelines.d.ts.map +1 -0
  41. package/dist/service/pipelines.js +68 -0
  42. package/dist/service/quote-versions.d.ts +674 -0
  43. package/dist/service/quote-versions.d.ts.map +1 -0
  44. package/dist/service/quote-versions.js +400 -0
  45. package/dist/service/quotes.d.ts +826 -0
  46. package/dist/service/quotes.d.ts.map +1 -0
  47. package/dist/service/quotes.js +110 -0
  48. package/dist/validation.d.ts +2 -0
  49. package/dist/validation.d.ts.map +1 -0
  50. package/dist/validation.js +1 -0
  51. package/package.json +72 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-sales.d.ts","sourceRoot":"","sources":["../src/schema-sales.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,164 @@
1
+ import { typeId, typeIdRef } from "@voyant-travel/db/lib/typeid-column";
2
+ import { boolean, date, index, integer, jsonb, pgTable, text, timestamp, uniqueIndex, } from "drizzle-orm/pg-core";
3
+ import { entityTypeEnum, participantRoleEnum, quoteStatusEnum, quoteVersionStatusEnum, } from "./schema-shared.js";
4
+ export const pipelines = pgTable("pipelines", {
5
+ id: typeId("pipelines"),
6
+ entityType: entityTypeEnum("entity_type").notNull().default("quote"),
7
+ name: text("name").notNull(),
8
+ isDefault: boolean("is_default").notNull().default(false),
9
+ sortOrder: integer("sort_order").notNull().default(0),
10
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
11
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
12
+ }, (table) => [
13
+ index("idx_pipelines_entity").on(table.entityType),
14
+ index("idx_pipelines_sort").on(table.sortOrder, table.createdAt),
15
+ index("idx_pipelines_entity_sort").on(table.entityType, table.sortOrder, table.createdAt),
16
+ uniqueIndex("uidx_pipelines_entity_name").on(table.entityType, table.name),
17
+ ]);
18
+ export const stages = pgTable("stages", {
19
+ id: typeId("stages"),
20
+ pipelineId: typeIdRef("pipeline_id")
21
+ .notNull()
22
+ .references(() => pipelines.id, { onDelete: "cascade" }),
23
+ name: text("name").notNull(),
24
+ sortOrder: integer("sort_order").notNull().default(0),
25
+ probability: integer("probability"),
26
+ isClosed: boolean("is_closed").notNull().default(false),
27
+ isWon: boolean("is_won").notNull().default(false),
28
+ isLost: boolean("is_lost").notNull().default(false),
29
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
30
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
31
+ }, (table) => [
32
+ index("idx_stages_pipeline").on(table.pipelineId),
33
+ index("idx_stages_sort").on(table.sortOrder, table.createdAt),
34
+ index("idx_stages_pipeline_sort").on(table.pipelineId, table.sortOrder, table.createdAt),
35
+ uniqueIndex("uidx_stages_pipeline_name").on(table.pipelineId, table.name),
36
+ ]);
37
+ export const quotes = pgTable("quotes", {
38
+ id: typeId("quotes"),
39
+ title: text("title").notNull(),
40
+ personId: typeIdRef("person_id"),
41
+ organizationId: typeIdRef("organization_id"),
42
+ pipelineId: typeIdRef("pipeline_id")
43
+ .notNull()
44
+ .references(() => pipelines.id, { onDelete: "restrict" }),
45
+ stageId: typeIdRef("stage_id")
46
+ .notNull()
47
+ .references(() => stages.id, { onDelete: "restrict" }),
48
+ ownerId: text("owner_id"),
49
+ status: quoteStatusEnum("status").notNull().default("open"),
50
+ acceptedVersionId: typeIdRef("accepted_version_id"),
51
+ valueAmountCents: integer("value_amount_cents"),
52
+ valueCurrency: text("value_currency"),
53
+ expectedCloseDate: date("expected_close_date"),
54
+ source: text("source"),
55
+ sourceRef: text("source_ref"),
56
+ lostReason: text("lost_reason"),
57
+ tags: jsonb("tags").$type().notNull().default([]),
58
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
59
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
60
+ stageChangedAt: timestamp("stage_changed_at", { withTimezone: true }).notNull().defaultNow(),
61
+ closedAt: timestamp("closed_at", { withTimezone: true }),
62
+ }, (table) => [
63
+ index("idx_quotes_person").on(table.personId),
64
+ index("idx_quotes_org").on(table.organizationId),
65
+ index("idx_quotes_pipeline").on(table.pipelineId),
66
+ index("idx_quotes_stage").on(table.stageId),
67
+ index("idx_quotes_owner").on(table.ownerId),
68
+ index("idx_quotes_status").on(table.status),
69
+ index("idx_quotes_accepted_version").on(table.acceptedVersionId),
70
+ index("idx_quotes_person_updated").on(table.personId, table.updatedAt),
71
+ index("idx_quotes_org_updated").on(table.organizationId, table.updatedAt),
72
+ index("idx_quotes_pipeline_updated").on(table.pipelineId, table.updatedAt),
73
+ index("idx_quotes_stage_updated").on(table.stageId, table.updatedAt),
74
+ index("idx_quotes_owner_updated").on(table.ownerId, table.updatedAt),
75
+ index("idx_quotes_status_updated").on(table.status, table.updatedAt),
76
+ ]);
77
+ export const quoteParticipants = pgTable("quote_participants", {
78
+ id: typeId("quote_participants"),
79
+ quoteId: typeIdRef("quote_id")
80
+ .notNull()
81
+ .references(() => quotes.id, { onDelete: "cascade" }),
82
+ personId: typeIdRef("person_id").notNull(),
83
+ role: participantRoleEnum("role").notNull().default("other"),
84
+ isPrimary: boolean("is_primary").notNull().default(false),
85
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
86
+ }, (table) => [
87
+ index("idx_quote_participants_quote").on(table.quoteId),
88
+ index("idx_quote_participants_quote_primary").on(table.quoteId, table.isPrimary, table.createdAt),
89
+ index("idx_quote_participants_person").on(table.personId),
90
+ uniqueIndex("uidx_quote_participants_unique").on(table.quoteId, table.personId),
91
+ ]);
92
+ export const quoteProducts = pgTable("quote_products", {
93
+ id: typeId("quote_products"),
94
+ quoteId: typeIdRef("quote_id")
95
+ .notNull()
96
+ .references(() => quotes.id, { onDelete: "cascade" }),
97
+ productId: text("product_id"),
98
+ supplierServiceId: text("supplier_service_id"),
99
+ nameSnapshot: text("name_snapshot").notNull(),
100
+ description: text("description"),
101
+ quantity: integer("quantity").notNull().default(1),
102
+ unitPriceAmountCents: integer("unit_price_amount_cents"),
103
+ costAmountCents: integer("cost_amount_cents"),
104
+ currency: text("currency"),
105
+ discountAmountCents: integer("discount_amount_cents"),
106
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
107
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
108
+ }, (table) => [
109
+ index("idx_quote_products_quote").on(table.quoteId),
110
+ index("idx_quote_products_quote_created").on(table.quoteId, table.createdAt),
111
+ index("idx_quote_products_product").on(table.productId),
112
+ index("idx_quote_products_supplier_service").on(table.supplierServiceId),
113
+ ]);
114
+ export const quoteVersions = pgTable("quote_versions", {
115
+ id: typeId("quote_versions"),
116
+ quoteId: typeIdRef("quote_id")
117
+ .notNull()
118
+ .references(() => quotes.id, { onDelete: "cascade" }),
119
+ label: text("label"),
120
+ status: quoteVersionStatusEnum("status").notNull().default("draft"),
121
+ supersedesId: typeIdRef("supersedes_id").references(() => quoteVersions.id, {
122
+ onDelete: "set null",
123
+ }),
124
+ tripSnapshotId: text("trip_snapshot_id"),
125
+ validUntil: date("valid_until"),
126
+ currency: text("currency").notNull(),
127
+ subtotalAmountCents: integer("subtotal_amount_cents").notNull().default(0),
128
+ taxAmountCents: integer("tax_amount_cents").notNull().default(0),
129
+ totalAmountCents: integer("total_amount_cents").notNull().default(0),
130
+ notes: text("notes"),
131
+ sentAt: timestamp("sent_at", { withTimezone: true }),
132
+ viewedAt: timestamp("viewed_at", { withTimezone: true }),
133
+ decidedAt: timestamp("decided_at", { withTimezone: true }),
134
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
135
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
136
+ archivedAt: timestamp("archived_at", { withTimezone: true }),
137
+ }, (table) => [
138
+ index("idx_quote_versions_quote").on(table.quoteId),
139
+ index("idx_quote_versions_status").on(table.status),
140
+ index("idx_quote_versions_supersedes").on(table.supersedesId),
141
+ index("idx_quote_versions_trip_snapshot").on(table.tripSnapshotId),
142
+ index("idx_quote_versions_quote_updated").on(table.quoteId, table.updatedAt),
143
+ index("idx_quote_versions_status_updated").on(table.status, table.updatedAt),
144
+ ]);
145
+ export const quoteVersionLines = pgTable("quote_version_lines", {
146
+ id: typeId("quote_version_lines"),
147
+ quoteVersionId: typeIdRef("quote_version_id")
148
+ .notNull()
149
+ .references(() => quoteVersions.id, { onDelete: "cascade" }),
150
+ productId: text("product_id"),
151
+ supplierServiceId: text("supplier_service_id"),
152
+ description: text("description").notNull(),
153
+ quantity: integer("quantity").notNull().default(1),
154
+ unitPriceAmountCents: integer("unit_price_amount_cents").notNull().default(0),
155
+ totalAmountCents: integer("total_amount_cents").notNull().default(0),
156
+ currency: text("currency").notNull(),
157
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
158
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
159
+ }, (table) => [
160
+ index("idx_quote_version_lines_version").on(table.quoteVersionId),
161
+ index("idx_quote_version_lines_version_created").on(table.quoteVersionId, table.createdAt),
162
+ index("idx_quote_version_lines_product").on(table.productId),
163
+ index("idx_quote_version_lines_supplier_service").on(table.supplierServiceId),
164
+ ]);
@@ -0,0 +1,5 @@
1
+ export declare const entityTypeEnum: import("drizzle-orm/pg-core").PgEnum<["organization", "person", "quote", "activity"]>;
2
+ export declare const quoteStatusEnum: import("drizzle-orm/pg-core").PgEnum<["open", "won", "lost", "archived"]>;
3
+ export declare const quoteVersionStatusEnum: import("drizzle-orm/pg-core").PgEnum<["draft", "sent", "accepted", "declined", "superseded", "expired"]>;
4
+ export declare const participantRoleEnum: import("drizzle-orm/pg-core").PgEnum<["traveler", "booker", "decision_maker", "finance", "other"]>;
5
+ //# sourceMappingURL=schema-shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-shared.d.ts","sourceRoot":"","sources":["../src/schema-shared.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,uFAAyE,CAAA;AAEpG,eAAO,MAAM,eAAe,2EAA8D,CAAA;AAE1F,eAAO,MAAM,sBAAsB,0GAOjC,CAAA;AAEF,eAAO,MAAM,mBAAmB,oGAM9B,CAAA"}
@@ -0,0 +1,18 @@
1
+ import { pgEnum } from "drizzle-orm/pg-core";
2
+ export const entityTypeEnum = pgEnum("entity_type", ["organization", "person", "quote", "activity"]);
3
+ export const quoteStatusEnum = pgEnum("quote_status", ["open", "won", "lost", "archived"]);
4
+ export const quoteVersionStatusEnum = pgEnum("quote_version_status", [
5
+ "draft",
6
+ "sent",
7
+ "accepted",
8
+ "declined",
9
+ "superseded",
10
+ "expired",
11
+ ]);
12
+ export const participantRoleEnum = pgEnum("participant_role", [
13
+ "traveler",
14
+ "booker",
15
+ "decision_maker",
16
+ "finance",
17
+ "other",
18
+ ]);
@@ -0,0 +1,5 @@
1
+ export { bookingQuoteDetails } from "./booking-extension.js";
2
+ export * from "./schema-relations.js";
3
+ export * from "./schema-sales.js";
4
+ export * from "./schema-shared.js";
5
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAC5D,cAAc,uBAAuB,CAAA;AACrC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA"}
package/dist/schema.js ADDED
@@ -0,0 +1,4 @@
1
+ export { bookingQuoteDetails } from "./booking-extension.js";
2
+ export * from "./schema-relations.js";
3
+ export * from "./schema-sales.js";
4
+ export * from "./schema-shared.js";
@@ -0,0 +1,22 @@
1
+ export declare function paginate<T extends object>(rowsQuery: Promise<T[]>, countQuery: Promise<Array<{
2
+ count: number;
3
+ }>>, limit: number, offset: number): Promise<{
4
+ data: T[];
5
+ total: number;
6
+ limit: number;
7
+ offset: number;
8
+ }>;
9
+ export declare function toDateOrNull(value: string | null | undefined): Date | null;
10
+ export declare function normalizeContactValue(kind: "email" | "phone" | "website", value: string): string;
11
+ export declare function isManagedBySource(metadata: Record<string, unknown> | null | undefined, source: string): boolean;
12
+ export declare function toNullableTrimmed(value: string | null | undefined): string | null;
13
+ export declare function formatAddress(address: {
14
+ fullText: string | null;
15
+ line1: string | null;
16
+ line2: string | null;
17
+ city: string | null;
18
+ region: string | null;
19
+ postalCode: string | null;
20
+ country: string | null;
21
+ }): string | null;
22
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/service/helpers.ts"],"names":[],"mappings":"AAAA,wBAAsB,QAAQ,CAAC,CAAC,SAAS,MAAM,EAC7C,SAAS,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,EACvB,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,EAC7C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM;;;;;GAUf;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,eAE5D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,UAKvF;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EACpD,MAAM,EAAE,MAAM,WAGf;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,iBAGjE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACrC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB,iBAaA"}
@@ -0,0 +1,39 @@
1
+ export async function paginate(rowsQuery, countQuery, limit, offset) {
2
+ const [data, countResult] = await Promise.all([rowsQuery, countQuery]);
3
+ return {
4
+ data,
5
+ total: countResult[0]?.count ?? 0,
6
+ limit,
7
+ offset,
8
+ };
9
+ }
10
+ export function toDateOrNull(value) {
11
+ return value ? new Date(value) : null;
12
+ }
13
+ export function normalizeContactValue(kind, value) {
14
+ if (kind === "email" || kind === "website") {
15
+ return value.trim().toLowerCase();
16
+ }
17
+ return value.trim();
18
+ }
19
+ export function isManagedBySource(metadata, source) {
20
+ return metadata?.managedBy === source;
21
+ }
22
+ export function toNullableTrimmed(value) {
23
+ const trimmed = value?.trim();
24
+ return trimmed ? trimmed : null;
25
+ }
26
+ export function formatAddress(address) {
27
+ if (address.fullText) {
28
+ return address.fullText;
29
+ }
30
+ const parts = [
31
+ address.line1,
32
+ address.line2,
33
+ address.city,
34
+ address.region,
35
+ address.postalCode,
36
+ address.country,
37
+ ].filter(Boolean);
38
+ return parts.length > 0 ? parts.join(", ") : null;
39
+ }