placementt-core 1.400.1069 → 1.400.1071

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/lib/pricing.d.ts CHANGED
@@ -1,62 +1,24 @@
1
- /**
2
- * The 2026 pricing model.
3
- *
4
- * A quote is a base fee plus a per-product fee, and every per-product fee is
5
- * graduated: the tiers are bands, not lookups, so a school with 1,250 students
6
- * pays the tier 1 rate on its first 1,000 and the tier 2 rate on the next 250.
7
- * Anything above the final threshold keeps paying the final tier's rate.
8
- *
9
- * Work experience is charged per placement (Y10 and Y12 are quoted separately,
10
- * and a year group left blank is a year group not taking the product); events
11
- * and destinations are charged per student. Only events and destinations carry
12
- * a partial-product discount — that is how the pricing sheet has it, and it is
13
- * why `partialProductDiscount` names those two rather than being one number.
14
- *
15
- * A trust is quoted as one account on its combined roll, and earns a discount
16
- * on the whole quote for the number of schools it brings on — a smaller one
17
- * below the threshold, a larger one at or above it.
18
- *
19
- * Every figure lives in `PricingConfig` so it can be stored in Firestore and
20
- * adjusted without a deploy. `DEFAULT_PRICING_CONFIG` is the fallback and the
21
- * seed for that document.
22
- */
23
- /** One band of the graduated fee, with the rate each product charges in it. */
24
1
  export type PricingTier = {
25
- /** Upper bound of the band, in students (events and destinations). */
26
2
  students: number;
27
- /** Upper bound of the band, in placements (work experience). */
28
3
  placements: number;
29
- /** £ per placement. */
30
4
  wex: number;
31
- /** £ per student. */
32
5
  events: number;
33
- /** £ per student. */
34
6
  destinations: number;
35
7
  };
