@cyc-seattle/platform 1.47.0 → 1.48.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 +1 -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
|
@@ -247,7 +247,7 @@ export interface PlanBoatRate extends Versioned {
|
|
|
247
247
|
/** How many boats of the class the organization has. */
|
|
248
248
|
quantity: number | null;
|
|
249
249
|
}
|
|
250
|
-
/** A group of shared costs, such as G&A, divided among all of the plan
|
|
250
|
+
/** A group of shared costs, such as G&A, divided among all of the plan's offerings by MTDC. */
|
|
251
251
|
export interface PlanPool extends Versioned {
|
|
252
252
|
name: string;
|
|
253
253
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyc-seattle/platform",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.48.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",
|