Spaces:
Running
Running
feat: implement business date utility and financial reporting system including expense tracking, platform sales, and profit-loss analysis endpoints
ec62259 Download src/controllers/expenseController.js from aliroohan179/face: direct link, hf CLI and curl.
- Browser
- Download file 4.15 kB
-
https://huggingface.co/spaces/aliroohan179/face/resolve/main/src/controllers/expenseController.js
- Command line
-
hf download hf://spaces/aliroohan179/face/src/controllers/expenseController.js
-
curl -L -o expenseController.js https://huggingface.co/spaces/aliroohan179/face/resolve/main/src/controllers/expenseController.js
4.15 kB
| const Expense = require('../models/Expense'); | |
| const { log } = require('../utils/activityLogger'); | |
| // GET /api/expenses | |
| exports.getExpenses = async (req, res) => { | |
| const { from, to, category, page = 1, limit = 50 } = req.query; | |
| const filter = {}; | |
| if (category) filter.category = category; | |
| if (from || to) { | |
| filter.date = {}; | |
| if (from) filter.date.$gte = new Date(from); | |
| if (to) filter.date.$lte = new Date(new Date(to).setHours(23, 59, 59, 999)); | |
| } | |
| const [expenses, total] = await Promise.all([ | |
| Expense.find(filter) | |
| .populate('createdBy', 'name username') | |
| .sort({ date: -1 }) | |
| .skip((page - 1) * limit) | |
| .limit(Number(limit)), | |
| Expense.countDocuments(filter), | |
| ]); | |
| res.json({ expenses, total, page: Number(page), pages: Math.ceil(total / limit) }); | |
| }; | |
| // POST /api/expenses | |
| exports.createExpense = async (req, res) => { | |
| const expense = await Expense.create({ ...req.body, createdBy: req.user._id }); | |
| log(req.user, 'ADD_EXPENSE', { entity: 'Expense', entityId: expense._id, detail: `${expense.title} Rs.${expense.amount}` }); | |
| res.status(201).json(expense); | |
| }; | |
| // POST /api/expenses/bulk — create multiple expenses in one go | |
| exports.createBulkExpenses = async (req, res) => { | |
| const { expenses, date } = req.body; | |
| if (!Array.isArray(expenses) || expenses.length === 0) { | |
| return res.status(400).json({ message: 'Array of expenses is required' }); | |
| } | |
| const defaultDate = date ? new Date(date) : new Date(); | |
| const docsToInsert = expenses | |
| .filter((e) => e.title && e.title.trim() && !isNaN(Number(e.amount)) && Number(e.amount) >= 0) | |
| .map((e) => ({ | |
| title: e.title.trim(), | |
| category: e.category || 'Misc', | |
| amount: Number(e.amount), | |
| date: e.date ? new Date(e.date) : defaultDate, | |
| note: e.note || '', | |
| recurring: Boolean(e.recurring), | |
| frequency: e.recurring ? e.frequency || 'monthly' : null, | |
| createdBy: req.user._id, | |
| })); | |
| if (docsToInsert.length === 0) { | |
| return res.status(400).json({ message: 'No valid expense entries provided' }); | |
| } | |
| const created = await Expense.insertMany(docsToInsert); | |
| const totalAmount = created.reduce((s, e) => s + e.amount, 0); | |
| log(req.user, 'BULK_EXPENSE', { | |
| entity: 'Expense', | |
| detail: `Added ${created.length} expenses totalling Rs.${totalAmount}`, | |
| }); | |
| res.status(201).json({ count: created.length, expenses: created }); | |
| }; | |
| // PUT /api/expenses/:id | |
| exports.updateExpense = async (req, res) => { | |
| const expense = await Expense.findById(req.params.id); | |
| if (!expense) return res.status(404).json({ message: 'Expense not found' }); | |
| // Build human-readable diff for audit trail | |
| const changedFields = Object.keys(req.body) | |
| .filter((k) => String(expense[k]) !== String(req.body[k])) | |
| .map((k) => `${k}: "${expense[k]}" → "${req.body[k]}"`) | |
| .join(', '); | |
| Object.assign(expense, req.body); | |
| expense.editLog.push({ user: req.user._id, changes: changedFields || 'no change' }); | |
| await expense.save(); | |
| log(req.user, 'EDIT_EXPENSE', { entity: 'Expense', entityId: expense._id, detail: changedFields }); | |
| res.json(expense); | |
| }; | |
| // DELETE /api/expenses/:id | |
| exports.deleteExpense = async (req, res) => { | |
| const expense = await Expense.findByIdAndDelete(req.params.id); | |
| if (!expense) return res.status(404).json({ message: 'Expense not found' }); | |
| log(req.user, 'DELETE_EXPENSE', { entity: 'Expense', entityId: expense._id, detail: expense.title }); | |
| res.json({ message: 'Expense deleted' }); | |
| }; | |
| // POST /api/expenses/:id/duplicate — copy to next month (recurring helper) | |
| exports.duplicateExpense = async (req, res) => { | |
| const source = await Expense.findById(req.params.id); | |
| if (!source) return res.status(404).json({ message: 'Expense not found' }); | |
| const newDate = new Date(source.date); | |
| newDate.setMonth(newDate.getMonth() + 1); | |
| const copy = await Expense.create({ | |
| title: source.title, | |
| category: source.category, | |
| amount: source.amount, | |
| date: newDate, | |
| note: source.note, | |
| recurring: source.recurring, | |
| frequency: source.frequency, | |
| createdBy: req.user._id, | |
| }); | |
| res.status(201).json(copy); | |
| }; | |