36
8
  export type PricingConfig = {
37
- /** No school is quoted below this, however little the products come to. */
38
9
  minFee: number;
39
- /** Charged on every quote. Not itemised to the customer. */
40
10
  baseFee: number;
41
- /** In ascending order of threshold. The last band's rate runs to infinity. */
42
11
  tiers: PricingTier[];
43
- /** Taken off a single product's fee, as a fraction (0.2 = 20% off). */
44
12
  partialProductDiscount: {
45
13
  events: number;
46
14
  destinations: number;
47
15
  };
48
- /** Taken off the whole quote when a school takes the full platform. */
49
16
  fullPlatformDiscount: number;
50
- /** Taken off the whole quote for a trust, on how many schools it brings. */
51
17
  trust: {
52
- /** Schools at which the larger discount takes over. */
53
18
  threshold: number;
54
- /** Fraction off a trust below the threshold. */
55
19
  discount: number;
56
- /** Fraction off a trust at or above it. */
57
20
  largeDiscount: number;
58
21
  };
59
- /** Careers hubs are a flat platform fee plus a fee for each school. */
60
22
  hub: {
61
23
  baseFee: number;
62
24
  perSchoolFee: number;
@@ -64,181 +26,93 @@ export type PricingConfig = {
64
26
  };
65
27
  /** The pricing sheet's figures, as agreed for 2026. */
66
28
  export declare const DEFAULT_PRICING_CONFIG: PricingConfig;
67
- /** Where the adjustable copy of the config lives. */
68
29
  export declare const PRICING_CONFIG_PATH: string[];
69
30
  export type SchoolPricingInput = {
70
- /** Roll size. Drives the events and destinations fees. */
71
31
  students?: number;
72
- /** Y10 placements. Undefined means the year group isn't taking work experience. */
73
32
  wexY10?: number;
74
- /** Y12 placements. Undefined means the year group isn't taking work experience. */
75
33
  wexY12?: number;
76
34
  events?: boolean;
77
- /** Discount the events fee on its own. */
78
35
  eventsDiscount?: boolean;
79
36
  destinations?: boolean;
80
- /** Discount the destinations fee on its own. */
81
37
  destinationsDiscount?: boolean;
82
- /** Discount the whole quote, for a school taking the full platform. */
83
38
  fullPlatformDiscount?: boolean;
84
- /** Schools in the trust being quoted. Two or more earns a trust discount. */
85
39
  schools?: number;
86
- /** A negotiated discount off the whole quote, as a fraction (0.1 = 10% off).
87
- * Taken after the full-platform discount and before the minimum fee. */
88
40
  bespokeDiscount?: number;
89
- /** A price agreed with the customer, which replaces everything above it —
90
- * the discounts, the minimum fee and the total the products came to. */
91
41
  quoteOverride?: number;
92
42
  };
93
43
  export type PricingLineKey = "base" | "wexY10" | "wexY12" | "events" | "destinations" | "hubBase" | "hubSchools";
94
44
  export type PricingLine = {
95
45
  key: PricingLineKey;
96
46
  label: string;
97
- /** What the product costs after any partial-product discount. */
98
47
  amount: number;
99
- /** The fee before its partial-product discount, when one was applied. */
100
48
  undiscounted?: number;
101
- /** How the amount was reached, band by band. Empty for the base fee. */
102
49
  bands: PricingBand[];
103
50
  };
104
- /** One band's contribution to a product's fee, for showing the working. */
105
51
  export type PricingBand = {
106
- /** 1-indexed, matching the tier's position in the config. */
107
52
  tier: number;
108
- /** Units charged in this band. */
109
53
  quantity: number;
110
54
  rate: number;
111
55
  amount: number;
112
56
  };
113
57
  export type SchoolPricingResult = {
114
58
  lines: PricingLine[];
115
- /** The lines added up, before the full-platform discount. */
116
59
  subtotal: number;
117
- /** £ taken off the subtotal by the full-platform discount. */
118
60
  fullPlatformDiscountApplied: number;
119
- /** £ taken off by the trust discount, after the full-platform one. */
120
61
  trustDiscountApplied: number;
121
- /** The rate that trust discount was taken at, as a fraction. */
122
62
  trustDiscountRate: number;
123
- /** £ taken off by the negotiated discount, after the trust one. */
124
63
  bespokeDiscountApplied: number;
125
- /** True when the minimum fee, not the products, set the price. */
126
64
  minFeeApplied: boolean;
127
- /** True when an agreed price replaced the calculated one. */
128
65
  overrideApplied: boolean;
129
66
  total: number;
130
67
  };
131
68
  export type HubPricingResult = {
132
69
  baseFee: number;
133
- /** The per-school fee multiplied by the number of schools. */
134
70
  schoolFees: number;
135
71
  total: number;
136
72
  };
137
- /**
138
- * What a trust comes off its quote for its size.
139
- *
140
- * A trust is more than one school, so a count below two is a single school and
141
- * earns nothing — the discount is for bringing schools with you.
142
- *
143
- * @param {number} [schools] schools in the trust.
144
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
145
- * @return {number} the discount, as a fraction of the quote.
146
- */
147
73
  export declare function trustDiscountRate(schools?: number, config?: PricingConfig): number;
148
- /**
149
- * Price a school, trust or hub-school quote.
150
- *
151
- * Products the school isn't taking are left out of `lines` entirely, so the
152
- * breakdown only ever shows what is being charged for.
153
- *
154
- * @param {object} input the roll, the year groups and the products taken.
155
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
156
- * @return {object} the total, and the working that reached it.
157
- */
158
74
  export declare function calculateSchoolPrice(input: SchoolPricingInput, config?: PricingConfig): SchoolPricingResult;
159
- /**
160
- * Price a careers hub: the platform fee, plus a fee for every school on it.
161
- *
162
- * @param {number} schools how many schools the hub brings on.
163
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
164
- * @return {object} the platform fee, the school fees and the total.
165
- */
166
75
  export declare function calculateHubPrice(schools: number, config?: PricingConfig): HubPricingResult;
167
- /**
168
- * Fill a stored config out with the defaults.
169
- *
170
- * The Firestore document is hand-edited, so treat every branch of it as
171
- * possibly absent — a config saved before a tier was added should still price,
172
- * rather than throwing on a missing rate.
173
- *
174
- * @param {object} [stored] whatever the Firestore document holds.
175
- * @return {object} a complete config, safe to price with.
176
- */
177
76
  export declare function mergePricingConfig(stored?: Partial<PricingConfig>): PricingConfig;
178
- /** The terms agreed with one customer. Stored at `institutes/{oId}.pricing`. */
179
77
  export type InstitutePricing = {
180
- /** The roll we quoted on. Defaults to the institute's own student count. */
181
78
  students?: number;
182
- /** Y10 placements quoted. Undefined means the year group isn't on work experience. */
183
79
  wexY10?: number;
184
- /** Y12 placements quoted. Undefined means the year group isn't on work experience. */
185
80
  wexY12?: number;
186
- /** Take the partial-product discount off the events fee. */
187
81
  eventsDiscount?: boolean;
188
- /** Take the partial-product discount off the destinations fee. */
189
82
  destinationsDiscount?: boolean;
190
- /** Take the full-platform discount off the whole quote. */
191
83
  fullPlatformDiscount?: boolean;
192
- /** A negotiated discount off the whole quote, as a fraction (0.1 = 10% off). */
193
84
  bespokeDiscount?: number;
194
- /** Why the negotiated discount or the agreed price was given. Admin-only. */
195
85
  discountReason?: string;
196
- /** A price agreed with the customer, which replaces the calculated one. */
197
86
  quoteOverride?: number;
198
- /** Schools in the trust, which set its discount. Trust accounts only. */
199
87
  schools?: number;
200
- /** Schools on the hub. Hub accounts only. */
201
88
  hubSchools?: number;
202
- /** The quote as last saved, for the customer to see. */
203
89
  quote?: InstituteQuoteSnapshot;
204
90
  };
205
- /** A priced institute: the same shape for a school, a trust and a hub. */
206
91
  export type InstituteQuote = {
207
92
  kind: "school" | "hub";
208
- /** Every fee charged, with the working behind it. */
209
93
  lines: PricingLine[];
210
- /** The lines added up, before any whole-quote discount. */
211
94
  subtotal: number;
212
95
  fullPlatformDiscountApplied: number;
213
96
  trustDiscountApplied: number;
214
- /** The rate the trust discount was taken at, as a fraction. */
215
97
  trustDiscountRate: number;
216
98
  bespokeDiscountApplied: number;
217
99
  minFeeApplied: boolean;
218
100
  overrideApplied: boolean;
219
101
  total: number;
220
102
  };
221
- /** What the customer is shown: the total, and what makes it up. No rates. */
222
103
  export type InstituteQuoteSnapshot = {
223
104
  lines: {
224
105
  key: PricingLineKey;
225
106
  label: string;
226
107
  amount: number;
227
108
  }[];
228
- /** The lines added up. */
229
109
  subtotal: number;
230
- /** Everything taken off the lines to reach the total, as one figure. */
231
110
  discountTotal: number;
232
- /** True when the total is a floor or an agreed price above what the lines
233
- * come to, so the breakdown is shown as an illustration, not a sum. */
234
111
  minimum: boolean;
235
112
  total: number;
236
- /** ISO date the quote was last worked out. */
237
113
  updated: string;
238
114
  };
239
- /** What the quote needs to know about the institute being priced. */
240
115
  export type InstituteQuoteInput = {
241
- /** What the institute has, as resolved by `resolveProducts`. */
242
116
  products: {
243
117
  wex?: boolean;
244
118
  events?: boolean;
@@ -246,60 +120,12 @@ export type InstituteQuoteInput = {
246
120
  isHub?: boolean;
247
121
  isTrust?: boolean;
248
122
  };
249
- /** The institute's roll, when the agreed terms don't name one. */
250
123
  students?: number;
251
- /** Schools in the trust, when the agreed terms don't name a number. */
252
124
  schools?: number;
253
- /** Schools on the hub, when the agreed terms don't name a number. */
254
125
  hubSchools?: number;
255
- /** The terms agreed with this customer. */
256
126
  pricing?: InstitutePricing;
257
127
  };
258
- /**
259
- * Price a school and present it as an institute quote.
260
- *
261
- * @param {object} input the roll, the year groups, the products and the terms.
262
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
263
- * @return {object} the quote, ready to render.
264
- */
265
128
  export declare function schoolQuote(input: SchoolPricingInput, config?: PricingConfig): InstituteQuote;
266
- /**
267
- * Price a careers hub as an institute quote: the platform fee, then the schools.
268
- *
269
- * Hubs have no products to discount individually and no minimum fee to fall back
270
- * on, so the only terms that reach them are the negotiated discount and an
271
- * agreed price.
272
- *
273
- * @param {number} schools how many schools the hub brings on.
274
- * @param {object} [pricing] the terms agreed with this hub.
275
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
276
- * @return {object} the quote, ready to render.
277
- */
278
129
  export declare function hubQuote(schools: number, pricing?: InstitutePricing, config?: PricingConfig): InstituteQuote;
279
- /**
280
- * Price an institute on what it actually has and what was agreed with it.
281
- *
282
- * The products come from `resolveProducts` rather than the raw flags, so a trust
283
- * is priced on the union of its schools' products the same way it is gated on
284
- * them, and it is a trust — not any school with a number against it — that earns
285
- * the trust discount. A year group with no placements agreed is a year group not on work
286
- * experience — except where the institute has work experience and neither year
287
- * group has been filled in, which is a quote waiting to be finished rather than
288
- * a school not taking the product, so Y10 is charged at zero to say so.
289
- *
290
- * @param {object} input the institute's products, roll and agreed terms.
291
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
292
- * @return {object} the quote, ready to render.
293
- */
294
130
  export declare function instituteQuote(input: InstituteQuoteInput, config?: PricingConfig): InstituteQuote;
295
- /**
296
- * Reduce a quote to what the customer sees: the fees, and one discount figure.
297
- *
298
- * The base fee isn't itemised to customers, so it is folded into the first fee
299
- * they are shown — and where there is nothing to fold it into, it stands alone
300
- * as the platform fee it is.
301
- *
302
- * @param {object} quote the priced institute.
303
- * @return {object} the snapshot to store on the institute.
304
- */
305
131
  export declare function quoteSnapshot(quote: InstituteQuote): InstituteQuoteSnapshot;
package/lib/pricing.js CHANGED
@@ -1,26 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * The 2026 pricing model.
4
- *
5
- * A quote is a base fee plus a per-product fee, and every per-product fee is
6
- * graduated: the tiers are bands, not lookups, so a school with 1,250 students
7
- * pays the tier 1 rate on its first 1,000 and the tier 2 rate on the next 250.
8
- * Anything above the final threshold keeps paying the final tier's rate.
9
- *
10
- * Work experience is charged per placement (Y10 and Y12 are quoted separately,
11
- * and a year group left blank is a year group not taking the product); events
12
- * and destinations are charged per student. Only events and destinations carry
13
- * a partial-product discount — that is how the pricing sheet has it, and it is
14
- * why `partialProductDiscount` names those two rather than being one number.
15
- *
16
- * A trust is quoted as one account on its combined roll, and earns a discount
17
- * on the whole quote for the number of schools it brings on — a smaller one
18
- * below the threshold, a larger one at or above it.
19
- *
20
- * Every figure lives in `PricingConfig` so it can be stored in Firestore and
21
- * adjusted without a deploy. `DEFAULT_PRICING_CONFIG` is the fallback and the
22
- * seed for that document.
23
- */
24
2
  Object.defineProperty(exports, "__esModule", { value: true });
25
3
  exports.PRICING_CONFIG_PATH = exports.DEFAULT_PRICING_CONFIG = void 0;
26
4
  exports.trustDiscountRate = trustDiscountRate;
@@ -41,20 +19,11 @@ exports.DEFAULT_PRICING_CONFIG = {
41
19
  { students: 2000, placements: 200, wex: 1.5, events: 0.25, destinations: 0.25 },
42
20
  ],
43
21
  partialProductDiscount: { events: 0.2, destinations: 0.2 },
44
- fullPlatformDiscount: 0.2,
22
+ fullPlatformDiscount: 0.1,
45
23
  trust: { threshold: 10, discount: 0.05, largeDiscount: 0.1 },
46
24
  hub: { baseFee: 2000, perSchoolFee: 150 },
47
25
  };
48
- /** Where the adjustable copy of the config lives. */
49
26
  exports.PRICING_CONFIG_PATH = ["adminConfig", "pricing"];
50
- /**
51
- * Charge `quantity` across the bands, each band at its own rate, with anything
52
- * above the final threshold staying on the final band's rate.
53
- *
54
- * @param {number} quantity students or placements, depending on the product.
55
- * @param {Array} bands each band's upper threshold and its rate.
56
- * @return {Array} one entry per band that charged something.
57
- */
58
27
  function graduated(quantity, bands) {
59
28
  if (!(quantity > 0) || !bands.length)
60
29
  return [];
@@ -66,8 +35,6 @@ function graduated(quantity, bands) {
66
35
  if (inBand > 0)
67
36
  lines.push({ tier: index + 1, quantity: inBand, rate: band.rate, amount: inBand * band.rate });
68
37
  });
69
- // Everything past the last threshold keeps paying the last rate, so it belongs
70
- // in the last band rather than as a second row at the same rate.
71
38
  const last = bands[bands.length - 1];
72
39
  const overflow = Math.max(quantity - last.limit, 0);
73
40
  if (overflow > 0) {
@@ -85,33 +52,14 @@ function graduated(quantity, bands) {
85
52
  const sum = (values) => values.reduce((total, value) => total + value, 0);
86
53
  const clampFraction = (value) => Math.min(Math.max(value || 0, 0), 1);
87
54
  const bandsTotal = (bands) => sum(bands.map((b) => b.amount));
88
- /**
89
- * What a trust comes off its quote for its size.
90
- *
91
- * A trust is more than one school, so a count below two is a single school and
92
- * earns nothing — the discount is for bringing schools with you.
93
- *
94
- * @param {number} [schools] schools in the trust.
95
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
96
- * @return {number} the discount, as a fraction of the quote.
97
- */
98
55
  function trustDiscountRate(schools, config = exports.DEFAULT_PRICING_CONFIG) {
99
56
  const count = schools || 0;
100
57
  if (count < 2)
101
58
  return 0;
102
59
  return count >= config.trust.threshold ? config.trust.largeDiscount : config.trust.discount;
103
60
  }
104
- /**
105
- * Price a school, trust or hub-school quote.
106
- *
107
- * Products the school isn't taking are left out of `lines` entirely, so the
108
- * breakdown only ever shows what is being charged for.
109
- *
110
- * @param {object} input the roll, the year groups and the products taken.
111
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
112
- * @return {object} the total, and the working that reached it.
113
- */
114
61
  function calculateSchoolPrice(input, config = exports.DEFAULT_PRICING_CONFIG) {
62
+ const schoolCount = Math.max(input.schools || 0, 0);
115
63
  const students = input.students || 0;
116
64
  const tiers = config.tiers;
117
65
  const perPlacement = tiers.map((t) => ({ limit: t.placements, rate: t.wex }));
@@ -135,7 +83,17 @@ function calculateSchoolPrice(input, config = exports.DEFAULT_PRICING_CONFIG) {
135
83
  };
136
84
  addPerStudent("events", "Employer events", input.events, input.eventsDiscount);
137
85
  addPerStudent("destinations", "Destinations", input.destinations, input.destinationsDiscount);
138
- const subtotal = sum(lines.map((l) => l.amount));
86
+ const scaledLines = schoolCount > 1 ? lines.map((line) => ({
87
+ ...line,
88
+ amount: line.amount * schoolCount,
89
+ ...(line.undiscounted !== undefined ? { undiscounted: line.undiscounted * schoolCount } : {}),
90
+ bands: line.bands.map((band) => ({
91
+ ...band,
92
+ quantity: band.quantity * schoolCount,
93
+ amount: band.amount * schoolCount,
94
+ })),
95
+ })) : lines;
96
+ const subtotal = sum(scaledLines.map((l) => l.amount));
139
97
  const discounted = input.fullPlatformDiscount ? subtotal * (1 - config.fullPlatformDiscount) : subtotal;
140
98
  const trustRate = trustDiscountRate(input.schools, config);
141
99
  const trusted = discounted * (1 - trustRate);
@@ -143,7 +101,7 @@ function calculateSchoolPrice(input, config = exports.DEFAULT_PRICING_CONFIG) {
143
101
  const floored = Math.max(negotiated, config.minFee);
144
102
  const overridden = input.quoteOverride !== undefined && input.quoteOverride >= 0;
145
103
  return {
146
- lines,
104
+ lines: scaledLines,
147
105
  subtotal,
148
106
  fullPlatformDiscountApplied: subtotal - discounted,
149
107
  trustDiscountApplied: discounted - trusted,
@@ -156,27 +114,10 @@ function calculateSchoolPrice(input, config = exports.DEFAULT_PRICING_CONFIG) {
156
114
  total: overridden ? input.quoteOverride : floored,
157
115
  };
158
116
  }
159
- /**
160
- * Price a careers hub: the platform fee, plus a fee for every school on it.
161
- *
162
- * @param {number} schools how many schools the hub brings on.
163
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
164
- * @return {object} the platform fee, the school fees and the total.
165
- */
166
117
  function calculateHubPrice(schools, config = exports.DEFAULT_PRICING_CONFIG) {
167
118
  const schoolFees = Math.max(schools || 0, 0) * config.hub.perSchoolFee;
168
119
  return { baseFee: config.hub.baseFee, schoolFees, total: config.hub.baseFee + schoolFees };
169
120
  }
170
- /**
171
- * Fill a stored config out with the defaults.
172
- *
173
- * The Firestore document is hand-edited, so treat every branch of it as
174
- * possibly absent — a config saved before a tier was added should still price,
175
- * rather than throwing on a missing rate.
176
- *
177
- * @param {object} [stored] whatever the Firestore document holds.
178
- * @return {object} a complete config, safe to price with.
179
- */
180
121
  function mergePricingConfig(stored) {
181
122
  const d = exports.DEFAULT_PRICING_CONFIG;
182
123
  if (!stored)
@@ -191,28 +132,9 @@ function mergePricingConfig(stored) {
191
132
  hub: { ...d.hub, ...stored.hub },
192
133
  };
193
134
  }
194
- /**
195
- * Price a school and present it as an institute quote.
196
- *
197
- * @param {object} input the roll, the year groups, the products and the terms.
198
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
199
- * @return {object} the quote, ready to render.
200
- */
201
135
  function schoolQuote(input, config = exports.DEFAULT_PRICING_CONFIG) {
202
136
  return { kind: "school", ...calculateSchoolPrice(input, config) };
203
137
  }
204
- /**
205
- * Price a careers hub as an institute quote: the platform fee, then the schools.
206
- *
207
- * Hubs have no products to discount individually and no minimum fee to fall back
208
- * on, so the only terms that reach them are the negotiated discount and an
209
- * agreed price.
210
- *
211
- * @param {number} schools how many schools the hub brings on.
212
- * @param {object} [pricing] the terms agreed with this hub.
213
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
214
- * @return {object} the quote, ready to render.
215
- */
216
138
  function hubQuote(schools, pricing, config = exports.DEFAULT_PRICING_CONFIG) {
217
139
  const count = Math.max(schools || 0, 0);
218
140
  const hub = calculateHubPrice(count, config);
@@ -243,21 +165,6 @@ function hubQuote(schools, pricing, config = exports.DEFAULT_PRICING_CONFIG) {
243
165
  total: overridden ? pricing?.quoteOverride : negotiated,
244
166
  };
245
167
  }
246
- /**
247
- * Price an institute on what it actually has and what was agreed with it.
248
- *
249
- * The products come from `resolveProducts` rather than the raw flags, so a trust
250
- * is priced on the union of its schools' products the same way it is gated on
251
- * them, and it is a trust — not any school with a number against it — that earns
252
- * the trust discount. A year group with no placements agreed is a year group not on work
253
- * experience — except where the institute has work experience and neither year
254
- * group has been filled in, which is a quote waiting to be finished rather than
255
- * a school not taking the product, so Y10 is charged at zero to say so.
256
- *
257
- * @param {object} input the institute's products, roll and agreed terms.
258
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
259
- * @return {object} the quote, ready to render.
260
- */
261
168
  function instituteQuote(input, config = exports.DEFAULT_PRICING_CONFIG) {
262
169
  const pricing = input.pricing;
263
170
  if (input.products.isHub) {
@@ -281,16 +188,6 @@ function instituteQuote(input, config = exports.DEFAULT_PRICING_CONFIG) {
281
188
  quoteOverride: pricing?.quoteOverride,
282
189
  }, config);
283
190
  }
284
- /**
285
- * Reduce a quote to what the customer sees: the fees, and one discount figure.
286
- *
287
- * The base fee isn't itemised to customers, so it is folded into the first fee
288
- * they are shown — and where there is nothing to fold it into, it stands alone
289
- * as the platform fee it is.
290
- *
291
- * @param {object} quote the priced institute.
292
- * @return {object} the snapshot to store on the institute.
293
- */
294
191
  function quoteSnapshot(quote) {
295
192
  const itemised = quote.lines.filter((l) => l.key !== "base");
296
193
  const base = quote.lines.find((l) => l.key === "base")?.amount || 0;
@@ -305,9 +202,6 @@ function quoteSnapshot(quote) {
305
202
  return {
306
203
  lines,
307
204
  subtotal,
308
- // The customer is shown the fees and one discount, so whatever separates
309
- // the two — the whole-quote discounts, an agreed price below them — is
310
- // that one figure, and a total above the fees is flagged instead.
311
205
  discountTotal: Math.max(subtotal - quote.total, 0),
312
206
  minimum: quote.total > subtotal,
313
207
  total: quote.total,
@@ -1 +1 @@
1
- {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"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"}
@@ -117,17 +117,18 @@ describe("mergePricingConfig", () => {
117
117
  });
118
118
  });
119
119
  describe("the trust discount", () => {
120
- const trust = (schools) => ({ students: 10000, events: true, destinations: true, schools });
121
- it("gives a trust under ten schools 5% off the whole quote", () => {
122
- const result = (0, pricing_1.calculateSchoolPrice)(trust(9));
123
- const plain = (0, pricing_1.calculateSchoolPrice)({ ...trust(9), schools: undefined });
120
+ it("prices a trust as one school price multiplied by the trust size before the discount", () => {
121
+ const base = { students: 500, events: true, destinations: true };
122
+ const single = (0, pricing_1.calculateSchoolPrice)(base);
123
+ const result = (0, pricing_1.calculateSchoolPrice)({ ...base, schools: 9 });
124
124
  expect(result.trustDiscountRate).toBe(0.05);
125
- expect(result.total).toBeCloseTo(plain.total * 0.95, 6);
126
- expect(result.trustDiscountApplied).toBeCloseTo(plain.total * 0.05, 6);
125
+ expect(result.total).toBeCloseTo(single.total * 9 * 0.95, 6);
126
+ expect(result.trustDiscountApplied).toBeCloseTo(single.total * 9 * 0.05, 6);
127
127
  });
128
128
  it("gives a trust of ten schools or more 10%", () => {
129
- expect((0, pricing_1.calculateSchoolPrice)(trust(10)).trustDiscountRate).toBe(0.1);
130
- expect((0, pricing_1.calculateSchoolPrice)(trust(40)).trustDiscountRate).toBe(0.1);
129
+ const base = { students: 500, events: true, destinations: true };
130
+ expect((0, pricing_1.calculateSchoolPrice)({ ...base, schools: 10 }).trustDiscountRate).toBe(0.1);
131
+ expect((0, pricing_1.calculateSchoolPrice)({ ...base, schools: 40 }).trustDiscountRate).toBe(0.1);
131
132
  });
132
133
  it("gives a single school nothing, however it is counted", () => {
133
134
  expect((0, pricing_1.trustDiscountRate)(undefined)).toBe(0);
@@ -136,13 +137,14 @@ describe("the trust discount", () => {
136
137
  });
137
138
  it("is taken after the full-platform discount and before the negotiated one", () => {
138
139
  const input = {
139
- students: 10000, wexY10: 500, events: true, destinations: true,
140
+ students: 500, events: true, destinations: true,
140
141
  fullPlatformDiscount: true, schools: 12, bespokeDiscount: 0.1,
141
142
  };
142
143
  const result = (0, pricing_1.calculateSchoolPrice)(input);
143
- const expected = result.subtotal * 0.8 * 0.9 * 0.9;
144
+ const single = (0, pricing_1.calculateSchoolPrice)({ students: 500, events: true, destinations: true });
145
+ const expected = single.subtotal * 12 * 0.8 * 0.9 * 0.9;
144
146
  expect(result.total).toBeCloseTo(expected, 6);
145
- expect(result.trustDiscountApplied).toBeCloseTo(result.subtotal * 0.8 * 0.1, 6);
147
+ expect(result.trustDiscountApplied).toBeCloseTo(single.subtotal * 12 * 0.8 * 0.1, 6);
146
148
  });
147
149
  it("only reaches an institute that is actually a trust", () => {
148
150
  const priced = (isTrust) => (0, pricing_1.instituteQuote)({
@@ -1 +1 @@
1
- {"version":3,"file":"pricing.test.js","sourceRoot":"","sources":["../src/pricing.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pricing.test.js","sourceRoot":"","sources":["../src/pricing.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "private": false,
3
3
  "name": "placementt-core",
4
4
  "author": "Placementt",
5
- "version": "1.400.1069",
5
+ "version": "1.400.1071",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
8
8
  "scripts": {
@@ -147,21 +147,20 @@ describe("mergePricingConfig", () => {
147
147
  });
148
148
 
149
149
  describe("the trust discount", () => {
150
- const trust = (schools: number): SchoolPricingInput =>
151
- ({students: 10000, events: true, destinations: true, schools});
152
-
153
- it("gives a trust under ten schools 5% off the whole quote", () => {
154
- const result = calculateSchoolPrice(trust(9));
155
- const plain = calculateSchoolPrice({...trust(9), schools: undefined});
150
+ it("prices a trust as one school price multiplied by the trust size before the discount", () => {
151
+ const base = {students: 500, events: true, destinations: true};
152
+ const single = calculateSchoolPrice(base);
153
+ const result = calculateSchoolPrice({...base, schools: 9});
156
154
 
157
155
  expect(result.trustDiscountRate).toBe(0.05);
158
- expect(result.total).toBeCloseTo(plain.total * 0.95, 6);
159
- expect(result.trustDiscountApplied).toBeCloseTo(plain.total * 0.05, 6);
156
+ expect(result.total).toBeCloseTo(single.total * 9 * 0.95, 6);
157
+ expect(result.trustDiscountApplied).toBeCloseTo(single.total * 9 * 0.05, 6);
160
158
  });
161
159
 
162
160
  it("gives a trust of ten schools or more 10%", () => {
163
- expect(calculateSchoolPrice(trust(10)).trustDiscountRate).toBe(0.1);
164
- expect(calculateSchoolPrice(trust(40)).trustDiscountRate).toBe(0.1);
161
+ const base = {students: 500, events: true, destinations: true};
162
+ expect(calculateSchoolPrice({...base, schools: 10}).trustDiscountRate).toBe(0.1);
163
+ expect(calculateSchoolPrice({...base, schools: 40}).trustDiscountRate).toBe(0.1);
165
164
  });
166
165
 
167
166
  it("gives a single school nothing, however it is counted", () => {
@@ -172,14 +171,15 @@ describe("the trust discount", () => {
172
171
 
173
172
  it("is taken after the full-platform discount and before the negotiated one", () => {
174
173
  const input: SchoolPricingInput = {
175
- students: 10000, wexY10: 500, events: true, destinations: true,
174
+ students: 500, events: true, destinations: true,
176
175
  fullPlatformDiscount: true, schools: 12, bespokeDiscount: 0.1,
177
176
  };
178
177
  const result = calculateSchoolPrice(input);
179
- const expected = result.subtotal * 0.8 * 0.9 * 0.9;
178
+ const single = calculateSchoolPrice({students: 500, events: true, destinations: true});
179
+ const expected = single.subtotal * 12 * 0.8 * 0.9 * 0.9;
180
180
 
181
181
  expect(result.total).toBeCloseTo(expected, 6);
182
- expect(result.trustDiscountApplied).toBeCloseTo(result.subtotal * 0.8 * 0.1, 6);
182
+ expect(result.trustDiscountApplied).toBeCloseTo(single.subtotal * 12 * 0.8 * 0.1, 6);
183
183
  });
184
184
 
185
185
  it("only reaches an institute that is actually a trust", () => {
package/src/pricing.ts CHANGED
@@ -1,61 +1,23 @@
1
- /**
2
- * The 2026 pricing model.
3
- *
4
- * A quote is a base fee plus a per-product fee, and every per-product fee is
5
- * graduated: the tiers are bands, not lookups, so a school with 1,250 students
6
- * pays the tier 1 rate on its first 1,000 and the tier 2 rate on the next 250.
7
- * Anything above the final threshold keeps paying the final tier's rate.
8
- *
9
- * Work experience is charged per placement (Y10 and Y12 are quoted separately,
10
- * and a year group left blank is a year group not taking the product); events
11
- * and destinations are charged per student. Only events and destinations carry
12
- * a partial-product discount — that is how the pricing sheet has it, and it is
13
- * why `partialProductDiscount` names those two rather than being one number.
14
- *
15
- * A trust is quoted as one account on its combined roll, and earns a discount
16
- * on the whole quote for the number of schools it brings on — a smaller one
17
- * below the threshold, a larger one at or above it.
18
- *
19
- * Every figure lives in `PricingConfig` so it can be stored in Firestore and
20
- * adjusted without a deploy. `DEFAULT_PRICING_CONFIG` is the fallback and the
21
- * seed for that document.
22
- */
23
-
24
- /** One band of the graduated fee, with the rate each product charges in it. */
1
+
25
2
  export type PricingTier = {
26
- /** Upper bound of the band, in students (events and destinations). */
27
3
  students: number,
28
- /** Upper bound of the band, in placements (work experience). */
29
4
  placements: number,
30
- /** £ per placement. */
31
5
  wex: number,
32
- /** £ per student. */
33
6
  events: number,
34
- /** £ per student. */
35
7
  destinations: number,
36
8
  };
37
9
 
38
10
  export type PricingConfig = {
39
- /** No school is quoted below this, however little the products come to. */
40
11
  minFee: number,
41
- /** Charged on every quote. Not itemised to the customer. */
42
12
  baseFee: number,
43
- /** In ascending order of threshold. The last band's rate runs to infinity. */
44
13
  tiers: PricingTier[],
45
- /** Taken off a single product's fee, as a fraction (0.2 = 20% off). */
46
14
  partialProductDiscount: {events: number, destinations: number},
47
- /** Taken off the whole quote when a school takes the full platform. */
48
15
  fullPlatformDiscount: number,
49
- /** Taken off the whole quote for a trust, on how many schools it brings. */
50
16
  trust: {
51
- /** Schools at which the larger discount takes over. */
52
17
  threshold: number,
53
- /** Fraction off a trust below the threshold. */
54
18
  discount: number,
55
- /** Fraction off a trust at or above it. */
56
19
  largeDiscount: number,
57
20
  },
58
- /** Careers hubs are a flat platform fee plus a fee for each school. */
59
21
  hub: {baseFee: number, perSchoolFee: number},
60
22
  };
61
23
 
@@ -69,36 +31,24 @@ export const DEFAULT_PRICING_CONFIG: PricingConfig = {
69
31
  {students: 2000, placements: 200, wex: 1.5, events: 0.25, destinations: 0.25},
70
32
  ],
71
33
  partialProductDiscount: {events: 0.2, destinations: 0.2},
72
- fullPlatformDiscount: 0.2,
34
+ fullPlatformDiscount: 0.1,
73
35
  trust: {threshold: 10, discount: 0.05, largeDiscount: 0.1},
74
36
  hub: {baseFee: 2000, perSchoolFee: 150},
75
37
  };
76
38
 
77
- /** Where the adjustable copy of the config lives. */
78
39
  export const PRICING_CONFIG_PATH = ["adminConfig", "pricing"];
79
40
 
80
41
  export type SchoolPricingInput = {
81
- /** Roll size. Drives the events and destinations fees. */
82
42
  students?: number,
83
- /** Y10 placements. Undefined means the year group isn't taking work experience. */
84
43
  wexY10?: number,
85
- /** Y12 placements. Undefined means the year group isn't taking work experience. */
86
44
  wexY12?: number,
87
45
  events?: boolean,
88
- /** Discount the events fee on its own. */
89
46
  eventsDiscount?: boolean,
90
47
  destinations?: boolean,
91
- /** Discount the destinations fee on its own. */
92
48
  destinationsDiscount?: boolean,
93
- /** Discount the whole quote, for a school taking the full platform. */
94
49
  fullPlatformDiscount?: boolean,
95
- /** Schools in the trust being quoted. Two or more earns a trust discount. */
96
50
  schools?: number,
97
- /** A negotiated discount off the whole quote, as a fraction (0.1 = 10% off).
98
- * Taken after the full-platform discount and before the minimum fee. */
99
51
  bespokeDiscount?: number,
100
- /** A price agreed with the customer, which replaces everything above it —
101
- * the discounts, the minimum fee and the total the products came to. */
102
52
  quoteOverride?: number,
103
53
  };
104
54
 
@@ -108,19 +58,13 @@ export type PricingLineKey =
108
58
  export type PricingLine = {
109
59
  key: PricingLineKey,
110
60
  label: string,
111
- /** What the product costs after any partial-product discount. */
112
61
  amount: number,
113
- /** The fee before its partial-product discount, when one was applied. */
114
62
  undiscounted?: number,
115
- /** How the amount was reached, band by band. Empty for the base fee. */
116
63
  bands: PricingBand[],
117
64
  };
118
65
 
119
- /** One band's contribution to a product's fee, for showing the working. */
120
66
  export type PricingBand = {
121
- /** 1-indexed, matching the tier's position in the config. */
122
67
  tier: number,
123
- /** Units charged in this band. */
124
68
  quantity: number,
125
69
  rate: number,
126
70
  amount: number,
@@ -128,38 +72,22 @@ export type PricingBand = {
128
72
 
129
73
  export type SchoolPricingResult = {
130
74
  lines: PricingLine[],
131
- /** The lines added up, before the full-platform discount. */
132
75
  subtotal: number,
133
- /** £ taken off the subtotal by the full-platform discount. */
134
76
  fullPlatformDiscountApplied: number,
135
- /** £ taken off by the trust discount, after the full-platform one. */
136
77
  trustDiscountApplied: number,
137
- /** The rate that trust discount was taken at, as a fraction. */
138
78
  trustDiscountRate: number,
139
- /** £ taken off by the negotiated discount, after the trust one. */
140
79
  bespokeDiscountApplied: number,
141
- /** True when the minimum fee, not the products, set the price. */
142
80
  minFeeApplied: boolean,
143
- /** True when an agreed price replaced the calculated one. */
144
81
  overrideApplied: boolean,
145
82
  total: number,
146
83
  };
147
84
 
148
85
  export type HubPricingResult = {
149
86
  baseFee: number,
150
- /** The per-school fee multiplied by the number of schools. */
151
87
  schoolFees: number,
152
88
  total: number,
153
89
  };
154
90
 
155
- /**
156
- * Charge `quantity` across the bands, each band at its own rate, with anything
157
- * above the final threshold staying on the final band's rate.
158
- *
159
- * @param {number} quantity students or placements, depending on the product.
160
- * @param {Array} bands each band's upper threshold and its rate.
161
- * @return {Array} one entry per band that charged something.
162
- */
163
91
  function graduated(quantity: number, bands: {limit: number, rate: number}[]): PricingBand[] {
164
92
  if (!(quantity > 0) || !bands.length) return [];
165
93
 
@@ -172,8 +100,6 @@ function graduated(quantity: number, bands: {limit: number, rate: number}[]): Pr
172
100
  if (inBand > 0) lines.push({tier: index + 1, quantity: inBand, rate: band.rate, amount: inBand * band.rate});
173
101
  });
174
102
 
175
- // Everything past the last threshold keeps paying the last rate, so it belongs
176
- // in the last band rather than as a second row at the same rate.
177
103
  const last = bands[bands.length - 1];
178
104
  const overflow = Math.max(quantity - last.limit, 0);
179
105
  if (overflow > 0) {
@@ -195,16 +121,6 @@ const clampFraction = (value?: number) => Math.min(Math.max(value || 0, 0), 1);
195
121
 
196
122
  const bandsTotal = (bands: PricingBand[]) => sum(bands.map((b) => b.amount));
197
123
 
198
- /**
199
- * What a trust comes off its quote for its size.
200
- *
201
- * A trust is more than one school, so a count below two is a single school and
202
- * earns nothing — the discount is for bringing schools with you.
203
- *
204
- * @param {number} [schools] schools in the trust.
205
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
206
- * @return {number} the discount, as a fraction of the quote.
207
- */
208
124
  export function trustDiscountRate(
209
125
  schools?: number,
210
126
  config: PricingConfig = DEFAULT_PRICING_CONFIG,
@@ -214,20 +130,11 @@ export function trustDiscountRate(
214
130
  return count >= config.trust.threshold ? config.trust.largeDiscount : config.trust.discount;
215
131
  }
216
132
 
217
- /**
218
- * Price a school, trust or hub-school quote.
219
- *
220
- * Products the school isn't taking are left out of `lines` entirely, so the
221
- * breakdown only ever shows what is being charged for.
222
- *
223
- * @param {object} input the roll, the year groups and the products taken.
224
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
225
- * @return {object} the total, and the working that reached it.
226
- */
227
133
  export function calculateSchoolPrice(
228
134
  input: SchoolPricingInput,
229
135
  config: PricingConfig = DEFAULT_PRICING_CONFIG,
230
136
  ): SchoolPricingResult {
137
+ const schoolCount = Math.max(input.schools || 0, 0);
231
138
  const students = input.students || 0;
232
139
  const tiers = config.tiers;
233
140
 
@@ -255,7 +162,18 @@ export function calculateSchoolPrice(
255
162
  addPerStudent("events", "Employer events", input.events, input.eventsDiscount);
256
163
  addPerStudent("destinations", "Destinations", input.destinations, input.destinationsDiscount);
257
164
 
258
- const subtotal = sum(lines.map((l) => l.amount));
165
+ const scaledLines = schoolCount > 1 ? lines.map((line) => ({
166
+ ...line,
167
+ amount: line.amount * schoolCount,
168
+ ...(line.undiscounted !== undefined ? {undiscounted: line.undiscounted * schoolCount} : {}),
169
+ bands: line.bands.map((band) => ({
170
+ ...band,
171
+ quantity: band.quantity * schoolCount,
172
+ amount: band.amount * schoolCount,
173
+ })),
174
+ })) : lines;
175
+
176
+ const subtotal = sum(scaledLines.map((l) => l.amount));
259
177
  const discounted = input.fullPlatformDiscount ? subtotal * (1 - config.fullPlatformDiscount) : subtotal;
260
178
  const trustRate = trustDiscountRate(input.schools, config);
261
179
  const trusted = discounted * (1 - trustRate);
@@ -264,7 +182,7 @@ export function calculateSchoolPrice(
264
182
  const overridden = input.quoteOverride !== undefined && input.quoteOverride >= 0;
265
183
 
266
184
  return {
267
- lines,
185
+ lines: scaledLines,
268
186
  subtotal,
269
187
  fullPlatformDiscountApplied: subtotal - discounted,
270
188
  trustDiscountApplied: discounted - trusted,
@@ -278,13 +196,6 @@ export function calculateSchoolPrice(
278
196
  };
279
197
  }
280
198
 
281
- /**
282
- * Price a careers hub: the platform fee, plus a fee for every school on it.
283
- *
284
- * @param {number} schools how many schools the hub brings on.
285
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
286
- * @return {object} the platform fee, the school fees and the total.
287
- */
288
199
  export function calculateHubPrice(
289
200
  schools: number,
290
201
  config: PricingConfig = DEFAULT_PRICING_CONFIG,
@@ -293,16 +204,6 @@ export function calculateHubPrice(
293
204
  return {baseFee: config.hub.baseFee, schoolFees, total: config.hub.baseFee + schoolFees};
294
205
  }
295
206
 
296
- /**
297
- * Fill a stored config out with the defaults.
298
- *
299
- * The Firestore document is hand-edited, so treat every branch of it as
300
- * possibly absent — a config saved before a tier was added should still price,
301
- * rather than throwing on a missing rate.
302
- *
303
- * @param {object} [stored] whatever the Firestore document holds.
304
- * @return {object} a complete config, safe to price with.
305
- */
306
207
  export function mergePricingConfig(stored?: Partial<PricingConfig>): PricingConfig {
307
208
  const d = DEFAULT_PRICING_CONFIG;
308
209
  if (!stored) return d;
@@ -318,59 +219,27 @@ export function mergePricingConfig(stored?: Partial<PricingConfig>): PricingConf
318
219
  };
319
220
  }
320
221
 
321
- /* -------------------------------------------------------------------------
322
- * An institute's own quote.
323
- *
324
- * The calculator above prices a hypothetical school. What an actual customer
325
- * pays is that calculation plus the terms agreed with them — the year groups
326
- * and roll we quoted on, which discounts we gave, and any price we simply shook
327
- * hands on — so those live on the institute, in `InstitutePricing`, and the
328
- * quote is worked out from the two together.
329
- *
330
- * A school never reads the pricing configuration itself (it is admin data, and
331
- * a school has no business seeing the tiers), so the agreed quote is snapshotted
332
- * onto the institute as `InstitutePricing.quote` whenever an admin saves. That
333
- * snapshot is what the school's own billing page shows.
334
- * ---------------------------------------------------------------------- */
335
-
336
- /** The terms agreed with one customer. Stored at `institutes/{oId}.pricing`. */
337
222
  export type InstitutePricing = {
338
- /** The roll we quoted on. Defaults to the institute's own student count. */
339
223
  students?: number,
340
- /** Y10 placements quoted. Undefined means the year group isn't on work experience. */
341
224
  wexY10?: number,
342
- /** Y12 placements quoted. Undefined means the year group isn't on work experience. */
343
225
  wexY12?: number,
344
- /** Take the partial-product discount off the events fee. */
345
226
  eventsDiscount?: boolean,
346
- /** Take the partial-product discount off the destinations fee. */
347
227
  destinationsDiscount?: boolean,
348
- /** Take the full-platform discount off the whole quote. */
349
228
  fullPlatformDiscount?: boolean,
350
- /** A negotiated discount off the whole quote, as a fraction (0.1 = 10% off). */
351
229
  bespokeDiscount?: number,
352
- /** Why the negotiated discount or the agreed price was given. Admin-only. */
353
230
  discountReason?: string,
354
- /** A price agreed with the customer, which replaces the calculated one. */
355
231
  quoteOverride?: number,
356
- /** Schools in the trust, which set its discount. Trust accounts only. */
357
232
  schools?: number,
358
- /** Schools on the hub. Hub accounts only. */
359
233
  hubSchools?: number,
360
- /** The quote as last saved, for the customer to see. */
361
234
  quote?: InstituteQuoteSnapshot,
362
235
  };
363
236
 
364
- /** A priced institute: the same shape for a school, a trust and a hub. */
365
237
  export type InstituteQuote = {
366
238
  kind: "school"|"hub",
367
- /** Every fee charged, with the working behind it. */
368
239
  lines: PricingLine[],
369
- /** The lines added up, before any whole-quote discount. */
370
240
  subtotal: number,
371
241
  fullPlatformDiscountApplied: number,
372
242
  trustDiscountApplied: number,
373
- /** The rate the trust discount was taken at, as a fraction. */
374
243
  trustDiscountRate: number,
375
244
  bespokeDiscountApplied: number,
376
245
  minFeeApplied: boolean,
@@ -378,42 +247,23 @@ export type InstituteQuote = {
378
247
  total: number,
379
248
  };
380
249
 
381
- /** What the customer is shown: the total, and what makes it up. No rates. */
382
250
  export type InstituteQuoteSnapshot = {
383
251
  lines: {key: PricingLineKey, label: string, amount: number}[],
384
- /** The lines added up. */
385
252
  subtotal: number,
386
- /** Everything taken off the lines to reach the total, as one figure. */
387
253
  discountTotal: number,
388
- /** True when the total is a floor or an agreed price above what the lines
389
- * come to, so the breakdown is shown as an illustration, not a sum. */
390
254
  minimum: boolean,
391
255
  total: number,
392
- /** ISO date the quote was last worked out. */
393
256
  updated: string,
394
257
  };
395
258
 
396
- /** What the quote needs to know about the institute being priced. */
397
259
  export type InstituteQuoteInput = {
398
- /** What the institute has, as resolved by `resolveProducts`. */
399
260
  products: {wex?: boolean, events?: boolean, destinations?: boolean, isHub?: boolean, isTrust?: boolean},
400
- /** The institute's roll, when the agreed terms don't name one. */
401
261
  students?: number,
402
- /** Schools in the trust, when the agreed terms don't name a number. */
403
262
  schools?: number,
404
- /** Schools on the hub, when the agreed terms don't name a number. */
405
263
  hubSchools?: number,
406
- /** The terms agreed with this customer. */
407
264
  pricing?: InstitutePricing,
408
265
  };
409
266
 
410
- /**
411
- * Price a school and present it as an institute quote.
412
- *
413
- * @param {object} input the roll, the year groups, the products and the terms.
414
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
415
- * @return {object} the quote, ready to render.
416
- */
417
267
  export function schoolQuote(
418
268
  input: SchoolPricingInput,
419
269
  config: PricingConfig = DEFAULT_PRICING_CONFIG,
@@ -421,18 +271,6 @@ export function schoolQuote(
421
271
  return {kind: "school", ...calculateSchoolPrice(input, config)};
422
272
  }
423
273
 
424
- /**
425
- * Price a careers hub as an institute quote: the platform fee, then the schools.
426
- *
427
- * Hubs have no products to discount individually and no minimum fee to fall back
428
- * on, so the only terms that reach them are the negotiated discount and an
429
- * agreed price.
430
- *
431
- * @param {number} schools how many schools the hub brings on.
432
- * @param {object} [pricing] the terms agreed with this hub.
433
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
434
- * @return {object} the quote, ready to render.
435
- */
436
274
  export function hubQuote(
437
275
  schools: number,
438
276
  pricing?: InstitutePricing,
@@ -471,21 +309,6 @@ export function hubQuote(
471
309
  };
472
310
  }
473
311
 
474
- /**
475
- * Price an institute on what it actually has and what was agreed with it.
476
- *
477
- * The products come from `resolveProducts` rather than the raw flags, so a trust
478
- * is priced on the union of its schools' products the same way it is gated on
479
- * them, and it is a trust — not any school with a number against it — that earns
480
- * the trust discount. A year group with no placements agreed is a year group not on work
481
- * experience — except where the institute has work experience and neither year
482
- * group has been filled in, which is a quote waiting to be finished rather than
483
- * a school not taking the product, so Y10 is charged at zero to say so.
484
- *
485
- * @param {object} input the institute's products, roll and agreed terms.
486
- * @param {object} [config] the figures to price with. Defaults to the 2026 sheet.
487
- * @return {object} the quote, ready to render.
488
- */
489
312
  export function instituteQuote(
490
313
  input: InstituteQuoteInput,
491
314
  config: PricingConfig = DEFAULT_PRICING_CONFIG,
@@ -516,16 +339,6 @@ export function instituteQuote(
516
339
  }, config);
517
340
  }
518
341
 
519
- /**
520
- * Reduce a quote to what the customer sees: the fees, and one discount figure.
521
- *
522
- * The base fee isn't itemised to customers, so it is folded into the first fee
523
- * they are shown — and where there is nothing to fold it into, it stands alone
524
- * as the platform fee it is.
525
- *
526
- * @param {object} quote the priced institute.
527
- * @return {object} the snapshot to store on the institute.
528
- */
529
342
  export function quoteSnapshot(quote: InstituteQuote): InstituteQuoteSnapshot {
530
343
  const itemised = quote.lines.filter((l) => l.key !== "base");
531
344
  const base = quote.lines.find((l) => l.key === "base")?.amount || 0;
@@ -543,9 +356,6 @@ export function quoteSnapshot(quote: InstituteQuote): InstituteQuoteSnapshot {
543
356
  return {
544
357
  lines,
545
358
  subtotal,
546
- // The customer is shown the fees and one discount, so whatever separates
547
- // the two — the whole-quote discounts, an agreed price below them — is
548
- // that one figure, and a total above the fees is flagged instead.
549
359
  discountTotal: Math.max(subtotal - quote.total, 0),
550
360
  minimum: quote.total > subtotal,
551
361
  total: quote.total,