Files
craftvia/src/server/actions/emergency/review.ts
T
msolarczekandClaude Opus 5 ef61754a0a L8 Notdienst: mobile Erfassung /m/emergency in drei Schritten
Kunde suchen oder vorläufig anlegen, Einsatzort mit Ansprechpartner,
Grund mit optionaler Sprachnotiz, Beginn und Team; Start über die Sync-Op.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 13:12:33 +02:00

126 lines
4.9 KiB
TypeScript

"use server";
import { revalidatePath } from "next/cache";
import { moduleGuard } from "@/server/action-guard";
import { formObject, toActionError, type ActionState } from "@/server/api/action-state";
import { ctxFromGuard, type ServiceCtx } from "@/server/services/context";
import {
assignEmergencySite,
assignEmergencyToCustomer,
completeEmergencyOrderData,
confirmEmergencyCustomer,
correctEmergencySite,
mergeEmergencyCustomer,
releaseEmergencyForBilling,
} from "@/server/services/emergency/review";
/** Backoffice review of emergency call-outs (/work-orders/emergency-review/[id]). */
const guard = moduleGuard("emergency");
function done(workOrderId: string): ActionState {
revalidatePath("/work-orders/emergency-review");
revalidatePath(`/work-orders/emergency-review/${workOrderId}`);
revalidatePath(`/work-orders/${workOrderId}`);
return { status: "ok" };
}
async function run(workOrderId: string, fn: () => Promise<unknown>): Promise<ActionState> {
try {
await fn();
} catch (err) {
return toActionError(err);
}
return done(workOrderId);
}
const version = (fd: FormData) => {
const v = Number(fd.get("baseVersion"));
return Number.isInteger(v) && v > 0 ? v : undefined;
};
export async function confirmCustomerAction(workOrderId: string): Promise<ActionState> {
let ctx: ServiceCtx;
try {
ctx = ctxFromGuard(await guard("emergency:review", "customer:write"));
} catch (err) {
return toActionError(err);
}
return run(workOrderId, () => confirmEmergencyCustomer(ctx, workOrderId));
}
export async function assignCustomerAction(workOrderId: string, _prev: ActionState, fd: FormData): Promise<ActionState> {
let ctx: ServiceCtx;
try {
ctx = ctxFromGuard(await guard("emergency:review", "customer:write", "work_order:write"));
} catch (err) {
return toActionError(err);
}
return run(workOrderId, async () => assignEmergencyToCustomer(ctx, { workOrderId, targetCustomerId: await targetCustomer(ctx, fd) }));
}
/** Target from the candidate select or, as a fallback, from an entered customer number (tenant-bound lookup). */
async function targetCustomer(ctx: ServiceCtx, fd: FormData): Promise<string> {
const id = String(fd.get("targetCustomerId") ?? "").trim();
if (id) return id;
const customerNumber = String(fd.get("targetCustomerNumber") ?? "").trim();
if (!customerNumber) return "";
const c = await ctx.db.customer.findFirst({ where: { customerNumber, deletedAt: null }, select: { id: true } });
return c?.id ?? "__unknown__";
}
export async function mergeCustomerAction(workOrderId: string, _prev: ActionState, fd: FormData): Promise<ActionState> {
let ctx: ServiceCtx;
try {
ctx = ctxFromGuard(await guard("emergency:review", "customer:merge"));
} catch (err) {
return toActionError(err);
}
const confirm = fd.get("confirm") === "on" || fd.get("confirm") === "1";
return run(workOrderId, async () => mergeEmergencyCustomer(ctx, { workOrderId, targetCustomerId: await targetCustomer(ctx, fd), confirm }));
}
export async function correctSiteAction(workOrderId: string, _prev: ActionState, fd: FormData): Promise<ActionState> {
let ctx: ServiceCtx;
try {
ctx = ctxFromGuard(await guard("emergency:review", "site:write"));
} catch (err) {
return toActionError(err);
}
const values = formObject(fd, ["name", "street", "houseNumber", "postalCode", "city", "onSiteContact", "phone", "accessNotes"]);
const patch = { ...values, name: values.name ?? undefined };
const confirm = fd.get("confirm") === "on" || fd.get("confirm") === "1";
return run(workOrderId, () => correctEmergencySite(ctx, { workOrderId, patch, confirm }));
}
export async function assignSiteAction(workOrderId: string, _prev: ActionState, fd: FormData): Promise<ActionState> {
let ctx: ServiceCtx;
try {
ctx = ctxFromGuard(await guard("emergency:review", "work_order:write"));
} catch (err) {
return toActionError(err);
}
const siteId = String(fd.get("siteId") ?? "");
return run(workOrderId, () => assignEmergencySite(ctx, { workOrderId, siteId }));
}
export async function completeOrderAction(workOrderId: string, _prev: ActionState, fd: FormData): Promise<ActionState> {
let ctx: ServiceCtx;
try {
ctx = ctxFromGuard(await guard("emergency:review", "work_order:write"));
} catch (err) {
return toActionError(err);
}
const values = formObject(fd, ["title", "description", "orderTypeId", "billingType"]);
return run(workOrderId, () => completeEmergencyOrderData(ctx, { workOrderId, ...values, baseVersion: version(fd) }));
}
export async function releaseBillingAction(workOrderId: string, _prev: ActionState, fd: FormData): Promise<ActionState> {
let ctx: ServiceCtx;
try {
ctx = ctxFromGuard(await guard("emergency:review", "work_order:release_billing"));
} catch (err) {
return toActionError(err);
}
return run(workOrderId, () => releaseEmergencyForBilling(ctx, { workOrderId, baseVersion: version(fd) }));
}