@voyant-travel/operator-settings 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/dist/hono-module.d.ts +11 -0
- package/dist/hono-module.d.ts.map +1 -0
- package/dist/hono-module.js +26 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/routes.d.ts +9 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +79 -0
- package/dist/schema.d.ts +984 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +124 -0
- package/dist/service.d.ts +187 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +265 -0
- package/dist/service.test.d.ts +2 -0
- package/dist/service.test.d.ts.map +1 -0
- package/dist/service.test.js +71 -0
- package/package.json +80 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmD3B,CAAA;AAEF,MAAM,MAAM,gBAAgB,GAAG,OAAO,gBAAgB,CAAC,YAAY,CAAA;AACnE,MAAM,MAAM,mBAAmB,GAAG,OAAO,gBAAgB,CAAC,YAAY,CAAA;AAEtE;;;;GAIG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgB1B,CAAA;AAEF,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAC,YAAY,CAAA;AACjE,MAAM,MAAM,kBAAkB,GAAG,OAAO,eAAe,CAAC,YAAY,CAAA;AAEpE;;;GAGG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQtC,CAAA;AAEF,MAAM,MAAM,2BAA2B,GAAG,OAAO,2BAA2B,CAAC,YAAY,CAAA;AACzF,MAAM,MAAM,8BAA8B,GAAG,OAAO,2BAA2B,CAAC,YAAY,CAAA;AAE5F;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAOlC,CAAA;AAEF,MAAM,MAAM,uBAAuB,GAAG,OAAO,uBAAuB,CAAC,YAAY,CAAA;AACjF,MAAM,MAAM,0BAA0B,GAAG,OAAO,uBAAuB,CAAC,YAAY,CAAA;AAEpF;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAM7B,CAAA;AAEF,MAAM,MAAM,kBAAkB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAA;AACvE,MAAM,MAAM,qBAAqB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAA"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@voyant-travel/operator-settings` drizzle schema — the operator-tenant
|
|
3
|
+
* identity + payment + booking-tax configuration tables.
|
|
4
|
+
*
|
|
5
|
+
* Generic to any deployment (every operator has a profile, payment
|
|
6
|
+
* instructions/defaults, and tax settings); the rows are per-deployment data.
|
|
7
|
+
* A deployment lists this package in `voyant.config` `additionalSchemas` so its
|
|
8
|
+
* tables fold into the combined migration history. Table names + TypeID
|
|
9
|
+
* prefixes are unchanged from the prior starter-local schema (migration parity).
|
|
10
|
+
*/
|
|
11
|
+
import { typeId } from "@voyant-travel/db/lib/typeid-column";
|
|
12
|
+
import { jsonb, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
|
13
|
+
/**
|
|
14
|
+
* Original catch-all table from the first booking-journey settings
|
|
15
|
+
* pass. Runtime code reads/writes the narrower tables below:
|
|
16
|
+
* `operator_profile`, `operator_payment_instructions`,
|
|
17
|
+
* `operator_payment_defaults`, and `booking_tax_settings`.
|
|
18
|
+
*/
|
|
19
|
+
export const operatorSettings = pgTable("operator_settings", {
|
|
20
|
+
id: typeId("operator_settings"),
|
|
21
|
+
// Identity
|
|
22
|
+
name: text("name"),
|
|
23
|
+
legalName: text("legal_name"),
|
|
24
|
+
vatId: text("vat_id"),
|
|
25
|
+
registrationNumber: text("registration_number"),
|
|
26
|
+
// Contact
|
|
27
|
+
address: text("address"),
|
|
28
|
+
phone: text("phone"),
|
|
29
|
+
email: text("email"),
|
|
30
|
+
website: text("website"),
|
|
31
|
+
// Banking
|
|
32
|
+
iban: text("iban"),
|
|
33
|
+
bank: text("bank"),
|
|
34
|
+
// Travel licensing — generic enough to cover tourism authority
|
|
35
|
+
// numbers (ANPC, ABTA, IATA), hotel star-rating registries, and
|
|
36
|
+
// cruise flag-state numbers.
|
|
37
|
+
license: text("license"),
|
|
38
|
+
licenseAuthority: text("license_authority"),
|
|
39
|
+
// Signing officer for contracts (the human whose name appears on
|
|
40
|
+
// the operator-side signature line of a staff-issued contract).
|
|
41
|
+
signatoryName: text("signatory_name"),
|
|
42
|
+
signatoryRole: text("signatory_role"),
|
|
43
|
+
/**
|
|
44
|
+
* Default customer-facing payment policy applied when no per-supplier
|
|
45
|
+
* / per-category / per-listing / per-booking override exists.
|
|
46
|
+
*
|
|
47
|
+
* Shape mirrors the `PaymentPolicy` interface in `@voyant-travel/finance`.
|
|
48
|
+
* Stored as jsonb so the shape can evolve (multi-installment plans,
|
|
49
|
+
* grace-period overrides per pax band, etc.) without a migration
|
|
50
|
+
* for every change.
|
|
51
|
+
*
|
|
52
|
+
* `null` means "no policy configured yet — fall through to
|
|
53
|
+
* hard-coded `noDepositPolicy` (100% upfront)".
|
|
54
|
+
*/
|
|
55
|
+
customerPaymentPolicy: jsonb("customer_payment_policy"),
|
|
56
|
+
// Columns retained for migrations from pre-booking-tax-settings
|
|
57
|
+
// beta databases. Runtime code reads/writes `booking_tax_settings`.
|
|
58
|
+
taxPriceMode: text("tax_price_mode").notNull().default("inclusive"),
|
|
59
|
+
taxPolicyProfileId: text("tax_policy_profile_id"),
|
|
60
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
61
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* Single-row operator profile: the legal/trading identity that
|
|
65
|
+
* contracts with the customer. Used by contract variables and public
|
|
66
|
+
* checkout / booking-preview legal blocks.
|
|
67
|
+
*/
|
|
68
|
+
export const operatorProfile = pgTable("operator_profile", {
|
|
69
|
+
id: typeId("operator_profile"),
|
|
70
|
+
name: text("name"),
|
|
71
|
+
legalName: text("legal_name"),
|
|
72
|
+
vatId: text("vat_id"),
|
|
73
|
+
registrationNumber: text("registration_number"),
|
|
74
|
+
address: text("address"),
|
|
75
|
+
phone: text("phone"),
|
|
76
|
+
email: text("email"),
|
|
77
|
+
website: text("website"),
|
|
78
|
+
license: text("license"),
|
|
79
|
+
licenseAuthority: text("license_authority"),
|
|
80
|
+
signatoryName: text("signatory_name"),
|
|
81
|
+
signatoryRole: text("signatory_role"),
|
|
82
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
83
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
84
|
+
});
|
|
85
|
+
/**
|
|
86
|
+
* Single-row operator payment instructions for customer-facing
|
|
87
|
+
* collection rails such as bank transfer.
|
|
88
|
+
*/
|
|
89
|
+
export const operatorPaymentInstructions = pgTable("operator_payment_instructions", {
|
|
90
|
+
id: typeId("operator_payment_instructions"),
|
|
91
|
+
bankTransferBeneficiary: text("bank_transfer_beneficiary"),
|
|
92
|
+
iban: text("iban"),
|
|
93
|
+
bank: text("bank"),
|
|
94
|
+
notes: text("notes"),
|
|
95
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
96
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
97
|
+
});
|
|
98
|
+
/**
|
|
99
|
+
* Single-row operator-level customer payment default. The booking
|
|
100
|
+
* payment-policy cascade uses this only when supplier/category/listing
|
|
101
|
+
* and booking-level policies do not override it.
|
|
102
|
+
*/
|
|
103
|
+
export const operatorPaymentDefaults = pgTable("operator_payment_defaults", {
|
|
104
|
+
id: typeId("operator_payment_defaults"),
|
|
105
|
+
customerPaymentPolicy: jsonb("customer_payment_policy"),
|
|
106
|
+
bookingCheckoutUrlTemplate: text("booking_checkout_url_template"),
|
|
107
|
+
invoicePayUrlTemplate: text("invoice_pay_url_template"),
|
|
108
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
109
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
110
|
+
});
|
|
111
|
+
/**
|
|
112
|
+
* Single-row booking tax configuration for booking-create previews,
|
|
113
|
+
* quote recomputation, and booking item tax-line materialization.
|
|
114
|
+
*
|
|
115
|
+
* Kept separate from operator identity and payment instructions:
|
|
116
|
+
* these fields are finance/booking tax policy knobs.
|
|
117
|
+
*/
|
|
118
|
+
export const bookingTaxSettings = pgTable("booking_tax_settings", {
|
|
119
|
+
id: typeId("booking_tax_settings"),
|
|
120
|
+
taxPriceMode: text("tax_price_mode").notNull().default("inclusive"),
|
|
121
|
+
taxPolicyProfileId: text("tax_policy_profile_id"),
|
|
122
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
123
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
124
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operator settings data access — readers/writers + validation for the
|
|
3
|
+
* operator profile, payment instructions/defaults, and booking-tax settings.
|
|
4
|
+
*
|
|
5
|
+
* Transport-agnostic (no Hono): a deployment mounts the HTTP routes over these
|
|
6
|
+
* and injects the readers into the standard modules that need them (legal
|
|
7
|
+
* contract variables, quotes proposal, commerce checkout tax, finance
|
|
8
|
+
* booking-tax). The schema lives in `./schema`.
|
|
9
|
+
*/
|
|
10
|
+
import type { BookingTaxSettings, PaymentPolicy } from "@voyant-travel/finance";
|
|
11
|
+
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { operatorPaymentDefaults, operatorPaymentInstructions, operatorProfile } from "./schema.js";
|
|
14
|
+
export declare const updateOperatorProfileSchema: z.ZodObject<{
|
|
15
|
+
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
16
|
+
legalName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
17
|
+
vatId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
18
|
+
registrationNumber: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
19
|
+
address: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
20
|
+
phone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
21
|
+
email: z.ZodUnion<[z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodLiteral<"">]>;
|
|
22
|
+
website: z.ZodUnion<[z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodLiteral<"">]>;
|
|
23
|
+
license: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
24
|
+
licenseAuthority: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
25
|
+
signatoryName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
26
|
+
signatoryRole: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
export declare const updateOperatorPaymentInstructionsSchema: z.ZodObject<{
|
|
29
|
+
bankTransferBeneficiary: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
30
|
+
iban: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
31
|
+
bank: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
32
|
+
notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
33
|
+
}, z.core.$strip>;
|
|
34
|
+
export declare const updateOperatorPaymentDefaultsSchema: z.ZodObject<{
|
|
35
|
+
customerPaymentPolicy: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
36
|
+
deposit: z.ZodObject<{
|
|
37
|
+
kind: z.ZodEnum<{
|
|
38
|
+
none: "none";
|
|
39
|
+
percent: "percent";
|
|
40
|
+
fixed_cents: "fixed_cents";
|
|
41
|
+
}>;
|
|
42
|
+
percent: z.ZodOptional<z.ZodNumber>;
|
|
43
|
+
amountCents: z.ZodOptional<z.ZodNumber>;
|
|
44
|
+
}, z.core.$strip>;
|
|
45
|
+
minDaysBeforeDepartureForDeposit: z.ZodNumber;
|
|
46
|
+
balanceDueDaysBeforeDeparture: z.ZodNumber;
|
|
47
|
+
balanceDueMinDaysFromNow: z.ZodNumber;
|
|
48
|
+
}, z.core.$strip>>>;
|
|
49
|
+
bookingCheckoutUrlTemplate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
50
|
+
invoicePayUrlTemplate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
51
|
+
}, z.core.$strip>;
|
|
52
|
+
export declare const updateOperatorSettingsSchema: z.ZodObject<{
|
|
53
|
+
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
54
|
+
legalName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
55
|
+
vatId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
56
|
+
registrationNumber: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
57
|
+
address: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
58
|
+
phone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
59
|
+
email: z.ZodUnion<[z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodLiteral<"">]>;
|
|
60
|
+
website: z.ZodUnion<[z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodLiteral<"">]>;
|
|
61
|
+
license: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
62
|
+
licenseAuthority: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
63
|
+
signatoryName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
64
|
+
signatoryRole: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
65
|
+
bankTransferBeneficiary: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
66
|
+
iban: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
67
|
+
bank: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
68
|
+
notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
69
|
+
customerPaymentPolicy: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
70
|
+
deposit: z.ZodObject<{
|
|
71
|
+
kind: z.ZodEnum<{
|
|
72
|
+
none: "none";
|
|
73
|
+
percent: "percent";
|
|
74
|
+
fixed_cents: "fixed_cents";
|
|
75
|
+
}>;
|
|
76
|
+
percent: z.ZodOptional<z.ZodNumber>;
|
|
77
|
+
amountCents: z.ZodOptional<z.ZodNumber>;
|
|
78
|
+
}, z.core.$strip>;
|
|
79
|
+
minDaysBeforeDepartureForDeposit: z.ZodNumber;
|
|
80
|
+
balanceDueDaysBeforeDeparture: z.ZodNumber;
|
|
81
|
+
balanceDueMinDaysFromNow: z.ZodNumber;
|
|
82
|
+
}, z.core.$strip>>>;
|
|
83
|
+
bookingCheckoutUrlTemplate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
84
|
+
invoicePayUrlTemplate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
85
|
+
}, z.core.$strip>;
|
|
86
|
+
export type UpdateOperatorProfileInput = z.infer<typeof updateOperatorProfileSchema>;
|
|
87
|
+
export type UpdateOperatorPaymentInstructionsInput = z.infer<typeof updateOperatorPaymentInstructionsSchema>;
|
|
88
|
+
export type UpdateOperatorPaymentDefaultsInput = z.infer<typeof updateOperatorPaymentDefaultsSchema>;
|
|
89
|
+
export type UpdateOperatorSettingsInput = z.infer<typeof updateOperatorSettingsSchema>;
|
|
90
|
+
type OperatorProfileRow = typeof operatorProfile.$inferSelect;
|
|
91
|
+
type OperatorPaymentInstructionsRow = typeof operatorPaymentInstructions.$inferSelect;
|
|
92
|
+
type OperatorPaymentDefaultsRow = typeof operatorPaymentDefaults.$inferSelect;
|
|
93
|
+
export declare function getOperatorProfile(db: PostgresJsDatabase): Promise<{
|
|
94
|
+
id: string;
|
|
95
|
+
name: string | null;
|
|
96
|
+
legalName: string | null;
|
|
97
|
+
vatId: string | null;
|
|
98
|
+
registrationNumber: string | null;
|
|
99
|
+
address: string | null;
|
|
100
|
+
phone: string | null;
|
|
101
|
+
email: string | null;
|
|
102
|
+
website: string | null;
|
|
103
|
+
license: string | null;
|
|
104
|
+
licenseAuthority: string | null;
|
|
105
|
+
signatoryName: string | null;
|
|
106
|
+
signatoryRole: string | null;
|
|
107
|
+
createdAt: Date;
|
|
108
|
+
updatedAt: Date;
|
|
109
|
+
} | null>;
|
|
110
|
+
export declare function upsertOperatorProfile(db: PostgresJsDatabase, patch: UpdateOperatorProfileInput): Promise<{
|
|
111
|
+
id: string;
|
|
112
|
+
name: string | null;
|
|
113
|
+
createdAt: Date;
|
|
114
|
+
updatedAt: Date;
|
|
115
|
+
email: string | null;
|
|
116
|
+
phone: string | null;
|
|
117
|
+
vatId: string | null;
|
|
118
|
+
address: string | null;
|
|
119
|
+
legalName: string | null;
|
|
120
|
+
registrationNumber: string | null;
|
|
121
|
+
website: string | null;
|
|
122
|
+
license: string | null;
|
|
123
|
+
licenseAuthority: string | null;
|
|
124
|
+
signatoryName: string | null;
|
|
125
|
+
signatoryRole: string | null;
|
|
126
|
+
} | null>;
|
|
127
|
+
export declare function getOperatorPaymentInstructions(db: PostgresJsDatabase): Promise<{
|
|
128
|
+
id: string;
|
|
129
|
+
bankTransferBeneficiary: string | null;
|
|
130
|
+
iban: string | null;
|
|
131
|
+
bank: string | null;
|
|
132
|
+
notes: string | null;
|
|
133
|
+
createdAt: Date;
|
|
134
|
+
updatedAt: Date;
|
|
135
|
+
} | null>;
|
|
136
|
+
export declare function upsertOperatorPaymentInstructions(db: PostgresJsDatabase, patch: UpdateOperatorPaymentInstructionsInput): Promise<{
|
|
137
|
+
id: string;
|
|
138
|
+
createdAt: Date;
|
|
139
|
+
updatedAt: Date;
|
|
140
|
+
notes: string | null;
|
|
141
|
+
iban: string | null;
|
|
142
|
+
bankTransferBeneficiary: string | null;
|
|
143
|
+
bank: string | null;
|
|
144
|
+
} | null>;
|
|
145
|
+
export declare function getOperatorPaymentDefaults(db: PostgresJsDatabase): Promise<{
|
|
146
|
+
id: string;
|
|
147
|
+
customerPaymentPolicy: unknown;
|
|
148
|
+
bookingCheckoutUrlTemplate: string | null;
|
|
149
|
+
invoicePayUrlTemplate: string | null;
|
|
150
|
+
createdAt: Date;
|
|
151
|
+
updatedAt: Date;
|
|
152
|
+
} | null>;
|
|
153
|
+
export declare function upsertOperatorPaymentDefaults(db: PostgresJsDatabase, patch: UpdateOperatorPaymentDefaultsInput): Promise<{
|
|
154
|
+
id: string;
|
|
155
|
+
customerPaymentPolicy: unknown;
|
|
156
|
+
createdAt: Date;
|
|
157
|
+
updatedAt: Date;
|
|
158
|
+
bookingCheckoutUrlTemplate: string | null;
|
|
159
|
+
invoicePayUrlTemplate: string | null;
|
|
160
|
+
} | null>;
|
|
161
|
+
export declare function resolveOperatorDefaultPaymentPolicy(db: PostgresJsDatabase): Promise<PaymentPolicy | null>;
|
|
162
|
+
export declare function resolveBookingTaxSettings(db: PostgresJsDatabase): Promise<BookingTaxSettings>;
|
|
163
|
+
export declare function updateBookingTaxSettings(db: PostgresJsDatabase, patch: BookingTaxSettings): Promise<BookingTaxSettings>;
|
|
164
|
+
export declare function toPublicOperatorProfile(row: OperatorProfileRow, defaults?: OperatorPaymentDefaultsRow | null): PublicOperatorProfile;
|
|
165
|
+
export interface PublicOperatorProfile {
|
|
166
|
+
name: string;
|
|
167
|
+
legalName: string;
|
|
168
|
+
address: string;
|
|
169
|
+
phone: string;
|
|
170
|
+
email: string;
|
|
171
|
+
website: string;
|
|
172
|
+
license: string;
|
|
173
|
+
licenseAuthority: string;
|
|
174
|
+
customerPaymentPolicy: PaymentPolicy | null;
|
|
175
|
+
bookingCheckoutUrlTemplate: string | null;
|
|
176
|
+
invoicePayUrlTemplate: string | null;
|
|
177
|
+
}
|
|
178
|
+
type CombinedOperatorSettings = Partial<OperatorProfileRow> & Partial<OperatorPaymentInstructionsRow> & {
|
|
179
|
+
customerPaymentPolicy?: unknown;
|
|
180
|
+
bookingCheckoutUrlTemplate?: string | null;
|
|
181
|
+
invoicePayUrlTemplate?: string | null;
|
|
182
|
+
};
|
|
183
|
+
export declare function getOperatorSettings(db: PostgresJsDatabase): Promise<CombinedOperatorSettings | null>;
|
|
184
|
+
export declare function upsertOperatorSettings(db: PostgresJsDatabase, patch: UpdateOperatorSettingsInput): Promise<CombinedOperatorSettings | null>;
|
|
185
|
+
export declare function toPublicOperatorSettings(row: Awaited<ReturnType<typeof getOperatorSettings>>): PublicOperatorProfile;
|
|
186
|
+
export {};
|
|
187
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operator settings data access — readers/writers + validation for the
|
|
3
|
+
* operator profile, payment instructions/defaults, and booking-tax settings.
|
|
4
|
+
*
|
|
5
|
+
* Transport-agnostic (no Hono): a deployment mounts the HTTP routes over these
|
|
6
|
+
* and injects the readers into the standard modules that need them (legal
|
|
7
|
+
* contract variables, quotes proposal, commerce checkout tax, finance
|
|
8
|
+
* booking-tax). The schema lives in `./schema`.
|
|
9
|
+
*/
|
|
10
|
+
import { desc, eq } from "drizzle-orm";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { bookingTaxSettings, operatorPaymentDefaults, operatorPaymentInstructions, operatorProfile, } from "./schema.js";
|
|
13
|
+
const depositRuleSchema = z.object({
|
|
14
|
+
kind: z.enum(["none", "percent", "fixed_cents"]),
|
|
15
|
+
percent: z.number().min(0).max(100).optional(),
|
|
16
|
+
amountCents: z.number().int().min(0).optional(),
|
|
17
|
+
});
|
|
18
|
+
const paymentPolicySchema = z.object({
|
|
19
|
+
deposit: depositRuleSchema,
|
|
20
|
+
minDaysBeforeDepartureForDeposit: z.number().int().min(0),
|
|
21
|
+
balanceDueDaysBeforeDeparture: z.number().int().min(0),
|
|
22
|
+
balanceDueMinDaysFromNow: z.number().int().min(0),
|
|
23
|
+
});
|
|
24
|
+
export const updateOperatorProfileSchema = z.object({
|
|
25
|
+
name: z.string().nullable().optional(),
|
|
26
|
+
legalName: z.string().nullable().optional(),
|
|
27
|
+
vatId: z.string().nullable().optional(),
|
|
28
|
+
registrationNumber: z.string().nullable().optional(),
|
|
29
|
+
address: z.string().nullable().optional(),
|
|
30
|
+
phone: z.string().nullable().optional(),
|
|
31
|
+
email: z.string().email().nullable().optional().or(z.literal("")),
|
|
32
|
+
website: z.string().url().nullable().optional().or(z.literal("")),
|
|
33
|
+
license: z.string().nullable().optional(),
|
|
34
|
+
licenseAuthority: z.string().nullable().optional(),
|
|
35
|
+
signatoryName: z.string().nullable().optional(),
|
|
36
|
+
signatoryRole: z.string().nullable().optional(),
|
|
37
|
+
});
|
|
38
|
+
export const updateOperatorPaymentInstructionsSchema = z.object({
|
|
39
|
+
bankTransferBeneficiary: z.string().nullable().optional(),
|
|
40
|
+
iban: z.string().nullable().optional(),
|
|
41
|
+
bank: z.string().nullable().optional(),
|
|
42
|
+
notes: z.string().nullable().optional(),
|
|
43
|
+
});
|
|
44
|
+
export const updateOperatorPaymentDefaultsSchema = z.object({
|
|
45
|
+
customerPaymentPolicy: paymentPolicySchema.nullable().optional(),
|
|
46
|
+
bookingCheckoutUrlTemplate: z.string().trim().nullable().optional(),
|
|
47
|
+
invoicePayUrlTemplate: z.string().trim().nullable().optional(),
|
|
48
|
+
});
|
|
49
|
+
export const updateOperatorSettingsSchema = updateOperatorProfileSchema
|
|
50
|
+
.merge(updateOperatorPaymentInstructionsSchema)
|
|
51
|
+
.merge(updateOperatorPaymentDefaultsSchema);
|
|
52
|
+
export async function getOperatorProfile(db) {
|
|
53
|
+
const [row] = await db
|
|
54
|
+
.select()
|
|
55
|
+
.from(operatorProfile)
|
|
56
|
+
.orderBy(desc(operatorProfile.createdAt))
|
|
57
|
+
.limit(1);
|
|
58
|
+
return row ?? null;
|
|
59
|
+
}
|
|
60
|
+
export async function upsertOperatorProfile(db, patch) {
|
|
61
|
+
const existing = await getOperatorProfile(db);
|
|
62
|
+
if (!existing) {
|
|
63
|
+
const [created] = await db.insert(operatorProfile).values(patch).returning();
|
|
64
|
+
return created ?? null;
|
|
65
|
+
}
|
|
66
|
+
const [updated] = await db
|
|
67
|
+
.update(operatorProfile)
|
|
68
|
+
.set({ ...patch, updatedAt: new Date() })
|
|
69
|
+
.where(eq(operatorProfile.id, existing.id))
|
|
70
|
+
.returning();
|
|
71
|
+
return updated ?? null;
|
|
72
|
+
}
|
|
73
|
+
export async function getOperatorPaymentInstructions(db) {
|
|
74
|
+
const [row] = await db
|
|
75
|
+
.select()
|
|
76
|
+
.from(operatorPaymentInstructions)
|
|
77
|
+
.orderBy(desc(operatorPaymentInstructions.createdAt))
|
|
78
|
+
.limit(1);
|
|
79
|
+
return row ?? null;
|
|
80
|
+
}
|
|
81
|
+
export async function upsertOperatorPaymentInstructions(db, patch) {
|
|
82
|
+
const existing = await getOperatorPaymentInstructions(db);
|
|
83
|
+
if (!existing) {
|
|
84
|
+
const [created] = await db.insert(operatorPaymentInstructions).values(patch).returning();
|
|
85
|
+
return created ?? null;
|
|
86
|
+
}
|
|
87
|
+
const [updated] = await db
|
|
88
|
+
.update(operatorPaymentInstructions)
|
|
89
|
+
.set({ ...patch, updatedAt: new Date() })
|
|
90
|
+
.where(eq(operatorPaymentInstructions.id, existing.id))
|
|
91
|
+
.returning();
|
|
92
|
+
return updated ?? null;
|
|
93
|
+
}
|
|
94
|
+
export async function getOperatorPaymentDefaults(db) {
|
|
95
|
+
const [row] = await db
|
|
96
|
+
.select()
|
|
97
|
+
.from(operatorPaymentDefaults)
|
|
98
|
+
.orderBy(desc(operatorPaymentDefaults.createdAt))
|
|
99
|
+
.limit(1);
|
|
100
|
+
return row ?? null;
|
|
101
|
+
}
|
|
102
|
+
export async function upsertOperatorPaymentDefaults(db, patch) {
|
|
103
|
+
const existing = await getOperatorPaymentDefaults(db);
|
|
104
|
+
const values = {};
|
|
105
|
+
if ("customerPaymentPolicy" in patch) {
|
|
106
|
+
values.customerPaymentPolicy = (patch.customerPaymentPolicy ?? null);
|
|
107
|
+
}
|
|
108
|
+
if ("bookingCheckoutUrlTemplate" in patch) {
|
|
109
|
+
values.bookingCheckoutUrlTemplate = patch.bookingCheckoutUrlTemplate?.trim() || null;
|
|
110
|
+
}
|
|
111
|
+
if ("invoicePayUrlTemplate" in patch) {
|
|
112
|
+
values.invoicePayUrlTemplate = patch.invoicePayUrlTemplate?.trim() || null;
|
|
113
|
+
}
|
|
114
|
+
if (!existing) {
|
|
115
|
+
const [created] = await db.insert(operatorPaymentDefaults).values(values).returning();
|
|
116
|
+
return created ?? null;
|
|
117
|
+
}
|
|
118
|
+
if (Object.keys(values).length === 0)
|
|
119
|
+
return existing;
|
|
120
|
+
const [updated] = await db
|
|
121
|
+
.update(operatorPaymentDefaults)
|
|
122
|
+
.set({
|
|
123
|
+
...values,
|
|
124
|
+
updatedAt: new Date(),
|
|
125
|
+
})
|
|
126
|
+
.where(eq(operatorPaymentDefaults.id, existing.id))
|
|
127
|
+
.returning();
|
|
128
|
+
return updated ?? null;
|
|
129
|
+
}
|
|
130
|
+
export async function resolveOperatorDefaultPaymentPolicy(db) {
|
|
131
|
+
const defaults = await getOperatorPaymentDefaults(db);
|
|
132
|
+
return defaults?.customerPaymentPolicy ?? null;
|
|
133
|
+
}
|
|
134
|
+
export async function resolveBookingTaxSettings(db) {
|
|
135
|
+
const [settings] = await db
|
|
136
|
+
.select()
|
|
137
|
+
.from(bookingTaxSettings)
|
|
138
|
+
.orderBy(desc(bookingTaxSettings.createdAt))
|
|
139
|
+
.limit(1);
|
|
140
|
+
return {
|
|
141
|
+
taxPriceMode: settings?.taxPriceMode === "exclusive" ? "exclusive" : "inclusive",
|
|
142
|
+
taxPolicyProfileId: settings?.taxPolicyProfileId ?? null,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
export async function updateBookingTaxSettings(db, patch) {
|
|
146
|
+
const [existing] = await db
|
|
147
|
+
.select()
|
|
148
|
+
.from(bookingTaxSettings)
|
|
149
|
+
.orderBy(desc(bookingTaxSettings.createdAt))
|
|
150
|
+
.limit(1);
|
|
151
|
+
if (!existing) {
|
|
152
|
+
const [created] = await db
|
|
153
|
+
.insert(bookingTaxSettings)
|
|
154
|
+
.values({
|
|
155
|
+
taxPriceMode: patch.taxPriceMode === "exclusive" ? "exclusive" : "inclusive",
|
|
156
|
+
taxPolicyProfileId: patch.taxPolicyProfileId ?? null,
|
|
157
|
+
})
|
|
158
|
+
.returning();
|
|
159
|
+
return {
|
|
160
|
+
taxPriceMode: created?.taxPriceMode === "exclusive" ? "exclusive" : "inclusive",
|
|
161
|
+
taxPolicyProfileId: created?.taxPolicyProfileId ?? null,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const [updated] = await db
|
|
165
|
+
.update(bookingTaxSettings)
|
|
166
|
+
.set({
|
|
167
|
+
taxPriceMode: patch.taxPriceMode === "exclusive" ? "exclusive" : "inclusive",
|
|
168
|
+
taxPolicyProfileId: patch.taxPolicyProfileId ?? null,
|
|
169
|
+
updatedAt: new Date(),
|
|
170
|
+
})
|
|
171
|
+
.where(eq(bookingTaxSettings.id, existing.id))
|
|
172
|
+
.returning();
|
|
173
|
+
return {
|
|
174
|
+
taxPriceMode: updated?.taxPriceMode === "exclusive" ? "exclusive" : "inclusive",
|
|
175
|
+
taxPolicyProfileId: updated?.taxPolicyProfileId ?? null,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
export function toPublicOperatorProfile(row, defaults) {
|
|
179
|
+
return {
|
|
180
|
+
name: row.name ?? "",
|
|
181
|
+
legalName: row.legalName ?? "",
|
|
182
|
+
address: row.address ?? "",
|
|
183
|
+
phone: row.phone ?? "",
|
|
184
|
+
email: row.email ?? "",
|
|
185
|
+
website: row.website ?? "",
|
|
186
|
+
license: row.license ?? "",
|
|
187
|
+
licenseAuthority: row.licenseAuthority ?? "",
|
|
188
|
+
customerPaymentPolicy: defaults?.customerPaymentPolicy ?? null,
|
|
189
|
+
bookingCheckoutUrlTemplate: defaults?.bookingCheckoutUrlTemplate ?? null,
|
|
190
|
+
invoicePayUrlTemplate: defaults?.invoicePayUrlTemplate ?? null,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function combineOperatorSettings(profile, instructions, defaults) {
|
|
194
|
+
if (!profile && !instructions && !defaults)
|
|
195
|
+
return null;
|
|
196
|
+
return {
|
|
197
|
+
...(profile ?? {}),
|
|
198
|
+
bankTransferBeneficiary: instructions?.bankTransferBeneficiary ?? null,
|
|
199
|
+
iban: instructions?.iban ?? null,
|
|
200
|
+
bank: instructions?.bank ?? null,
|
|
201
|
+
notes: instructions?.notes ?? null,
|
|
202
|
+
customerPaymentPolicy: defaults?.customerPaymentPolicy ?? null,
|
|
203
|
+
bookingCheckoutUrlTemplate: defaults?.bookingCheckoutUrlTemplate ?? null,
|
|
204
|
+
invoicePayUrlTemplate: defaults?.invoicePayUrlTemplate ?? null,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
export async function getOperatorSettings(db) {
|
|
208
|
+
const [profile, instructions, defaults] = await Promise.all([
|
|
209
|
+
getOperatorProfile(db),
|
|
210
|
+
getOperatorPaymentInstructions(db),
|
|
211
|
+
getOperatorPaymentDefaults(db),
|
|
212
|
+
]);
|
|
213
|
+
return combineOperatorSettings(profile, instructions, defaults);
|
|
214
|
+
}
|
|
215
|
+
export async function upsertOperatorSettings(db, patch) {
|
|
216
|
+
const paymentDefaultsPatch = {};
|
|
217
|
+
if ("customerPaymentPolicy" in patch) {
|
|
218
|
+
paymentDefaultsPatch.customerPaymentPolicy = patch.customerPaymentPolicy;
|
|
219
|
+
}
|
|
220
|
+
if ("bookingCheckoutUrlTemplate" in patch) {
|
|
221
|
+
paymentDefaultsPatch.bookingCheckoutUrlTemplate = patch.bookingCheckoutUrlTemplate;
|
|
222
|
+
}
|
|
223
|
+
if ("invoicePayUrlTemplate" in patch) {
|
|
224
|
+
paymentDefaultsPatch.invoicePayUrlTemplate = patch.invoicePayUrlTemplate;
|
|
225
|
+
}
|
|
226
|
+
const [profile, instructions, defaults] = await Promise.all([
|
|
227
|
+
upsertOperatorProfile(db, {
|
|
228
|
+
name: patch.name,
|
|
229
|
+
legalName: patch.legalName,
|
|
230
|
+
vatId: patch.vatId,
|
|
231
|
+
registrationNumber: patch.registrationNumber,
|
|
232
|
+
address: patch.address,
|
|
233
|
+
phone: patch.phone,
|
|
234
|
+
email: patch.email,
|
|
235
|
+
website: patch.website,
|
|
236
|
+
license: patch.license,
|
|
237
|
+
licenseAuthority: patch.licenseAuthority,
|
|
238
|
+
signatoryName: patch.signatoryName,
|
|
239
|
+
signatoryRole: patch.signatoryRole,
|
|
240
|
+
}),
|
|
241
|
+
upsertOperatorPaymentInstructions(db, {
|
|
242
|
+
bankTransferBeneficiary: patch.bankTransferBeneficiary,
|
|
243
|
+
iban: patch.iban,
|
|
244
|
+
bank: patch.bank,
|
|
245
|
+
notes: patch.notes,
|
|
246
|
+
}),
|
|
247
|
+
upsertOperatorPaymentDefaults(db, paymentDefaultsPatch),
|
|
248
|
+
]);
|
|
249
|
+
return combineOperatorSettings(profile, instructions, defaults);
|
|
250
|
+
}
|
|
251
|
+
export function toPublicOperatorSettings(row) {
|
|
252
|
+
return {
|
|
253
|
+
name: row?.name ?? "",
|
|
254
|
+
legalName: row?.legalName ?? "",
|
|
255
|
+
address: row?.address ?? "",
|
|
256
|
+
phone: row?.phone ?? "",
|
|
257
|
+
email: row?.email ?? "",
|
|
258
|
+
website: row?.website ?? "",
|
|
259
|
+
license: row?.license ?? "",
|
|
260
|
+
licenseAuthority: row?.licenseAuthority ?? "",
|
|
261
|
+
customerPaymentPolicy: row?.customerPaymentPolicy ?? null,
|
|
262
|
+
bookingCheckoutUrlTemplate: row?.bookingCheckoutUrlTemplate ?? null,
|
|
263
|
+
invoicePayUrlTemplate: row?.invoicePayUrlTemplate ?? null,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.test.d.ts","sourceRoot":"","sources":["../src/service.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { toPublicOperatorProfile, updateOperatorPaymentDefaultsSchema, updateOperatorProfileSchema, } from "./service.js";
|
|
3
|
+
// Minimal row shape the DTO mapper reads (the full row also has id/timestamps).
|
|
4
|
+
function profileRow(overrides = {}) {
|
|
5
|
+
return {
|
|
6
|
+
id: "oppf_test",
|
|
7
|
+
name: "Acme Tours",
|
|
8
|
+
legalName: "Acme Tours SRL",
|
|
9
|
+
vatId: null,
|
|
10
|
+
registrationNumber: null,
|
|
11
|
+
address: "1 Main St",
|
|
12
|
+
phone: "+40 700 000 000",
|
|
13
|
+
email: "hello@acme.test",
|
|
14
|
+
website: "https://acme.test",
|
|
15
|
+
license: "ANPC-123",
|
|
16
|
+
licenseAuthority: "ANPC",
|
|
17
|
+
signatoryName: null,
|
|
18
|
+
signatoryRole: null,
|
|
19
|
+
createdAt: new Date(0),
|
|
20
|
+
updatedAt: new Date(0),
|
|
21
|
+
...overrides,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
describe("toPublicOperatorProfile", () => {
|
|
25
|
+
it("maps profile + payment defaults into the public DTO", () => {
|
|
26
|
+
const dto = toPublicOperatorProfile(profileRow(), {
|
|
27
|
+
id: "opdp_test",
|
|
28
|
+
customerPaymentPolicy: { deposit: { kind: "none" } },
|
|
29
|
+
bookingCheckoutUrlTemplate: "https://pay.acme.test/{booking}",
|
|
30
|
+
invoicePayUrlTemplate: null,
|
|
31
|
+
createdAt: new Date(0),
|
|
32
|
+
updatedAt: new Date(0),
|
|
33
|
+
});
|
|
34
|
+
expect(dto.name).toBe("Acme Tours");
|
|
35
|
+
expect(dto.email).toBe("hello@acme.test");
|
|
36
|
+
expect(dto.bookingCheckoutUrlTemplate).toBe("https://pay.acme.test/{booking}");
|
|
37
|
+
expect(dto.invoicePayUrlTemplate).toBeNull();
|
|
38
|
+
expect(dto.customerPaymentPolicy).toEqual({ deposit: { kind: "none" } });
|
|
39
|
+
});
|
|
40
|
+
it("coalesces missing identity fields to empty strings and policy to null", () => {
|
|
41
|
+
const dto = toPublicOperatorProfile(profileRow({ name: null, email: null, website: null, license: null }));
|
|
42
|
+
expect(dto.name).toBe("");
|
|
43
|
+
expect(dto.email).toBe("");
|
|
44
|
+
expect(dto.website).toBe("");
|
|
45
|
+
expect(dto.license).toBe("");
|
|
46
|
+
expect(dto.customerPaymentPolicy).toBeNull();
|
|
47
|
+
expect(dto.bookingCheckoutUrlTemplate).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe("validation schemas", () => {
|
|
51
|
+
it("accepts a valid profile patch and an empty email/website", () => {
|
|
52
|
+
expect(updateOperatorProfileSchema.safeParse({ name: "X", email: "" }).success).toBe(true);
|
|
53
|
+
expect(updateOperatorProfileSchema.safeParse({ email: "a@b.test" }).success).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
it("rejects a malformed email", () => {
|
|
56
|
+
expect(updateOperatorProfileSchema.safeParse({ email: "not-an-email" }).success).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
it("validates the customer payment policy shape", () => {
|
|
59
|
+
expect(updateOperatorPaymentDefaultsSchema.safeParse({
|
|
60
|
+
customerPaymentPolicy: {
|
|
61
|
+
deposit: { kind: "percent", percent: 20 },
|
|
62
|
+
minDaysBeforeDepartureForDeposit: 0,
|
|
63
|
+
balanceDueDaysBeforeDeparture: 30,
|
|
64
|
+
balanceDueMinDaysFromNow: 7,
|
|
65
|
+
},
|
|
66
|
+
}).success).toBe(true);
|
|
67
|
+
expect(updateOperatorPaymentDefaultsSchema.safeParse({
|
|
68
|
+
customerPaymentPolicy: { deposit: { kind: "bogus" } },
|
|
69
|
+
}).success).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
});
|