// /components/payments/paymentService.js 'use strict'; const Payment = require('./paymentModel'); const AppError = require('../../utils/AppError'); const eventEmitter = require('../../events/eventEmitter'); const EVENT_NAMES = require('../../constants/eventNames'); const { calculateMeta } = require('../../utils/pagination'); const getAllPayments = async (query) => { const page = parseInt(query.page) || 1; const limit = Math.min(parseInt(query.limit) || 20, 200); const skip = (page - 1) * limit; const filter = {}; if (query.userId) filter.user = query.userId; if (query.status) filter.status = query.status; const [items, total] = await Promise.all([ Payment.find(filter) .populate({ path: 'user', select: 'name' }) .populate({ path: 'classes', select: 'name' }) .populate({ path: 'course', select: 'title' }) .skip(skip).limit(limit).sort({ createdAt: -1 }).lean(), Payment.countDocuments(filter) ]); return { data: items, meta: calculateMeta(total, page, limit) }; }; const getPaymentById = async (id) => { const payment = await Payment.findById(id) .populate({ path: 'user', select: 'name phoneNumber' }) .populate({ path: 'classes', select: 'name tuitionFee' }) .populate({ path: 'course', select: 'title price' }) .lean(); if (!payment) throw new AppError('PAYMENT_NOT_FOUND'); return payment; }; const createPayment = async (body, actorId = null) => { const payment = await Payment.create({ ...body, paidAmount: body.paidAmount || 0 }); if (actorId) { eventEmitter.emit(EVENT_NAMES.PAYMENT_CREATED, { paymentId: payment._id, userId: payment.user, actorId }); } return getPaymentById(payment._id); }; const updatePayment = async (id, body, actorId = null) => { const payment = await Payment.findById(id); if (!payment) throw new AppError('PAYMENT_NOT_FOUND'); const previousStatus = payment.status; Object.assign(payment, body); await payment.save(); if (body.status && body.status !== previousStatus) { eventEmitter.emit(EVENT_NAMES.PAYMENT_STATUS_CHANGED, { paymentId: payment._id, userId: payment.user, oldStatus: previousStatus, newStatus: payment.status, actorId }); } return getPaymentById(payment._id); }; const deletePayment = async (id) => { const payment = await Payment.findByIdAndDelete(id); if (!payment) throw new AppError('PAYMENT_NOT_FOUND'); return null; }; const searchPayments = async (query) => getAllPayments(query); const addTransaction = async (paymentId, trxData, actorId = null) => { const payment = await Payment.findById(paymentId); if (!payment) throw new AppError('PAYMENT_NOT_FOUND'); payment.transactions.push({ ...trxData, recordedBy: actorId || trxData.recordedBy, date: trxData.date || new Date() }); payment.paidAmount = payment.transactions.reduce((sum, t) => sum + (t.amount || 0), 0); await payment.save(); return getPaymentById(paymentId); }; const getMyPayments = async (userId, query = {}) => { return getAllPayments({ ...query, userId }); }; module.exports = { getAllPayments, getPaymentById, createPayment, updatePayment, deletePayment, searchPayments, addTransaction, getMyPayments, // Aliases for older call sites getAll: getAllPayments, getOne: getPaymentById, create: createPayment, recordTransaction: addTransaction, remove: deletePayment };