refactor(api): extract dispo procedures

This commit is contained in:
2026-03-31 20:32:59 +02:00
parent af88b3528a
commit 1d3f1a007f
5 changed files with 683 additions and 188 deletions
+42 -187
View File
@@ -1,221 +1,76 @@
import {
ImportBatchStatus,
StagedRecordStatus,
DispoStagedRecordType,
} from "@capakraken/db";
import {
assessDispoImportReadiness,
stageDispoImportBatch,
} from "@capakraken/application";
import { z } from "zod";
import { adminProcedure, createTRPCRouter } from "../trpc.js";
import { commitImportBatch, cancelImportBatch, resolveStagedRecord } from "./dispo-management.js";
import {
cancelImportBatch,
commitImportBatch,
commitImportBatchInputSchema,
getImportBatch,
importBatchIdInputSchema,
listImportBatches,
listImportBatchesInputSchema,
listStagedAssignments,
listStagedAssignmentsInputSchema,
listStagedProjects,
listStagedProjectsInputSchema,
listStagedResources,
listStagedResourcesInputSchema,
listStagedUnresolvedRecords,
listStagedUnresolvedRecordsInputSchema,
listStagedVacations,
} from "./dispo-read.js";
// ─── Shared schemas ──────────────────────────────────────────────────────────
const paginationSchema = z.object({
cursor: z.string().optional(),
limit: z.number().int().min(1).max(200).default(50),
});
const importBatchStatusSchema = z.nativeEnum(ImportBatchStatus);
const stagedRecordStatusSchema = z.nativeEnum(StagedRecordStatus);
const stagedRecordTypeSchema = z.nativeEnum(DispoStagedRecordType);
const workbookPathSchema = z
.string()
.trim()
.min(1, "Workbook path is required.")
.refine((value) => value.toLowerCase().endsWith(".xlsx"), {
message: "Only .xlsx workbook paths are supported.",
});
// ─── Router ──────────────────────────────────────────────────────────────────
listStagedVacationsInputSchema,
resolveStagedRecord,
resolveStagedRecordInputSchema,
stageImportBatch,
stageImportBatchInputSchema,
validateImportBatch,
validateImportBatchInputSchema,
} from "./dispo-procedure-support.js";
import { adminProcedure, createTRPCRouter } from "../trpc.js";
export const dispoRouter = createTRPCRouter({
// ── 1. stageImportBatch ──────────────────────────────────────────────────
stageImportBatch: adminProcedure
.input(
z.object({
chargeabilityWorkbookPath: workbookPathSchema,
costWorkbookPath: workbookPathSchema.optional(),
notes: z.string().nullish(),
planningWorkbookPath: workbookPathSchema,
referenceWorkbookPath: workbookPathSchema,
rosterWorkbookPath: workbookPathSchema.optional(),
}),
)
.mutation(async ({ ctx, input }) => {
return stageDispoImportBatch(ctx.db, {
chargeabilityWorkbookPath: input.chargeabilityWorkbookPath,
planningWorkbookPath: input.planningWorkbookPath,
referenceWorkbookPath: input.referenceWorkbookPath,
...(input.costWorkbookPath !== undefined ? { costWorkbookPath: input.costWorkbookPath } : {}),
...(input.notes !== undefined ? { notes: input.notes } : {}),
...(input.rosterWorkbookPath !== undefined ? { rosterWorkbookPath: input.rosterWorkbookPath } : {}),
});
}),
// ── 2. validateImportBatch ───────────────────────────────────────────────
.input(stageImportBatchInputSchema)
.mutation(({ ctx, input }) => stageImportBatch(ctx, input)),
validateImportBatch: adminProcedure
.input(
z.object({
chargeabilityWorkbookPath: workbookPathSchema,
costWorkbookPath: workbookPathSchema.optional(),
importBatchId: z.string().optional(),
notes: z.string().nullish(),
planningWorkbookPath: workbookPathSchema,
referenceWorkbookPath: workbookPathSchema,
rosterWorkbookPath: workbookPathSchema.optional(),
}),
)
.query(async ({ input }) => {
return assessDispoImportReadiness({
chargeabilityWorkbookPath: input.chargeabilityWorkbookPath,
planningWorkbookPath: input.planningWorkbookPath,
referenceWorkbookPath: input.referenceWorkbookPath,
...(input.costWorkbookPath !== undefined ? { costWorkbookPath: input.costWorkbookPath } : {}),
...(input.importBatchId !== undefined ? { importBatchId: input.importBatchId } : {}),
...(input.notes !== undefined ? { notes: input.notes } : {}),
...(input.rosterWorkbookPath !== undefined ? { rosterWorkbookPath: input.rosterWorkbookPath } : {}),
});
}),
// ── 3. listImportBatches ─────────────────────────────────────────────────
.input(validateImportBatchInputSchema)
.query(({ input }) => validateImportBatch(input)),
listImportBatches: adminProcedure
.input(
paginationSchema.extend({
status: importBatchStatusSchema.optional(),
}),
)
.query(async ({ ctx, input }) => {
return listImportBatches(ctx.db, input);
}),
// ── 4. getImportBatch ────────────────────────────────────────────────────
.input(listImportBatchesInputSchema)
.query(({ ctx, input }) => listImportBatches(ctx, input)),
getImportBatch: adminProcedure
.input(z.object({ id: z.string() }))
.query(async ({ ctx, input }) => {
return getImportBatch(ctx.db, input.id);
}),
// ── 5. cancelImportBatch ─────────────────────────────────────────────────
.input(importBatchIdInputSchema)
.query(({ ctx, input }) => getImportBatch(ctx, input)),
cancelImportBatch: adminProcedure
.input(z.object({ id: z.string() }))
.mutation(async ({ ctx, input }) => {
return cancelImportBatch(ctx.db, { id: input.id, userId: ctx.dbUser?.id });
}),
// ── 6. listStagedResources ───────────────────────────────────────────────
.input(importBatchIdInputSchema)
.mutation(({ ctx, input }) => cancelImportBatch(ctx, input)),
listStagedResources: adminProcedure
.input(
paginationSchema.extend({
importBatchId: z.string(),
status: stagedRecordStatusSchema.optional(),
}),
)
.query(async ({ ctx, input }) => {
return listStagedResources(ctx.db, input);
}),
// ── 7. listStagedProjects ────────────────────────────────────────────────
.input(listStagedResourcesInputSchema)
.query(({ ctx, input }) => listStagedResources(ctx, input)),
listStagedProjects: adminProcedure
.input(
paginationSchema.extend({
importBatchId: z.string(),
isTbd: z.boolean().optional(),
status: stagedRecordStatusSchema.optional(),
}),
)
.query(async ({ ctx, input }) => {
return listStagedProjects(ctx.db, input);
}),
// ── 8. listStagedAssignments ─────────────────────────────────────────────
.input(listStagedProjectsInputSchema)
.query(({ ctx, input }) => listStagedProjects(ctx, input)),
listStagedAssignments: adminProcedure
.input(
paginationSchema.extend({
importBatchId: z.string(),
resourceExternalId: z.string().optional(),
status: stagedRecordStatusSchema.optional(),
}),
)
.query(async ({ ctx, input }) => {
return listStagedAssignments(ctx.db, input);
}),
// ── 9. listStagedVacations ───────────────────────────────────────────────
.input(listStagedAssignmentsInputSchema)
.query(({ ctx, input }) => listStagedAssignments(ctx, input)),
listStagedVacations: adminProcedure
.input(
paginationSchema.extend({
importBatchId: z.string(),
resourceExternalId: z.string().optional(),
}),
)
.query(async ({ ctx, input }) => {
return listStagedVacations(ctx.db, input);
}),
// ── 10. listStagedUnresolvedRecords ──────────────────────────────────────
.input(listStagedVacationsInputSchema)
.query(({ ctx, input }) => listStagedVacations(ctx, input)),
listStagedUnresolvedRecords: adminProcedure
.input(
paginationSchema.extend({
importBatchId: z.string(),
recordType: stagedRecordTypeSchema.optional(),
}),
)
.query(async ({ ctx, input }) => {
return listStagedUnresolvedRecords(ctx.db, input);
}),
// ── 11. resolveStagedRecord ──────────────────────────────────────────────
.input(listStagedUnresolvedRecordsInputSchema)
.query(({ ctx, input }) => listStagedUnresolvedRecords(ctx, input)),
resolveStagedRecord: adminProcedure
.input(
z.object({
action: z.enum(["APPROVE", "REJECT", "SKIP"]),
id: z.string(),
recordType: stagedRecordTypeSchema,
}),
)
.mutation(async ({ ctx, input }) => {
return resolveStagedRecord(ctx.db, input);
}),
// ── 12. commitImportBatch ────────────────────────────────────────────────
.input(resolveStagedRecordInputSchema)
.mutation(({ ctx, input }) => resolveStagedRecord(ctx, input)),
commitImportBatch: adminProcedure
.input(
z.object({
allowTbdUnresolved: z.boolean().optional(),
importBatchId: z.string(),
importTbdProjects: z.boolean().optional(),
}),
)
.mutation(async ({ ctx, input }) => {
return commitImportBatch(ctx.db, {
importBatchId: input.importBatchId,
allowTbdUnresolved: input.allowTbdUnresolved,
importTbdProjects: input.importTbdProjects,
userId: ctx.dbUser?.id,
});
}),
.input(commitImportBatchInputSchema)
.mutation(({ ctx, input }) => commitImportBatch(ctx, input)),
});