"use server"; import { revalidatePath } from "next/cache"; import type { ActionState } from "@/lib/work-orders/action-state"; import { moduleGuard } from "@/server/action-guard"; import { ok, str, toErrorState } from "@/server/actions/work_orders/_form"; import { ctxFromGuard } from "@/server/services/context"; import { confirmMilestone, createMilestone, deleteMilestone, markMilestoneReached, moveMilestone, rejectMilestone } from "@/server/services/billing/milestones"; /** * L14 milestones in the back office work order detail (tab "Meilensteine & Abrechnung"). * Define/sort/delete: `work_order:write`; confirm/reject: `billing:write`; report as reached on * behalf of the field: `work_order:write` (the field uses the sync op `milestone.reach`). */ const guard = moduleGuard("billing"); function revalidate(fd: FormData) { const workOrderId = str(fd, "workOrderId"); if (workOrderId) revalidatePath(`/work-orders/${workOrderId}`); revalidatePath("/billing"); } export async function createMilestoneAction(_prev: ActionState, fd: FormData): Promise { let tenantId: string | undefined; let actorId: string | undefined; try { const g = await guard("work_order:write"); tenantId = g.session.user.tenantId; actorId = g.session.user.id; await createMilestone(ctxFromGuard(g), { workOrderId: str(fd, "workOrderId") ?? "", title: str(fd, "title") ?? "", description: str(fd, "description") ?? null }); } catch (err) { return toErrorState(err, { tenantId, actorId, entity: "work_order_milestone" }); } revalidate(fd); return ok(); } export async function moveMilestoneAction(_prev: ActionState, fd: FormData): Promise { const id = str(fd, "milestoneId") ?? ""; let tenantId: string | undefined; let actorId: string | undefined; try { const g = await guard("work_order:write"); tenantId = g.session.user.tenantId; actorId = g.session.user.id; await moveMilestone(ctxFromGuard(g), { milestoneId: id, direction: str(fd, "direction") === "up" ? "up" : "down" }); } catch (err) { return toErrorState(err, { tenantId, actorId, entity: "work_order_milestone", entityId: id }); } revalidate(fd); return ok(); } export async function deleteMilestoneAction(_prev: ActionState, fd: FormData): Promise { const id = str(fd, "milestoneId") ?? ""; let tenantId: string | undefined; let actorId: string | undefined; try { const g = await guard("work_order:write"); tenantId = g.session.user.tenantId; actorId = g.session.user.id; await deleteMilestone(ctxFromGuard(g), id); } catch (err) { return toErrorState(err, { tenantId, actorId, entity: "work_order_milestone", entityId: id }); } revalidate(fd); return ok(); } export async function reachMilestoneAction(_prev: ActionState, fd: FormData): Promise { const id = str(fd, "milestoneId") ?? ""; let tenantId: string | undefined; let actorId: string | undefined; try { const g = await guard("work_order:write"); tenantId = g.session.user.tenantId; actorId = g.session.user.id; await markMilestoneReached(ctxFromGuard(g), { milestoneId: id, note: str(fd, "note") ?? null }); } catch (err) { return toErrorState(err, { tenantId, actorId, entity: "work_order_milestone", entityId: id }); } revalidate(fd); return ok(); } export async function confirmMilestoneAction(_prev: ActionState, fd: FormData): Promise { const id = str(fd, "milestoneId") ?? ""; let tenantId: string | undefined; let actorId: string | undefined; try { const g = await guard("billing:write"); tenantId = g.session.user.tenantId; actorId = g.session.user.id; await confirmMilestone(ctxFromGuard(g), id); } catch (err) { return toErrorState(err, { tenantId, actorId, entity: "work_order_milestone", entityId: id }); } revalidate(fd); return ok(); } export async function rejectMilestoneAction(_prev: ActionState, fd: FormData): Promise { const id = str(fd, "milestoneId") ?? ""; let tenantId: string | undefined; let actorId: string | undefined; try { const g = await guard("billing:write"); tenantId = g.session.user.tenantId; actorId = g.session.user.id; await rejectMilestone(ctxFromGuard(g), { milestoneId: id, reason: str(fd, "reason") ?? "" }); } catch (err) { return toErrorState(err, { tenantId, actorId, entity: "work_order_milestone", entityId: id }); } revalidate(fd); return ok(); }