feat(planning): ship holiday-aware planning and assistant upgrades
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
import { listAssignmentBookings } from "@capakraken/application";
|
||||
import { rankResources } from "@capakraken/staffing";
|
||||
import type { SkillEntry } from "@capakraken/shared";
|
||||
import type { SkillEntry, WeekdayAvailability } from "@capakraken/shared";
|
||||
import {
|
||||
calculateEffectiveAvailableHours,
|
||||
calculateEffectiveBookedHours,
|
||||
loadResourceDailyAvailabilityContexts,
|
||||
} from "./resource-capacity.js";
|
||||
import { createNotificationsForUsers } from "./create-notification.js";
|
||||
|
||||
/**
|
||||
@@ -58,6 +63,11 @@ type DbClient = Parameters<typeof listAssignmentBookings>[0] & {
|
||||
chargeabilityTarget: number;
|
||||
availability: unknown;
|
||||
valueScore: number | null;
|
||||
countryId: string | null;
|
||||
federalState: string | null;
|
||||
metroCityId: string | null;
|
||||
country: { code: string | null } | null;
|
||||
metroCity: { name: string | null } | null;
|
||||
}>>;
|
||||
};
|
||||
notification: {
|
||||
@@ -154,27 +164,54 @@ export async function generateAutoSuggestions(
|
||||
endDate: demand.endDate,
|
||||
resourceIds: resources.map((r) => r.id),
|
||||
});
|
||||
const contexts = await loadResourceDailyAvailabilityContexts(
|
||||
db as Parameters<typeof loadResourceDailyAvailabilityContexts>[0],
|
||||
resources.map((resource) => ({
|
||||
id: resource.id,
|
||||
availability: resource.availability as unknown as WeekdayAvailability,
|
||||
countryId: resource.countryId,
|
||||
countryCode: resource.country?.code,
|
||||
federalState: resource.federalState,
|
||||
metroCityId: resource.metroCityId,
|
||||
metroCityName: resource.metroCity?.name,
|
||||
})),
|
||||
demand.startDate,
|
||||
demand.endDate,
|
||||
);
|
||||
|
||||
// 5. Enrich resources with utilization data for the demand's date range
|
||||
const enrichedResources = resources.map((resource) => {
|
||||
const avail = resource.availability as
|
||||
| { monday?: number; tuesday?: number; wednesday?: number; thursday?: number; friday?: number }
|
||||
| null;
|
||||
const totalAvailableHours = avail?.monday ?? 8;
|
||||
const availability = resource.availability as unknown as WeekdayAvailability;
|
||||
const context = contexts.get(resource.id);
|
||||
const resourceBookings = bookings.filter((b) => b.resourceId === resource.id);
|
||||
|
||||
const allocatedHoursPerDay = resourceBookings.reduce(
|
||||
(sum, b) => sum + b.hoursPerDay,
|
||||
const totalAvailableHours = calculateEffectiveAvailableHours({
|
||||
availability,
|
||||
periodStart: demand.startDate,
|
||||
periodEnd: demand.endDate,
|
||||
context,
|
||||
});
|
||||
const allocatedHours = resourceBookings.reduce(
|
||||
(sum, booking) =>
|
||||
sum + calculateEffectiveBookedHours({
|
||||
availability,
|
||||
startDate: booking.startDate,
|
||||
endDate: booking.endDate,
|
||||
hoursPerDay: booking.hoursPerDay,
|
||||
periodStart: demand.startDate,
|
||||
periodEnd: demand.endDate,
|
||||
context,
|
||||
}),
|
||||
0,
|
||||
);
|
||||
|
||||
const utilizationPercent =
|
||||
totalAvailableHours > 0
|
||||
? Math.min(100, (allocatedHoursPerDay / totalAvailableHours) * 100)
|
||||
? Math.min(100, (allocatedHours / totalAvailableHours) * 100)
|
||||
: 0;
|
||||
|
||||
const wouldExceedCapacity =
|
||||
allocatedHoursPerDay + demand.hoursPerDay > totalAvailableHours;
|
||||
const wouldExceedCapacity = totalAvailableHours > 0
|
||||
? allocatedHours + demand.hoursPerDay > totalAvailableHours
|
||||
: demand.hoursPerDay > 0;
|
||||
|
||||
return {
|
||||
id: resource.id,
|
||||
|
||||
Reference in New Issue
Block a user