fix(types): replace structural DB types with Pick<PrismaClient> and remove Prisma boundary as any casts
Replace ~440 lines of hand-written structural DB client types across 7 lib files with `Pick<PrismaClient, ...>` from @capakraken/db. This eliminates all `as any` casts at Prisma boundaries (cron routes, allocation effects, vacation procedures) and surfaces two pre-existing bugs: - weekly-digest.ts: `db.allocation.count()` called non-existent model (fixed → demandRequirement) - estimate-reminders.ts: `submittedAt` field doesn't exist on EstimateVersion (fixed → updatedAt) Also adds root eslint.config.mjs so lint-staged can lint package files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import type { PrismaClient } from "@capakraken/db";
|
||||
import { listAssignmentBookings } from "@capakraken/application";
|
||||
import { rankResources } from "@capakraken/staffing";
|
||||
import type { SkillEntry, WeekdayAvailability } from "@capakraken/shared";
|
||||
@@ -8,107 +9,10 @@ import {
|
||||
} from "./resource-capacity.js";
|
||||
import { createNotificationsForUsers } from "./create-notification.js";
|
||||
|
||||
/**
|
||||
* Minimal DB interface for auto-staffing — avoids importing the full PrismaClient.
|
||||
* Follows the same pattern as budget-alerts.ts.
|
||||
*/
|
||||
type DbClient = Parameters<typeof listAssignmentBookings>[0] & {
|
||||
demandRequirement: {
|
||||
findUnique: (args: {
|
||||
where: { id: string };
|
||||
select: {
|
||||
id: true;
|
||||
projectId: true;
|
||||
startDate: true;
|
||||
endDate: true;
|
||||
hoursPerDay: true;
|
||||
role: true;
|
||||
roleId: true;
|
||||
headcount: true;
|
||||
budgetCents: true;
|
||||
};
|
||||
}) => Promise<{
|
||||
id: string;
|
||||
projectId: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
hoursPerDay: number;
|
||||
role: string | null;
|
||||
roleId: string | null;
|
||||
headcount: number;
|
||||
budgetCents: number;
|
||||
} | null>;
|
||||
};
|
||||
project: {
|
||||
findUnique: (args: {
|
||||
where: { id: string };
|
||||
select: { id: true; name: true };
|
||||
}) => Promise<{ id: string; name: string } | null>;
|
||||
};
|
||||
role: {
|
||||
findUnique: (args: {
|
||||
where: { id: string };
|
||||
select: { id: true; name: true };
|
||||
}) => Promise<{ id: string; name: string } | null>;
|
||||
};
|
||||
resource: {
|
||||
findMany: (args: {
|
||||
where: { isActive: true };
|
||||
select?: {
|
||||
id?: true;
|
||||
displayName?: true;
|
||||
eid?: true;
|
||||
skills?: true;
|
||||
lcrCents?: true;
|
||||
chargeabilityTarget?: true;
|
||||
valueScore?: true;
|
||||
availability?: true;
|
||||
countryId?: true;
|
||||
federalState?: true;
|
||||
metroCityId?: true;
|
||||
country?: { select: { code: true } };
|
||||
metroCity?: { select: { name: true } };
|
||||
};
|
||||
take?: number;
|
||||
}) => Promise<Array<{
|
||||
id: string;
|
||||
displayName: string;
|
||||
eid: string | null;
|
||||
skills: unknown;
|
||||
lcrCents: number;
|
||||
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: {
|
||||
create: (args: {
|
||||
data: {
|
||||
userId: string;
|
||||
type: string;
|
||||
category: string;
|
||||
priority: string;
|
||||
title: string;
|
||||
body: string;
|
||||
entityId: string;
|
||||
entityType: string;
|
||||
link: string;
|
||||
channel: string;
|
||||
};
|
||||
}) => Promise<{ id: string; userId: string }>;
|
||||
};
|
||||
user: {
|
||||
findMany: (args: {
|
||||
where: { systemRole: { in: string[] } };
|
||||
select: { id: true };
|
||||
}) => Promise<Array<{ id: string }>>;
|
||||
};
|
||||
};
|
||||
type DbClient = Pick<
|
||||
PrismaClient,
|
||||
"assignment" | "demandRequirement" | "project" | "role" | "resource" | "notification" | "user"
|
||||
>;
|
||||
|
||||
const TOP_N = 3;
|
||||
|
||||
@@ -224,7 +128,8 @@ export async function generateAutoSuggestions(
|
||||
});
|
||||
const allocatedHours = resourceBookings.reduce(
|
||||
(sum, booking) =>
|
||||
sum + calculateEffectiveBookedHours({
|
||||
sum +
|
||||
calculateEffectiveBookedHours({
|
||||
availability,
|
||||
startDate: booking.startDate,
|
||||
endDate: booking.endDate,
|
||||
@@ -237,13 +142,12 @@ export async function generateAutoSuggestions(
|
||||
);
|
||||
|
||||
const utilizationPercent =
|
||||
totalAvailableHours > 0
|
||||
? Math.min(100, (allocatedHours / totalAvailableHours) * 100)
|
||||
: 0;
|
||||
totalAvailableHours > 0 ? Math.min(100, (allocatedHours / totalAvailableHours) * 100) : 0;
|
||||
|
||||
const wouldExceedCapacity = totalAvailableHours > 0
|
||||
? allocatedHours + demand.hoursPerDay > totalAvailableHours
|
||||
: demand.hoursPerDay > 0;
|
||||
const wouldExceedCapacity =
|
||||
totalAvailableHours > 0
|
||||
? allocatedHours + demand.hoursPerDay > totalAvailableHours
|
||||
: demand.hoursPerDay > 0;
|
||||
|
||||
return {
|
||||
id: resource.id,
|
||||
@@ -260,8 +164,7 @@ export async function generateAutoSuggestions(
|
||||
});
|
||||
|
||||
// 6. Rank resources using the staffing algorithm
|
||||
const budgetLcrCentsPerHour =
|
||||
demand.budgetCents > 0 ? demand.budgetCents : undefined;
|
||||
const budgetLcrCentsPerHour = demand.budgetCents > 0 ? demand.budgetCents : undefined;
|
||||
|
||||
const ranked = rankResources({
|
||||
requiredSkills,
|
||||
|
||||
Reference in New Issue
Block a user