import prisma from '../utils/db'; import { catatAudit } from '../utils/audit'; import { moveToTrash } from '../utils/trash'; import { APIError } from '../utils/errors'; export class MemberService { static async getMembers(skip = 0, limit = 10, pencarian?: string) { const where: any = { deleted_at: null, }; if (pencarian) { where.OR = [ { nama: { contains: pencarian } }, { kode: { contains: pencarian } }, { nomor_hp: { contains: pencarian } }, ]; } return await prisma.member.findMany({ where, skip, take: limit, }); } static async getMemberById(id: number) { const member = await prisma.member.findFirst({ where: { id, deleted_at: null }, }); if (!member) { throw new APIError('Member tidak ditemukan', 404); } return member; } static async create(data: any, actorId: number, ip: string, userAgent: string) { const existingPhone = await prisma.member.findFirst({ where: { nomor_hp: data.nomor_hp }, }); const existingCode = await prisma.member.findUnique({ where: { kode: data.kode }, }); const existing = existingPhone || existingCode; if (existing) { if (existing.deleted_at === null) { const fieldType = existing.id === existingPhone?.id ? 'Nomor HP' : 'Kode'; const fieldVal = existing.id === existingPhone?.id ? data.nomor_hp : data.kode; throw new APIError(`${fieldType} member '${fieldVal}' sudah terdaftar/digunakan`, 400); } else { // Soft deleted duplicate. Reactivate and update! const restored = await prisma.member.update({ where: { id: existing.id }, data: { deleted_at: null, aktif: 1, kode: data.kode, nama: data.nama, nomor_hp: data.nomor_hp, alamat: data.alamat ?? null, limit_bon: data.limit_bon ?? 0, foto_url: data.foto_url ?? null, }, }); await catatAudit({ userId: actorId, aksi: 'restore', entitas: 'member', entitasId: restored.id, dataBaru: { kode: restored.kode, nama: restored.nama, nomor_hp: restored.nomor_hp, limit_bon: Number(restored.limit_bon) }, ipAddress: ip, userAgent, }); return restored; } } const member = await prisma.member.create({ data: { kode: data.kode, nama: data.nama, nomor_hp: data.nomor_hp, alamat: data.alamat ?? null, limit_bon: data.limit_bon ?? 0, total_bon: 0, foto_url: data.foto_url ?? null, aktif: data.aktif ?? 1, }, }); await catatAudit({ userId: actorId, aksi: 'create', entitas: 'member', entitasId: member.id, dataBaru: { kode: member.kode, nama: member.nama, nomor_hp: member.nomor_hp, limit_bon: Number(member.limit_bon) }, ipAddress: ip, userAgent, }); return member; } static async update(id: number, data: any, actorId: number, ip: string, userAgent: string) { const member = await this.getMemberById(id); const dataLama = { nama: member.nama, nomor_hp: member.nomor_hp, alamat: member.alamat, total_poin: member.total_poin, limit_bon: Number(member.limit_bon), total_bon: Number(member.total_bon), foto_url: member.foto_url, aktif: member.aktif, }; if (data.nomor_hp !== undefined) { const dup = await prisma.member.findFirst({ where: { nomor_hp: data.nomor_hp, id: { not: id } }, }); if (dup) { throw new APIError('Nomor HP tersebut sudah digunakan oleh member lain', 400); } } const updateData: any = {}; if (data.nama !== undefined) updateData.nama = data.nama; if (data.nomor_hp !== undefined) updateData.nomor_hp = data.nomor_hp; if (data.alamat !== undefined) updateData.alamat = data.alamat; if (data.total_poin !== undefined) updateData.total_poin = data.total_poin; if (data.limit_bon !== undefined) updateData.limit_bon = data.limit_bon; if (data.total_bon !== undefined) updateData.total_bon = data.total_bon; if (data.foto_url !== undefined) updateData.foto_url = data.foto_url; if (data.aktif !== undefined) updateData.aktif = data.aktif; const updated = await prisma.member.update({ where: { id }, data: updateData, }); const dataBaru = { nama: updated.nama, nomor_hp: updated.nomor_hp, alamat: updated.alamat, total_poin: updated.total_poin, limit_bon: Number(updated.limit_bon), total_bon: Number(updated.total_bon), foto_url: updated.foto_url, aktif: updated.aktif, }; await catatAudit({ userId: actorId, aksi: 'update', entitas: 'member', entitasId: updated.id, dataLama, dataBaru, ipAddress: ip, userAgent, }); return updated; } static async delete(id: number, actorId: number, ip: string, userAgent: string) { const member = await this.getMemberById(id); if (Number(member.total_bon) > 0) { throw new APIError(`Tidak dapat menghapus member. Masih memiliki tanggungan kasbon sebesar Rp ${Number(member.total_bon).toLocaleString('id-ID')}`, 400); } await moveToTrash({ instance: member, entityType: 'member', actorId, }); await catatAudit({ userId: actorId, aksi: 'delete', entitas: 'member', entitasId: member.id, dataLama: { kode: member.kode, nama: member.nama, aktif: 1 }, dataBaru: { kode: member.kode, nama: member.nama, aktif: 0 }, ipAddress: ip, userAgent, }); return true; } }