feat(planning): ship holiday-aware planning and assistant upgrades

This commit is contained in:
2026-03-28 22:49:28 +01:00
parent 2a005794e7
commit 4f48afe7b4
151 changed files with 17738 additions and 1940 deletions
+67 -14
View File
@@ -215,12 +215,63 @@ async function buildProjectsSheet(wb: ExcelJS.Workbook) {
}
async function buildAllocationsSheet(wb: ExcelJS.Workbook) {
const allocations = await prisma.allocation.findMany({
orderBy: [{ project: { startDate: "asc" } }, { resource: { displayName: "asc" } }],
include: {
resource: { select: { eid: true, displayName: true, chapter: true } },
project: { select: { shortCode: true, name: true } },
},
const [assignments, demandRequirements] = await Promise.all([
prisma.assignment.findMany({
orderBy: [{ project: { startDate: "asc" } }, { resource: { displayName: "asc" } }],
include: {
resource: { select: { eid: true, displayName: true, chapter: true } },
project: { select: { shortCode: true, name: true, startDate: true } },
},
}),
prisma.demandRequirement.findMany({
orderBy: [{ project: { startDate: "asc" } }, { role: "asc" }],
include: {
project: { select: { shortCode: true, name: true, startDate: true } },
},
}),
]);
const allocations = [
...assignments.map((assignment) => ({
projectStartDate: assignment.project.startDate,
projectCode: assignment.project.shortCode,
projectName: assignment.project.name,
eid: assignment.resource?.eid ?? "",
resourceName: assignment.resource?.displayName ?? "",
chapter: assignment.resource?.chapter ?? "",
role: assignment.role ?? "",
startDate: assignment.startDate,
endDate: assignment.endDate,
hoursPerDay: assignment.hoursPerDay,
dailyCostCents: assignment.dailyCostCents,
status: assignment.status,
})),
...demandRequirements.map((demandRequirement) => ({
projectStartDate: demandRequirement.project.startDate,
projectCode: demandRequirement.project.shortCode,
projectName: demandRequirement.project.name,
eid: "",
resourceName: "",
chapter: "",
role: demandRequirement.role ?? "",
startDate: demandRequirement.startDate,
endDate: demandRequirement.endDate,
hoursPerDay: demandRequirement.hoursPerDay,
dailyCostCents: 0,
status: demandRequirement.status,
})),
].sort((left, right) => {
const startDiff = left.projectStartDate.getTime() - right.projectStartDate.getTime();
if (startDiff !== 0) {
return startDiff;
}
const resourceDiff = left.resourceName.localeCompare(right.resourceName);
if (resourceDiff !== 0) {
return resourceDiff;
}
return left.role.localeCompare(right.role);
});
const ws = wb.addWorksheet("Allocations", {
@@ -245,12 +296,12 @@ async function buildAllocationsSheet(wb: ExcelJS.Workbook) {
for (const alloc of allocations) {
const row: AnyRow = ws.getRow(rowIdx);
row.values = [
alloc.project.shortCode,
alloc.project.name,
alloc.resource?.eid ?? "",
alloc.resource?.displayName ?? "",
alloc.resource?.chapter ?? "",
alloc.role ?? "",
alloc.projectCode,
alloc.projectName,
alloc.eid,
alloc.resourceName,
alloc.chapter,
alloc.role,
fmtDate(alloc.startDate),
fmtDate(alloc.endDate),
alloc.hoursPerDay,
@@ -268,11 +319,13 @@ async function buildAllocationsSheet(wb: ExcelJS.Workbook) {
}
async function buildSummarySheet(wb: ExcelJS.Workbook) {
const [resourceCount, projectCount, allocationCount] = await Promise.all([
const [resourceCount, projectCount, assignmentCount, demandRequirementCount] = await Promise.all([
prisma.resource.count(),
prisma.project.count(),
prisma.allocation.count(),
prisma.assignment.count(),
prisma.demandRequirement.count(),
]);
const allocationCount = assignmentCount + demandRequirementCount;
const ws = wb.addWorksheet("Summary");
ws.columns = [