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.

171 lines
4.8 KiB
TypeScript

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<SubGroupProductInput, "kedProduk">;
// 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<boolean> {
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<number> {
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<boolean> {
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<number> {
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<boolean> {
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<SubGroupProductPage> {
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,
};
}