"use server"; import { revalidatePath } from "next/cache"; import { currentUserId } from "@/lib/auth/session"; import { runMutation, type ActionResult } from "@/lib/db/rpc"; const POSITION_PATHS = ["/positions", "/orgchart", "/"]; async function callRpc( fn: "create_position" | "clone_position" | "update_position" | "delete_position" | "set_position_cost_center", payload: Record, revalidate: string[] ): Promise { const result = await runMutation(await currentUserId(), fn, payload); if (!result.success) return result; for (const path of revalidate) revalidatePath(path); return result; } export async function createPosition(payload: { org_unit_id: string; job_title: string; is_chief: boolean; valid_from: string; }): Promise { return callRpc("create_position", payload, POSITION_PATHS); } /** * Eine Planstelle als Kopie einer bestehenden anlegen. * * Kein eigenes Formular: Einheit, Tätigkeit und Kontierung kommen von der * Vorlage, getippt wird nichts. Genau das ist der Zweck — zwölf gleiche * Planstellen von Hand anzulegen erzeugt zwölf Gelegenheiten, die Tätigkeit * unterschiedlich zu schreiben, und ab der zweiten Schreibweise ist jede * Auswertung nach Tätigkeit falsch. * * Dass Leitungsplanstellen ausgenommen sind, entscheidet die SQL-Funktion und * nicht diese Stelle. Die Oberfläche blendet den Knopf dort aus, aber das ist * eine Bequemlichkeit, keine Regel. */ export async function clonePosition(payload: { position_id: string; valid_from?: string }): Promise { return callRpc("clone_position", payload, POSITION_PATHS); } /** * Ändert eine Planstelle. * * Weggelassene Felder bleiben, wie sie sind. `valid_to: null` beendet die * Befristung ausdrücklich — deshalb ist es hier `string | null` und nicht * optional: „nicht mitgeschickt" und „auf leer setzen" müssen unterscheidbar * bleiben. */ export async function updatePosition(payload: { position_id: string; org_unit_id?: string; job_title?: string; is_chief?: boolean; valid_from?: string; valid_to?: string | null; }): Promise { return callRpc("update_position", payload, POSITION_PATHS); } export async function deletePosition(positionId: string): Promise { return callRpc("delete_position", { position_id: positionId }, POSITION_PATHS); } /** * Kontiert eine Planstelle auf eine andere Kostenstelle — zum Stichtag. * * Kein Feld im Änderungsdialog, sondern ein eigener Vorgang, weil es einer * ist: die laufende Kontierung wird beendet, die neue beginnt. Die alte * einfach zu überschreiben hiesse, die Kosten der Vergangenheit nachträglich * anderswohin zu buchen. */ export async function setPositionCostCenter(payload: { position_id: string; cost_center_id: string; valid_from: string; }): Promise { return callRpc("set_position_cost_center", payload, POSITION_PATHS); }