@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.
- package/LICENSE +201 -0
- package/README.md +145 -0
- package/dist/accepted-quote-version-reservation-golden-flow.test.d.ts +2 -0
- package/dist/accepted-quote-version-reservation-golden-flow.test.d.ts.map +1 -0
- package/dist/accepted-quote-version-reservation-golden-flow.test.js +398 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/interface.d.ts +18 -0
- package/dist/interface.d.ts.map +1 -0
- package/dist/interface.js +246 -0
- package/dist/interface.test.d.ts +2 -0
- package/dist/interface.test.d.ts.map +1 -0
- package/dist/interface.test.js +357 -0
- package/dist/markets/index.d.ts +11 -0
- package/dist/markets/index.d.ts.map +1 -0
- package/dist/markets/index.js +12 -0
- package/dist/markets/routes.d.ts +1182 -0
- package/dist/markets/routes.d.ts.map +1 -0
- package/dist/markets/routes.js +209 -0
- package/dist/markets/schema.d.ts +1527 -0
- package/dist/markets/schema.d.ts.map +1 -0
- package/dist/markets/schema.js +240 -0
- package/dist/markets/service-core.d.ts +253 -0
- package/dist/markets/service-core.d.ts.map +1 -0
- package/dist/markets/service-core.js +242 -0
- package/dist/markets/service-rules.d.ts +191 -0
- package/dist/markets/service-rules.d.ts.map +1 -0
- package/dist/markets/service-rules.js +155 -0
- package/dist/markets/service-shared.d.ts +36 -0
- package/dist/markets/service-shared.d.ts.map +1 -0
- package/dist/markets/service-shared.js +7 -0
- package/dist/markets/service.d.ts +43 -0
- package/dist/markets/service.d.ts.map +1 -0
- package/dist/markets/service.js +42 -0
- package/dist/markets/validation.d.ts +451 -0
- package/dist/markets/validation.d.ts.map +1 -0
- package/dist/markets/validation.js +160 -0
- package/dist/pricing/events.d.ts +53 -0
- package/dist/pricing/events.d.ts.map +1 -0
- package/dist/pricing/events.js +28 -0
- package/dist/pricing/index.d.ts +15 -0
- package/dist/pricing/index.d.ts.map +1 -0
- package/dist/pricing/index.js +18 -0
- package/dist/pricing/routes-core.d.ts +981 -0
- package/dist/pricing/routes-core.d.ts.map +1 -0
- package/dist/pricing/routes-core.js +102 -0
- package/dist/pricing/routes-public.d.ts +136 -0
- package/dist/pricing/routes-public.d.ts.map +1 -0
- package/dist/pricing/routes-public.js +14 -0
- package/dist/pricing/routes-rules.d.ts +1339 -0
- package/dist/pricing/routes-rules.d.ts.map +1 -0
- package/dist/pricing/routes-rules.js +138 -0
- package/dist/pricing/routes-shared.d.ts +14 -0
- package/dist/pricing/routes-shared.d.ts.map +1 -0
- package/dist/pricing/routes-shared.js +3 -0
- package/dist/pricing/routes.d.ts +7 -0
- package/dist/pricing/routes.d.ts.map +1 -0
- package/dist/pricing/routes.js +6 -0
- package/dist/pricing/schema-catalogs.d.ts +467 -0
- package/dist/pricing/schema-catalogs.d.ts.map +1 -0
- package/dist/pricing/schema-catalogs.js +47 -0
- package/dist/pricing/schema-categories.d.ts +497 -0
- package/dist/pricing/schema-categories.d.ts.map +1 -0
- package/dist/pricing/schema-categories.js +54 -0
- package/dist/pricing/schema-departure-overrides.d.ts +228 -0
- package/dist/pricing/schema-departure-overrides.d.ts.map +1 -0
- package/dist/pricing/schema-departure-overrides.js +36 -0
- package/dist/pricing/schema-option-rules.d.ts +1770 -0
- package/dist/pricing/schema-option-rules.d.ts.map +1 -0
- package/dist/pricing/schema-option-rules.js +181 -0
- package/dist/pricing/schema-policies.d.ts +395 -0
- package/dist/pricing/schema-policies.d.ts.map +1 -0
- package/dist/pricing/schema-policies.js +41 -0
- package/dist/pricing/schema-relations.d.ts +59 -0
- package/dist/pricing/schema-relations.d.ts.map +1 -0
- package/dist/pricing/schema-relations.js +111 -0
- package/dist/pricing/schema-shared.d.ts +11 -0
- package/dist/pricing/schema-shared.d.ts.map +1 -0
- package/dist/pricing/schema-shared.js +67 -0
- package/dist/pricing/schema.d.ts +8 -0
- package/dist/pricing/schema.d.ts.map +1 -0
- package/dist/pricing/schema.js +7 -0
- package/dist/pricing/service-catalog-plane-pricing.d.ts +95 -0
- package/dist/pricing/service-catalog-plane-pricing.d.ts.map +1 -0
- package/dist/pricing/service-catalog-plane-pricing.js +382 -0
- package/dist/pricing/service-catalogs.d.ts +139 -0
- package/dist/pricing/service-catalogs.d.ts.map +1 -0
- package/dist/pricing/service-catalogs.js +89 -0
- package/dist/pricing/service-categories.d.ts +147 -0
- package/dist/pricing/service-categories.d.ts.map +1 -0
- package/dist/pricing/service-categories.js +105 -0
- package/dist/pricing/service-departure-overrides.d.ts +67 -0
- package/dist/pricing/service-departure-overrides.d.ts.map +1 -0
- package/dist/pricing/service-departure-overrides.js +54 -0
- package/dist/pricing/service-option-rules.d.ts +321 -0
- package/dist/pricing/service-option-rules.d.ts.map +1 -0
- package/dist/pricing/service-option-rules.js +340 -0
- package/dist/pricing/service-policies.d.ts +123 -0
- package/dist/pricing/service-policies.d.ts.map +1 -0
- package/dist/pricing/service-policies.js +95 -0
- package/dist/pricing/service-public.d.ts +89 -0
- package/dist/pricing/service-public.d.ts.map +1 -0
- package/dist/pricing/service-public.js +473 -0
- package/dist/pricing/service-rule-resolver.d.ts +67 -0
- package/dist/pricing/service-rule-resolver.d.ts.map +1 -0
- package/dist/pricing/service-rule-resolver.js +204 -0
- package/dist/pricing/service-shared.d.ts +53 -0
- package/dist/pricing/service-shared.d.ts.map +1 -0
- package/dist/pricing/service-shared.js +4 -0
- package/dist/pricing/service-transfer-rules.d.ts +211 -0
- package/dist/pricing/service-transfer-rules.d.ts.map +1 -0
- package/dist/pricing/service-transfer-rules.js +139 -0
- package/dist/pricing/service.d.ts +79 -0
- package/dist/pricing/service.d.ts.map +1 -0
- package/dist/pricing/service.js +78 -0
- package/dist/pricing/validation-public.d.ts +412 -0
- package/dist/pricing/validation-public.d.ts.map +1 -0
- package/dist/pricing/validation-public.js +111 -0
- package/dist/pricing/validation-shared.d.ts +71 -0
- package/dist/pricing/validation-shared.d.ts.map +1 -0
- package/dist/pricing/validation-shared.js +63 -0
- package/dist/pricing/validation.d.ts +987 -0
- package/dist/pricing/validation.d.ts.map +1 -0
- package/dist/pricing/validation.js +307 -0
- package/dist/promotions/events.d.ts +38 -0
- package/dist/promotions/events.d.ts.map +1 -0
- package/dist/promotions/events.js +25 -0
- package/dist/promotions/index.d.ts +12 -0
- package/dist/promotions/index.d.ts.map +1 -0
- package/dist/promotions/index.js +17 -0
- package/dist/promotions/routes-shared.d.ts +14 -0
- package/dist/promotions/routes-shared.d.ts.map +1 -0
- package/dist/promotions/routes-shared.js +3 -0
- package/dist/promotions/routes.d.ts +395 -0
- package/dist/promotions/routes.d.ts.map +1 -0
- package/dist/promotions/routes.js +55 -0
- package/dist/promotions/schema.d.ts +675 -0
- package/dist/promotions/schema.d.ts.map +1 -0
- package/dist/promotions/schema.js +126 -0
- package/dist/promotions/service-booking-confirmed.d.ts +77 -0
- package/dist/promotions/service-booking-confirmed.d.ts.map +1 -0
- package/dist/promotions/service-booking-confirmed.js +134 -0
- package/dist/promotions/service-boundary-scheduler.d.ts +85 -0
- package/dist/promotions/service-boundary-scheduler.d.ts.map +1 -0
- package/dist/promotions/service-boundary-scheduler.js +141 -0
- package/dist/promotions/service-catalog-evaluator.d.ts +22 -0
- package/dist/promotions/service-catalog-evaluator.d.ts.map +1 -0
- package/dist/promotions/service-catalog-evaluator.js +33 -0
- package/dist/promotions/service-catalog-plane-promotions.d.ts +73 -0
- package/dist/promotions/service-catalog-plane-promotions.d.ts.map +1 -0
- package/dist/promotions/service-catalog-plane-promotions.js +118 -0
- package/dist/promotions/service-evaluator.d.ts +134 -0
- package/dist/promotions/service-evaluator.d.ts.map +1 -0
- package/dist/promotions/service-evaluator.js +302 -0
- package/dist/promotions/service-storefront.d.ts +147 -0
- package/dist/promotions/service-storefront.d.ts.map +1 -0
- package/dist/promotions/service-storefront.js +326 -0
- package/dist/promotions/service.d.ts +143 -0
- package/dist/promotions/service.d.ts.map +1 -0
- package/dist/promotions/service.js +359 -0
- package/dist/promotions/validation.d.ts +195 -0
- package/dist/promotions/validation.d.ts.map +1 -0
- package/dist/promotions/validation.js +167 -0
- package/dist/promotions/workflow-bulk-reindex.d.ts +36 -0
- package/dist/promotions/workflow-bulk-reindex.d.ts.map +1 -0
- package/dist/promotions/workflow-bulk-reindex.js +53 -0
- package/dist/promotions/workflow-runtime.d.ts +17 -0
- package/dist/promotions/workflow-runtime.d.ts.map +1 -0
- package/dist/promotions/workflow-runtime.js +9 -0
- package/dist/runtime.d.ts +18 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +27 -0
- package/dist/runtime.test.d.ts +2 -0
- package/dist/runtime.test.d.ts.map +1 -0
- package/dist/runtime.test.js +25 -0
- package/dist/schema.d.ts +5 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +4 -0
- package/dist/sellability/index.d.ts +13 -0
- package/dist/sellability/index.d.ts.map +1 -0
- package/dist/sellability/index.js +17 -0
- package/dist/sellability/routes.d.ts +2332 -0
- package/dist/sellability/routes.d.ts.map +1 -0
- package/dist/sellability/routes.js +166 -0
- package/dist/sellability/schema.d.ts +1716 -0
- package/dist/sellability/schema.d.ts.map +1 -0
- package/dist/sellability/schema.js +278 -0
- package/dist/sellability/service-records.d.ts +316 -0
- package/dist/sellability/service-records.d.ts.map +1 -0
- package/dist/sellability/service-records.js +253 -0
- package/dist/sellability/service-resolve.d.ts +72 -0
- package/dist/sellability/service-resolve.d.ts.map +1 -0
- package/dist/sellability/service-resolve.js +580 -0
- package/dist/sellability/service-shared.d.ts +124 -0
- package/dist/sellability/service-shared.d.ts.map +1 -0
- package/dist/sellability/service-shared.js +96 -0
- package/dist/sellability/service-snapshots.d.ts +191 -0
- package/dist/sellability/service-snapshots.d.ts.map +1 -0
- package/dist/sellability/service-snapshots.js +153 -0
- package/dist/sellability/service.d.ts +1038 -0
- package/dist/sellability/service.d.ts.map +1 -0
- package/dist/sellability/service.js +17 -0
- package/dist/sellability/validation.d.ts +477 -0
- package/dist/sellability/validation.d.ts.map +1 -0
- package/dist/sellability/validation.js +192 -0
- package/dist/types.d.ts +239 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- 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
|
+
}
|