@cyc-seattle/platform 1.47.0 → 1.49.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/planning/activities.d.ts +1 -1
- package/dist/planning/activities.js +1 -3
- package/dist/planning/bonus.js +1 -4
- package/dist/planning/context.d.ts +0 -9
- package/dist/planning/context.js +0 -12
- package/dist/planning/dates.d.ts +0 -8
- package/dist/planning/dates.js +0 -19
- package/dist/planning/engine.js +3 -3
- package/dist/planning/measures.d.ts +1 -1
- package/dist/planning/measures.js +2 -4
- package/dist/planning/pools.d.ts +1 -1
- package/dist/planning/pools.js +3 -7
- package/dist/planning/revenue.js +1 -1
- package/dist/planning/staffing.js +1 -1
- package/dist/planning/types.d.ts +10 -2
- package/dist/schema/index.d.ts +17 -1
- package/package.json +1 -1
|
@@ -15,7 +15,7 @@ export interface ScheduledActivity {
|
|
|
15
15
|
uses_boats: boolean;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* The plan
|
|
18
|
+
* The plan's activities. Paid time is start to end plus setup and put-away; on-water time is
|
|
19
19
|
* `on_water_minutes` if set, otherwise the whole span, and zero when the activity uses no boats.
|
|
20
20
|
*/
|
|
21
21
|
export declare function scheduleActivities(ctx: Context): ScheduledActivity[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { minutesOf } from "./dates.js";
|
|
2
2
|
/**
|
|
3
|
-
* The plan
|
|
3
|
+
* The plan's activities. Paid time is start to end plus setup and put-away; on-water time is
|
|
4
4
|
* `on_water_minutes` if set, otherwise the whole span, and zero when the activity uses no boats.
|
|
5
5
|
*/
|
|
6
6
|
export function scheduleActivities(ctx) {
|
|
@@ -21,8 +21,6 @@ export function scheduleActivities(ctx) {
|
|
|
21
21
|
ctx.warn("unknown_offering", `Activity ${activity.key} is for an offering that isn't in the plan`, activity.key);
|
|
22
22
|
continue;
|
|
23
23
|
}
|
|
24
|
-
if (!ctx.inYear(offering))
|
|
25
|
-
continue;
|
|
26
24
|
const timed = activity.start_time != null && activity.end_time != null;
|
|
27
25
|
const start = timed ? minutesOf(activity.start_time) : null;
|
|
28
26
|
let end = timed ? minutesOf(activity.end_time) : null;
|
package/dist/planning/bonus.js
CHANGED
|
@@ -84,10 +84,7 @@ function baseOf(ctx, owner) {
|
|
|
84
84
|
}
|
|
85
85
|
products.add(row.product_id);
|
|
86
86
|
}
|
|
87
|
-
const offeringKeys = new Set(ctx
|
|
88
|
-
.fyOfferings()
|
|
89
|
-
.filter((o) => products.has(o.product_id))
|
|
90
|
-
.map((o) => o.key));
|
|
87
|
+
const offeringKeys = new Set(ctx.data.plan_offerings.filter((o) => products.has(o.product_id)).map((o) => o.key));
|
|
91
88
|
const afterPools = (owner.basis ?? "after_shared_costs") === "after_shared_costs";
|
|
92
89
|
let net = 0;
|
|
93
90
|
for (const e of ctx.entries) {
|
|
@@ -26,11 +26,6 @@ export interface Entry {
|
|
|
26
26
|
export type EntryInput = Omit<Entry, "stage" | "mtdc_share" | "wages"> & Partial<Pick<Entry, "stage" | "mtdc_share" | "wages">>;
|
|
27
27
|
/** Lookups over the input, and the entries recorded so far, shared by every step. */
|
|
28
28
|
export declare class Context {
|
|
29
|
-
/** The plan year's first and last days. */
|
|
30
|
-
readonly fy: {
|
|
31
|
-
from: DateString;
|
|
32
|
-
to: DateString;
|
|
33
|
-
};
|
|
34
29
|
readonly entries: Entry[];
|
|
35
30
|
readonly warnings: EngineWarning[];
|
|
36
31
|
readonly accounts: Map<Id, Account>;
|
|
@@ -62,8 +57,4 @@ export declare class Context {
|
|
|
62
57
|
accountUnder(accountId: Id, ancestorId: Id): boolean;
|
|
63
58
|
/** The account a product's sales are booked to: its program area's, or its own outside an area. */
|
|
64
59
|
revenueAccount(product: Product): Id | null;
|
|
65
|
-
/** Whether the offering counts in the plan year: it does, whole, in the year it starts. */
|
|
66
|
-
inYear(offering: PlanOffering): boolean;
|
|
67
|
-
/** The plan year's offerings. */
|
|
68
|
-
fyOfferings(): PlanOffering[];
|
|
69
60
|
}
|
package/dist/planning/context.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { fiscalYearDates, within } from "./dates.js";
|
|
2
1
|
/** The warnings about what the blocks are given, or when they run. The rest are about the data. */
|
|
3
2
|
const ALLOCATION_WARNINGS = new Set([
|
|
4
3
|
"over_inventory_boats",
|
|
@@ -21,8 +20,6 @@ const COST_TYPES = {
|
|
|
21
20
|
};
|
|
22
21
|
/** Lookups over the input, and the entries recorded so far, shared by every step. */
|
|
23
22
|
export class Context {
|
|
24
|
-
/** The plan year's first and last days. */
|
|
25
|
-
fy;
|
|
26
23
|
entries = [];
|
|
27
24
|
warnings = [];
|
|
28
25
|
accounts;
|
|
@@ -41,7 +38,6 @@ export class Context {
|
|
|
41
38
|
data;
|
|
42
39
|
constructor(data) {
|
|
43
40
|
this.data = data;
|
|
44
|
-
this.fy = fiscalYearDates(data.fiscal_year, data.organization.fiscal_year_start_month);
|
|
45
41
|
this.accounts = new Map(data.accounts.map((a) => [a.id, a]));
|
|
46
42
|
this.products = new Map(data.products.map((p) => [p.id, p]));
|
|
47
43
|
this.areas = new Map(data.program_areas.map((a) => [a.id, a]));
|
|
@@ -124,12 +120,4 @@ export class Context {
|
|
|
124
120
|
}
|
|
125
121
|
return area.revenue_account_id;
|
|
126
122
|
}
|
|
127
|
-
/** Whether the offering counts in the plan year: it does, whole, in the year it starts. */
|
|
128
|
-
inYear(offering) {
|
|
129
|
-
return within(offering.starts_on, this.fy.from, this.fy.to);
|
|
130
|
-
}
|
|
131
|
-
/** The plan year's offerings. */
|
|
132
|
-
fyOfferings() {
|
|
133
|
-
return this.data.plan_offerings.filter((o) => this.inYear(o));
|
|
134
|
-
}
|
|
135
123
|
}
|
package/dist/planning/dates.d.ts
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
import type { DateString, TimeString } from "./types.ts";
|
|
2
|
-
export declare function addDays(date: DateString, days: number): DateString;
|
|
3
2
|
/** ISO weekday, 1 = Monday. */
|
|
4
3
|
export declare function isoWeekday(date: DateString): number;
|
|
5
|
-
/** Fiscal year N ends in calendar year N, so a July start makes FY2027 2026-07-01 to 2027-06-30. */
|
|
6
|
-
export declare function fiscalYearDates(fiscalYear: number, startMonth: number): {
|
|
7
|
-
from: DateString;
|
|
8
|
-
to: DateString;
|
|
9
|
-
};
|
|
10
4
|
export declare function minutesOf(time: TimeString): number;
|
|
11
|
-
/** Whether `date` falls in a window whose open ends are unbounded. */
|
|
12
|
-
export declare function within(date: DateString, from: DateString | null | undefined, to: DateString | null | undefined): boolean;
|
package/dist/planning/dates.js
CHANGED
|
@@ -1,31 +1,12 @@
|
|
|
1
1
|
function toUtc(date) {
|
|
2
2
|
return new Date(`${date}T00:00:00Z`);
|
|
3
3
|
}
|
|
4
|
-
function fromUtc(d) {
|
|
5
|
-
return d.toISOString().slice(0, 10);
|
|
6
|
-
}
|
|
7
|
-
export function addDays(date, days) {
|
|
8
|
-
const d = toUtc(date);
|
|
9
|
-
d.setUTCDate(d.getUTCDate() + days);
|
|
10
|
-
return fromUtc(d);
|
|
11
|
-
}
|
|
12
4
|
/** ISO weekday, 1 = Monday. */
|
|
13
5
|
export function isoWeekday(date) {
|
|
14
6
|
const day = toUtc(date).getUTCDay();
|
|
15
7
|
return day === 0 ? 7 : day;
|
|
16
8
|
}
|
|
17
|
-
/** Fiscal year N ends in calendar year N, so a July start makes FY2027 2026-07-01 to 2027-06-30. */
|
|
18
|
-
export function fiscalYearDates(fiscalYear, startMonth) {
|
|
19
|
-
const startYear = startMonth === 1 ? fiscalYear : fiscalYear - 1;
|
|
20
|
-
const from = `${startYear}-${String(startMonth).padStart(2, "0")}-01`;
|
|
21
|
-
const nextYear = `${startYear + 1}-${String(startMonth).padStart(2, "0")}-01`;
|
|
22
|
-
return { from, to: addDays(nextYear, -1) };
|
|
23
|
-
}
|
|
24
9
|
export function minutesOf(time) {
|
|
25
10
|
const [h, m] = time.split(":").map(Number);
|
|
26
11
|
return h * 60 + m;
|
|
27
12
|
}
|
|
28
|
-
/** Whether `date` falls in a window whose open ends are unbounded. */
|
|
29
|
-
export function within(date, from, to) {
|
|
30
|
-
return (from == null || date >= from) && (to == null || date <= to);
|
|
31
|
-
}
|
package/dist/planning/engine.js
CHANGED
|
@@ -28,7 +28,7 @@ export function computePlan(data) {
|
|
|
28
28
|
const charged = bookSalaries(ctx, coaching, booker);
|
|
29
29
|
booker.flush();
|
|
30
30
|
bookPayrollTax(ctx);
|
|
31
|
-
const mtdcByOffering = new Map(ctx.
|
|
31
|
+
const mtdcByOffering = new Map(ctx.data.plan_offerings.map((o) => [o.key, mtdc(ctx, o.key)]));
|
|
32
32
|
allocatePools(ctx);
|
|
33
33
|
const bonuses = bookBonuses(ctx);
|
|
34
34
|
const lines = sumLines(ctx);
|
|
@@ -49,7 +49,7 @@ export function computePlan(data) {
|
|
|
49
49
|
* products in no area. The catalog stays live, so the database can't check these.
|
|
50
50
|
*/
|
|
51
51
|
function checkAreas(ctx) {
|
|
52
|
-
for (const offering of ctx.
|
|
52
|
+
for (const offering of ctx.data.plan_offerings) {
|
|
53
53
|
const product = ctx.products.get(offering.product_id);
|
|
54
54
|
if (product === undefined)
|
|
55
55
|
continue;
|
|
@@ -200,7 +200,7 @@ function summarize(ctx, lines, mtdcByOffering) {
|
|
|
200
200
|
...counts,
|
|
201
201
|
};
|
|
202
202
|
};
|
|
203
|
-
const offerings = ctx.
|
|
203
|
+
const offerings = [...ctx.data.plan_offerings].sort((a, b) => a.key.localeCompare(b.key));
|
|
204
204
|
const offeringPnl = offerings.map((o) => {
|
|
205
205
|
const enrollment = ctx.enrollment.get(o.id) ?? 0;
|
|
206
206
|
const days = ctx.days.get(o.key) ?? 0;
|
|
@@ -4,7 +4,7 @@ import type { Id } from "./types.ts";
|
|
|
4
4
|
export declare function mtdc(ctx: Context, offeringKey: Id): number;
|
|
5
5
|
export declare function revenue(ctx: Context, offeringKey: Id): number;
|
|
6
6
|
/**
|
|
7
|
-
* How an amount on a product or program area divides among its
|
|
7
|
+
* How an amount on a product or program area divides among its offerings, by MTDC. Null
|
|
8
8
|
* when it has none.
|
|
9
9
|
*/
|
|
10
10
|
export declare function spreadWeights(ctx: Context, type: "product" | "program_area", key: Id): Map<Id, number> | null;
|
|
@@ -19,13 +19,11 @@ export function revenue(ctx, offeringKey) {
|
|
|
19
19
|
return sumEntries(ctx, offeringKey, (e) => (ctx.accountType(e.account_id) === "revenue" ? 1 : 0));
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* How an amount on a product or program area divides among its
|
|
22
|
+
* How an amount on a product or program area divides among its offerings, by MTDC. Null
|
|
23
23
|
* when it has none.
|
|
24
24
|
*/
|
|
25
25
|
export function spreadWeights(ctx, type, key) {
|
|
26
|
-
const offerings = ctx
|
|
27
|
-
.fyOfferings()
|
|
28
|
-
.filter((o) => type === "product" ? o.product_id === key : ctx.products.get(o.product_id)?.program_area_id === key);
|
|
26
|
+
const offerings = ctx.data.plan_offerings.filter((o) => type === "product" ? o.product_id === key : ctx.products.get(o.product_id)?.program_area_id === key);
|
|
29
27
|
if (offerings.length === 0)
|
|
30
28
|
return null;
|
|
31
29
|
const weights = new Map(offerings.map((o) => [o.key, Math.max(0, mtdc(ctx, o.key))]));
|
package/dist/planning/pools.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Context } from "./context.ts";
|
|
2
2
|
/**
|
|
3
|
-
* Divides everything in each pool among the plan
|
|
3
|
+
* Divides everything in each pool among the plan's programs' offerings by MTDC, keeping each
|
|
4
4
|
* amount's account and function. Non-program's net offsets the pools first (cyc-seattle/platform#242):
|
|
5
5
|
* that part of each pooled amount stays with the organization, so a gain shrinks every program's
|
|
6
6
|
* share and a loss grows it. Shared costs never count toward MTDC, so one pool's split never shifts
|
package/dist/planning/pools.js
CHANGED
|
@@ -5,10 +5,7 @@ function isProgram(ctx, productId) {
|
|
|
5
5
|
}
|
|
6
6
|
/** Non-program's net before the pools: its offerings' revenue less their own costs. */
|
|
7
7
|
function nonProgramNet(ctx) {
|
|
8
|
-
const keys = new Set(ctx
|
|
9
|
-
.fyOfferings()
|
|
10
|
-
.filter((o) => !isProgram(ctx, o.product_id))
|
|
11
|
-
.map((o) => o.key));
|
|
8
|
+
const keys = new Set(ctx.data.plan_offerings.filter((o) => !isProgram(ctx, o.product_id)).map((o) => o.key));
|
|
12
9
|
let net = 0;
|
|
13
10
|
for (const e of ctx.entries) {
|
|
14
11
|
if (e.cost_center_type !== "offering" || !keys.has(e.cost_center_key ?? ""))
|
|
@@ -22,15 +19,14 @@ function nonProgramNet(ctx) {
|
|
|
22
19
|
return net;
|
|
23
20
|
}
|
|
24
21
|
/**
|
|
25
|
-
* Divides everything in each pool among the plan
|
|
22
|
+
* Divides everything in each pool among the plan's programs' offerings by MTDC, keeping each
|
|
26
23
|
* amount's account and function. Non-program's net offsets the pools first (cyc-seattle/platform#242):
|
|
27
24
|
* that part of each pooled amount stays with the organization, so a gain shrinks every program's
|
|
28
25
|
* share and a loss grows it. Shared costs never count toward MTDC, so one pool's split never shifts
|
|
29
26
|
* another's.
|
|
30
27
|
*/
|
|
31
28
|
export function allocatePools(ctx) {
|
|
32
|
-
const weights = new Map(ctx
|
|
33
|
-
.fyOfferings()
|
|
29
|
+
const weights = new Map(ctx.data.plan_offerings
|
|
34
30
|
.filter((o) => isProgram(ctx, o.product_id))
|
|
35
31
|
.map((o) => [o.key, Math.max(0, mtdc(ctx, o.key))]));
|
|
36
32
|
const total = [...weights.values()].reduce((t, w) => t + w, 0);
|
package/dist/planning/revenue.js
CHANGED
|
@@ -15,7 +15,7 @@ export function countDays(ctx, activities) {
|
|
|
15
15
|
* account, a cost.
|
|
16
16
|
*/
|
|
17
17
|
export function bookRevenue(ctx) {
|
|
18
|
-
for (const offering of ctx.
|
|
18
|
+
for (const offering of ctx.data.plan_offerings) {
|
|
19
19
|
const product = ctx.products.get(offering.product_id);
|
|
20
20
|
if (product === undefined) {
|
|
21
21
|
ctx.warn("unknown_product", `Offering ${offering.key} is for a product that doesn't exist`, offering.key);
|
|
@@ -405,7 +405,7 @@ export class Staffing {
|
|
|
405
405
|
const { ctx } = this;
|
|
406
406
|
for (const trip of ctx.data.plan_trips) {
|
|
407
407
|
const offering = ctx.offeringsById.get(trip.offering_id);
|
|
408
|
-
if (offering === undefined
|
|
408
|
+
if (offering === undefined)
|
|
409
409
|
continue;
|
|
410
410
|
const travel = (a) => a.kind === "travel" && a.trip?.id === trip.id;
|
|
411
411
|
const staffed = [...blocks.values()].filter((b) => b.members.some(travel));
|
package/dist/planning/types.d.ts
CHANGED
|
@@ -88,7 +88,8 @@ export type ActivityKind = "practice" | "regatta" | "clinic" | "travel";
|
|
|
88
88
|
export interface PlanOffering extends Versioned {
|
|
89
89
|
product_id: Id;
|
|
90
90
|
name: string;
|
|
91
|
-
|
|
91
|
+
/** A `plan_terms` key in the Operations Plan, or none. */
|
|
92
|
+
term_key: Id | null;
|
|
92
93
|
starts_on: DateString;
|
|
93
94
|
ends_on: DateString;
|
|
94
95
|
capacity: number | null;
|
|
@@ -247,7 +248,7 @@ export interface PlanBoatRate extends Versioned {
|
|
|
247
248
|
/** How many boats of the class the organization has. */
|
|
248
249
|
quantity: number | null;
|
|
249
250
|
}
|
|
250
|
-
/** A group of shared costs, such as G&A, divided among all of the plan
|
|
251
|
+
/** A group of shared costs, such as G&A, divided among all of the plan's offerings by MTDC. */
|
|
251
252
|
export interface PlanPool extends Versioned {
|
|
252
253
|
name: string;
|
|
253
254
|
}
|
|
@@ -281,6 +282,12 @@ export interface PlanClosedDate extends Versioned {
|
|
|
281
282
|
date: DateString;
|
|
282
283
|
name: string;
|
|
283
284
|
}
|
|
285
|
+
/** A planning period in the Operations Plan, such as Summer 2027, which an offering can be in. */
|
|
286
|
+
export interface PlanTerm extends Versioned {
|
|
287
|
+
name: string;
|
|
288
|
+
starts_on: DateString;
|
|
289
|
+
ends_on: DateString;
|
|
290
|
+
}
|
|
284
291
|
/** The catalog plus every row of the plan versions being calculated. */
|
|
285
292
|
export interface PlanData {
|
|
286
293
|
/** The plan year. Fiscal year N ends in calendar year N. */
|
|
@@ -321,6 +328,7 @@ export interface PlanData {
|
|
|
321
328
|
plan_bonus_owner_products: PlanBonusOwnerProduct[];
|
|
322
329
|
plan_closed_dates?: PlanClosedDate[];
|
|
323
330
|
plan_capital_items?: PlanCapitalItem[];
|
|
331
|
+
plan_terms?: PlanTerm[];
|
|
324
332
|
}
|
|
325
333
|
/** One line of cost or revenue for the plan year, on one offering, product, pool or the organization. */
|
|
326
334
|
export interface CostLine {
|
package/dist/schema/index.d.ts
CHANGED
|
@@ -57,6 +57,7 @@ export interface Schema {
|
|
|
57
57
|
plan_salaried_bookings: PlanSalariedBookings[];
|
|
58
58
|
plan_salaried_positions: PlanSalariedPositions[];
|
|
59
59
|
plan_staff_levels: PlanStaffLevels[];
|
|
60
|
+
plan_terms: PlanTerms[];
|
|
60
61
|
plan_trips: PlanTrips[];
|
|
61
62
|
plan_versions: PlanVersions[];
|
|
62
63
|
plans: Plans[];
|
|
@@ -706,7 +707,8 @@ export interface PlanOfferings {
|
|
|
706
707
|
key: string;
|
|
707
708
|
product_id: string | Products;
|
|
708
709
|
name: string;
|
|
709
|
-
|
|
710
|
+
/** The term it runs in: copy the Key from a term in the Operations Plan. Leave it empty for an offering in no term. */
|
|
711
|
+
term_key: string | null;
|
|
710
712
|
starts_on: string;
|
|
711
713
|
ends_on: string;
|
|
712
714
|
capacity: number | null;
|
|
@@ -971,6 +973,20 @@ export interface PlanStaffLevels {
|
|
|
971
973
|
/** In cents: 12500 is $125.00. */
|
|
972
974
|
hourly_rate_cents: number | null;
|
|
973
975
|
}
|
|
976
|
+
/** A planning period, such as Summer 2027, with its start and end dates. An offering in a Program Plan can be in one of its Financial Plan's terms. */
|
|
977
|
+
export interface PlanTerms {
|
|
978
|
+
id: string;
|
|
979
|
+
/** The version this row belongs to. */
|
|
980
|
+
version_id: string | PlanVersions;
|
|
981
|
+
/** Stays the same across versions. Copy it where another row asks for this one's Key. */
|
|
982
|
+
key: string;
|
|
983
|
+
/** What it's called, such as Summer 2027. */
|
|
984
|
+
name: string;
|
|
985
|
+
/** Its first day. */
|
|
986
|
+
starts_on: string;
|
|
987
|
+
/** Its last day. */
|
|
988
|
+
ends_on: string;
|
|
989
|
+
}
|
|
974
990
|
/** A trip an offering travels for, such as an away regatta. */
|
|
975
991
|
export interface PlanTrips {
|
|
976
992
|
id: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyc-seattle/platform",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.49.0",
|
|
4
4
|
"description": "Pulumi components for CYC's shared Google Cloud platform: WebService, the platform outputs reader, and the stacks that build the platform",
|
|
5
5
|
"author": "CYC Community Sailing Center",
|
|
6
6
|
"license": "Apache-2.0",
|