refactor: matrix stuff
This commit is contained in:
parent
f862097417
commit
e7de8ee3e0
@ -197,6 +197,39 @@ async function getMyMatrixSummary(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function removeUser(req, res) {
|
||||||
|
try {
|
||||||
|
const { matrixInstanceId, userId } = req.body;
|
||||||
|
const data = await MatrixService.removeUser({ matrixInstanceId, userId, actorUser: req.user });
|
||||||
|
return res.json({ success: true, data });
|
||||||
|
} catch (err) {
|
||||||
|
const status = err.status || 500;
|
||||||
|
return res.status(status).json({ success: false, message: err.message || 'Could not remove user' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function assignVacancy(req, res) {
|
||||||
|
try {
|
||||||
|
const { matrixInstanceId, parentUserId, position, userId } = req.body;
|
||||||
|
const data = await MatrixService.assignVacancy({ matrixInstanceId, parentUserId, position, userId, actorUser: req.user });
|
||||||
|
return res.json({ success: true, data });
|
||||||
|
} catch (err) {
|
||||||
|
const status = err.status || 500;
|
||||||
|
return res.status(status).json({ success: false, message: err.message || 'Could not assign vacancy' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listVacancies(req, res) {
|
||||||
|
try {
|
||||||
|
const matrixInstanceId = req.query.matrixInstanceId || req.query.matrixId || req.query.id;
|
||||||
|
const data = await MatrixService.listVacancies({ matrixInstanceId, actorUser: req.user });
|
||||||
|
return res.json({ success: true, data });
|
||||||
|
} catch (err) {
|
||||||
|
const status = err.status || 500;
|
||||||
|
return res.status(status).json({ success: false, message: err.message || 'Could not list vacancies' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
create,
|
create,
|
||||||
stats,
|
stats,
|
||||||
@ -208,5 +241,8 @@ module.exports = {
|
|||||||
activate, // NEW
|
activate, // NEW
|
||||||
listMyMatrices, // NEW
|
listMyMatrices, // NEW
|
||||||
getMyOverviewByInstance, // NEW
|
getMyOverviewByInstance, // NEW
|
||||||
getMyMatrixSummary // NEW
|
getMyMatrixSummary, // NEW
|
||||||
|
removeUser, // NEW
|
||||||
|
assignVacancy, // NEW
|
||||||
|
listVacancies // NEW
|
||||||
};
|
};
|
||||||
|
|||||||
@ -403,19 +403,13 @@ async function getUserSearchCandidates({ q, type = 'all', rootUserId, matrixInst
|
|||||||
async function getMatrixUsers({ rootUserId, matrixInstanceId, maxDepth = 5, limit = 100, offset = 0, includeRoot = false, rogueOnly = false }) {
|
async function getMatrixUsers({ rootUserId, matrixInstanceId, maxDepth = 5, limit = 100, offset = 0, includeRoot = false, rogueOnly = false }) {
|
||||||
const conn = await pool.getConnection();
|
const conn = await pool.getConnection();
|
||||||
try {
|
try {
|
||||||
let policyDepth = null;
|
// Determine policy: root unlimited, ego capped at 5
|
||||||
try {
|
let startDepth = includeRoot ? 0 : 1;
|
||||||
const [pRows] = await conn.query(
|
const [instRows] = await conn.query(`SELECT root_user_id FROM matrix_instances WHERE id = ? LIMIT 1`, [matrixInstanceId]);
|
||||||
`SELECT max_depth FROM matrix_instances WHERE id = ? LIMIT 1`,
|
const isRootAnchor = Number(instRows[0]?.root_user_id) === Number(rootUserId);
|
||||||
[matrixInstanceId]
|
|
||||||
);
|
|
||||||
if (pRows.length) policyDepth = pRows[0].max_depth == null ? null : Number(pRows[0].max_depth);
|
|
||||||
} catch (_) {}
|
|
||||||
|
|
||||||
let requestedDepth = Number(maxDepth);
|
let requestedDepth = Number(maxDepth);
|
||||||
if (!Number.isFinite(requestedDepth) || requestedDepth < 0) requestedDepth = 0;
|
if (!Number.isFinite(requestedDepth) || requestedDepth < 0) requestedDepth = isRootAnchor ? 20 : 5;
|
||||||
const depthLimit = policyDepth == null ? requestedDepth : Math.min(requestedDepth, policyDepth);
|
const depthLimit = isRootAnchor ? requestedDepth : Math.min(requestedDepth, 5);
|
||||||
const startDepth = includeRoot ? 0 : 1;
|
|
||||||
if (startDepth > depthLimit) return [];
|
if (startDepth > depthLimit) return [];
|
||||||
|
|
||||||
const rogueClause = rogueOnly ? 'AND (e.rogue_user = TRUE)' : '';
|
const rogueClause = rogueOnly ? 'AND (e.rogue_user = TRUE)' : '';
|
||||||
@ -968,6 +962,187 @@ async function activateInstance(instanceId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEW: ensure vacancy table exists
|
||||||
|
async function ensureVacancyTable(conn) {
|
||||||
|
await conn.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS matrix_vacancies (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
matrix_instance_id INT NOT NULL,
|
||||||
|
parent_user_id INT NOT NULL,
|
||||||
|
position INT NOT NULL,
|
||||||
|
depth INT NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE KEY uniq_vacancy (matrix_instance_id, parent_user_id, position)
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: remove user and create vacancy
|
||||||
|
async function removeUserAndCreateVacancy(matrixInstanceId, userId) {
|
||||||
|
const conn = await pool.getConnection();
|
||||||
|
try {
|
||||||
|
await conn.beginTransaction();
|
||||||
|
const mid = Number(matrixInstanceId);
|
||||||
|
const uid = Number(userId);
|
||||||
|
if (!Number.isFinite(mid) || mid <= 0 || !Number.isFinite(uid) || uid <= 0) {
|
||||||
|
const err = new Error('Invalid parameters');
|
||||||
|
err.status = 400;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
const [rootRow] = await conn.query(`SELECT root_user_id FROM matrix_instances WHERE id = ? LIMIT 1`, [mid]);
|
||||||
|
if (!rootRow.length) { const err = new Error('Matrix instance not found'); err.status = 404; throw err; }
|
||||||
|
if (Number(rootRow[0].root_user_id) === uid) {
|
||||||
|
const err = new Error('Cannot remove root user');
|
||||||
|
err.status = 400;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
const [edgeRows] = await conn.query(
|
||||||
|
`SELECT parent_user_id, position FROM user_tree_edges WHERE matrix_instance_id = ? AND child_user_id = ? LIMIT 1`,
|
||||||
|
[mid, uid]
|
||||||
|
);
|
||||||
|
if (!edgeRows.length) { const err = new Error('User not in matrix'); err.status = 404; throw err; }
|
||||||
|
const parentId = Number(edgeRows[0].parent_user_id);
|
||||||
|
const pos = Number(edgeRows[0].position);
|
||||||
|
await ensureVacancyTable(conn);
|
||||||
|
const [depthRows] = await conn.query(
|
||||||
|
`SELECT depth FROM user_tree_closure WHERE matrix_instance_id = ? AND ancestor_user_id = ? AND descendant_user_id = ? LIMIT 1`,
|
||||||
|
[mid, rootRow[0].root_user_id, parentId]
|
||||||
|
);
|
||||||
|
const depth = Number(depthRows[0]?.depth || 0) + 1;
|
||||||
|
await conn.query(
|
||||||
|
`INSERT INTO matrix_vacancies (matrix_instance_id, parent_user_id, position, depth)
|
||||||
|
VALUES (?,?,?,?)
|
||||||
|
ON DUPLICATE KEY UPDATE depth = VALUES(depth), created_at = CURRENT_TIMESTAMP`,
|
||||||
|
[mid, parentId, pos, depth]
|
||||||
|
);
|
||||||
|
// delete subtree closure and edges
|
||||||
|
await conn.query(
|
||||||
|
`DELETE c FROM user_tree_closure c
|
||||||
|
JOIN user_tree_closure d ON d.matrix_instance_id = c.matrix_instance_id AND d.descendant_user_id = c.descendant_user_id
|
||||||
|
WHERE d.matrix_instance_id = ? AND d.ancestor_user_id = ?`,
|
||||||
|
[mid, uid]
|
||||||
|
);
|
||||||
|
await conn.query(
|
||||||
|
`DELETE FROM user_tree_edges WHERE matrix_instance_id = ? AND child_user_id IN (
|
||||||
|
SELECT descendant_user_id FROM user_tree_closure WHERE matrix_instance_id = ? AND ancestor_user_id = ?
|
||||||
|
)`,
|
||||||
|
[mid, mid, uid]
|
||||||
|
);
|
||||||
|
await conn.commit();
|
||||||
|
return { matrixInstanceId: mid, vacancy: { parentUserId: parentId, position: pos, depth } };
|
||||||
|
} catch (e) {
|
||||||
|
try { await conn.rollback(); } catch (_) {}
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: list vacancies
|
||||||
|
async function listVacancies(matrixInstanceId) {
|
||||||
|
const conn = await pool.getConnection();
|
||||||
|
try {
|
||||||
|
const mid = Number(matrixInstanceId);
|
||||||
|
if (!Number.isFinite(mid) || mid <= 0) return [];
|
||||||
|
await ensureVacancyTable(conn);
|
||||||
|
const [rootRow] = await conn.query(`SELECT root_user_id FROM matrix_instances WHERE id = ? LIMIT 1`, [mid]);
|
||||||
|
const rootId = Number(rootRow[0]?.root_user_id || 0);
|
||||||
|
const [rows] = await conn.query(
|
||||||
|
`SELECT v.matrix_instance_id AS matrixInstanceId, v.parent_user_id AS parentUserId, v.position, v.depth, v.created_at AS createdAt
|
||||||
|
FROM matrix_vacancies v
|
||||||
|
WHERE v.matrix_instance_id = ?
|
||||||
|
ORDER BY v.created_at DESC`,
|
||||||
|
[mid]
|
||||||
|
);
|
||||||
|
return rows.map(r => ({
|
||||||
|
matrixInstanceId: mid,
|
||||||
|
parentUserId: Number(r.parentUserId),
|
||||||
|
position: Number(r.position),
|
||||||
|
depth: Number(r.depth),
|
||||||
|
root: rootId === Number(r.parentUserId),
|
||||||
|
createdAt: r.createdAt
|
||||||
|
}));
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: assign user to vacancy
|
||||||
|
async function assignUserToVacancy({ matrixInstanceId, parentUserId, position, userId }) {
|
||||||
|
const conn = await pool.getConnection();
|
||||||
|
try {
|
||||||
|
await conn.beginTransaction();
|
||||||
|
const mid = Number(matrixInstanceId);
|
||||||
|
const pid = Number(parentUserId);
|
||||||
|
const pos = Number(position);
|
||||||
|
const uid = Number(userId);
|
||||||
|
if (![mid, pid, pos, uid].every(n => Number.isFinite(n) && n > 0)) {
|
||||||
|
const err = new Error('Invalid parameters');
|
||||||
|
err.status = 400;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
await ensureVacancyTable(conn);
|
||||||
|
const [vacRows] = await conn.query(
|
||||||
|
`SELECT depth FROM matrix_vacancies WHERE matrix_instance_id = ? AND parent_user_id = ? AND position = ? LIMIT 1`,
|
||||||
|
[mid, pid, pos]
|
||||||
|
);
|
||||||
|
if (!vacRows.length) { const err = new Error('Vacancy not found'); err.status = 404; throw err; }
|
||||||
|
const depthFromParent = Number(vacRows[0].depth);
|
||||||
|
const [rootRow] = await conn.query(`SELECT root_user_id FROM matrix_instances WHERE id = ? LIMIT 1`, [mid]);
|
||||||
|
const rootId = Number(rootRow[0]?.root_user_id || 0);
|
||||||
|
const [depthRows] = await conn.query(
|
||||||
|
`SELECT depth FROM user_tree_closure WHERE matrix_instance_id = ? AND ancestor_user_id = ? AND descendant_user_id = ? LIMIT 1`,
|
||||||
|
[mid, rootId, pid]
|
||||||
|
);
|
||||||
|
const parentDepth = Number(depthRows[0]?.depth || 0);
|
||||||
|
if (pid !== rootId && parentDepth + 1 > 5) {
|
||||||
|
const err = new Error('Depth exceeds ego policy');
|
||||||
|
err.status = 400;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
const [inMatrix] = await conn.query(
|
||||||
|
`SELECT 1 FROM user_tree_closure WHERE matrix_instance_id = ? AND descendant_user_id = ? LIMIT 1`,
|
||||||
|
[mid, uid]
|
||||||
|
);
|
||||||
|
if (inMatrix.length) {
|
||||||
|
const err = new Error('User already in matrix');
|
||||||
|
err.status = 409;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
await conn.query(
|
||||||
|
`INSERT INTO user_tree_edges (matrix_instance_id, parent_user_id, child_user_id, position, rogue_user)
|
||||||
|
VALUES (?,?,?,?,FALSE)`,
|
||||||
|
[mid, pid, uid, pos]
|
||||||
|
);
|
||||||
|
await conn.query(
|
||||||
|
`INSERT IGNORE INTO user_tree_closure (matrix_instance_id, ancestor_user_id, descendant_user_id, depth)
|
||||||
|
VALUES (?,?,?,0)`,
|
||||||
|
[mid, uid, uid]
|
||||||
|
);
|
||||||
|
const [ancRows] = await conn.query(
|
||||||
|
`SELECT ancestor_user_id, depth FROM user_tree_closure WHERE matrix_instance_id = ? AND descendant_user_id = ?`,
|
||||||
|
[mid, pid]
|
||||||
|
);
|
||||||
|
if (ancRows.length) {
|
||||||
|
const values = ancRows.map(r => `(${mid}, ${Number(r.ancestor_user_id)}, ${uid}, ${Number(r.depth) + 1})`).join(',');
|
||||||
|
await conn.query(
|
||||||
|
`INSERT IGNORE INTO user_tree_closure (matrix_instance_id, ancestor_user_id, descendant_user_id, depth) VALUES ${values}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await conn.query(
|
||||||
|
`DELETE FROM matrix_vacancies WHERE matrix_instance_id = ? AND parent_user_id = ? AND position = ?`,
|
||||||
|
[mid, pid, pos]
|
||||||
|
);
|
||||||
|
await conn.commit();
|
||||||
|
return { matrixInstanceId: mid, parentUserId: pid, position: pos, userId: uid };
|
||||||
|
} catch (e) {
|
||||||
|
try { await conn.rollback(); } catch (_) {}
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createMatrix,
|
createMatrix,
|
||||||
ensureUserExistsByEmail,
|
ensureUserExistsByEmail,
|
||||||
@ -982,5 +1157,8 @@ module.exports = {
|
|||||||
deactivateInstance, // NEW
|
deactivateInstance, // NEW
|
||||||
activateInstance, // NEW
|
activateInstance, // NEW
|
||||||
listInstancesForUser, // NEW
|
listInstancesForUser, // NEW
|
||||||
userBelongsToInstance // NEW
|
userBelongsToInstance, // NEW
|
||||||
|
removeUserAndCreateVacancy, // NEW
|
||||||
|
listVacancies, // NEW
|
||||||
|
assignUserToVacancy // NEW
|
||||||
};
|
};
|
||||||
|
|||||||
@ -134,8 +134,11 @@ router.get('/matrix/:id/overview', authMiddleware, MatrixController.getMyOvervie
|
|||||||
router.get('/matrix/:id/summary', authMiddleware, MatrixController.getMyMatrixSummary);
|
router.get('/matrix/:id/summary', authMiddleware, MatrixController.getMyMatrixSummary);
|
||||||
|
|
||||||
// Tax GETs
|
// Tax GETs
|
||||||
router.get('/tax/vat-rates', authMiddleware, adminOnly, TaxController.getAllVatRates);
|
router.get('/tax/vat-rates', authMiddleware, TaxController.getAllVatRates);
|
||||||
router.get('/tax/vat-history/:countryCode', authMiddleware, adminOnly, TaxController.getVatHistory);
|
router.get('/tax/vat-history/:countryCode', authMiddleware, adminOnly, TaxController.getVatHistory);
|
||||||
|
|
||||||
|
// NEW: Admin list vacancies for a matrix
|
||||||
|
router.get('/admin/matrix/vacancies', authMiddleware, adminOnly, MatrixController.listVacancies);
|
||||||
|
|
||||||
// export
|
// export
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
@ -123,6 +123,10 @@ router.post('/company-stamps', authMiddleware, adminOnly, forceCompanyForAdmin,
|
|||||||
router.post('/admin/coffee', authMiddleware, adminOnly, upload.single('picture'), CoffeeController.create);
|
router.post('/admin/coffee', authMiddleware, adminOnly, upload.single('picture'), CoffeeController.create);
|
||||||
// NEW: add user into matrix
|
// NEW: add user into matrix
|
||||||
router.post('/admin/matrix/add-user', authMiddleware, adminOnly, MatrixController.addUser); // already added
|
router.post('/admin/matrix/add-user', authMiddleware, adminOnly, MatrixController.addUser); // already added
|
||||||
|
// NEW: remove matrix user and create vacancy
|
||||||
|
router.post('/admin/matrix/remove-user', authMiddleware, adminOnly, MatrixController.removeUser);
|
||||||
|
// NEW: assign user to vacancy
|
||||||
|
router.post('/admin/matrix/assign-vacancy', authMiddleware, adminOnly, MatrixController.assignVacancy);
|
||||||
// NEW: Admin create pool
|
// NEW: Admin create pool
|
||||||
router.post('/admin/pools', authMiddleware, adminOnly, PoolController.create);
|
router.post('/admin/pools', authMiddleware, adminOnly, PoolController.create);
|
||||||
// NEW: import VAT rates CSV
|
// NEW: import VAT rates CSV
|
||||||
|
|||||||
@ -320,9 +320,11 @@ async function getMyOverview({ userId, actorUser }) {
|
|||||||
|
|
||||||
// Load instance policy and root user
|
// Load instance policy and root user
|
||||||
const instanceInfo = await MatrixRepository.getInstanceInfo(mid); // helper added below
|
const instanceInfo = await MatrixRepository.getInstanceInfo(mid); // helper added below
|
||||||
const policyDepth = instanceInfo?.max_depth == null ? 5 : Number(instanceInfo.max_depth);
|
|
||||||
const rootUserId = Number(instanceInfo?.root_user_id || 0);
|
const rootUserId = Number(instanceInfo?.root_user_id || 0);
|
||||||
const isRoot = rootUserId === uid;
|
const isRoot = rootUserId === uid;
|
||||||
|
const policyDepth = instanceInfo?.max_depth == null
|
||||||
|
? (isRoot ? 20 : 5)
|
||||||
|
: (isRoot ? Math.max(20, Number(instanceInfo.max_depth)) : Math.min(5, Number(instanceInfo.max_depth)));
|
||||||
|
|
||||||
// Fetch descendants anchored at requester
|
// Fetch descendants anchored at requester
|
||||||
const users = await MatrixRepository.getMatrixUsers({
|
const users = await MatrixRepository.getMatrixUsers({
|
||||||
@ -413,7 +415,9 @@ async function listMyMatrices({ userId, actorUser }) {
|
|||||||
const mid = Number(m.id ?? m.matrixInstanceId);
|
const mid = Number(m.id ?? m.matrixInstanceId);
|
||||||
// Load instance policy and root user
|
// Load instance policy and root user
|
||||||
const instanceInfo = await MatrixRepository.getInstanceInfo(mid);
|
const instanceInfo = await MatrixRepository.getInstanceInfo(mid);
|
||||||
const policyDepth = instanceInfo?.max_depth == null ? 5 : Number(instanceInfo.max_depth);
|
const policyDepth = instanceInfo?.max_depth == null
|
||||||
|
? (instanceInfo?.root_user_id === uid ? 20 : 5)
|
||||||
|
: (instanceInfo?.root_user_id === uid ? Math.max(20, Number(instanceInfo.max_depth)) : Math.min(5, Number(instanceInfo.max_depth)));
|
||||||
|
|
||||||
const users = await MatrixRepository.getMatrixUsers({
|
const users = await MatrixRepository.getMatrixUsers({
|
||||||
rootUserId: uid,
|
rootUserId: uid,
|
||||||
@ -500,9 +504,11 @@ async function getMyOverviewByInstance({ userId, matrixInstanceId, actorUser })
|
|||||||
err.status = 404;
|
err.status = 404;
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
const policyDepth = instanceInfo?.max_depth == null ? 5 : Number(instanceInfo.max_depth);
|
|
||||||
const rootUserId = Number(instanceInfo?.root_user_id || 0);
|
const rootUserId = Number(instanceInfo?.root_user_id || 0);
|
||||||
const isRoot = rootUserId === uid;
|
const isRoot = rootUserId === uid;
|
||||||
|
const policyDepth = instanceInfo?.max_depth == null
|
||||||
|
? (isRoot ? 20 : 5)
|
||||||
|
: (isRoot ? Math.max(20, Number(instanceInfo.max_depth)) : Math.min(5, Number(instanceInfo.max_depth)));
|
||||||
|
|
||||||
// Fetch descendants anchored at requester within this instance
|
// Fetch descendants anchored at requester within this instance
|
||||||
const users = await MatrixRepository.getMatrixUsers({
|
const users = await MatrixRepository.getMatrixUsers({
|
||||||
@ -602,7 +608,7 @@ async function getMyMatrixSummary({ userId, matrixInstanceId, actorUser }) {
|
|||||||
err.status = 404;
|
err.status = 404;
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
const policyDepth = instanceInfo?.max_depth == null ? 5 : Number(instanceInfo.max_depth);
|
const policyDepth = instanceInfo?.max_depth == null ? (instanceInfo?.root_user_id === uid ? 20 : 5) : (instanceInfo?.root_user_id === uid ? Math.max(20, Number(instanceInfo.max_depth)) : Math.min(5, Number(instanceInfo.max_depth)));
|
||||||
|
|
||||||
// Fetch all descendants anchored at requester within this instance
|
// Fetch all descendants anchored at requester within this instance
|
||||||
const users = await MatrixRepository.getMatrixUsers({
|
const users = await MatrixRepository.getMatrixUsers({
|
||||||
@ -719,6 +725,34 @@ async function activate({ matrixInstanceId, matrixId, rootUserId, topNodeEmail,
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEW: remove user and create vacancy (admin)
|
||||||
|
async function removeUser({ matrixInstanceId, userId, actorUser }) {
|
||||||
|
if (!actorUser) { const err = new Error('Unauthorized'); err.status = 401; throw err; }
|
||||||
|
if (!isAdmin(actorUser)) { const err = new Error('Forbidden: Admins only'); err.status = 403; throw err; }
|
||||||
|
const mid = Number(matrixInstanceId);
|
||||||
|
const uid = Number(userId);
|
||||||
|
if (!Number.isFinite(mid) || mid <= 0 || !Number.isFinite(uid) || uid <= 0) {
|
||||||
|
const err = new Error('Invalid parameters');
|
||||||
|
err.status = 400;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return await MatrixRepository.removeUserAndCreateVacancy(mid, uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: assign user to vacancy (admin)
|
||||||
|
async function assignVacancy({ matrixInstanceId, parentUserId, position, userId, actorUser }) {
|
||||||
|
if (!actorUser) { const err = new Error('Unauthorized'); err.status = 401; throw err; }
|
||||||
|
if (!isAdmin(actorUser)) { const err = new Error('Forbidden: Admins only'); err.status = 403; throw err; }
|
||||||
|
return await MatrixRepository.assignUserToVacancy({ matrixInstanceId, parentUserId, position, userId });
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: list vacancies (admin)
|
||||||
|
async function listVacancies({ matrixInstanceId, actorUser }) {
|
||||||
|
if (!actorUser) { const err = new Error('Unauthorized'); err.status = 401; throw err; }
|
||||||
|
if (!isAdmin(actorUser)) { const err = new Error('Forbidden: Admins only'); err.status = 403; throw err; }
|
||||||
|
return await MatrixRepository.listVacancies(matrixInstanceId);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
create,
|
create,
|
||||||
getStats,
|
getStats,
|
||||||
@ -730,5 +764,8 @@ module.exports = {
|
|||||||
getMyOverviewByInstance, // NEW
|
getMyOverviewByInstance, // NEW
|
||||||
getMyMatrixSummary, // NEW
|
getMyMatrixSummary, // NEW
|
||||||
deactivate, // NEW
|
deactivate, // NEW
|
||||||
activate // NEW
|
activate, // NEW
|
||||||
|
removeUser, // NEW
|
||||||
|
assignVacancy, // NEW
|
||||||
|
listVacancies // NEW
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user