import { err, ok } from "@tailor-platform/erp-kit/core"; import { roleLifecycle } from "../db/role.lifecycle.generated"; import type { Transaction } from "../generated/kysely-tailordb"; import { InvalidStatusTransitionError, RoleHasActiveAssignmentsError, RoleNotFoundError, } from "../lib/errors.generated"; import { recomputePermissionsForUsersWithRole } from "../lib/recomputeUserPermissions"; export interface DeactivateRoleInput { roleId: string; } /** * Function: deactivateRole * * Transitions a role from ACTIVE to INACTIVE status. * Blocked if the role still has active UserRole assignments. */ export async function run(db: Transaction, input: DeactivateRoleInput) { const role = await db .selectFrom("Role") .selectAll() .where("id", "=", input.roleId) .forUpdate() .executeTakeFirst(); if (!role) { return err(new RoleNotFoundError(input.roleId)); } const nextStatus = roleLifecycle.tryTransition(role.status, "deactivate"); if (!nextStatus) { return err(new InvalidStatusTransitionError(input.roleId)); } const activeAssignments = await db .selectFrom("UserRole") .selectAll() .where("roleId", "=", input.roleId) .execute(); if (activeAssignments.length > 0) { return err(new RoleHasActiveAssignmentsError(input.roleId)); } const updatedRole = await db .updateTable("Role") .set({ status: nextStatus, }) .where("id", "=", input.roleId) .returningAll() .executeTakeFirstOrThrow(); await recomputePermissionsForUsersWithRole(db, input.roleId); return ok({ role: updatedRole }); }