"use server"; import { revalidatePath } from "next/cache"; import { AppwriteException, ID, Permission, Query, Role } from "node-appwrite"; import { z } from "zod"; import { logAudit } from "./audit"; import { DATABASE_ID, TABLES, type ClinicPricing, type Connection, } from "./schema"; import { createAdminClient } from "./server"; import { requireRole, requireTenant, requireTenantKind, } from "./tenant-guard"; import type { ClinicPricingActionState, ClinicPricingFormState, } from "./clinic-pricing-types"; import { clinicPricingSchema } from "@/lib/validation/clinic-pricing"; function appwriteError(e: unknown, fallback = "Beklenmeyen bir hata oluştu."): string { if (e instanceof AppwriteException) return e.message || fallback; return process.env.NODE_ENV !== "production" && e instanceof Error ? `${fallback} (${e.message})` : fallback; } function flattenErrors(err: z.ZodError): Record { const out: Record = {}; for (const issue of err.issues) { const key = issue.path.join("."); if (key && !out[key]) out[key] = issue.message; } return out; } function pricingPermissions(labTenantId: string, clinicTenantId: string): string[] { return [ Permission.read(Role.team(labTenantId)), Permission.read(Role.team(clinicTenantId)), Permission.update(Role.team(labTenantId, "owner")), Permission.update(Role.team(labTenantId, "admin")), Permission.delete(Role.team(labTenantId, "owner")), Permission.delete(Role.team(labTenantId, "admin")), ]; } function pickFields(formData: FormData) { return { clinicTenantId: String(formData.get("clinicTenantId") ?? "").trim(), prostheticType: String(formData.get("prostheticType") ?? "").trim(), customUnitPrice: String(formData.get("customUnitPrice") ?? "").trim(), discountPercent: String(formData.get("discountPercent") ?? "").trim(), currency: String(formData.get("currency") ?? "").trim(), }; } export async function setClinicPricingAction( _prev: ClinicPricingFormState, formData: FormData, ): Promise { let ctx; try { ctx = await requireTenant(); requireRole(ctx, ["owner", "admin"]); requireTenantKind(ctx, ["lab"]); } catch { return { ok: false, error: "Fiyatlandırma yalnızca laboratuvar hesapları için." }; } const parsed = clinicPricingSchema.safeParse(pickFields(formData)); if (!parsed.success) { return { ok: false, error: "Form geçersiz.", fieldErrors: flattenErrors(parsed.error) }; } const { tablesDB } = createAdminClient(); // Verify the lab actually has an approved connection with this clinic const connRes = await tablesDB.listRows({ databaseId: DATABASE_ID, tableId: TABLES.connections, queries: [ Query.equal("labTenantId", ctx.tenantId), Query.equal("clinicTenantId", parsed.data.clinicTenantId), Query.equal("status", "approved"), Query.limit(1), ], }); if (!(connRes.rows[0] as unknown as Connection | undefined)) { return { ok: false, error: "Onaylı bir bağlantınız yok.", fieldErrors: { clinicTenantId: "Bağlantı yok." }, }; } try { const existing = await tablesDB.listRows({ databaseId: DATABASE_ID, tableId: TABLES.clinicPricing, queries: [ Query.equal("labTenantId", ctx.tenantId), Query.equal("clinicTenantId", parsed.data.clinicTenantId), Query.equal("prostheticType", parsed.data.prostheticType), Query.limit(1), ], }); const row = existing.rows[0] as unknown as ClinicPricing | undefined; const payload = { labTenantId: ctx.tenantId, clinicTenantId: parsed.data.clinicTenantId, prostheticType: parsed.data.prostheticType, customUnitPrice: parsed.data.customUnitPrice, discountPercent: parsed.data.discountPercent, currency: parsed.data.currency, createdBy: ctx.user.id, }; if (row) { await tablesDB.updateRow(DATABASE_ID, TABLES.clinicPricing, row.$id, payload); void logAudit({ tenantId: ctx.tenantId, userId: ctx.user.id, action: "update", entityType: "clinic_pricing", entityId: row.$id, changes: payload, }); } else { const created = await tablesDB.createRow( DATABASE_ID, TABLES.clinicPricing, ID.unique(), payload, pricingPermissions(ctx.tenantId, parsed.data.clinicTenantId), ); void logAudit({ tenantId: ctx.tenantId, userId: ctx.user.id, action: "create", entityType: "clinic_pricing", entityId: created.$id, changes: payload, }); } } catch (e) { return { ok: false, error: appwriteError(e, "Fiyatlandırma kaydedilemedi.") }; } revalidatePath("/connections"); return { ok: true }; } export async function clearClinicPricingAction( _prev: ClinicPricingActionState, formData: FormData, ): Promise { const id = String(formData.get("id") ?? "").trim(); if (!id) return { ok: false, error: "Kayıt bulunamadı." }; let ctx; try { ctx = await requireTenant(); requireRole(ctx, ["owner", "admin"]); requireTenantKind(ctx, ["lab"]); } catch { return { ok: false, error: "Yetkiniz yok." }; } try { const { tablesDB } = createAdminClient(); const row = (await tablesDB.getRow( DATABASE_ID, TABLES.clinicPricing, id, )) as unknown as ClinicPricing; if (row.labTenantId !== ctx.tenantId) { return { ok: false, error: "Yetkiniz yok." }; } await tablesDB.deleteRow(DATABASE_ID, TABLES.clinicPricing, id); void logAudit({ tenantId: ctx.tenantId, userId: ctx.user.id, action: "delete", entityType: "clinic_pricing", entityId: id, }); } catch (e) { return { ok: false, error: appwriteError(e, "Silinemedi.") }; } revalidatePath("/connections"); return { ok: true }; }