You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
4.8 KiB
TypeScript

"use server";
import { revalidatePath } from "next/cache";
import { z } from "zod";
import { getCurrentUser } from "@/data/auth";
import {
countPemakaianGroupProduct,
deleteGroupProduct,
insertGroupProduct,
updateGroupProduct,
type GroupProductInput,
} from "@/data/group-product";
import {
logActionError,
type DeleteState,
type FormState,
} from "@/lib/server-action";
type GroupProductField = keyof typeof groupProductSchema.shape;
export type SimpanGroupProductState = FormState<GroupProductField> & {
kdStrip?: string;
};
export type HapusGroupProductState = DeleteState;
// Batas atas = rentang kolom `int` MariaDB.
const angka = z.coerce
.number({ error: "Harus berupa angka" })
.int("Harus bilangan bulat")
.min(0, "Tidak boleh negatif")
.max(2_000_000_000, "Maksimal 2.000.000.000");
const groupProductSchema = z.object({
namaStrip: z
.string()
.trim()
.min(1, "Nama wajib diisi")
.max(50, "Nama maksimal 50 karakter"),
murah1: angka,
murah2: angka,
sedang1: angka,
sedang2: angka,
mahal1: angka,
mahal2: angka,
benikMurah: angka,
benikSedang: angka,
benikMahal: angka,
kelBrg: z.string().trim().max(1, "Kelompok barang maksimal 1 karakter"),
kelJns: z.string().trim().max(1, "Kategori maksimal 1 karakter"),
persenTiwul: z.coerce
.number({ error: "Harus berupa angka" })
.min(0, "Tidak boleh negatif")
.max(100, "Maksimal 100"),
});
function emptyStringToNull(value: string): string | null {
return value === "" ? null : value;
}
// Kolom angka dikosongkan berarti 0, kecuali persen tiwul yang default-nya 0.025.
function angkaAtauDefault(value: string, def: string): string {
return value === "" ? def : value;
}
export async function simpanGroupProduct(
_prevState: SimpanGroupProductState,
formData: FormData,
): Promise<SimpanGroupProductState> {
const user = await getCurrentUser();
const fields = Object.keys(groupProductSchema.shape) as GroupProductField[];
const raw = Object.fromEntries(
fields.map((field) => [field, String(formData.get(field) ?? "")]),
) as Record<GroupProductField, string>;
const values: Record<GroupProductField, string> = {
...raw,
murah1: angkaAtauDefault(raw.murah1, "0"),
murah2: angkaAtauDefault(raw.murah2, "0"),
sedang1: angkaAtauDefault(raw.sedang1, "0"),
sedang2: angkaAtauDefault(raw.sedang2, "0"),
mahal1: angkaAtauDefault(raw.mahal1, "0"),
mahal2: angkaAtauDefault(raw.mahal2, "0"),
benikMurah: angkaAtauDefault(raw.benikMurah, "0"),
benikSedang: angkaAtauDefault(raw.benikSedang, "0"),
benikMahal: angkaAtauDefault(raw.benikMahal, "0"),
persenTiwul: angkaAtauDefault(raw.persenTiwul, "0.025"),
};
const parsed = groupProductSchema.safeParse(values);
if (!parsed.success) {
return {
ok: false,
fieldErrors: z.flattenError(parsed.error).fieldErrors,
values,
};
}
const input: GroupProductInput = {
...parsed.data,
kelBrg: emptyStringToNull(parsed.data.kelBrg),
};
const kdStrip = String(formData.get("kdStrip") ?? "").trim();
try {
if (kdStrip === "") {
const kdBaru = await insertGroupProduct(input);
revalidatePath("/master/group-product");
return { ok: true, kdStrip: kdBaru };
}
const updated = await updateGroupProduct(kdStrip, input);
if (!updated) {
return { ok: false, error: "Group product tidak ditemukan", values };
}
revalidatePath("/master/group-product");
return { ok: true, kdStrip };
} catch (error) {
logActionError(
"simpanGroupProduct",
{ userId: user.id, kdStrip: kdStrip || "(baru)", nama: input.namaStrip },
error,
);
return {
ok: false,
error: "Gagal menyimpan group product, coba simpan ulang",
values,
};
}
}
export async function hapusGroupProduct(
_prevState: HapusGroupProductState,
formData: FormData,
): Promise<HapusGroupProductState> {
const user = await getCurrentUser();
const kdStrip = String(formData.get("kdStrip") ?? "").trim();
if (kdStrip === "") {
return { ok: false, error: "Kode group product tidak ada" };
}
try {
const dipakai = await countPemakaianGroupProduct(kdStrip);
if (dipakai.barang > 0 || dipakai.subGroup > 0) {
return {
ok: false,
error: `Group product masih dipakai ${dipakai.barang} barang dan ${dipakai.subGroup} sub group product, tidak bisa dihapus`,
};
}
const deleted = await deleteGroupProduct(kdStrip);
if (!deleted) {
return { ok: false, error: "Group product tidak ditemukan" };
}
revalidatePath("/master/group-product");
return { ok: true };
} catch (error) {
logActionError("hapusGroupProduct", { userId: user.id, kdStrip }, error);
return {
ok: false,
error: "Gagal menghapus group product, coba hapus ulang",
};
}
}