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.

205 lines
5.5 KiB
TypeScript

import "server-only";
import { cache } from "react";
import { asc, count, desc, 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 GroupProduct = {
kdStrip: string;
namaStrip: string;
murah1: number;
murah2: number;
sedang1: number;
sedang2: number;
mahal1: number;
mahal2: number;
benikMurah: number;
benikSedang: number;
benikMahal: number;
kelBrg: string | null;
kelJns: string;
persenTiwul: number;
};
export type GroupProductPage = {
rows: GroupProduct[];
hasNext: boolean;
};
// kdStrip dibuat sistem, bukan isian form.
export type GroupProductInput = Omit<GroupProduct, "kdStrip">;
function isKdStripExist(error: unknown): boolean {
const e = error as {
errno?: number;
code?: string;
cause?: { errno?: number; code?: string };
};
return (
e?.errno === 1062 ||
e?.code === "ER_DUP_ENTRY" ||
e?.cause?.errno === 1062 ||
e?.cause?.code === "ER_DUP_ENTRY"
);
}
// Kode = 2 digit urut dari kode tertinggi ("09" -> "10"); semua kode existing numerik.
// Sengaja otomatis, padahal PHP lama mengisinya manual (`ajaxstrip.php`).
async function generateKodeStrip(): Promise<string> {
const [row] = await amigoDb
.select({ kdStrip: strip.kd_strip })
.from(strip)
.orderBy(desc(strip.kd_strip))
.limit(1);
const urut = Number.parseInt(row?.kdStrip ?? "", 10);
return String(Number.isNaN(urut) ? 0 : urut + 1).padStart(2, "0");
}
export async function insertGroupProduct(
input: GroupProductInput,
): Promise<string> {
// MyISAM tidak punya transaksi; baca max + insert bisa balapan dengan PHP lama,
// jadi kalau kode sudah dipakai proses lain, ulangi baca max + insert.
for (let attempt = 0; attempt < 3; attempt++) {
const kdStrip = await generateKodeStrip();
try {
await amigoDb.insert(strip).values({
kd_strip: kdStrip,
nama_strip: input.namaStrip,
murah1: input.murah1,
murah2: input.murah2,
sedang1: input.sedang1,
sedang2: input.sedang2,
mahal1: input.mahal1,
mahal2: input.mahal2,
benik_murah: input.benikMurah,
benik_sedang: input.benikSedang,
benik_mahal: input.benikMahal,
kel_brg: input.kelBrg,
kel_jns: input.kelJns,
persen_tiwul: input.persenTiwul,
});
return kdStrip;
} catch (error) {
if (!isKdStripExist(error) || attempt === 2) throw error;
}
}
throw new Error("Kode group product baru sedang dipakai proses lain");
}
export async function updateGroupProduct(
kdStrip: string,
input: GroupProductInput,
): Promise<boolean> {
const [res] = await amigoDb
.update(strip)
.set({
nama_strip: input.namaStrip,
murah1: input.murah1,
murah2: input.murah2,
sedang1: input.sedang1,
sedang2: input.sedang2,
mahal1: input.mahal1,
mahal2: input.mahal2,
benik_murah: input.benikMurah,
benik_sedang: input.benikSedang,
benik_mahal: input.benikMahal,
kel_brg: input.kelBrg,
kel_jns: input.kelJns,
persen_tiwul: input.persenTiwul,
})
.where(eq(strip.kd_strip, kdStrip));
return res.affectedRows > 0;
}
export type PemakaianGroupProduct = {
barang: number;
subGroup: number;
};
export async function countPemakaianGroupProduct(
kdStrip: string,
): Promise<PemakaianGroupProduct> {
const [[barangRow], [subGroupRow]] = await Promise.all([
amigoDb
.select({ total: count() })
.from(barang)
.where(eq(barang.kode_strip, kdStrip)),
amigoDb
.select({ total: count() })
.from(kedalaman_produk)
.where(eq(kedalaman_produk.kode_strip, kdStrip)),
]);
return { barang: barangRow?.total ?? 0, subGroup: subGroupRow?.total ?? 0 };
}
export async function deleteGroupProduct(kdStrip: string): Promise<boolean> {
const [res] = await amigoDb.delete(strip).where(eq(strip.kd_strip, kdStrip));
return res.affectedRows > 0;
}
export type GroupProductOption = {
kdStrip: string;
namaStrip: string;
};
// Untuk select di form sub group product; jumlah group sedikit, jadi tanpa pagination.
// `cache`: dipanggil beberapa komponen di satu render, query cukup sekali per request.
export const listGroupProductOptions = cache(
async (): Promise<GroupProductOption[]> =>
amigoDb
.select({ kdStrip: strip.kd_strip, namaStrip: strip.nama_strip })
.from(strip)
.orderBy(asc(strip.kd_strip)),
);
export async function listGroupProduct(
search?: string,
page = 1,
): Promise<GroupProductPage> {
const q = search?.trim();
const pattern = q ? `%${escapeLike(q)}%` : "";
const rows = await amigoDb
.select({
kdStrip: strip.kd_strip,
namaStrip: strip.nama_strip,
murah1: strip.murah1,
murah2: strip.murah2,
sedang1: strip.sedang1,
sedang2: strip.sedang2,
mahal1: strip.mahal1,
mahal2: strip.mahal2,
benikMurah: strip.benik_murah,
benikSedang: strip.benik_sedang,
benikMahal: strip.benik_mahal,
kelBrg: strip.kel_brg,
kelJns: strip.kel_jns,
persenTiwul: strip.persen_tiwul,
})
.from(strip)
.where(
q
? or(like(strip.nama_strip, pattern), like(strip.kd_strip, pattern))
: undefined,
)
.orderBy(asc(strip.kd_strip))
.limit(PER_PAGE + 1)
.offset((page - 1) * PER_PAGE);
return {
rows: rows.slice(0, PER_PAGE),
hasNext: rows.length > PER_PAGE,
};
}