Files
CapaKraken/packages/engine/src/allocation/chargeability.ts
T
Hartmut cd78f72f33 chore: full technical rename planarchy → capakraken
Complete rename of all technical identifiers across the codebase:

Package names (11 packages):
- @planarchy/* → @capakraken/* in all package.json, tsconfig, imports

Import statements: 277 files, 548 occurrences replaced

Database & Docker:
- PostgreSQL user/db: planarchy → capakraken
- Docker volumes: planarchy_pgdata → capakraken_pgdata
- Connection strings updated in docker-compose, .env, CI

CI/CD:
- GitHub Actions workflow: all filter commands updated
- Test database credentials updated

Infrastructure:
- Redis channel: planarchy:sse → capakraken:sse
- Logger service name: planarchy-api → capakraken-api
- Anonymization seed updated
- Start/stop/restart scripts updated

Test data:
- Seed emails: @planarchy.dev → @capakraken.dev
- E2E test credentials: all 11 spec files updated
- Email defaults: @planarchy.app → @capakraken.app
- localStorage keys: planarchy_* → capakraken_*

Documentation: 30+ .md files updated

Verification:
- pnpm install: workspace resolution works
- TypeScript: only pre-existing TS2589 (no new errors)
- Engine: 310/310 tests pass
- Staffing: 37/37 tests pass

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-27 13:18:09 +01:00

81 lines
2.6 KiB
TypeScript

import type { WeekdayAvailability } from "@capakraken/shared";
export interface ChargeabilityAllocation {
startDate: Date;
endDate: Date;
hoursPerDay: number;
}
export interface ChargeabilityResult {
availableHours: number;
bookedHours: number;
chargeability: number; // 0-100, rounded
}
// Maps JS getDay() (0=Sun..6=Sat) to WeekdayAvailability keys
const DAY_KEYS: (keyof WeekdayAvailability)[] = [
"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday",
];
/** Count working hours a resource has available in [start, end] based on their schedule. */
export function computeAvailableHours(
availability: WeekdayAvailability,
start: Date,
end: Date,
): number {
let hours = 0;
const cur = new Date(start);
cur.setHours(0, 0, 0, 0);
const endNorm = new Date(end);
endNorm.setHours(0, 0, 0, 0);
while (cur <= endNorm) {
const key = DAY_KEYS[cur.getDay()];
hours += key ? (availability[key] ?? 0) : 0;
cur.setDate(cur.getDate() + 1);
}
return hours;
}
/** Count booked hours from allocations overlapping [start, end], working days only. */
export function computeBookedHours(
availability: WeekdayAvailability,
allocations: ChargeabilityAllocation[],
start: Date,
end: Date,
): number {
let hours = 0;
const startNorm = new Date(start); startNorm.setHours(0, 0, 0, 0);
const endNorm = new Date(end); endNorm.setHours(0, 0, 0, 0);
for (const alloc of allocations) {
const aStart = new Date(alloc.startDate); aStart.setHours(0, 0, 0, 0);
const aEnd = new Date(alloc.endDate); aEnd.setHours(0, 0, 0, 0);
const overlapStart = aStart > startNorm ? aStart : startNorm;
const overlapEnd = aEnd < endNorm ? aEnd : endNorm;
if (overlapStart > overlapEnd) continue;
const cur = new Date(overlapStart);
while (cur <= overlapEnd) {
const key = DAY_KEYS[cur.getDay()];
if (key && (availability[key] ?? 0) > 0) {
hours += alloc.hoursPerDay;
}
cur.setDate(cur.getDate() + 1);
}
}
return hours;
}
/** Compute chargeability metrics for a resource over a date range. */
export function computeChargeability(
availability: WeekdayAvailability,
allocations: ChargeabilityAllocation[],
start: Date,
end: Date,
): ChargeabilityResult {
const availableHours = computeAvailableHours(availability, start, end);
const bookedHours = computeBookedHours(availability, allocations, start, end);
const chargeability = availableHours > 0
? Math.min(100, Math.round((bookedHours / availableHours) * 100))
: 0;
return { availableHours, bookedHours, chargeability };
}