@voyant-travel/commerce 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +145 -0
  3. package/dist/accepted-quote-version-reservation-golden-flow.test.d.ts +2 -0
  4. package/dist/accepted-quote-version-reservation-golden-flow.test.d.ts.map +1 -0
  5. package/dist/accepted-quote-version-reservation-golden-flow.test.js +398 -0
  6. package/dist/index.d.ts +15 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +14 -0
  9. package/dist/interface.d.ts +18 -0
  10. package/dist/interface.d.ts.map +1 -0
  11. package/dist/interface.js +246 -0
  12. package/dist/interface.test.d.ts +2 -0
  13. package/dist/interface.test.d.ts.map +1 -0
  14. package/dist/interface.test.js +357 -0
  15. package/dist/markets/index.d.ts +11 -0
  16. package/dist/markets/index.d.ts.map +1 -0
  17. package/dist/markets/index.js +12 -0
  18. package/dist/markets/routes.d.ts +1182 -0
  19. package/dist/markets/routes.d.ts.map +1 -0
  20. package/dist/markets/routes.js +209 -0
  21. package/dist/markets/schema.d.ts +1527 -0
  22. package/dist/markets/schema.d.ts.map +1 -0
  23. package/dist/markets/schema.js +240 -0
  24. package/dist/markets/service-core.d.ts +253 -0
  25. package/dist/markets/service-core.d.ts.map +1 -0
  26. package/dist/markets/service-core.js +242 -0
  27. package/dist/markets/service-rules.d.ts +191 -0
  28. package/dist/markets/service-rules.d.ts.map +1 -0
  29. package/dist/markets/service-rules.js +155 -0
  30. package/dist/markets/service-shared.d.ts +36 -0
  31. package/dist/markets/service-shared.d.ts.map +1 -0
  32. package/dist/markets/service-shared.js +7 -0
  33. package/dist/markets/service.d.ts +43 -0
  34. package/dist/markets/service.d.ts.map +1 -0
  35. package/dist/markets/service.js +42 -0
  36. package/dist/markets/validation.d.ts +451 -0
  37. package/dist/markets/validation.d.ts.map +1 -0
  38. package/dist/markets/validation.js +160 -0
  39. package/dist/pricing/events.d.ts +53 -0
  40. package/dist/pricing/events.d.ts.map +1 -0
  41. package/dist/pricing/events.js +28 -0
  42. package/dist/pricing/index.d.ts +15 -0
  43. package/dist/pricing/index.d.ts.map +1 -0
  44. package/dist/pricing/index.js +18 -0
  45. package/dist/pricing/routes-core.d.ts +981 -0
  46. package/dist/pricing/routes-core.d.ts.map +1 -0
  47. package/dist/pricing/routes-core.js +102 -0
  48. package/dist/pricing/routes-public.d.ts +136 -0
  49. package/dist/pricing/routes-public.d.ts.map +1 -0
  50. package/dist/pricing/routes-public.js +14 -0
  51. package/dist/pricing/routes-rules.d.ts +1339 -0
  52. package/dist/pricing/routes-rules.d.ts.map +1 -0
  53. package/dist/pricing/routes-rules.js +138 -0
  54. package/dist/pricing/routes-shared.d.ts +14 -0
  55. package/dist/pricing/routes-shared.d.ts.map +1 -0
  56. package/dist/pricing/routes-shared.js +3 -0
  57. package/dist/pricing/routes.d.ts +7 -0
  58. package/dist/pricing/routes.d.ts.map +1 -0
  59. package/dist/pricing/routes.js +6 -0
  60. package/dist/pricing/schema-catalogs.d.ts +467 -0
  61. package/dist/pricing/schema-catalogs.d.ts.map +1 -0
  62. package/dist/pricing/schema-catalogs.js +47 -0
  63. package/dist/pricing/schema-categories.d.ts +497 -0
  64. package/dist/pricing/schema-categories.d.ts.map +1 -0
  65. package/dist/pricing/schema-categories.js +54 -0
  66. package/dist/pricing/schema-departure-overrides.d.ts +228 -0
  67. package/dist/pricing/schema-departure-overrides.d.ts.map +1 -0
  68. package/dist/pricing/schema-departure-overrides.js +36 -0
  69. package/dist/pricing/schema-option-rules.d.ts +1770 -0
  70. package/dist/pricing/schema-option-rules.d.ts.map +1 -0
  71. package/dist/pricing/schema-option-rules.js +181 -0
  72. package/dist/pricing/schema-policies.d.ts +395 -0
  73. package/dist/pricing/schema-policies.d.ts.map +1 -0
  74. package/dist/pricing/schema-policies.js +41 -0
  75. package/dist/pricing/schema-relations.d.ts +59 -0
  76. package/dist/pricing/schema-relations.d.ts.map +1 -0
  77. package/dist/pricing/schema-relations.js +111 -0
  78. package/dist/pricing/schema-shared.d.ts +11 -0
  79. package/dist/pricing/schema-shared.d.ts.map +1 -0
  80. package/dist/pricing/schema-shared.js +67 -0
  81. package/dist/pricing/schema.d.ts +8 -0
  82. package/dist/pricing/schema.d.ts.map +1 -0
  83. package/dist/pricing/schema.js +7 -0
  84. package/dist/pricing/service-catalog-plane-pricing.d.ts +95 -0
  85. package/dist/pricing/service-catalog-plane-pricing.d.ts.map +1 -0
  86. package/dist/pricing/service-catalog-plane-pricing.js +382 -0
  87. package/dist/pricing/service-catalogs.d.ts +139 -0
  88. package/dist/pricing/service-catalogs.d.ts.map +1 -0
  89. package/dist/pricing/service-catalogs.js +89 -0
  90. package/dist/pricing/service-categories.d.ts +147 -0
  91. package/dist/pricing/service-categories.d.ts.map +1 -0
  92. package/dist/pricing/service-categories.js +105 -0
  93. package/dist/pricing/service-departure-overrides.d.ts +67 -0
  94. package/dist/pricing/service-departure-overrides.d.ts.map +1 -0
  95. package/dist/pricing/service-departure-overrides.js +54 -0
  96. package/dist/pricing/service-option-rules.d.ts +321 -0
  97. package/dist/pricing/service-option-rules.d.ts.map +1 -0
  98. package/dist/pricing/service-option-rules.js +340 -0
  99. package/dist/pricing/service-policies.d.ts +123 -0
  100. package/dist/pricing/service-policies.d.ts.map +1 -0
  101. package/dist/pricing/service-policies.js +95 -0
  102. package/dist/pricing/service-public.d.ts +89 -0
  103. package/dist/pricing/service-public.d.ts.map +1 -0
  104. package/dist/pricing/service-public.js +473 -0
  105. package/dist/pricing/service-rule-resolver.d.ts +67 -0
  106. package/dist/pricing/service-rule-resolver.d.ts.map +1 -0
  107. package/dist/pricing/service-rule-resolver.js +204 -0
  108. package/dist/pricing/service-shared.d.ts +53 -0
  109. package/dist/pricing/service-shared.d.ts.map +1 -0
  110. package/dist/pricing/service-shared.js +4 -0
  111. package/dist/pricing/service-transfer-rules.d.ts +211 -0
  112. package/dist/pricing/service-transfer-rules.d.ts.map +1 -0
  113. package/dist/pricing/service-transfer-rules.js +139 -0
  114. package/dist/pricing/service.d.ts +79 -0
  115. package/dist/pricing/service.d.ts.map +1 -0
  116. package/dist/pricing/service.js +78 -0
  117. package/dist/pricing/validation-public.d.ts +412 -0
  118. package/dist/pricing/validation-public.d.ts.map +1 -0
  119. package/dist/pricing/validation-public.js +111 -0
  120. package/dist/pricing/validation-shared.d.ts +71 -0
  121. package/dist/pricing/validation-shared.d.ts.map +1 -0
  122. package/dist/pricing/validation-shared.js +63 -0
  123. package/dist/pricing/validation.d.ts +987 -0
  124. package/dist/pricing/validation.d.ts.map +1 -0
  125. package/dist/pricing/validation.js +307 -0
  126. package/dist/promotions/events.d.ts +38 -0
  127. package/dist/promotions/events.d.ts.map +1 -0
  128. package/dist/promotions/events.js +25 -0
  129. package/dist/promotions/index.d.ts +12 -0
  130. package/dist/promotions/index.d.ts.map +1 -0
  131. package/dist/promotions/index.js +17 -0
  132. package/dist/promotions/routes-shared.d.ts +14 -0
  133. package/dist/promotions/routes-shared.d.ts.map +1 -0
  134. package/dist/promotions/routes-shared.js +3 -0
  135. package/dist/promotions/routes.d.ts +395 -0
  136. package/dist/promotions/routes.d.ts.map +1 -0
  137. package/dist/promotions/routes.js +55 -0
  138. package/dist/promotions/schema.d.ts +675 -0
  139. package/dist/promotions/schema.d.ts.map +1 -0
  140. package/dist/promotions/schema.js +126 -0
  141. package/dist/promotions/service-booking-confirmed.d.ts +77 -0
  142. package/dist/promotions/service-booking-confirmed.d.ts.map +1 -0
  143. package/dist/promotions/service-booking-confirmed.js +134 -0
  144. package/dist/promotions/service-boundary-scheduler.d.ts +85 -0
  145. package/dist/promotions/service-boundary-scheduler.d.ts.map +1 -0
  146. package/dist/promotions/service-boundary-scheduler.js +141 -0
  147. package/dist/promotions/service-catalog-evaluator.d.ts +22 -0
  148. package/dist/promotions/service-catalog-evaluator.d.ts.map +1 -0
  149. package/dist/promotions/service-catalog-evaluator.js +33 -0
  150. package/dist/promotions/service-catalog-plane-promotions.d.ts +73 -0
  151. package/dist/promotions/service-catalog-plane-promotions.d.ts.map +1 -0
  152. package/dist/promotions/service-catalog-plane-promotions.js +118 -0
  153. package/dist/promotions/service-evaluator.d.ts +134 -0
  154. package/dist/promotions/service-evaluator.d.ts.map +1 -0
  155. package/dist/promotions/service-evaluator.js +302 -0
  156. package/dist/promotions/service-storefront.d.ts +147 -0
  157. package/dist/promotions/service-storefront.d.ts.map +1 -0
  158. package/dist/promotions/service-storefront.js +326 -0
  159. package/dist/promotions/service.d.ts +143 -0
  160. package/dist/promotions/service.d.ts.map +1 -0
  161. package/dist/promotions/service.js +359 -0
  162. package/dist/promotions/validation.d.ts +195 -0
  163. package/dist/promotions/validation.d.ts.map +1 -0
  164. package/dist/promotions/validation.js +167 -0
  165. package/dist/promotions/workflow-bulk-reindex.d.ts +36 -0
  166. package/dist/promotions/workflow-bulk-reindex.d.ts.map +1 -0
  167. package/dist/promotions/workflow-bulk-reindex.js +53 -0
  168. package/dist/promotions/workflow-runtime.d.ts +17 -0
  169. package/dist/promotions/workflow-runtime.d.ts.map +1 -0
  170. package/dist/promotions/workflow-runtime.js +9 -0
  171. package/dist/runtime.d.ts +18 -0
  172. package/dist/runtime.d.ts.map +1 -0
  173. package/dist/runtime.js +27 -0
  174. package/dist/runtime.test.d.ts +2 -0
  175. package/dist/runtime.test.d.ts.map +1 -0
  176. package/dist/runtime.test.js +25 -0
  177. package/dist/schema.d.ts +5 -0
  178. package/dist/schema.d.ts.map +1 -0
  179. package/dist/schema.js +4 -0
  180. package/dist/sellability/index.d.ts +13 -0
  181. package/dist/sellability/index.d.ts.map +1 -0
  182. package/dist/sellability/index.js +17 -0
  183. package/dist/sellability/routes.d.ts +2332 -0
  184. package/dist/sellability/routes.d.ts.map +1 -0
  185. package/dist/sellability/routes.js +166 -0
  186. package/dist/sellability/schema.d.ts +1716 -0
  187. package/dist/sellability/schema.d.ts.map +1 -0
  188. package/dist/sellability/schema.js +278 -0
  189. package/dist/sellability/service-records.d.ts +316 -0
  190. package/dist/sellability/service-records.d.ts.map +1 -0
  191. package/dist/sellability/service-records.js +253 -0
  192. package/dist/sellability/service-resolve.d.ts +72 -0
  193. package/dist/sellability/service-resolve.d.ts.map +1 -0
  194. package/dist/sellability/service-resolve.js +580 -0
  195. package/dist/sellability/service-shared.d.ts +124 -0
  196. package/dist/sellability/service-shared.d.ts.map +1 -0
  197. package/dist/sellability/service-shared.js +96 -0
  198. package/dist/sellability/service-snapshots.d.ts +191 -0
  199. package/dist/sellability/service-snapshots.d.ts.map +1 -0
  200. package/dist/sellability/service-snapshots.js +153 -0
  201. package/dist/sellability/service.d.ts +1038 -0
  202. package/dist/sellability/service.d.ts.map +1 -0
  203. package/dist/sellability/service.js +17 -0
  204. package/dist/sellability/validation.d.ts +477 -0
  205. package/dist/sellability/validation.d.ts.map +1 -0
  206. package/dist/sellability/validation.js +192 -0
  207. package/dist/types.d.ts +239 -0
  208. package/dist/types.d.ts.map +1 -0
  209. package/dist/types.js +1 -0
  210. package/package.json +62 -0
