rename(phase 1): CapaKraken → Nexus across code, UI, docs, CI (#61)
CI / Architecture Guardrails (push) Successful in 2m38s
CI / Assistant Split Regression (push) Successful in 3m33s
CI / Typecheck (push) Successful in 3m51s
CI / Lint (push) Successful in 5m2s
CI / E2E Tests (push) Has been cancelled
CI / Fresh-Linux Docker Deploy (push) Has been cancelled
CI / Release Images (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Architecture Guardrails (push) Successful in 2m38s
CI / Assistant Split Regression (push) Successful in 3m33s
CI / Typecheck (push) Successful in 3m51s
CI / Lint (push) Successful in 5m2s
CI / E2E Tests (push) Has been cancelled
CI / Fresh-Linux Docker Deploy (push) Has been cancelled
CI / Release Images (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
rename(phase 1): CapaKraken → Nexus across code, UI, docs, CI (#61) Co-authored-by: Hartmut Nörenberg <hn@hartmut-noerenberg.com> Co-committed-by: Hartmut Nörenberg <hn@hartmut-noerenberg.com>
This commit was merged in pull request #61.
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
import type { WeekdayAvailability } from "@capakraken/shared";
|
||||
import type { WeekdayAvailability } from "@nexus/shared";
|
||||
import { fmtEur } from "../lib/format-utils.js";
|
||||
import { type GraphLink, type GraphNode, fmtPct, fmtNum, l, n } from "./computation-graph-shared.js";
|
||||
import {
|
||||
type GraphLink,
|
||||
type GraphNode,
|
||||
fmtPct,
|
||||
fmtNum,
|
||||
l,
|
||||
n,
|
||||
} from "./computation-graph-shared.js";
|
||||
|
||||
type ResourceGraphResolvedHoliday = {
|
||||
date: string;
|
||||
@@ -116,84 +123,510 @@ function describeWeeklyAvailability(availability: WeekdayAvailability): {
|
||||
}
|
||||
|
||||
export function buildResourceGraphSnapshot(input: ResourceGraphPresentationInput) {
|
||||
const { totalHours: weeklyTotalHours, label: availabilityLabel } = describeWeeklyAvailability(input.weeklyAvailability);
|
||||
const { totalHours: weeklyTotalHours, label: availabilityLabel } = describeWeeklyAvailability(
|
||||
input.weeklyAvailability,
|
||||
);
|
||||
const hasScheduleRules = !!input.scheduleRules;
|
||||
|
||||
const nodes: GraphNode[] = [
|
||||
n("input.fte", "FTE", fmtNum(input.resource.fte, 2), "ratio", "INPUT", "Resource FTE factor", 0),
|
||||
n("input.country", "Country", input.resource.country?.name ?? input.resource.country?.code ?? "—", "text", "INPUT", "Country used for base working-time and national holiday rules", 0),
|
||||
n("input.state", "State", input.resource.federalState ?? "—", "text", "INPUT", "Federal state / region used for regional holidays", 0),
|
||||
n("input.city", "City", input.resource.metroCity?.name ?? "—", "text", "INPUT", "City / metro used for local holidays", 0),
|
||||
n("input.holidayContext", "Holiday Context", input.holidayScopeSummary, "text", "INPUT", "Resolved holiday scope chain: country / state / city", 0),
|
||||
n("input.holidayExamples", "Holiday Dates", input.holidayExamples, "text", "INPUT", `Resolved holidays in ${input.month}; scopes: COUNTRY ${input.holidayScopeBreakdown.COUNTRY ?? 0}, STATE ${input.holidayScopeBreakdown.STATE ?? 0}, CITY ${input.holidayScopeBreakdown.CITY ?? 0}`, 0),
|
||||
n("input.dailyHours", "Country Hours", `${input.dailyHours} h`, "hours", "INPUT", `Base daily working hours (${input.resource.country?.code ?? "?"})`, 0),
|
||||
n(
|
||||
"input.fte",
|
||||
"FTE",
|
||||
fmtNum(input.resource.fte, 2),
|
||||
"ratio",
|
||||
"INPUT",
|
||||
"Resource FTE factor",
|
||||
0,
|
||||
),
|
||||
n(
|
||||
"input.country",
|
||||
"Country",
|
||||
input.resource.country?.name ?? input.resource.country?.code ?? "—",
|
||||
"text",
|
||||
"INPUT",
|
||||
"Country used for base working-time and national holiday rules",
|
||||
0,
|
||||
),
|
||||
n(
|
||||
"input.state",
|
||||
"State",
|
||||
input.resource.federalState ?? "—",
|
||||
"text",
|
||||
"INPUT",
|
||||
"Federal state / region used for regional holidays",
|
||||
0,
|
||||
),
|
||||
n(
|
||||
"input.city",
|
||||
"City",
|
||||
input.resource.metroCity?.name ?? "—",
|
||||
"text",
|
||||
"INPUT",
|
||||
"City / metro used for local holidays",
|
||||
0,
|
||||
),
|
||||
n(
|
||||
"input.holidayContext",
|
||||
"Holiday Context",
|
||||
input.holidayScopeSummary,
|
||||
"text",
|
||||
"INPUT",
|
||||
"Resolved holiday scope chain: country / state / city",
|
||||
0,
|
||||
),
|
||||
n(
|
||||
"input.holidayExamples",
|
||||
"Holiday Dates",
|
||||
input.holidayExamples,
|
||||
"text",
|
||||
"INPUT",
|
||||
`Resolved holidays in ${input.month}; scopes: COUNTRY ${input.holidayScopeBreakdown.COUNTRY ?? 0}, STATE ${input.holidayScopeBreakdown.STATE ?? 0}, CITY ${input.holidayScopeBreakdown.CITY ?? 0}`,
|
||||
0,
|
||||
),
|
||||
n(
|
||||
"input.dailyHours",
|
||||
"Country Hours",
|
||||
`${input.dailyHours} h`,
|
||||
"hours",
|
||||
"INPUT",
|
||||
`Base daily working hours (${input.resource.country?.code ?? "?"})`,
|
||||
0,
|
||||
),
|
||||
...(hasScheduleRules
|
||||
? [n("input.scheduleRules", "Schedule Rules", "Spain", "—", "INPUT", "Variable daily hours (regular/friday/summer)", 0)]
|
||||
? [
|
||||
n(
|
||||
"input.scheduleRules",
|
||||
"Schedule Rules",
|
||||
"Spain",
|
||||
"—",
|
||||
"INPUT",
|
||||
"Variable daily hours (regular/friday/summer)",
|
||||
0,
|
||||
),
|
||||
]
|
||||
: []),
|
||||
n("input.weeklyAvail", "Weekly Avail.", `${weeklyTotalHours}h`, "h/week", "INPUT", `Resource availability: ${availabilityLabel}`, 0),
|
||||
n("input.lcrCents", "LCR", fmtEur(input.resource.lcrCents), "cents/h", "INPUT", "Loaded Cost Rate per hour", 0),
|
||||
n("input.hoursPerDay", "Hours/Day", fmtNum(input.avgHoursPerDay), "hours", "INPUT", "Average hours/day across assignments", 0),
|
||||
n("input.absences", "Absences", `${input.absenceCount}`, "count", "INPUT", `Absence days in ${input.month} (${input.vacationDayCount} vacation, ${input.sickDayCount} sick${input.halfDayCount > 0 ? `, ${input.halfDayCount} half-day` : ""})`, 0),
|
||||
n("input.publicHolidays", "Public Holidays", `${input.publicHolidayCount}`, "count", "INPUT", `Resolved holidays in ${input.month}; ${input.publicHolidayWorkdayCount} hit configured working days`, 0),
|
||||
n("input.calcRules", "Active Rules", `${input.calcRulesCount}`, "count", "INPUT", "Active calculation rules", 0),
|
||||
n("input.targetPct", "Target", fmtPct(input.targetPct), "%", "INPUT", `Chargeability target (${input.resource.managementLevelGroup?.name ?? "legacy"})`, 0),
|
||||
n("input.assignmentCount", "Assignments", `${input.assignmentCount}`, "count", "INPUT", `Active assignments in ${input.month}`, 0),
|
||||
n(
|
||||
"input.weeklyAvail",
|
||||
"Weekly Avail.",
|
||||
`${weeklyTotalHours}h`,
|
||||
"h/week",
|
||||
"INPUT",
|
||||
`Resource availability: ${availabilityLabel}`,
|
||||
0,
|
||||
),
|
||||
n(
|
||||
"input.lcrCents",
|
||||
"LCR",
|
||||
fmtEur(input.resource.lcrCents),
|
||||
"cents/h",
|
||||
"INPUT",
|
||||
"Loaded Cost Rate per hour",
|
||||
0,
|
||||
),
|
||||
n(
|
||||
"input.hoursPerDay",
|
||||
"Hours/Day",
|
||||
fmtNum(input.avgHoursPerDay),
|
||||
"hours",
|
||||
"INPUT",
|
||||
"Average hours/day across assignments",
|
||||
0,
|
||||
),
|
||||
n(
|
||||
"input.absences",
|
||||
"Absences",
|
||||
`${input.absenceCount}`,
|
||||
"count",
|
||||
"INPUT",
|
||||
`Absence days in ${input.month} (${input.vacationDayCount} vacation, ${input.sickDayCount} sick${input.halfDayCount > 0 ? `, ${input.halfDayCount} half-day` : ""})`,
|
||||
0,
|
||||
),
|
||||
n(
|
||||
"input.publicHolidays",
|
||||
"Public Holidays",
|
||||
`${input.publicHolidayCount}`,
|
||||
"count",
|
||||
"INPUT",
|
||||
`Resolved holidays in ${input.month}; ${input.publicHolidayWorkdayCount} hit configured working days`,
|
||||
0,
|
||||
),
|
||||
n(
|
||||
"input.calcRules",
|
||||
"Active Rules",
|
||||
`${input.calcRulesCount}`,
|
||||
"count",
|
||||
"INPUT",
|
||||
"Active calculation rules",
|
||||
0,
|
||||
),
|
||||
n(
|
||||
"input.targetPct",
|
||||
"Target",
|
||||
fmtPct(input.targetPct),
|
||||
"%",
|
||||
"INPUT",
|
||||
`Chargeability target (${input.resource.managementLevelGroup?.name ?? "legacy"})`,
|
||||
0,
|
||||
),
|
||||
n(
|
||||
"input.assignmentCount",
|
||||
"Assignments",
|
||||
`${input.assignmentCount}`,
|
||||
"count",
|
||||
"INPUT",
|
||||
`Active assignments in ${input.month}`,
|
||||
0,
|
||||
),
|
||||
|
||||
n("sah.calendarDays", "Calendar Days", `${input.sahCalendarDays}`, "days", "SAH", "Total calendar days in period", 1),
|
||||
n("sah.weekendDays", "Weekend Days", `${input.sahWeekendDays}`, "days", "SAH", "Saturday + Sunday count", 1),
|
||||
n("sah.grossWorkingDays", "Gross Work Days", `${input.baseWorkingDays}`, "days", "SAH", "Working days from the resource-specific weekly availability before holidays/absences", 1, "count(availability > 0)"),
|
||||
n("sah.baseHours", "Base Hours", fmtNum(input.baseAvailableHours), "hours", "SAH", "Available hours from weekly availability before holiday/absence deductions", 1, "Σ(daily availability)"),
|
||||
n("sah.publicHolidayDays", "Holiday Ded.", `${input.publicHolidayWorkdayCount}`, "days", "SAH", "Holiday workdays deducted after applying country/state/city scope and weekday availability", 1),
|
||||
n("sah.publicHolidayHours", "Holiday Hrs Ded.", fmtNum(input.publicHolidayHoursDeduction), "hours", "SAH", "Hours removed by resolved public holidays", 1, "Σ(availability on holiday dates)"),
|
||||
n("sah.absenceDays", "Absence Ded.", `${input.absenceCount}`, "days", "SAH", "Vacation/sick days that hit working days and are not already public holidays", 1),
|
||||
n("sah.absenceHours", "Absence Hrs Ded.", fmtNum(input.absenceHoursDeduction), "hours", "SAH", "Hours removed by vacation/sick absences", 1, "Σ(availability × absence fraction)"),
|
||||
n("sah.netWorkingDays", "Net Work Days", `${input.effectiveWorkingDays}`, "days", "SAH", "Remaining working days after holiday and absence deductions", 2, "gross - holidays - absences"),
|
||||
n("sah.effectiveHoursPerDay", "Eff. Hrs/Day", fmtNum(input.effectiveHoursPerWorkingDay), "hours", "SAH", "Average effective hours per remaining working day", 2, "SAH / net work days"),
|
||||
n("sah.sah", "SAH", fmtNum(input.effectiveAvailableHours), "hours", "SAH", "Effective available hours after weekly availability, local holidays and absences", 2, "base hours - holiday hours - absence hours"),
|
||||
n(
|
||||
"sah.calendarDays",
|
||||
"Calendar Days",
|
||||
`${input.sahCalendarDays}`,
|
||||
"days",
|
||||
"SAH",
|
||||
"Total calendar days in period",
|
||||
1,
|
||||
),
|
||||
n(
|
||||
"sah.weekendDays",
|
||||
"Weekend Days",
|
||||
`${input.sahWeekendDays}`,
|
||||
"days",
|
||||
"SAH",
|
||||
"Saturday + Sunday count",
|
||||
1,
|
||||
),
|
||||
n(
|
||||
"sah.grossWorkingDays",
|
||||
"Gross Work Days",
|
||||
`${input.baseWorkingDays}`,
|
||||
"days",
|
||||
"SAH",
|
||||
"Working days from the resource-specific weekly availability before holidays/absences",
|
||||
1,
|
||||
"count(availability > 0)",
|
||||
),
|
||||
n(
|
||||
"sah.baseHours",
|
||||
"Base Hours",
|
||||
fmtNum(input.baseAvailableHours),
|
||||
"hours",
|
||||
"SAH",
|
||||
"Available hours from weekly availability before holiday/absence deductions",
|
||||
1,
|
||||
"Σ(daily availability)",
|
||||
),
|
||||
n(
|
||||
"sah.publicHolidayDays",
|
||||
"Holiday Ded.",
|
||||
`${input.publicHolidayWorkdayCount}`,
|
||||
"days",
|
||||
"SAH",
|
||||
"Holiday workdays deducted after applying country/state/city scope and weekday availability",
|
||||
1,
|
||||
),
|
||||
n(
|
||||
"sah.publicHolidayHours",
|
||||
"Holiday Hrs Ded.",
|
||||
fmtNum(input.publicHolidayHoursDeduction),
|
||||
"hours",
|
||||
"SAH",
|
||||
"Hours removed by resolved public holidays",
|
||||
1,
|
||||
"Σ(availability on holiday dates)",
|
||||
),
|
||||
n(
|
||||
"sah.absenceDays",
|
||||
"Absence Ded.",
|
||||
`${input.absenceCount}`,
|
||||
"days",
|
||||
"SAH",
|
||||
"Vacation/sick days that hit working days and are not already public holidays",
|
||||
1,
|
||||
),
|
||||
n(
|
||||
"sah.absenceHours",
|
||||
"Absence Hrs Ded.",
|
||||
fmtNum(input.absenceHoursDeduction),
|
||||
"hours",
|
||||
"SAH",
|
||||
"Hours removed by vacation/sick absences",
|
||||
1,
|
||||
"Σ(availability × absence fraction)",
|
||||
),
|
||||
n(
|
||||
"sah.netWorkingDays",
|
||||
"Net Work Days",
|
||||
`${input.effectiveWorkingDays}`,
|
||||
"days",
|
||||
"SAH",
|
||||
"Remaining working days after holiday and absence deductions",
|
||||
2,
|
||||
"gross - holidays - absences",
|
||||
),
|
||||
n(
|
||||
"sah.effectiveHoursPerDay",
|
||||
"Eff. Hrs/Day",
|
||||
fmtNum(input.effectiveHoursPerWorkingDay),
|
||||
"hours",
|
||||
"SAH",
|
||||
"Average effective hours per remaining working day",
|
||||
2,
|
||||
"SAH / net work days",
|
||||
),
|
||||
n(
|
||||
"sah.sah",
|
||||
"SAH",
|
||||
fmtNum(input.effectiveAvailableHours),
|
||||
"hours",
|
||||
"SAH",
|
||||
"Effective available hours after weekly availability, local holidays and absences",
|
||||
2,
|
||||
"base hours - holiday hours - absence hours",
|
||||
),
|
||||
|
||||
n("alloc.workingDays", "Work Days", `${input.totalWorkingDaysInMonth}`, "days", "ALLOCATION", "Working days covered by assignments in period", 1, "Σ(overlap workdays)"),
|
||||
n("alloc.totalHours", "Total Hours", fmtNum(input.totalAllocHours), "hours", "ALLOCATION", "Sum of effective hours across assignments", 2, "Σ(min(h/day, avail) × workdays)"),
|
||||
n("alloc.dailyCostCents", "Daily Cost", fmtEur(input.dailyCostCents), "EUR", "ALLOCATION", "Cost per working day", 1, "hoursPerDay × LCR"),
|
||||
n("alloc.totalCostCents", "Total Cost", fmtEur(input.totalAllocCostCents), "EUR", "ALLOCATION", "Sum of daily costs", 2, "Σ(dailyCost × workdays)"),
|
||||
n("alloc.utilizationPct", "Utilization", `${input.utilizationPct.toFixed(1)}%`, "%", "ALLOCATION", "Allocation utilization: allocated hours / SAH", 3, "totalHours / SAH × 100"),
|
||||
n(
|
||||
"alloc.workingDays",
|
||||
"Work Days",
|
||||
`${input.totalWorkingDaysInMonth}`,
|
||||
"days",
|
||||
"ALLOCATION",
|
||||
"Working days covered by assignments in period",
|
||||
1,
|
||||
"Σ(overlap workdays)",
|
||||
),
|
||||
n(
|
||||
"alloc.totalHours",
|
||||
"Total Hours",
|
||||
fmtNum(input.totalAllocHours),
|
||||
"hours",
|
||||
"ALLOCATION",
|
||||
"Sum of effective hours across assignments",
|
||||
2,
|
||||
"Σ(min(h/day, avail) × workdays)",
|
||||
),
|
||||
n(
|
||||
"alloc.dailyCostCents",
|
||||
"Daily Cost",
|
||||
fmtEur(input.dailyCostCents),
|
||||
"EUR",
|
||||
"ALLOCATION",
|
||||
"Cost per working day",
|
||||
1,
|
||||
"hoursPerDay × LCR",
|
||||
),
|
||||
n(
|
||||
"alloc.totalCostCents",
|
||||
"Total Cost",
|
||||
fmtEur(input.totalAllocCostCents),
|
||||
"EUR",
|
||||
"ALLOCATION",
|
||||
"Sum of daily costs",
|
||||
2,
|
||||
"Σ(dailyCost × workdays)",
|
||||
),
|
||||
n(
|
||||
"alloc.utilizationPct",
|
||||
"Utilization",
|
||||
`${input.utilizationPct.toFixed(1)}%`,
|
||||
"%",
|
||||
"ALLOCATION",
|
||||
"Allocation utilization: allocated hours / SAH",
|
||||
3,
|
||||
"totalHours / SAH × 100",
|
||||
),
|
||||
...(input.hasRulesEffect
|
||||
? [
|
||||
n("alloc.chargeableHours", "Chargeable Hrs", fmtNum(input.totalChargeableHours), "hours", "ALLOCATION", "Rules-adjusted chargeable hours", 2, "rules-adjusted"),
|
||||
n("alloc.projectCostCents", "Project Cost", fmtEur(input.totalProjectCostCents), "EUR", "ALLOCATION", "Rules-adjusted project cost", 2, "rules-adjusted"),
|
||||
]
|
||||
n(
|
||||
"alloc.chargeableHours",
|
||||
"Chargeable Hrs",
|
||||
fmtNum(input.totalChargeableHours),
|
||||
"hours",
|
||||
"ALLOCATION",
|
||||
"Rules-adjusted chargeable hours",
|
||||
2,
|
||||
"rules-adjusted",
|
||||
),
|
||||
n(
|
||||
"alloc.projectCostCents",
|
||||
"Project Cost",
|
||||
fmtEur(input.totalProjectCostCents),
|
||||
"EUR",
|
||||
"ALLOCATION",
|
||||
"Rules-adjusted project cost",
|
||||
2,
|
||||
"rules-adjusted",
|
||||
),
|
||||
]
|
||||
: []),
|
||||
|
||||
...(input.absenceCount > 0
|
||||
? [
|
||||
n("rules.activeRules", "Matched Rules", `${input.calcRulesCount} rules`, "—", "RULES", "Rules evaluated for absence days", 1),
|
||||
n("rules.costEffect", "Cost Effect", input.hasRulesEffect ? "ZERO" : "—", "—", "RULES", "How absent days affect project cost", 1, "CHARGE / ZERO / REDUCE"),
|
||||
n("rules.chgEffect", "Chg Effect", input.hasRulesEffect ? "COUNT" : "—", "—", "RULES", "How absent days affect chargeability", 1, "COUNT / SKIP"),
|
||||
...(input.hasRulesEffect
|
||||
? [n("rules.costReduction", "Cost Reduction", "per rule", "—", "RULES", "Cost reduction percentage applied to absent hours", 2, "normalCost × (100 - reductionPct) / 100")]
|
||||
: []),
|
||||
]
|
||||
n(
|
||||
"rules.activeRules",
|
||||
"Matched Rules",
|
||||
`${input.calcRulesCount} rules`,
|
||||
"—",
|
||||
"RULES",
|
||||
"Rules evaluated for absence days",
|
||||
1,
|
||||
),
|
||||
n(
|
||||
"rules.costEffect",
|
||||
"Cost Effect",
|
||||
input.hasRulesEffect ? "ZERO" : "—",
|
||||
"—",
|
||||
"RULES",
|
||||
"How absent days affect project cost",
|
||||
1,
|
||||
"CHARGE / ZERO / REDUCE",
|
||||
),
|
||||
n(
|
||||
"rules.chgEffect",
|
||||
"Chg Effect",
|
||||
input.hasRulesEffect ? "COUNT" : "—",
|
||||
"—",
|
||||
"RULES",
|
||||
"How absent days affect chargeability",
|
||||
1,
|
||||
"COUNT / SKIP",
|
||||
),
|
||||
...(input.hasRulesEffect
|
||||
? [
|
||||
n(
|
||||
"rules.costReduction",
|
||||
"Cost Reduction",
|
||||
"per rule",
|
||||
"—",
|
||||
"RULES",
|
||||
"Cost reduction percentage applied to absent hours",
|
||||
2,
|
||||
"normalCost × (100 - reductionPct) / 100",
|
||||
),
|
||||
]
|
||||
: []),
|
||||
]
|
||||
: []),
|
||||
|
||||
n("chg.chgHours", "Chg Hours", fmtNum(input.chargeableHours), "hours", "CHARGEABILITY", "Total chargeable hours against effective SAH", 2, "chargeability × SAH"),
|
||||
n("chg.chg", "Chargeability", fmtPct(input.forecast.chg), "%", "CHARGEABILITY", "Chargeability ratio", 3, "chgHours / SAH"),
|
||||
n(
|
||||
"chg.chgHours",
|
||||
"Chg Hours",
|
||||
fmtNum(input.chargeableHours),
|
||||
"hours",
|
||||
"CHARGEABILITY",
|
||||
"Total chargeable hours against effective SAH",
|
||||
2,
|
||||
"chargeability × SAH",
|
||||
),
|
||||
n(
|
||||
"chg.chg",
|
||||
"Chargeability",
|
||||
fmtPct(input.forecast.chg),
|
||||
"%",
|
||||
"CHARGEABILITY",
|
||||
"Chargeability ratio",
|
||||
3,
|
||||
"chgHours / SAH",
|
||||
),
|
||||
...(input.forecast.bd > 0
|
||||
? [n("chg.bd", "BD Ratio", fmtPct(input.forecast.bd), "%", "CHARGEABILITY", `Business development: ${fmtNum(input.forecast.bd * input.effectiveAvailableHours)}h`, 3, "bdHours / SAH")]
|
||||
? [
|
||||
n(
|
||||
"chg.bd",
|
||||
"BD Ratio",
|
||||
fmtPct(input.forecast.bd),
|
||||
"%",
|
||||
"CHARGEABILITY",
|
||||
`Business development: ${fmtNum(input.forecast.bd * input.effectiveAvailableHours)}h`,
|
||||
3,
|
||||
"bdHours / SAH",
|
||||
),
|
||||
]
|
||||
: []),
|
||||
...(input.forecast.mdi > 0
|
||||
? [n("chg.mdi", "MD&I Ratio", fmtPct(input.forecast.mdi), "%", "CHARGEABILITY", `MD&I hours: ${fmtNum(input.forecast.mdi * input.effectiveAvailableHours)}h`, 3, "mdiHours / SAH")]
|
||||
? [
|
||||
n(
|
||||
"chg.mdi",
|
||||
"MD&I Ratio",
|
||||
fmtPct(input.forecast.mdi),
|
||||
"%",
|
||||
"CHARGEABILITY",
|
||||
`MD&I hours: ${fmtNum(input.forecast.mdi * input.effectiveAvailableHours)}h`,
|
||||
3,
|
||||
"mdiHours / SAH",
|
||||
),
|
||||
]
|
||||
: []),
|
||||
...(input.forecast.mo > 0
|
||||
? [n("chg.mo", "M&O Ratio", fmtPct(input.forecast.mo), "%", "CHARGEABILITY", `M&O hours: ${fmtNum(input.forecast.mo * input.effectiveAvailableHours)}h`, 3, "moHours / SAH")]
|
||||
? [
|
||||
n(
|
||||
"chg.mo",
|
||||
"M&O Ratio",
|
||||
fmtPct(input.forecast.mo),
|
||||
"%",
|
||||
"CHARGEABILITY",
|
||||
`M&O hours: ${fmtNum(input.forecast.mo * input.effectiveAvailableHours)}h`,
|
||||
3,
|
||||
"moHours / SAH",
|
||||
),
|
||||
]
|
||||
: []),
|
||||
...(input.forecast.pdr > 0
|
||||
? [n("chg.pdr", "PD&R Ratio", fmtPct(input.forecast.pdr), "%", "CHARGEABILITY", `PD&R hours: ${fmtNum(input.forecast.pdr * input.effectiveAvailableHours)}h`, 3, "pdrHours / SAH")]
|
||||
? [
|
||||
n(
|
||||
"chg.pdr",
|
||||
"PD&R Ratio",
|
||||
fmtPct(input.forecast.pdr),
|
||||
"%",
|
||||
"CHARGEABILITY",
|
||||
`PD&R hours: ${fmtNum(input.forecast.pdr * input.effectiveAvailableHours)}h`,
|
||||
3,
|
||||
"pdrHours / SAH",
|
||||
),
|
||||
]
|
||||
: []),
|
||||
...(input.forecast.absence > 0
|
||||
? [n("chg.absence", "Absence Ratio", fmtPct(input.forecast.absence), "%", "CHARGEABILITY", `Absence hours: ${fmtNum(input.forecast.absence * input.effectiveAvailableHours)}h`, 3, "absenceHours / SAH")]
|
||||
? [
|
||||
n(
|
||||
"chg.absence",
|
||||
"Absence Ratio",
|
||||
fmtPct(input.forecast.absence),
|
||||
"%",
|
||||
"CHARGEABILITY",
|
||||
`Absence hours: ${fmtNum(input.forecast.absence * input.effectiveAvailableHours)}h`,
|
||||
3,
|
||||
"absenceHours / SAH",
|
||||
),
|
||||
]
|
||||
: []),
|
||||
n("chg.unassigned", "Unassigned", fmtPct(input.forecast.unassigned), "%", "CHARGEABILITY", `${fmtNum(input.forecast.unassigned * input.effectiveAvailableHours)}h of ${fmtNum(input.effectiveAvailableHours)}h SAH not assigned`, 3, "max(0, SAH - assigned) / SAH"),
|
||||
n("chg.target", "Target", fmtPct(input.targetPct), "%", "CHARGEABILITY", "Chargeability target from management level", 3),
|
||||
n("chg.gap", "Gap to Target", `${input.forecast.chg - input.targetPct >= 0 ? "+" : ""}${((input.forecast.chg - input.targetPct) * 100).toFixed(1)} pp`, "pp", "CHARGEABILITY", `Chargeability (${fmtPct(input.forecast.chg)}) vs. target (${fmtPct(input.targetPct)})`, 3, "chargeability − target"),
|
||||
n(
|
||||
"chg.unassigned",
|
||||
"Unassigned",
|
||||
fmtPct(input.forecast.unassigned),
|
||||
"%",
|
||||
"CHARGEABILITY",
|
||||
`${fmtNum(input.forecast.unassigned * input.effectiveAvailableHours)}h of ${fmtNum(input.effectiveAvailableHours)}h SAH not assigned`,
|
||||
3,
|
||||
"max(0, SAH - assigned) / SAH",
|
||||
),
|
||||
n(
|
||||
"chg.target",
|
||||
"Target",
|
||||
fmtPct(input.targetPct),
|
||||
"%",
|
||||
"CHARGEABILITY",
|
||||
"Chargeability target from management level",
|
||||
3,
|
||||
),
|
||||
n(
|
||||
"chg.gap",
|
||||
"Gap to Target",
|
||||
`${input.forecast.chg - input.targetPct >= 0 ? "+" : ""}${((input.forecast.chg - input.targetPct) * 100).toFixed(1)} pp`,
|
||||
"pp",
|
||||
"CHARGEABILITY",
|
||||
`Chargeability (${fmtPct(input.forecast.chg)}) vs. target (${fmtPct(input.targetPct)})`,
|
||||
3,
|
||||
"chargeability − target",
|
||||
),
|
||||
|
||||
...input.budgetNodes,
|
||||
];
|
||||
@@ -209,7 +642,9 @@ export function buildResourceGraphSnapshot(input: ResourceGraphPresentationInput
|
||||
l("input.holidayExamples", "sah.publicHolidayDays", "resolved dates", 2),
|
||||
l("input.holidayExamples", "sah.publicHolidayHours", "remove matching day hours", 2),
|
||||
l("input.absences", "sah.absenceHours", "remove absence fractions", 1),
|
||||
...(hasScheduleRules ? [l("input.scheduleRules", "sah.effectiveHoursPerDay", "variable h/day", 1)] : []),
|
||||
...(hasScheduleRules
|
||||
? [l("input.scheduleRules", "sah.effectiveHoursPerDay", "variable h/day", 1)]
|
||||
: []),
|
||||
l("sah.calendarDays", "sah.grossWorkingDays", "− weekends", 2),
|
||||
l("sah.weekendDays", "sah.grossWorkingDays", "−", 1),
|
||||
l("input.publicHolidays", "sah.publicHolidayDays", "∩ workdays", 1),
|
||||
@@ -237,24 +672,31 @@ export function buildResourceGraphSnapshot(input: ResourceGraphPresentationInput
|
||||
|
||||
...(input.absenceCount > 0
|
||||
? [
|
||||
l("input.calcRules", "rules.activeRules", "filter active", 1),
|
||||
l("input.absences", "rules.activeRules", "match trigger", 1),
|
||||
l("rules.activeRules", "rules.costEffect", "→ effect", 1),
|
||||
l("rules.activeRules", "rules.chgEffect", "→ effect", 1),
|
||||
]
|
||||
l("input.calcRules", "rules.activeRules", "filter active", 1),
|
||||
l("input.absences", "rules.activeRules", "match trigger", 1),
|
||||
l("rules.activeRules", "rules.costEffect", "→ effect", 1),
|
||||
l("rules.activeRules", "rules.chgEffect", "→ effect", 1),
|
||||
]
|
||||
: []),
|
||||
|
||||
...(input.hasRulesEffect
|
||||
? [
|
||||
l("rules.costEffect", "alloc.projectCostCents", "apply", 2),
|
||||
l("alloc.totalCostCents", "alloc.projectCostCents", "adjust", 1),
|
||||
l("rules.chgEffect", "alloc.chargeableHours", "apply", 2),
|
||||
l("alloc.totalHours", "alloc.chargeableHours", "adjust", 1),
|
||||
...(input.absenceCount > 0 ? [l("rules.costEffect", "rules.costReduction", "reduce %", 1)] : []),
|
||||
]
|
||||
l("rules.costEffect", "alloc.projectCostCents", "apply", 2),
|
||||
l("alloc.totalCostCents", "alloc.projectCostCents", "adjust", 1),
|
||||
l("rules.chgEffect", "alloc.chargeableHours", "apply", 2),
|
||||
l("alloc.totalHours", "alloc.chargeableHours", "adjust", 1),
|
||||
...(input.absenceCount > 0
|
||||
? [l("rules.costEffect", "rules.costReduction", "reduce %", 1)]
|
||||
: []),
|
||||
]
|
||||
: []),
|
||||
|
||||
l(input.hasRulesEffect ? "alloc.chargeableHours" : "alloc.totalHours", "chg.chgHours", "Σ Chg", 2),
|
||||
l(
|
||||
input.hasRulesEffect ? "alloc.chargeableHours" : "alloc.totalHours",
|
||||
"chg.chgHours",
|
||||
"Σ Chg",
|
||||
2,
|
||||
),
|
||||
l("chg.chgHours", "chg.chg", "÷ SAH", 2),
|
||||
l("sah.sah", "chg.chg", "÷", 2),
|
||||
...(input.forecast.bd > 0 ? [l("sah.sah", "chg.bd", "÷", 1)] : []),
|
||||
|
||||
Reference in New Issue
Block a user