151 lines
5.0 KiB
JavaScript
151 lines
5.0 KiB
JavaScript
const express = require('express');
|
|
const prisma = require('../db');
|
|
const { asyncHandler, httpError } = require('../lib/errors');
|
|
const { isValidPhone, normalizePhone } = require('../lib/phone');
|
|
const { canAccessDealer } = require('../lib/access');
|
|
const { userPublicSelect, publicUser } = require('../lib/users');
|
|
const { requireAuth, requireRole } = require('../middleware/auth');
|
|
|
|
const router = express.Router();
|
|
|
|
router.use(requireAuth);
|
|
|
|
router.get(
|
|
'/dealers',
|
|
requireRole('superAdmin'),
|
|
asyncHandler(async (req, res) => {
|
|
const dealers = await prisma.dealer.findMany({ orderBy: { name: 'asc' } });
|
|
return res.json({ success: true, data: dealers });
|
|
})
|
|
);
|
|
|
|
router.post(
|
|
'/dealers',
|
|
requireRole('superAdmin'),
|
|
asyncHandler(async (req, res) => {
|
|
const name = String(req.body?.name || '').trim();
|
|
const code = String(req.body?.code || '').trim();
|
|
const district = String(req.body?.district || '').trim() || null;
|
|
const address = String(req.body?.address || '').trim() || null;
|
|
const rawPhone = String(req.body?.phone || '').trim();
|
|
const phone = rawPhone ? normalizePhone(rawPhone) : null;
|
|
if (!name || !code) throw httpError(400, 'Name and code are required');
|
|
if (phone && !isValidPhone(phone)) {
|
|
throw httpError(400, 'Dealer phone must be a Sri Lankan mobile number');
|
|
}
|
|
|
|
const dealer = await prisma.dealer.create({
|
|
data: { name, code, district, address, phone },
|
|
});
|
|
return res.status(201).json({ success: true, data: dealer });
|
|
})
|
|
);
|
|
|
|
router.get(
|
|
'/sales-reps',
|
|
requireRole('superAdmin'),
|
|
asyncHandler(async (req, res) => {
|
|
const reps = await prisma.user.findMany({
|
|
where: { role: 'salesRep' },
|
|
select: userPublicSelect,
|
|
orderBy: { name: 'asc' },
|
|
});
|
|
return res.json({ success: true, data: reps.map((rep) => publicUser(rep)) });
|
|
})
|
|
);
|
|
|
|
router.post(
|
|
'/sales-reps',
|
|
requireRole('superAdmin'),
|
|
asyncHandler(async (req, res) => {
|
|
const name = String(req.body?.name || '').trim();
|
|
const phone = normalizePhone(req.body?.phone);
|
|
const dealerIds = Array.isArray(req.body?.dealerIds) ? req.body.dealerIds : [];
|
|
if (!name || !isValidPhone(phone)) {
|
|
throw httpError(400, 'Name and a 947XXXXXXXX phone are required');
|
|
}
|
|
if (!dealerIds.every((id) => typeof id === 'string' && id.trim())) {
|
|
throw httpError(400, 'dealerIds must be a list of ids');
|
|
}
|
|
const uniqueIds = [...new Set(dealerIds.map((id) => id.trim()))];
|
|
if (uniqueIds.length) {
|
|
const found = await prisma.dealer.findMany({ where: { id: { in: uniqueIds } } });
|
|
if (found.length !== uniqueIds.length) throw httpError(404, 'Dealer not found');
|
|
}
|
|
|
|
const user = await prisma.user.create({
|
|
data: {
|
|
name,
|
|
phone,
|
|
role: 'salesRep',
|
|
assignments: {
|
|
create: uniqueIds.map((dealerId) => ({ dealerId })),
|
|
},
|
|
},
|
|
select: userPublicSelect,
|
|
});
|
|
return res.status(201).json({ success: true, data: publicUser(user) });
|
|
})
|
|
);
|
|
|
|
router.patch(
|
|
'/assign-dealer',
|
|
requireRole('superAdmin'),
|
|
asyncHandler(async (req, res) => {
|
|
const salesRepId = String(req.body?.salesRepId || '').trim();
|
|
const dealerId = String(req.body?.dealerId || '').trim();
|
|
const assigned = req.body?.assigned !== false;
|
|
if (!salesRepId || !dealerId) {
|
|
throw httpError(400, 'salesRepId and dealerId are required');
|
|
}
|
|
|
|
const [rep, dealer] = await Promise.all([
|
|
prisma.user.findUnique({ where: { id: salesRepId } }),
|
|
prisma.dealer.findUnique({ where: { id: dealerId } }),
|
|
]);
|
|
if (!rep || rep.role !== 'salesRep') throw httpError(404, 'Sales rep not found');
|
|
if (!dealer) throw httpError(404, 'Dealer not found');
|
|
|
|
if (assigned) {
|
|
await prisma.dealerAssignment.upsert({
|
|
where: { salesRepId_dealerId: { salesRepId, dealerId } },
|
|
update: {},
|
|
create: { salesRepId, dealerId },
|
|
});
|
|
} else {
|
|
await prisma.dealerAssignment.deleteMany({ where: { salesRepId, dealerId } });
|
|
}
|
|
|
|
const updated = await prisma.user.findUnique({
|
|
where: { id: salesRepId },
|
|
select: userPublicSelect,
|
|
});
|
|
return res.json({ success: true, data: publicUser(updated) });
|
|
})
|
|
);
|
|
|
|
router.post(
|
|
'/dealer-users',
|
|
requireRole('superAdmin', 'salesRep'),
|
|
asyncHandler(async (req, res) => {
|
|
const name = String(req.body?.name || '').trim();
|
|
const phone = normalizePhone(req.body?.phone);
|
|
const dealerId = String(req.body?.dealerId || '').trim();
|
|
if (!name || !isValidPhone(phone) || !dealerId) {
|
|
throw httpError(400, 'Name, phone, and dealerId are required');
|
|
}
|
|
if (!canAccessDealer(req.user, dealerId)) throw httpError(403, 'Forbidden');
|
|
|
|
const dealer = await prisma.dealer.findUnique({ where: { id: dealerId } });
|
|
if (!dealer) throw httpError(404, 'Dealer not found');
|
|
|
|
const user = await prisma.user.create({
|
|
data: { name, phone, role: 'dealer', dealerId },
|
|
select: userPublicSelect,
|
|
});
|
|
return res.status(201).json({ success: true, data: publicUser(user) });
|
|
})
|
|
);
|
|
|
|
module.exports = router;
|