refactor(api): extract dispo procedures
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
import {
|
||||
DispoStagedRecordType,
|
||||
ImportBatchStatus,
|
||||
StagedRecordStatus,
|
||||
} from "@capakraken/db";
|
||||
import {
|
||||
assessDispoImportReadiness,
|
||||
stageDispoImportBatch as stageDispoImportBatchApplication,
|
||||
} from "@capakraken/application";
|
||||
import { z } from "zod";
|
||||
import type { TRPCContext } from "../trpc.js";
|
||||
import {
|
||||
cancelImportBatch as cancelImportBatchMutation,
|
||||
commitImportBatch as commitImportBatchMutation,
|
||||
resolveStagedRecord as resolveStagedRecordMutation,
|
||||
} from "./dispo-management.js";
|
||||
import {
|
||||
getImportBatch as getImportBatchQuery,
|
||||
listImportBatches as listImportBatchesQuery,
|
||||
listStagedAssignments as listStagedAssignmentsQuery,
|
||||
listStagedProjects as listStagedProjectsQuery,
|
||||
listStagedResources as listStagedResourcesQuery,
|
||||
listStagedUnresolvedRecords as listStagedUnresolvedRecordsQuery,
|
||||
listStagedVacations as listStagedVacationsQuery,
|
||||
} from "./dispo-read.js";
|
||||
|
||||
type DispoProcedureContext = Pick<TRPCContext, "db" | "dbUser">;
|
||||
|
||||
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.",
|
||||
});
|
||||
|
||||
export const stageImportBatchInputSchema = z.object({
|
||||
chargeabilityWorkbookPath: workbookPathSchema,
|
||||
costWorkbookPath: workbookPathSchema.optional(),
|
||||
notes: z.string().nullish(),
|
||||
planningWorkbookPath: workbookPathSchema,
|
||||
referenceWorkbookPath: workbookPathSchema,
|
||||
rosterWorkbookPath: workbookPathSchema.optional(),
|
||||
});
|
||||
|
||||
export const validateImportBatchInputSchema = z.object({
|
||||
chargeabilityWorkbookPath: workbookPathSchema,
|
||||
costWorkbookPath: workbookPathSchema.optional(),
|
||||
importBatchId: z.string().optional(),
|
||||
notes: z.string().nullish(),
|
||||
planningWorkbookPath: workbookPathSchema,
|
||||
referenceWorkbookPath: workbookPathSchema,
|
||||
rosterWorkbookPath: workbookPathSchema.optional(),
|
||||
});
|
||||
|
||||
export const listImportBatchesInputSchema = paginationSchema.extend({
|
||||
status: importBatchStatusSchema.optional(),
|
||||
});
|
||||
|
||||
export const importBatchIdInputSchema = z.object({
|
||||
id: z.string(),
|
||||
});
|
||||
|
||||
export const listStagedResourcesInputSchema = paginationSchema.extend({
|
||||
importBatchId: z.string(),
|
||||
status: stagedRecordStatusSchema.optional(),
|
||||
});
|
||||
|
||||
export const listStagedProjectsInputSchema = paginationSchema.extend({
|
||||
importBatchId: z.string(),
|
||||
isTbd: z.boolean().optional(),
|
||||
status: stagedRecordStatusSchema.optional(),
|
||||
});
|
||||
|
||||
export const listStagedAssignmentsInputSchema = paginationSchema.extend({
|
||||
importBatchId: z.string(),
|
||||
resourceExternalId: z.string().optional(),
|
||||
status: stagedRecordStatusSchema.optional(),
|
||||
});
|
||||
|
||||
export const listStagedVacationsInputSchema = paginationSchema.extend({
|
||||
importBatchId: z.string(),
|
||||
resourceExternalId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const listStagedUnresolvedRecordsInputSchema = paginationSchema.extend({
|
||||
importBatchId: z.string(),
|
||||
recordType: stagedRecordTypeSchema.optional(),
|
||||
});
|
||||
|
||||
export const resolveStagedRecordInputSchema = z.object({
|
||||
action: z.enum(["APPROVE", "REJECT", "SKIP"]),
|
||||
id: z.string(),
|
||||
recordType: stagedRecordTypeSchema,
|
||||
});
|
||||
|
||||
export const commitImportBatchInputSchema = z.object({
|
||||
allowTbdUnresolved: z.boolean().optional(),
|
||||
importBatchId: z.string(),
|
||||
importTbdProjects: z.boolean().optional(),
|
||||
});
|
||||
|
||||
type StageImportBatchInput = z.infer<typeof stageImportBatchInputSchema>;
|
||||
type ValidateImportBatchInput = z.infer<typeof validateImportBatchInputSchema>;
|
||||
type ListImportBatchesInput = z.infer<typeof listImportBatchesInputSchema>;
|
||||
type ImportBatchIdInput = z.infer<typeof importBatchIdInputSchema>;
|
||||
type ListStagedResourcesInput = z.infer<typeof listStagedResourcesInputSchema>;
|
||||
type ListStagedProjectsInput = z.infer<typeof listStagedProjectsInputSchema>;
|
||||
type ListStagedAssignmentsInput = z.infer<typeof listStagedAssignmentsInputSchema>;
|
||||
type ListStagedVacationsInput = z.infer<typeof listStagedVacationsInputSchema>;
|
||||
type ListStagedUnresolvedRecordsInput = z.infer<typeof listStagedUnresolvedRecordsInputSchema>;
|
||||
type ResolveStagedRecordInput = z.infer<typeof resolveStagedRecordInputSchema>;
|
||||
type CommitImportBatchInput = z.infer<typeof commitImportBatchInputSchema>;
|
||||
|
||||
export async function stageImportBatch(
|
||||
ctx: DispoProcedureContext,
|
||||
input: StageImportBatchInput,
|
||||
) {
|
||||
return stageDispoImportBatchApplication(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 } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
export async function validateImportBatch(input: ValidateImportBatchInput) {
|
||||
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 } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
export async function listImportBatches(ctx: DispoProcedureContext, input: ListImportBatchesInput) {
|
||||
return listImportBatchesQuery(ctx.db, input);
|
||||
}
|
||||
|
||||
export async function getImportBatch(ctx: DispoProcedureContext, input: ImportBatchIdInput) {
|
||||
return getImportBatchQuery(ctx.db, input.id);
|
||||
}
|
||||
|
||||
export async function cancelImportBatch(ctx: DispoProcedureContext, input: ImportBatchIdInput) {
|
||||
return cancelImportBatchMutation(ctx.db, { id: input.id, userId: ctx.dbUser?.id });
|
||||
}
|
||||
|
||||
export async function listStagedResources(
|
||||
ctx: DispoProcedureContext,
|
||||
input: ListStagedResourcesInput,
|
||||
) {
|
||||
return listStagedResourcesQuery(ctx.db, input);
|
||||
}
|
||||
|
||||
export async function listStagedProjects(
|
||||
ctx: DispoProcedureContext,
|
||||
input: ListStagedProjectsInput,
|
||||
) {
|
||||
return listStagedProjectsQuery(ctx.db, input);
|
||||
}
|
||||
|
||||
export async function listStagedAssignments(
|
||||
ctx: DispoProcedureContext,
|
||||
input: ListStagedAssignmentsInput,
|
||||
) {
|
||||
return listStagedAssignmentsQuery(ctx.db, input);
|
||||
}
|
||||
|
||||
export async function listStagedVacations(
|
||||
ctx: DispoProcedureContext,
|
||||
input: ListStagedVacationsInput,
|
||||
) {
|
||||
return listStagedVacationsQuery(ctx.db, input);
|
||||
}
|
||||
|
||||
export async function listStagedUnresolvedRecords(
|
||||
ctx: DispoProcedureContext,
|
||||
input: ListStagedUnresolvedRecordsInput,
|
||||
) {
|
||||
return listStagedUnresolvedRecordsQuery(ctx.db, input);
|
||||
}
|
||||
|
||||
export async function resolveStagedRecord(
|
||||
ctx: DispoProcedureContext,
|
||||
input: ResolveStagedRecordInput,
|
||||
) {
|
||||
return resolveStagedRecordMutation(ctx.db, input);
|
||||
}
|
||||
|
||||
export async function commitImportBatch(
|
||||
ctx: DispoProcedureContext,
|
||||
input: CommitImportBatchInput,
|
||||
) {
|
||||
return commitImportBatchMutation(ctx.db, {
|
||||
importBatchId: input.importBatchId,
|
||||
allowTbdUnresolved: input.allowTbdUnresolved,
|
||||
importTbdProjects: input.importTbdProjects,
|
||||
userId: ctx.dbUser?.id,
|
||||
});
|
||||
}
|
||||
@@ -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)),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user