Files
CapaKraken/packages/api/src/router/allocation-assignment-procedures.ts
T

223 lines
6.6 KiB
TypeScript

import {
createAssignment,
updateAssignment,
} from "@capakraken/application";
import {
AllocationStatus,
CreateAllocationSchema,
CreateAssignmentSchema,
PermissionKey,
UpdateAllocationSchema,
UpdateAssignmentSchema,
} from "@capakraken/shared";
import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import {
publishAllocationCreated,
publishAllocationDeleted,
publishAllocationUpdated,
publishBatchAllocationDeletes,
publishBatchAllocationStatusUpdates,
} from "./allocation-assignment-effects.js";
import {
batchDeleteAllocationsWithAudit,
batchUpdateAllocationStatusWithAudit,
createAllocationReadModelEntry,
deleteAllocationWithAudit,
deleteAssignmentWithAudit,
ensureAssignmentRecord,
updateAllocationWithAudit,
} from "./allocation-assignment-mutations.js";
import {
managerProcedure,
requirePermission,
} from "../trpc.js";
export const allocationAssignmentProcedures = {
create: managerProcedure
.input(CreateAllocationSchema)
.mutation(async ({ ctx, input }) => {
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
const allocation = await ctx.db.$transaction(async (tx) =>
createAllocationReadModelEntry(
tx as Parameters<typeof createAssignment>[0],
input,
));
publishAllocationCreated(ctx.db, {
id: allocation.id,
projectId: allocation.projectId,
resourceId: allocation.resourceId,
}, { dispatchWebhook: true });
return allocation;
}),
createAssignment: managerProcedure
.input(CreateAssignmentSchema)
.mutation(async ({ ctx, input }) => {
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
const assignment = await ctx.db.$transaction(async (tx) => {
return createAssignment(
tx as unknown as Parameters<typeof createAssignment>[0],
input,
);
});
publishAllocationCreated(ctx.db, {
id: assignment.id,
projectId: assignment.projectId,
resourceId: assignment.resourceId,
});
return assignment;
}),
ensureAssignment: managerProcedure
.input(z.object({
resourceId: z.string(),
projectId: z.string(),
startDate: z.coerce.date(),
endDate: z.coerce.date(),
hoursPerDay: z.number().min(0.5).max(24),
role: z.string().optional(),
}))
.mutation(async ({ ctx, input }) => {
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
const result = await ensureAssignmentRecord(ctx.db, input);
if (result.action === "reactivated") {
publishAllocationUpdated(ctx.db, {
id: result.assignment.id,
projectId: result.assignment.projectId,
resourceId: result.assignment.resourceId,
}, { dispatchWebhook: true });
return result;
}
publishAllocationCreated(ctx.db, {
id: result.assignment.id,
projectId: result.assignment.projectId,
resourceId: result.assignment.resourceId,
});
return result;
}),
updateAssignment: managerProcedure
.input(z.object({ id: z.string(), data: UpdateAssignmentSchema }))
.mutation(async ({ ctx, input }) => {
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
const existing = await findUniqueOrThrow(
ctx.db.assignment.findUnique({
where: { id: input.id },
select: { resourceId: true },
}),
"Assignment",
);
const updated = await ctx.db.$transaction(async (tx) => {
return updateAssignment(
tx as unknown as Parameters<typeof updateAssignment>[0],
input.id,
input.data,
);
});
publishAllocationUpdated(ctx.db, {
id: updated.id,
projectId: updated.projectId,
resourceId: updated.resourceId,
resourceIds: [existing.resourceId, updated.resourceId],
}, { dispatchWebhook: true });
return updated;
}),
update: managerProcedure
.input(z.object({ id: z.string(), data: UpdateAllocationSchema }))
.mutation(async ({ ctx, input }) => {
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
const { existing, updated } = await updateAllocationWithAudit(ctx.db, input.id, input.data);
const affectedResourceIds = [existing.entry.resourceId, updated.resourceId].filter(
(resourceId): resourceId is string => Boolean(resourceId),
);
publishAllocationUpdated(ctx.db, {
id: updated.id,
projectId: updated.projectId,
resourceId: updated.resourceId,
resourceIds: affectedResourceIds,
});
return updated;
}),
deleteAssignment: managerProcedure
.input(z.object({ id: z.string() }))
.mutation(async ({ ctx, input }) => {
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
const existing = await deleteAssignmentWithAudit(ctx.db, input.id);
publishAllocationDeleted(ctx.db, {
id: existing.id,
projectId: existing.projectId,
resourceId: existing.resourceId,
});
return { success: true };
}),
delete: managerProcedure
.input(z.object({ id: z.string() }))
.mutation(async ({ ctx, input }) => {
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
const existing = await deleteAllocationWithAudit(ctx.db, input.id);
publishAllocationDeleted(ctx.db, {
id: existing.entry.id,
projectId: existing.projectId,
resourceId: existing.entry.resourceId,
});
return { success: true };
}),
batchDelete: managerProcedure
.input(z.object({ ids: z.array(z.string()).min(1).max(100) }))
.mutation(async ({ ctx, input }) => {
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
const existing = await batchDeleteAllocationsWithAudit(ctx.db, input.ids);
publishBatchAllocationDeletes(ctx.db, existing.map((allocation) => ({
id: allocation.entry.id,
projectId: allocation.projectId,
resourceId: allocation.entry.resourceId,
})));
return { count: existing.length };
}),
batchUpdateStatus: managerProcedure
.input(
z.object({
ids: z.array(z.string()).min(1).max(100),
status: z.nativeEnum(AllocationStatus),
}),
)
.mutation(async ({ ctx, input }) => {
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
const updated = await batchUpdateAllocationStatusWithAudit(ctx.db, input);
publishBatchAllocationStatusUpdates(ctx.db, updated.map((allocation) => ({
id: allocation.id,
projectId: allocation.projectId,
resourceId: allocation.resourceId,
})));
return { count: updated.length };
}),
};