import "server-only"; import { and, asc, count, eq, like, or } from "drizzle-orm"; import { amigoDb } from "@/db/amigo"; import { barang, kedalaman_produk, strip } from "@/db/amigo/schema"; import { PER_PAGE } from "@/lib/pagination"; import { escapeLike } from "@/lib/utils"; export type SubGroupProduct = { id: number; kodeKedProduk: string; kedProduk: string; kodeStrip: string | null; namaStrip: string | null; }; export type SubGroupProductPage = { rows: SubGroupProduct[]; hasNext: boolean; }; // Identitas sub group = pasangan (kodeStrip, kodeKedProduk); `id` hanya primary key teknis. export type SubGroupProductInput = { kodeKedProduk: string; kedProduk: string; kodeStrip: string; }; // Kode tidak ikut diubah: barang menunjuk sub group lewat pasangan kode, jadi mengganti kode // membuat barang yatim. PHP lama juga hanya mengubah nama (`ajaxdlmpdk.php`). export type SubGroupProductUpdate = Pick; // Dilempar untuk isian yang ditolak aturan bisnis (kode dobel, group tidak ada), supaya action // bisa membedakannya dari error DB lain yang perlu di-log. export class SubGroupProductDitolakError extends Error {} async function findSubGroupProduct( kodeStrip: string, kodeKedProduk: string, ): Promise<{ id: number } | undefined> { const [row] = await amigoDb .select({ id: kedalaman_produk.id }) .from(kedalaman_produk) .where( and( eq(kedalaman_produk.kode_strip, kodeStrip), eq(kedalaman_produk.kode_ked_produk, kodeKedProduk), ), ) .limit(1); return row; } async function isGroupProductExist(kodeStrip: string): Promise { const [row] = await amigoDb .select({ kdStrip: strip.kd_strip }) .from(strip) .where(eq(strip.kd_strip, kodeStrip)) .limit(1); return row !== undefined; } export async function insertSubGroupProduct( input: SubGroupProductInput, ): Promise { if (!(await isGroupProductExist(input.kodeStrip))) { throw new SubGroupProductDitolakError( `Group product ${input.kodeStrip} tidak ditemukan, pilih ulang group product`, ); } // Tidak ada unique index di DB (MyISAM), jadi cek dobel manual sebelum insert. if (await findSubGroupProduct(input.kodeStrip, input.kodeKedProduk)) { throw new SubGroupProductDitolakError( `Kode ${input.kodeKedProduk} sudah dipakai di group product ${input.kodeStrip}`, ); } const [res] = await amigoDb.insert(kedalaman_produk).values({ kode_ked_produk: input.kodeKedProduk, ked_produk: input.kedProduk, kode_strip: input.kodeStrip, }); return res.insertId; } export async function updateSubGroupProduct( id: number, input: SubGroupProductUpdate, ): Promise { const [res] = await amigoDb .update(kedalaman_produk) .set({ ked_produk: input.kedProduk }) .where(eq(kedalaman_produk.id, id)); return res.affectedRows > 0; } // `barang` menunjuk sub group lewat pasangan (kode_strip, ked_produk), bukan lewat id. export async function countPemakaianSubGroupProduct( id: number, ): Promise { const [row] = await amigoDb .select({ total: count() }) .from(barang) .innerJoin( kedalaman_produk, and( eq(barang.kode_strip, kedalaman_produk.kode_strip), eq(barang.ked_produk, kedalaman_produk.kode_ked_produk), ), ) .where(eq(kedalaman_produk.id, id)); return row?.total ?? 0; } export async function deleteSubGroupProduct(id: number): Promise { const [res] = await amigoDb .delete(kedalaman_produk) .where(eq(kedalaman_produk.id, id)); return res.affectedRows > 0; } export async function listSubGroupProduct( search?: string, page = 1, ): Promise { const q = search?.trim(); const pattern = q ? `%${escapeLike(q)}%` : ""; const rows = await amigoDb .select({ id: kedalaman_produk.id, kodeKedProduk: kedalaman_produk.kode_ked_produk, kedProduk: kedalaman_produk.ked_produk, kodeStrip: kedalaman_produk.kode_strip, namaStrip: strip.nama_strip, }) .from(kedalaman_produk) // leftJoin: ada data lama yang menunjuk kode group yang sudah tidak ada. .leftJoin(strip, eq(kedalaman_produk.kode_strip, strip.kd_strip)) .where( q ? or( like(kedalaman_produk.ked_produk, pattern), like(kedalaman_produk.kode_strip, pattern), like(kedalaman_produk.kode_ked_produk, pattern), like(strip.nama_strip, pattern), ) : undefined, ) // id sebagai pengurut terakhir: urutan antar-query tetap stabil di batas halaman. .orderBy( asc(kedalaman_produk.kode_strip), asc(kedalaman_produk.kode_ked_produk), asc(kedalaman_produk.id), ) .limit(PER_PAGE + 1) .offset((page - 1) * PER_PAGE); return { rows: rows.slice(0, PER_PAGE), hasNext: rows.length > PER_PAGE, }; }