@voyant-travel/finance 0.137.4 → 0.137.5

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.
@@ -49,6 +49,7 @@ export interface PaymentPolicy {
49
49
  /** Default policy: 100% up-front, no deposit. Safe fallback for
50
50
  * agencies that haven't configured anything yet. */
51
51
  export declare const noDepositPolicy: PaymentPolicy;
52
+ export declare function isPaymentPolicy(value: unknown): value is PaymentPolicy;
52
53
  export type PaymentScheduleEntryType = "deposit" | "balance" | "full";
53
54
  export interface ComputedScheduleEntry {
54
55
  scheduleType: PaymentScheduleEntryType;
@@ -1 +1 @@
1
- {"version":3,"file":"payment-policy.d.ts","sourceRoot":"","sources":["../src/payment-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,aAAa,CAAA;AAE5D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAA;IACjB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,WAAW,CAAA;IACpB;gEAC4D;IAC5D,gCAAgC,EAAE,MAAM,CAAA;IACxC,uDAAuD;IACvD,6BAA6B,EAAE,MAAM,CAAA;IACrC;;+DAE2D;IAC3D,wBAAwB,EAAE,MAAM,CAAA;CACjC;AAED;qDACqD;AACrD,eAAO,MAAM,eAAe,EAAE,aAK7B,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAA;AAErE,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,wBAAwB,CAAA;IACtC,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,wBAAwB;IACxB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,wEAAwE;IACxE,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACxC,oEAAoE;IACpE,KAAK,CAAC,EAAE,IAAI,CAAA;CACb;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,oBAAoB,EAC3B,MAAM,EAAE,aAAa,GACpB,qBAAqB,EAAE,CA8DzB;AAaD,8DAA8D;AAC9D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAgBtF;AAED,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,SAAS,GACT,UAAU,GACV,UAAU,GACV,kBAAkB,CAAA;AAEtB,MAAM,WAAW,0BAA0B;IACzC,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IACpC,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IACpC,cAAc,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IACrC,cAAc,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IACrC,eAAe,EAAE,aAAa,CAAA;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,aAAa,CAAA;IACrB,MAAM,EAAE,mBAAmB,CAAA;CAC5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,0BAA0B,GACjC,qBAAqB,CAMvB;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,aAAa,EACrB,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,KAAK,GAAE,IAAiB,GACvB,OAAO,CAKT"}
1
+ {"version":3,"file":"payment-policy.d.ts","sourceRoot":"","sources":["../src/payment-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,aAAa,CAAA;AAE5D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAA;IACjB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,WAAW,CAAA;IACpB;gEAC4D;IAC5D,gCAAgC,EAAE,MAAM,CAAA;IACxC,uDAAuD;IACvD,6BAA6B,EAAE,MAAM,CAAA;IACrC;;+DAE2D;IAC3D,wBAAwB,EAAE,MAAM,CAAA;CACjC;AAED;qDACqD;AACrD,eAAO,MAAM,eAAe,EAAE,aAK7B,CAAA;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAStE;AAmBD,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAA;AAErE,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,wBAAwB,CAAA;IACtC,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,wBAAwB;IACxB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,wEAAwE;IACxE,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACxC,oEAAoE;IACpE,KAAK,CAAC,EAAE,IAAI,CAAA;CACb;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,oBAAoB,EAC3B,MAAM,EAAE,aAAa,GACpB,qBAAqB,EAAE,CA+DzB;AAaD,8DAA8D;AAC9D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAgBtF;AAED,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,SAAS,GACT,UAAU,GACV,UAAU,GACV,kBAAkB,CAAA;AAEtB,MAAM,WAAW,0BAA0B;IACzC,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IACpC,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IACpC,cAAc,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IACrC,cAAc,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IACrC,eAAe,EAAE,aAAa,CAAA;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,aAAa,CAAA;IACrB,MAAM,EAAE,mBAAmB,CAAA;CAC5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,0BAA0B,GACjC,qBAAqB,CAevB;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,aAAa,EACrB,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,KAAK,GAAE,IAAiB,GACvB,OAAO,CAMT"}
@@ -34,6 +34,32 @@ export const noDepositPolicy = {
34
34
  balanceDueDaysBeforeDeparture: 0,
35
35
  balanceDueMinDaysFromNow: 0,
36
36
  };
37
+ export function isPaymentPolicy(value) {
38
+ if (!value || typeof value !== "object")
39
+ return false;
40
+ const policy = value;
41
+ return (isDepositRule(policy.deposit) &&
42
+ isNonNegativeInteger(policy.minDaysBeforeDepartureForDeposit) &&
43
+ isNonNegativeInteger(policy.balanceDueDaysBeforeDeparture) &&
44
+ isNonNegativeInteger(policy.balanceDueMinDaysFromNow));
45
+ }
46
+ function isDepositRule(value) {
47
+ if (!value || typeof value !== "object")
48
+ return false;
49
+ const rule = value;
50
+ if (rule.kind === "none")
51
+ return true;
52
+ if (rule.kind === "percent") {
53
+ return typeof rule.percent === "number" && rule.percent >= 0 && rule.percent <= 100;
54
+ }
55
+ if (rule.kind === "fixed_cents") {
56
+ return isNonNegativeInteger(rule.amountCents);
57
+ }
58
+ return false;
59
+ }
60
+ function isNonNegativeInteger(value) {
61
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
62
+ }
37
63
  /**
38
64
  * Compute the customer-facing payment schedule for a booking.
39
65
  *
@@ -42,13 +68,14 @@ export const noDepositPolicy = {
42
68
  * scheduled for today.
43
69
  */
44
70
  export function computePaymentSchedule(input, policy) {
71
+ const effectivePolicy = isPaymentPolicy(policy) ? policy : noDepositPolicy;
45
72
  const total = Math.max(0, Math.round(input.totalCents));
46
73
  const today = input.today ?? new Date();
47
74
  const todayIso = isoDate(today);
48
75
  if (total === 0) {
49
76
  return [{ scheduleType: "full", amountCents: 0, currency: input.currency, dueDate: todayIso }];
50
77
  }
51
- if (policy.deposit.kind === "none") {
78
+ if (effectivePolicy.deposit.kind === "none") {
52
79
  return [
53
80
  { scheduleType: "full", amountCents: total, currency: input.currency, dueDate: todayIso },
54
81
  ];
@@ -63,12 +90,12 @@ export function computePaymentSchedule(input, policy) {
63
90
  ];
64
91
  }
65
92
  const daysUntilDeparture = wholeDaysBetween(today, departure);
66
- if (daysUntilDeparture < policy.minDaysBeforeDepartureForDeposit) {
93
+ if (daysUntilDeparture < effectivePolicy.minDaysBeforeDepartureForDeposit) {
67
94
  return [
68
95
  { scheduleType: "full", amountCents: total, currency: input.currency, dueDate: todayIso },
69
96
  ];
70
97
  }
71
- const depositCents = clampCents(computeDepositCents(total, policy.deposit), 0, total);
98
+ const depositCents = clampCents(computeDepositCents(total, effectivePolicy.deposit), 0, total);
72
99
  const balanceCents = total - depositCents;
73
100
  if (depositCents <= 0 || balanceCents <= 0) {
74
101
  // Edge: a policy that resolves to 0% deposit or 100% deposit
@@ -78,8 +105,8 @@ export function computePaymentSchedule(input, policy) {
78
105
  { scheduleType: "full", amountCents: total, currency: input.currency, dueDate: todayIso },
79
106
  ];
80
107
  }
81
- const rawBalanceDue = addDays(departure, -policy.balanceDueDaysBeforeDeparture);
82
- const earliestBalanceDue = addDays(today, policy.balanceDueMinDaysFromNow);
108
+ const rawBalanceDue = addDays(departure, -effectivePolicy.balanceDueDaysBeforeDeparture);
109
+ const earliestBalanceDue = addDays(today, effectivePolicy.balanceDueMinDaysFromNow);
83
110
  const balanceDue = rawBalanceDue.getTime() > earliestBalanceDue.getTime() ? rawBalanceDue : earliestBalanceDue;
84
111
  return [
85
112
  {
@@ -108,7 +135,7 @@ function computeDepositCents(total, rule) {
108
135
  }
109
136
  /** True when this policy is empty / "inherit from parent". */
110
137
  export function isPaymentPolicyEmpty(policy) {
111
- if (!policy)
138
+ if (!isPaymentPolicy(policy))
112
139
  return true;
113
140
  if (policy.deposit.kind === "none")
114
141
  return true;
@@ -139,15 +166,20 @@ export function isPaymentPolicyEmpty(policy) {
139
166
  * proves useful.
140
167
  */
141
168
  export function resolveEffectivePaymentPolicy(layers) {
142
- if (layers.bookingPolicy)
169
+ if (isPaymentPolicy(layers.bookingPolicy))
143
170
  return { policy: layers.bookingPolicy, source: "booking" };
144
- if (layers.listingPolicy)
171
+ if (isPaymentPolicy(layers.listingPolicy))
145
172
  return { policy: layers.listingPolicy, source: "listing" };
146
- if (layers.categoryPolicy)
173
+ if (isPaymentPolicy(layers.categoryPolicy)) {
147
174
  return { policy: layers.categoryPolicy, source: "category" };
148
- if (layers.supplierPolicy)
175
+ }
176
+ if (isPaymentPolicy(layers.supplierPolicy)) {
149
177
  return { policy: layers.supplierPolicy, source: "supplier" };
150
- return { policy: layers.operatorDefault, source: "operator_default" };
178
+ }
179
+ return {
180
+ policy: isPaymentPolicy(layers.operatorDefault) ? layers.operatorDefault : noDepositPolicy,
181
+ source: "operator_default",
182
+ };
151
183
  }
152
184
  /**
153
185
  * Predicate the storefront UI uses to decide whether to surface the
@@ -155,12 +187,13 @@ export function resolveEffectivePaymentPolicy(layers) {
155
187
  * the policy + departure date resolve to a single full-payment row.
156
188
  */
157
189
  export function policyShouldRequireFullPayment(policy, departureDate, today = new Date()) {
158
- if (policy.deposit.kind === "none")
190
+ const effectivePolicy = isPaymentPolicy(policy) ? policy : noDepositPolicy;
191
+ if (effectivePolicy.deposit.kind === "none")
159
192
  return true;
160
193
  const departure = parseIsoDate(departureDate);
161
194
  if (!departure)
162
195
  return true;
163
- return wholeDaysBetween(today, departure) < policy.minDaysBeforeDepartureForDeposit;
196
+ return wholeDaysBetween(today, departure) < effectivePolicy.minDaysBeforeDepartureForDeposit;
164
197
  }
165
198
  // ─────────────────────────────────────────────────────────────────
166
199
  // Helpers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyant-travel/finance",
3
- "version": "0.137.4",
3
+ "version": "0.137.5",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {