L13 Planung: Plantafel-, Einplan-, Empfehlungs-, Live- und Verzugsdienste mit API und Meldungen
Services board/schedule (L2 assign+update in inTransaction, Versionskonflikt, Audit), recommend (Luftlinie + freie Kolonnenzeit), live (Kolonnen, ohne Geräte-Koordinaten), watch (Verzug ab 80 %, Überschreitung, gefährdete Folgeaufträge, früher fertig mit Vorschlägen), Kolonnenkapazität. Job planning-watch alle 5 min, Events planning.capacity_freed/overrun/followup_at_risk (In-App an Backoffice + Teamleiter, Dedupe im Audit-Log), L12-Adapter für source/approvalStatus/manual. API /api/v1/planning/{board,schedule,recommendations,live} + OpenAPI. Tests core, recommend, live, watch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { can, ServiceError, type ServiceCtx } from "@/server/services/context";
|
||||
|
||||
/**
|
||||
* Who may use the planning views (L13):
|
||||
* - `work_order:read_all` (backoffice, admin): all teams; scheduling needs `work_order:assign` + `work_order:write`.
|
||||
* - `work_order:read_team` + leader of at least one active team (Teamleiter): read-only, own teams only.
|
||||
* - everyone else (Monteur): forbidden.
|
||||
*/
|
||||
export type PlanningAccess = {
|
||||
all: boolean;
|
||||
/** null = all teams of the tenant */
|
||||
teamIds: string[] | null;
|
||||
canSchedule: boolean;
|
||||
canManageTeams: boolean;
|
||||
};
|
||||
|
||||
export async function planningAccess(ctx: ServiceCtx): Promise<PlanningAccess> {
|
||||
if (can(ctx, "work_order:read_all")) {
|
||||
return {
|
||||
all: true,
|
||||
teamIds: null,
|
||||
canSchedule: can(ctx, "work_order:assign") && can(ctx, "work_order:write"),
|
||||
canManageTeams: can(ctx, "team:manage"),
|
||||
};
|
||||
}
|
||||
if (can(ctx, "work_order:read_team")) {
|
||||
const led = await ctx.db.team.findMany({ where: { leaderUserId: ctx.userId, status: "active", deletedAt: null }, select: { id: true } });
|
||||
if (led.length > 0) return { all: false, teamIds: led.map((t) => t.id), canSchedule: false, canManageTeams: false };
|
||||
}
|
||||
throw new ServiceError("forbidden", "planning not allowed");
|
||||
}
|
||||
|
||||
/** Recommendations and scheduling are backoffice functions (all teams + assign right). */
|
||||
export async function assertCanPlanAll(ctx: ServiceCtx): Promise<PlanningAccess> {
|
||||
const access = await planningAccess(ctx);
|
||||
if (!access.all || !can(ctx, "work_order:assign")) throw new ServiceError("forbidden", "missing permission work_order:assign");
|
||||
return access;
|
||||
}
|
||||
Reference in New Issue
Block a user