Spaces:
Sleeping
Sleeping
anoderb
feat: implement chatbot service, active shift logic corrections, audit logs, and void transaction tool
48013ee | import { Request, Response, NextFunction } from 'express'; | |
| import { OpsService } from '../services/ops.service'; | |
| import { sukses } from '../utils/response'; | |
| import { AuthenticatedRequest } from '../middlewares/auth.middleware'; | |
| export class OpsController { | |
| // Shift Kasir | |
| static async getActiveShift(req: AuthenticatedRequest, res: Response, next: NextFunction) { | |
| try { | |
| const userId = req.user.id; | |
| const shift = await OpsService.getActiveShift(userId); | |
| if (!shift) { | |
| return sukses(res, null, 'Tidak ada shift aktif untuk kasir saat ini'); | |
| } | |
| return sukses(res, shift, 'Shift aktif berhasil dimuat'); | |
| } catch (error) { | |
| next(error); | |
| } | |
| } | |
| static async openShift(req: AuthenticatedRequest, res: Response, next: NextFunction) { | |
| try { | |
| const actorId = req.user.id; | |
| const ip = (req.ip || req.socket.remoteAddress || 'unknown').replace('::ffff:', ''); | |
| const userAgent = (req.headers['user-agent'] || 'unknown') as string; | |
| const { modal_awal, catatan } = req.body; | |
| const shift = await OpsService.bukaShift(modal_awal, catatan, actorId, ip, userAgent); | |
| return sukses(res, shift, 'Shift berhasil dibuka'); | |
| } catch (error) { | |
| next(error); | |
| } | |
| } | |
| static async closeShift(req: AuthenticatedRequest, res: Response, next: NextFunction) { | |
| try { | |
| const actorId = req.user.id; | |
| const ip = (req.ip || req.socket.remoteAddress || 'unknown').replace('::ffff:', ''); | |
| const userAgent = (req.headers['user-agent'] || 'unknown') as string; | |
| const { modal_akhir_fisik, catatan } = req.body; | |
| const shift = await OpsService.tutupShift(modal_akhir_fisik, catatan, actorId, ip, userAgent); | |
| return sukses(res, shift, 'Shift kasir ditutup, selisih kas fisik berhasil dicatat'); | |
| } catch (error) { | |
| next(error); | |
| } | |
| } | |
| // Dashboard & Laporan | |
| static async getDashboardSummary(req: AuthenticatedRequest, res: Response, next: NextFunction) { | |
| try { | |
| const summary = await OpsService.getDashboardSummary(); | |
| return sukses(res, summary, 'Dashboard summary berhasil dimuat'); | |
| } catch (error) { | |
| next(error); | |
| } | |
| } | |
| static async getSalesReport(req: AuthenticatedRequest, res: Response, next: NextFunction) { | |
| try { | |
| const tglMulai = new Date(req.query.tgl_mulai as string); | |
| const tglSelesai = new Date(req.query.tgl_selesai as string); | |
| const report = await OpsService.buatLaporanPenjualan(tglMulai, tglSelesai); | |
| return sukses(res, report, 'Laporan penjualan berhasil dibuat'); | |
| } catch (error) { | |
| next(error); | |
| } | |
| } | |
| static async getProfitLossReport(req: AuthenticatedRequest, res: Response, next: NextFunction) { | |
| try { | |
| const tglMulai = new Date(req.query.tgl_mulai as string); | |
| const tglSelesai = new Date(req.query.tgl_selesai as string); | |
| const report = await OpsService.buatLaporanLabaRugi(tglMulai, tglSelesai); | |
| return sukses(res, report, 'Laporan laba rugi berhasil dibuat'); | |
| } catch (error) { | |
| next(error); | |
| } | |
| } | |
| static async eksporLaporan(req: AuthenticatedRequest, res: Response, next: NextFunction) { | |
| try { | |
| const jenis = req.query.jenis as any; | |
| const format = req.query.format as any; | |
| const tglMulai = new Date(req.query.tgl_mulai as string); | |
| const tglSelesai = new Date(req.query.tgl_selesai as string); | |
| if (!['penjualan', 'laba-rugi', 'mutasi-stok'].includes(jenis)) { | |
| throw new Error('Jenis laporan tidak valid'); | |
| } | |
| if (!['csv', 'excel', 'pdf'].includes(format)) { | |
| throw new Error('Format laporan tidak valid. Harus csv, excel, atau pdf'); | |
| } | |
| const fileInfo = await OpsService.eksporLaporan({ | |
| jenis, | |
| format, | |
| tglMulai, | |
| tglSelesai, | |
| }); | |
| return sukses(res, fileInfo, 'Laporan berhasil diekspor'); | |
| } catch (error: any) { | |
| res.status(400).json({ sukses: false, pesan: error.message || 'Gagal mengekspor laporan' }); | |
| } | |
| } | |
| // Settings/Pengaturan | |
| static async getSettings(req: Request, res: Response, next: NextFunction) { | |
| try { | |
| const settings = await OpsService.getSettings(); | |
| return sukses(res, settings, 'Pengaturan toko berhasil dimuat'); | |
| } catch (error) { | |
| next(error); | |
| } | |
| } | |
| static async getSettingByKey(req: Request, res: Response, next: NextFunction) { | |
| try { | |
| const { kunci } = req.params; | |
| const value = await OpsService.getSettingByKey(kunci); | |
| return sukses(res, { kunci, nilai: value }, `Pengaturan '${kunci}' berhasil dimuat`); | |
| } catch (error) { | |
| next(error); | |
| } | |
| } | |
| static async updateSetting(req: AuthenticatedRequest, res: Response, next: NextFunction) { | |
| try { | |
| const { kunci } = req.params; | |
| const actorId = req.user.id; | |
| const ip = (req.ip || req.socket.remoteAddress || 'unknown').replace('::ffff:', ''); | |
| const userAgent = (req.headers['user-agent'] || 'unknown') as string; | |
| await OpsService.updateSetting(kunci, req.body, actorId, ip, userAgent); | |
| const value = await OpsService.getSettingByKey(kunci); | |
| return sukses(res, { kunci, nilai: value }, 'Pengaturan berhasil diperbarui'); | |
| } catch (error) { | |
| next(error); | |
| } | |
| } | |
| // Notifikasi | |
| static async listNotifications(req: Request, res: Response, next: NextFunction) { | |
| try { | |
| const skip = parseInt(req.query.skip as string) || 0; | |
| const limit = parseInt(req.query.limit as string) || 20; | |
| const dibaca = req.query.dibaca !== undefined ? parseInt(req.query.dibaca as string) : undefined; | |
| const notifs = await OpsService.getNotifications(skip, limit, dibaca); | |
| return sukses(res, notifs, 'Notifikasi berhasil dimuat'); | |
| } catch (error) { | |
| next(error); | |
| } | |
| } | |
| static async readAllNotifications(req: Request, res: Response, next: NextFunction) { | |
| try { | |
| const affected = await OpsService.markAllNotifAsRead(); | |
| return sukses(res, { jumlah_diubah: affected }, 'Semua notifikasi ditandai dibaca'); | |
| } catch (error) { | |
| next(error); | |
| } | |
| } | |
| static async readNotification(req: Request, res: Response, next: NextFunction) { | |
| try { | |
| const notifId = parseInt(req.params.notif_id); | |
| const notif = await OpsService.markNotifAsRead(notifId); | |
| return sukses(res, notif, 'Notifikasi ditandai dibaca'); | |
| } catch (error) { | |
| next(error); | |
| } | |
| } | |
| static async getUnreadNotificationCount(req: Request, res: Response, next: NextFunction) { | |
| try { | |
| const count = await OpsService.getUnreadNotifCount(); | |
| return sukses(res, { unread_count: count }, 'Jumlah notifikasi belum dibaca berhasil dihitung'); | |
| } catch (error) { | |
| next(error); | |
| } | |
| } | |
| } | |