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>
126 lines
4.9 KiB
TypeScript
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) }));
|
|
}
|