etsy-finance-tracker/server/src/routes/orders.ts
dlawler489 1a3bd33be8 Migrate frontend from localStorage to MongoDB API
- Remove localStorage from all 4 Redux slices (products, orders, expenses, customers)
- Layout fetches all data from API on mount; adds logout button with active nav highlighting
- Wire API calls in Products, Orders, Expenses pages for all CRUD operations
- DataImport uses POST /orders/bulk for CSV upserts and API for PDF slip orders
- MissingProductsModal creates products via API
- Relax Order model: optional customerId, embedded customer, fees, printingCost on items, default paymentStatus=paid
- Relax Expense model: free-string category, add taxDeductible/vendor/reference fields
- Add printingCost to Product model
- Add POST /orders/bulk endpoint for upsert-by-orderNumber
- Raise rate limit to 1000 req/15min for bulk imports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 08:48:05 +10:00

108 lines
3.4 KiB
TypeScript

import { Router, Response } from 'express';
import Order from '../models/Order';
import Customer from '../models/Customer';
import { AuthRequest } from '../middleware/authenticate';
const router = Router();
router.get('/', async (req: AuthRequest, res: Response) => {
try {
const { page = 1, limit = 200, status, paymentStatus } = req.query;
const filter: any = { userId: req.userId };
if (status) filter.status = status;
if (paymentStatus) filter.paymentStatus = paymentStatus;
const orders = await Order.find(filter)
.sort({ dateOrdered: -1 })
.limit(Number(limit))
.skip((Number(page) - 1) * Number(limit));
const total = await Order.countDocuments(filter);
res.json({ orders, total, page: Number(page), limit: Number(limit) });
} catch (err) {
res.status(500).json({ message: 'Failed to fetch orders', error: err });
}
});
router.post('/', async (req: AuthRequest, res: Response) => {
try {
const { _id, ...body } = req.body;
const order = new Order({ ...body, userId: req.userId });
await order.save();
if (order.customerId) {
await Customer.findOneAndUpdate(
{ _id: order.customerId, userId: req.userId },
{ $inc: { totalOrders: 1, totalSpent: order.total } }
);
}
res.status(201).json(order);
} catch (err) {
res.status(400).json({ message: 'Failed to create order', error: err });
}
});
router.post('/bulk', async (req: AuthRequest, res: Response) => {
try {
const orders: any[] = req.body;
const results = { created: 0, updated: 0, errors: 0 };
for (const orderData of orders) {
try {
const { _id, ...body } = orderData;
const existing = await Order.findOne({ orderNumber: body.orderNumber, userId: req.userId });
if (existing) {
await Order.findByIdAndUpdate(existing._id, { ...body, userId: req.userId }, { new: true, runValidators: true });
results.updated++;
} else {
await Order.create({ ...body, userId: req.userId });
results.created++;
}
} catch {
results.errors++;
}
}
res.json(results);
} catch (err) {
res.status(400).json({ message: 'Failed to bulk import orders', error: err });
}
});
router.get('/:id', async (req: AuthRequest, res: Response) => {
try {
const order = await Order.findOne({ _id: req.params.id, userId: req.userId });
if (!order) return res.status(404).json({ message: 'Order not found' });
res.json(order);
} catch (err) {
res.status(500).json({ message: 'Failed to fetch order', error: err });
}
});
router.put('/:id', async (req: AuthRequest, res: Response) => {
try {
const { _id, ...body } = req.body;
const order = await Order.findOneAndUpdate(
{ _id: req.params.id, userId: req.userId },
body,
{ new: true, runValidators: true }
);
if (!order) return res.status(404).json({ message: 'Order not found' });
res.json(order);
} catch (err) {
res.status(400).json({ message: 'Failed to update order', error: err });
}
});
router.delete('/:id', async (req: AuthRequest, res: Response) => {
try {
const order = await Order.findOneAndDelete({ _id: req.params.id, userId: req.userId });
if (!order) return res.status(404).json({ message: 'Order not found' });
res.json({ message: 'Order deleted' });
} catch (err) {
res.status(500).json({ message: 'Failed to delete order', error: err });
}
});
export default router;