@@ -0,0 +1,321 @@
1
+ import type { EventBus } from "@voyant-travel/core";
2
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
3
+ import { type PricingRuleChangeSource } from "./events.js";
4
+ /**
5
+ * Optional runtime context for pricing-rule mutations. When `eventBus`
6
+ * is wired the service emits `pricing.rule.changed` after a successful
7
+ * mutation so the catalog bridge can reindex the affected product.
8
+ *
9
+ * Keeping it optional preserves back-compat with existing callers that
10
+ * don't care about the catalog plane (e.g. data-import scripts).
11
+ */
12
+ export interface RuleMutationRuntime {
13
+ eventBus?: EventBus;
14
+ source?: PricingRuleChangeSource;
15
+ }
16
+ import type { CreateOptionPriceRuleInput, CreateOptionStartTimeRuleInput, CreateOptionUnitPriceRuleInput, CreateOptionUnitTierInput, OptionPriceRuleListQuery, OptionStartTimeRuleListQuery, OptionUnitPriceRuleListQuery, OptionUnitTierListQuery, UpdateOptionPriceRuleInput, UpdateOptionStartTimeRuleInput, UpdateOptionUnitPriceRuleInput, UpdateOptionUnitTierInput } from "./service-shared.js";
17
+ export declare function listOptionPriceRules(db: PostgresJsDatabase, query: OptionPriceRuleListQuery): Promise<{
18
+ data: {
19
+ id: string;
20
+ productId: string;
21
+ optionId: string;
22
+ priceCatalogId: string;
23
+ priceScheduleId: string | null;
24
+ cancellationPolicyId: string | null;
25
+ code: string | null;
26
+ name: string;
27
+ description: string | null;
28
+ pricingMode: "on_request" | "per_person" | "per_booking" | "starting_from" | "free";
29
+ baseSellAmountCents: number | null;
30
+ baseCostAmountCents: number | null;
31
+ minPerBooking: number | null;
32
+ maxPerBooking: number | null;
33
+ allPricingCategories: boolean;
34
+ isDefault: boolean;
35
+ active: boolean;
36
+ notes: string | null;
37
+ metadata: Record<string, unknown> | null;
38
+ createdAt: Date;
39
+ updatedAt: Date;
40
+ }[];
41
+ total: number;
42
+ limit: number;
43
+ offset: number;
44
+ }>;
45
+ export declare function getOptionPriceRuleById(db: PostgresJsDatabase, id: string): Promise<{
46
+ id: string;
47
+ productId: string;
48
+ optionId: string;
49
+ priceCatalogId: string;
50
+ priceScheduleId: string | null;
51
+ cancellationPolicyId: string | null;
52
+ code: string | null;
53
+ name: string;
54
+ description: string | null;
55
+ pricingMode: "on_request" | "per_person" | "per_booking" | "starting_from" | "free";
56
+ baseSellAmountCents: number | null;
57
+ baseCostAmountCents: number | null;
58
+ minPerBooking: number | null;
59
+ maxPerBooking: number | null;
60
+ allPricingCategories: boolean;
61
+ isDefault: boolean;
62
+ active: boolean;
63
+ notes: string | null;
64
+ metadata: Record<string, unknown> | null;
65
+ createdAt: Date;
66
+ updatedAt: Date;
67
+ } | null>;
68
+ export declare function createOptionPriceRule(db: PostgresJsDatabase, data: CreateOptionPriceRuleInput, runtime?: RuleMutationRuntime): Promise<{
69
+ id: string;
70
+ code: string | null;
71
+ active: boolean;
72
+ metadata: Record<string, unknown> | null;
73
+ description: string | null;
74
+ name: string;
75
+ createdAt: Date;
76
+ updatedAt: Date;
77
+ isDefault: boolean;
78
+ notes: string | null;
79
+ priceCatalogId: string;
80
+ productId: string;
81
+ optionId: string;
82
+ priceScheduleId: string | null;
83
+ cancellationPolicyId: string | null;
84
+ pricingMode: "on_request" | "per_person" | "per_booking" | "starting_from" | "free";
85
+ baseSellAmountCents: number | null;
86
+ baseCostAmountCents: number | null;
87
+ minPerBooking: number | null;
88
+ maxPerBooking: number | null;
89
+ allPricingCategories: boolean;
90
+ } | null>;
91
+ export declare function updateOptionPriceRule(db: PostgresJsDatabase, id: string, data: UpdateOptionPriceRuleInput, runtime?: RuleMutationRuntime): Promise<{
92
+ id: string;
93
+ productId: string;
94
+ optionId: string;
95
+ priceCatalogId: string;
96
+ priceScheduleId: string | null;
97
+ cancellationPolicyId: string | null;
98
+ code: string | null;
99
+ name: string;
100
+ description: string | null;
101
+ pricingMode: "on_request" | "per_person" | "per_booking" | "starting_from" | "free";
102
+ baseSellAmountCents: number | null;
103
+ baseCostAmountCents: number | null;
104
+ minPerBooking: number | null;
105
+ maxPerBooking: number | null;
106
+ allPricingCategories: boolean;
107
+ isDefault: boolean;
108
+ active: boolean;
109
+ notes: string | null;
110
+ metadata: Record<string, unknown> | null;
111
+ createdAt: Date;
112
+ updatedAt: Date;
113
+ } | null>;
114
+ export declare function deleteOptionPriceRule(db: PostgresJsDatabase, id: string, runtime?: RuleMutationRuntime): Promise<{
115
+ id: string;
116
+ } | null>;
117
+ export declare function listOptionUnitPriceRules(db: PostgresJsDatabase, query: OptionUnitPriceRuleListQuery): Promise<{
118
+ data: {
119
+ id: string;
120
+ optionPriceRuleId: string;
121
+ optionId: string;
122
+ unitId: string;
123
+ pricingCategoryId: string | null;
124
+ pricingMode: "on_request" | "per_person" | "per_booking" | "free" | "per_unit" | "included";
125
+ sellAmountCents: number | null;
126
+ costAmountCents: number | null;
127
+ minQuantity: number | null;
128
+ maxQuantity: number | null;
129
+ active: boolean;
130
+ sortOrder: number;
131
+ notes: string | null;
132
+ metadata: Record<string, unknown> | null;
133
+ createdAt: Date;
134
+ updatedAt: Date;
135
+ }[];
136
+ total: number;
137
+ limit: number;
138
+ offset: number;
139
+ }>;
140
+ export declare function getOptionUnitPriceRuleById(db: PostgresJsDatabase, id: string): Promise<{
141
+ id: string;
142
+ optionPriceRuleId: string;
143
+ optionId: string;
144
+ unitId: string;
145
+ pricingCategoryId: string | null;
146
+ pricingMode: "on_request" | "per_person" | "per_booking" | "free" | "per_unit" | "included";
147
+ sellAmountCents: number | null;
148
+ costAmountCents: number | null;
149
+ minQuantity: number | null;
150
+ maxQuantity: number | null;
151
+ active: boolean;
152
+ sortOrder: number;
153
+ notes: string | null;
154
+ metadata: Record<string, unknown> | null;
155
+ createdAt: Date;
156
+ updatedAt: Date;
157
+ } | null>;
158
+ export declare function createOptionUnitPriceRule(db: PostgresJsDatabase, data: CreateOptionUnitPriceRuleInput, runtime?: RuleMutationRuntime): Promise<{
159
+ id: string;
160
+ active: boolean;
161
+ metadata: Record<string, unknown> | null;
162
+ createdAt: Date;
163
+ updatedAt: Date;
164
+ sortOrder: number;
165
+ notes: string | null;
166
+ optionId: string;
167
+ pricingMode: "on_request" | "per_person" | "per_booking" | "free" | "per_unit" | "included";
168
+ optionPriceRuleId: string;
169
+ unitId: string;
170
+ pricingCategoryId: string | null;
171
+ sellAmountCents: number | null;
172
+ costAmountCents: number | null;
173
+ minQuantity: number | null;
174
+ maxQuantity: number | null;
175
+ } | null>;
176
+ export declare function updateOptionUnitPriceRule(db: PostgresJsDatabase, id: string, data: UpdateOptionUnitPriceRuleInput, runtime?: RuleMutationRuntime): Promise<{
177
+ id: string;
178
+ optionPriceRuleId: string;
179
+ optionId: string;
180
+ unitId: string;
181
+ pricingCategoryId: string | null;
182
+ pricingMode: "on_request" | "per_person" | "per_booking" | "free" | "per_unit" | "included";
183
+ sellAmountCents: number | null;
184
+ costAmountCents: number | null;
185
+ minQuantity: number | null;
186
+ maxQuantity: number | null;
187
+ active: boolean;
188
+ sortOrder: number;
189
+ notes: string | null;
190
+ metadata: Record<string, unknown> | null;
191
+ createdAt: Date;
192
+ updatedAt: Date;
193
+ } | null>;
194
+ export declare function deleteOptionUnitPriceRule(db: PostgresJsDatabase, id: string, runtime?: RuleMutationRuntime): Promise<{
195
+ id: string;
196
+ } | null>;
197
+ export declare function listOptionStartTimeRules(db: PostgresJsDatabase, query: OptionStartTimeRuleListQuery): Promise<{
198
+ data: {
199
+ id: string;
200
+ optionPriceRuleId: string;
201
+ optionId: string;
202
+ startTimeId: string;
203
+ ruleMode: "adjustment" | "override" | "included" | "excluded";
204
+ adjustmentType: "fixed" | "percentage" | null;
205
+ sellAdjustmentCents: number | null;
206
+ costAdjustmentCents: number | null;
207
+ adjustmentBasisPoints: number | null;
208
+ active: boolean;
209
+ notes: string | null;
210
+ createdAt: Date;
211
+ updatedAt: Date;
212
+ }[];
213
+ total: number;
214
+ limit: number;
215
+ offset: number;
216
+ }>;
217
+ export declare function getOptionStartTimeRuleById(db: PostgresJsDatabase, id: string): Promise<{
218
+ id: string;
219
+ optionPriceRuleId: string;
220
+ optionId: string;
221
+ startTimeId: string;
222
+ ruleMode: "adjustment" | "override" | "included" | "excluded";
223
+ adjustmentType: "fixed" | "percentage" | null;
224
+ sellAdjustmentCents: number | null;
225
+ costAdjustmentCents: number | null;
226
+ adjustmentBasisPoints: number | null;
227
+ active: boolean;
228
+ notes: string | null;
229
+ createdAt: Date;
230
+ updatedAt: Date;
231
+ } | null>;
232
+ export declare function createOptionStartTimeRule(db: PostgresJsDatabase, data: CreateOptionStartTimeRuleInput): Promise<{
233
+ id: string;
234
+ active: boolean;
235
+ createdAt: Date;
236
+ updatedAt: Date;
237
+ notes: string | null;
238
+ optionId: string;
239
+ optionPriceRuleId: string;
240
+ startTimeId: string;
241
+ ruleMode: "adjustment" | "override" | "included" | "excluded";
242
+ adjustmentType: "fixed" | "percentage" | null;
243
+ sellAdjustmentCents: number | null;
244
+ adjustmentBasisPoints: number | null;
245
+ costAdjustmentCents: number | null;
246
+ } | null>;
247
+ export declare function updateOptionStartTimeRule(db: PostgresJsDatabase, id: string, data: UpdateOptionStartTimeRuleInput): Promise<{
248
+ id: string;
249
+ optionPriceRuleId: string;
250
+ optionId: string;
251
+ startTimeId: string;
252
+ ruleMode: "adjustment" | "override" | "included" | "excluded";
253
+ adjustmentType: "fixed" | "percentage" | null;
254
+ sellAdjustmentCents: number | null;
255
+ costAdjustmentCents: number | null;
256
+ adjustmentBasisPoints: number | null;
257
+ active: boolean;
258
+ notes: string | null;
259
+ createdAt: Date;
260
+ updatedAt: Date;
261
+ } | null>;
262
+ export declare function deleteOptionStartTimeRule(db: PostgresJsDatabase, id: string): Promise<{
263
+ id: string;
264
+ } | null>;
265
+ export declare function listOptionUnitTiers(db: PostgresJsDatabase, query: OptionUnitTierListQuery): Promise<{
266
+ data: {
267
+ id: string;
268
+ optionUnitPriceRuleId: string;
269
+ minQuantity: number;
270
+ maxQuantity: number | null;
271
+ sellAmountCents: number | null;
272
+ costAmountCents: number | null;
273
+ active: boolean;
274
+ sortOrder: number;
275
+ createdAt: Date;
276
+ updatedAt: Date;
277
+ }[];
278
+ total: number;
279
+ limit: number;
280
+ offset: number;
281
+ }>;
282
+ export declare function getOptionUnitTierById(db: PostgresJsDatabase, id: string): Promise<{
283
+ id: string;
284
+ optionUnitPriceRuleId: string;
285
+ minQuantity: number;
286
+ maxQuantity: number | null;
287
+ sellAmountCents: number | null;
288
+ costAmountCents: number | null;
289
+ active: boolean;
290
+ sortOrder: number;
291
+ createdAt: Date;
292
+ updatedAt: Date;
293
+ } | null>;
294
+ export declare function createOptionUnitTier(db: PostgresJsDatabase, data: CreateOptionUnitTierInput): Promise<{
295
+ id: string;
296
+ active: boolean;
297
+ createdAt: Date;
298
+ updatedAt: Date;
299
+ sortOrder: number;
300
+ sellAmountCents: number | null;
301
+ costAmountCents: number | null;
302
+ minQuantity: number;
303
+ maxQuantity: number | null;
304
+ optionUnitPriceRuleId: string;
305
+ } | null>;
306
+ export declare function updateOptionUnitTier(db: PostgresJsDatabase, id: string, data: UpdateOptionUnitTierInput): Promise<{
307
+ id: string;
308
+ optionUnitPriceRuleId: string;
309
+ minQuantity: number;
310
+ maxQuantity: number | null;
311
+ sellAmountCents: number | null;
312
+ costAmountCents: number | null;
313
+ active: boolean;
314
+ sortOrder: number;
315
+ createdAt: Date;
316
+ updatedAt: Date;
317
+ } | null>;
318
+ export declare function deleteOptionUnitTier(db: PostgresJsDatabase, id: string): Promise<{
319
+ id: string;
320
+ } | null>;
321
+ //# sourceMappingURL=service-option-rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-option-rules.d.ts","sourceRoot":"","sources":["../../src/pricing/service-option-rules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,340 @@
1
+ import { RequestValidationError } from "@voyant-travel/hono";
2
+ import { and, asc, desc, eq, sql } from "drizzle-orm";
3
+ import { PRICING_RULE_CHANGED_EVENT, } from "./events.js";
4
+ import { optionPriceRules, optionStartTimeRules, optionUnitPriceRules, optionUnitTiers, } from "./schema.js";
5
+ async function emitRuleChanged(eventBus, payload) {
6
+ if (!eventBus)
7
+ return;
8
+ await eventBus.emit(PRICING_RULE_CHANGED_EVENT, payload, {
9
+ category: "domain",
10
+ source: "service",
11
+ });
12
+ }
13
+ // A `per_booking` rule produces a single flat amount for the whole booking;
14
+ // per-unit prices implicitly assume a per-unit (or per-person) multiplier.
15
+ // The two are contradictory — see #482.
16
+ const PER_BOOKING_REJECTS_UNIT_PRICES_MESSAGE = "Rules with pricingMode = 'per_booking' cannot carry per-unit prices. " +
17
+ "Use pricingMode = 'per_person' or 'starting_from' for unit-priced rules, " +
18
+ "or remove the unit prices to keep this rule a flat per-booking amount.";
19
+ import { paginate } from "./service-shared.js";
20
+ export async function listOptionPriceRules(db, query) {
21
+ const conditions = [];
22
+ if (query.productId)
23
+ conditions.push(eq(optionPriceRules.productId, query.productId));
24
+ if (query.optionId)
25
+ conditions.push(eq(optionPriceRules.optionId, query.optionId));
26
+ if (query.priceCatalogId)
27
+ conditions.push(eq(optionPriceRules.priceCatalogId, query.priceCatalogId));
28
+ if (query.priceScheduleId)
29
+ conditions.push(eq(optionPriceRules.priceScheduleId, query.priceScheduleId));
30
+ if (query.cancellationPolicyId) {
31
+ conditions.push(eq(optionPriceRules.cancellationPolicyId, query.cancellationPolicyId));
32
+ }
33
+ if (query.pricingMode)
34
+ conditions.push(eq(optionPriceRules.pricingMode, query.pricingMode));
35
+ if (query.active !== undefined)
36
+ conditions.push(eq(optionPriceRules.active, query.active));
37
+ const where = conditions.length ? and(...conditions) : undefined;
38
+ return paginate(db
39
+ .select()
40
+ .from(optionPriceRules)
41
+ .where(where)
42
+ .limit(query.limit)
43
+ .offset(query.offset)
44
+ .orderBy(desc(optionPriceRules.updatedAt)), db.select({ count: sql `count(*)::int` }).from(optionPriceRules).where(where), query.limit, query.offset);
45
+ }
46
+ export async function getOptionPriceRuleById(db, id) {
47
+ const [row] = await db.select().from(optionPriceRules).where(eq(optionPriceRules.id, id)).limit(1);
48
+ return row ?? null;
49
+ }
50
+ export async function createOptionPriceRule(db, data, runtime = {}) {
51
+ const [row] = await db.insert(optionPriceRules).values(data).returning();
52
+ if (!row)
53
+ return null;
54
+ await emitRuleChanged(runtime.eventBus, {
55
+ productId: row.productId,
56
+ ruleId: row.id,
57
+ kind: "option-rule",
58
+ source: runtime.source ?? "created",
59
+ });
60
+ return row;
61
+ }
62
+ export async function updateOptionPriceRule(db, id, data, runtime = {}) {
63
+ if (data.pricingMode === "per_booking") {
64
+ const [countRow] = await db
65
+ .select({ count: sql `count(*)::int` })
66
+ .from(optionUnitPriceRules)
67
+ .where(eq(optionUnitPriceRules.optionPriceRuleId, id));
68
+ const unitPriceCount = countRow?.count ?? 0;
69
+ if (unitPriceCount > 0) {
70
+ throw new RequestValidationError(PER_BOOKING_REJECTS_UNIT_PRICES_MESSAGE, {
71
+ ruleId: id,
72
+ unitPriceCount,
73
+ });
74
+ }
75
+ }
76
+ // Snapshot the pre-update productId so reassignment (rule moves
77
+ // between products) reindexes the *previous* product too. Without
78
+ // this, the projection on the old product keeps a stale MIN that
79
+ // includes a rule it no longer owns.
80
+ const [pre] = await db
81
+ .select({ productId: optionPriceRules.productId })
82
+ .from(optionPriceRules)
83
+ .where(eq(optionPriceRules.id, id))
84
+ .limit(1);
85
+ const [row] = await db
86
+ .update(optionPriceRules)
87
+ .set({ ...data, updatedAt: new Date() })
88
+ .where(eq(optionPriceRules.id, id))
89
+ .returning();
90
+ if (!row)
91
+ return null;
92
+ await emitRuleChanged(runtime.eventBus, {
93
+ productId: row.productId,
94
+ ruleId: row.id,
95
+ kind: "option-rule",
96
+ source: runtime.source ?? "updated",
97
+ });
98
+ if (pre && pre.productId !== row.productId) {
99
+ await emitRuleChanged(runtime.eventBus, {
100
+ productId: pre.productId,
101
+ ruleId: row.id,
102
+ kind: "option-rule",
103
+ source: runtime.source ?? "updated",
104
+ });
105
+ }
106
+ return row;
107
+ }
108
+ export async function deleteOptionPriceRule(db, id, runtime = {}) {
109
+ // Snapshot before deletion so the event payload carries productId —
110
+ // can't read it back from the deleted row.
111
+ const [snapshot] = await db
112
+ .select({ productId: optionPriceRules.productId })
113
+ .from(optionPriceRules)
114
+ .where(eq(optionPriceRules.id, id))
115
+ .limit(1);
116
+ const [row] = await db
117
+ .delete(optionPriceRules)
118
+ .where(eq(optionPriceRules.id, id))
119
+ .returning({ id: optionPriceRules.id });
120
+ if (!row)
121
+ return null;
122
+ if (snapshot) {
123
+ await emitRuleChanged(runtime.eventBus, {
124
+ productId: snapshot.productId,
125
+ ruleId: row.id,
126
+ kind: "option-rule",
127
+ source: runtime.source ?? "deleted",
128
+ });
129
+ }
130
+ return row;
131
+ }
132
+ export async function listOptionUnitPriceRules(db, query) {
133
+ const conditions = [];
134
+ if (query.optionPriceRuleId) {
135
+ conditions.push(eq(optionUnitPriceRules.optionPriceRuleId, query.optionPriceRuleId));
136
+ }
137
+ if (query.optionId)
138
+ conditions.push(eq(optionUnitPriceRules.optionId, query.optionId));
139
+ if (query.unitId)
140
+ conditions.push(eq(optionUnitPriceRules.unitId, query.unitId));
141
+ if (query.pricingCategoryId) {
142
+ conditions.push(eq(optionUnitPriceRules.pricingCategoryId, query.pricingCategoryId));
143
+ }
144
+ if (query.active !== undefined)
145
+ conditions.push(eq(optionUnitPriceRules.active, query.active));
146
+ const where = conditions.length ? and(...conditions) : undefined;
147
+ return paginate(db
148
+ .select()
149
+ .from(optionUnitPriceRules)
150
+ .where(where)
151
+ .limit(query.limit)
152
+ .offset(query.offset)
153
+ .orderBy(asc(optionUnitPriceRules.sortOrder), asc(optionUnitPriceRules.createdAt)), db.select({ count: sql `count(*)::int` }).from(optionUnitPriceRules).where(where), query.limit, query.offset);
154
+ }
155
+ export async function getOptionUnitPriceRuleById(db, id) {
156
+ const [row] = await db
157
+ .select()
158
+ .from(optionUnitPriceRules)
159
+ .where(eq(optionUnitPriceRules.id, id))
160
+ .limit(1);
161
+ return row ?? null;
162
+ }
163
+ /**
164
+ * Look up the productId on an option-unit-rule's parent rule. Used by
165
+ * the mutation paths to populate the `pricing.rule.changed` payload —
166
+ * unit rules don't carry productId directly, so we walk through their
167
+ * parent every time. One small extra query per mutation; pricing
168
+ * mutations aren't on a hot path so the cost is negligible.
169
+ */
170
+ async function getProductIdForUnitRule(db, unitRuleId) {
171
+ const [row] = await db
172
+ .select({ productId: optionPriceRules.productId })
173
+ .from(optionUnitPriceRules)
174
+ .innerJoin(optionPriceRules, eq(optionPriceRules.id, optionUnitPriceRules.optionPriceRuleId))
175
+ .where(eq(optionUnitPriceRules.id, unitRuleId))
176
+ .limit(1);
177
+ return row?.productId ?? null;
178
+ }
179
+ export async function createOptionUnitPriceRule(db, data, runtime = {}) {
180
+ const [parent] = await db
181
+ .select({ pricingMode: optionPriceRules.pricingMode, productId: optionPriceRules.productId })
182
+ .from(optionPriceRules)
183
+ .where(eq(optionPriceRules.id, data.optionPriceRuleId))
184
+ .limit(1);
185
+ if (parent?.pricingMode === "per_booking") {
186
+ throw new RequestValidationError(PER_BOOKING_REJECTS_UNIT_PRICES_MESSAGE, {
187
+ ruleId: data.optionPriceRuleId,
188
+ });
189
+ }
190
+ const [row] = await db.insert(optionUnitPriceRules).values(data).returning();
191
+ if (!row)
192
+ return null;
193
+ if (parent) {
194
+ await emitRuleChanged(runtime.eventBus, {
195
+ productId: parent.productId,
196
+ ruleId: row.id,
197
+ kind: "option-unit-rule",
198
+ source: runtime.source ?? "created",
199
+ });
200
+ }
201
+ return row;
202
+ }
203
+ export async function updateOptionUnitPriceRule(db, id, data, runtime = {}) {
204
+ // Snapshot the pre-update parent's productId. If the update reassigns
205
+ // `optionPriceRuleId` to a parent rule under a different product, the
206
+ // *previous* product also loses this unit-rule from its MIN
207
+ // candidate set and needs reindexing. Without this snapshot the old
208
+ // product's `priceFromAmountCents` stays stale.
209
+ const prevProductId = await getProductIdForUnitRule(db, id);
210
+ const [row] = await db
211
+ .update(optionUnitPriceRules)
212
+ .set({ ...data, updatedAt: new Date() })
213
+ .where(eq(optionUnitPriceRules.id, id))
214
+ .returning();
215
+ if (!row)
216
+ return null;
217
+ const nextProductId = await getProductIdForUnitRule(db, row.id);
218
+ if (nextProductId) {
219
+ await emitRuleChanged(runtime.eventBus, {
220
+ productId: nextProductId,
221
+ ruleId: row.id,
222
+ kind: "option-unit-rule",
223
+ source: runtime.source ?? "updated",
224
+ });
225
+ }
226
+ if (prevProductId && prevProductId !== nextProductId) {
227
+ await emitRuleChanged(runtime.eventBus, {
228
+ productId: prevProductId,
229
+ ruleId: row.id,
230
+ kind: "option-unit-rule",
231
+ source: runtime.source ?? "updated",
232
+ });
233
+ }
234
+ return row;
235
+ }
236
+ export async function deleteOptionUnitPriceRule(db, id, runtime = {}) {
237
+ // Snapshot productId before deletion — the row is gone after.
238
+ const productId = await getProductIdForUnitRule(db, id);
239
+ const [row] = await db
240
+ .delete(optionUnitPriceRules)
241
+ .where(eq(optionUnitPriceRules.id, id))
242
+ .returning({ id: optionUnitPriceRules.id });
243
+ if (!row)
244
+ return null;
245
+ if (productId) {
246
+ await emitRuleChanged(runtime.eventBus, {
247
+ productId,
248
+ ruleId: row.id,
249
+ kind: "option-unit-rule",
250
+ source: runtime.source ?? "deleted",
251
+ });
252
+ }
253
+ return row;
254
+ }
255
+ export async function listOptionStartTimeRules(db, query) {
256
+ const conditions = [];
257
+ if (query.optionPriceRuleId) {
258
+ conditions.push(eq(optionStartTimeRules.optionPriceRuleId, query.optionPriceRuleId));
259
+ }
260
+ if (query.optionId)
261
+ conditions.push(eq(optionStartTimeRules.optionId, query.optionId));
262
+ if (query.startTimeId)
263
+ conditions.push(eq(optionStartTimeRules.startTimeId, query.startTimeId));
264
+ if (query.active !== undefined)
265
+ conditions.push(eq(optionStartTimeRules.active, query.active));
266
+ const where = conditions.length ? and(...conditions) : undefined;
267
+ return paginate(db
268
+ .select()
269
+ .from(optionStartTimeRules)
270
+ .where(where)
271
+ .limit(query.limit)
272
+ .offset(query.offset)
273
+ .orderBy(asc(optionStartTimeRules.createdAt)), db.select({ count: sql `count(*)::int` }).from(optionStartTimeRules).where(where), query.limit, query.offset);
274
+ }
275
+ export async function getOptionStartTimeRuleById(db, id) {
276
+ const [row] = await db
277
+ .select()
278
+ .from(optionStartTimeRules)
279
+ .where(eq(optionStartTimeRules.id, id))
280
+ .limit(1);
281
+ return row ?? null;
282
+ }
283
+ export async function createOptionStartTimeRule(db, data) {
284
+ const [row] = await db.insert(optionStartTimeRules).values(data).returning();
285
+ return row ?? null;
286
+ }
287
+ export async function updateOptionStartTimeRule(db, id, data) {
288
+ const [row] = await db
289
+ .update(optionStartTimeRules)
290
+ .set({ ...data, updatedAt: new Date() })
291
+ .where(eq(optionStartTimeRules.id, id))
292
+ .returning();
293
+ return row ?? null;
294
+ }
295
+ export async function deleteOptionStartTimeRule(db, id) {
296
+ const [row] = await db
297
+ .delete(optionStartTimeRules)
298
+ .where(eq(optionStartTimeRules.id, id))
299
+ .returning({ id: optionStartTimeRules.id });
300
+ return row ?? null;
301
+ }
302
+ export async function listOptionUnitTiers(db, query) {
303
+ const conditions = [];
304
+ if (query.optionUnitPriceRuleId) {
305
+ conditions.push(eq(optionUnitTiers.optionUnitPriceRuleId, query.optionUnitPriceRuleId));
306
+ }
307
+ if (query.active !== undefined)
308
+ conditions.push(eq(optionUnitTiers.active, query.active));
309
+ const where = conditions.length ? and(...conditions) : undefined;
310
+ return paginate(db
311
+ .select()
312
+ .from(optionUnitTiers)
313
+ .where(where)
314
+ .limit(query.limit)
315
+ .offset(query.offset)
316
+ .orderBy(asc(optionUnitTiers.sortOrder), asc(optionUnitTiers.minQuantity)), db.select({ count: sql `count(*)::int` }).from(optionUnitTiers).where(where), query.limit, query.offset);
317
+ }
318
+ export async function getOptionUnitTierById(db, id) {
319
+ const [row] = await db.select().from(optionUnitTiers).where(eq(optionUnitTiers.id, id)).limit(1);
320
+ return row ?? null;
321
+ }
322
+ export async function createOptionUnitTier(db, data) {
323
+ const [row] = await db.insert(optionUnitTiers).values(data).returning();
324
+ return row ?? null;
325
+ }
326
+ export async function updateOptionUnitTier(db, id, data) {
327
+ const [row] = await db
328
+ .update(optionUnitTiers)
329
+ .set({ ...data, updatedAt: new Date() })
330
+ .where(eq(optionUnitTiers.id, id))
331
+ .returning();
332
+ return row ?? null;
333
+ }
334
+ export async function deleteOptionUnitTier(db, id) {
335
+ const [row] = await db
336
+ .delete(optionUnitTiers)
337
+ .where(eq(optionUnitTiers.id, id))
338
+ .returning({ id: optionUnitTiers.id });
339
+ return row ?? null;
340
+ }