Initial
This commit is contained in:
205
server/services/assignments.js
Normal file
205
server/services/assignments.js
Normal file
@@ -0,0 +1,205 @@
|
||||
const { all, audit, json, now, one, parseJson, run, tx } = require('../db');
|
||||
|
||||
function employeeFromRow(row) {
|
||||
if (!row) return null;
|
||||
return {
|
||||
...row,
|
||||
source_payload: parseJson(row.source_payload, null)
|
||||
};
|
||||
}
|
||||
|
||||
function assignmentFromRow(row) {
|
||||
if (!row) return null;
|
||||
return {
|
||||
...row,
|
||||
active: !row.released_at
|
||||
};
|
||||
}
|
||||
|
||||
function listEmployees(query = {}) {
|
||||
return all(`
|
||||
SELECT *
|
||||
FROM employees
|
||||
WHERE (? IS NULL OR status = ?)
|
||||
AND (? IS NULL OR name LIKE '%' || ? || '%' OR email LIKE '%' || ? || '%' OR employee_number LIKE '%' || ? || '%')
|
||||
ORDER BY name
|
||||
LIMIT 500
|
||||
`, [
|
||||
query.status || null,
|
||||
query.status || null,
|
||||
query.search || null,
|
||||
query.search || null,
|
||||
query.search || null,
|
||||
query.search || null
|
||||
]).map(employeeFromRow);
|
||||
}
|
||||
|
||||
function upsertEmployee(employee) {
|
||||
const timestamp = now();
|
||||
const workdayId = employee.workday_worker_id || employee.workdayWorkerId || employee.workerId || null;
|
||||
const existing = workdayId
|
||||
? one('SELECT id FROM employees WHERE workday_worker_id = ?', [workdayId])
|
||||
: one('SELECT id FROM employees WHERE employee_number = ? OR lower(email) = lower(?)', [employee.employee_number || '', employee.email || '']);
|
||||
|
||||
const values = [
|
||||
workdayId,
|
||||
employee.employee_number || employee.employeeNumber || employee.workerNumber || null,
|
||||
employee.name || employee.fullName || [employee.firstName, employee.lastName].filter(Boolean).join(' ') || 'Unnamed employee',
|
||||
employee.email || employee.workEmail || null,
|
||||
employee.department || employee.organization || null,
|
||||
employee.location || employee.workLocation || null,
|
||||
employee.manager_name || employee.managerName || null,
|
||||
employee.status || 'active',
|
||||
employee.source || 'manual',
|
||||
json(employee.source_payload || employee),
|
||||
employee.last_synced_at || null,
|
||||
timestamp
|
||||
];
|
||||
|
||||
if (existing) {
|
||||
run(
|
||||
`UPDATE employees SET
|
||||
workday_worker_id = COALESCE(?, workday_worker_id),
|
||||
employee_number = ?,
|
||||
name = ?,
|
||||
email = ?,
|
||||
department = ?,
|
||||
location = ?,
|
||||
manager_name = ?,
|
||||
status = ?,
|
||||
source = ?,
|
||||
source_payload = ?,
|
||||
last_synced_at = COALESCE(?, last_synced_at),
|
||||
updated_at = ?
|
||||
WHERE id = ?`,
|
||||
[...values, existing.id]
|
||||
);
|
||||
return one('SELECT * FROM employees WHERE id = ?', [existing.id]);
|
||||
}
|
||||
|
||||
const result = run(
|
||||
`INSERT INTO employees (
|
||||
workday_worker_id, employee_number, name, email, department, location, manager_name,
|
||||
status, source, source_payload, last_synced_at, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[...values, timestamp]
|
||||
);
|
||||
return one('SELECT * FROM employees WHERE id = ?', [result.lastInsertRowid]);
|
||||
}
|
||||
|
||||
function assignmentRows(query = {}) {
|
||||
return all(`
|
||||
SELECT aa.*, a.asset_id AS asset_code, a.description AS asset_description,
|
||||
e.name AS employee_name, e.email AS employee_email, e.employee_number
|
||||
FROM asset_assignments aa
|
||||
JOIN assets a ON a.id = aa.asset_id
|
||||
LEFT JOIN employees e ON e.id = aa.employee_id
|
||||
WHERE (? IS NULL OR aa.asset_id = ?)
|
||||
AND (? IS NULL OR aa.employee_id = ?)
|
||||
AND (? IS NULL OR aa.released_at IS NULL)
|
||||
ORDER BY aa.assigned_at DESC, aa.id DESC
|
||||
LIMIT 500
|
||||
`, [
|
||||
query.asset_id || null,
|
||||
query.asset_id || null,
|
||||
query.employee_id || null,
|
||||
query.employee_id || null,
|
||||
query.active ? 1 : null
|
||||
]).map(assignmentFromRow);
|
||||
}
|
||||
|
||||
function assignAsset(body, userId) {
|
||||
const timestamp = now();
|
||||
const asset = one('SELECT * FROM assets WHERE id = ?', [body.asset_id]);
|
||||
if (!asset) throw Object.assign(new Error('Asset not found'), { status: 404 });
|
||||
|
||||
const employee = body.employee_id ? one('SELECT * FROM employees WHERE id = ?', [body.employee_id]) : null;
|
||||
const department = body.department ?? employee?.department ?? asset.department ?? null;
|
||||
const location = body.location ?? employee?.location ?? asset.location ?? null;
|
||||
|
||||
return tx(() => {
|
||||
run(
|
||||
`UPDATE asset_assignments
|
||||
SET released_at = ?, status = 'released', release_reason = 'Reassigned', updated_at = ?
|
||||
WHERE asset_id = ? AND released_at IS NULL`,
|
||||
[timestamp, timestamp, body.asset_id]
|
||||
);
|
||||
|
||||
const result = run(
|
||||
`INSERT INTO asset_assignments (
|
||||
asset_id, employee_id, department, location, status, assigned_at, assigned_by, notes, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
body.asset_id,
|
||||
body.employee_id || null,
|
||||
department,
|
||||
location,
|
||||
'assigned',
|
||||
body.assigned_at || timestamp,
|
||||
userId,
|
||||
body.notes || null,
|
||||
timestamp,
|
||||
timestamp
|
||||
]
|
||||
);
|
||||
|
||||
run(
|
||||
`UPDATE assets SET
|
||||
custodian = ?,
|
||||
department = ?,
|
||||
location = ?,
|
||||
status = 'in_service',
|
||||
updated_at = ?
|
||||
WHERE id = ?`,
|
||||
[employee?.name || asset.custodian || null, department, location, timestamp, body.asset_id]
|
||||
);
|
||||
|
||||
const assignment = assignmentRows({ asset_id: body.asset_id, active: true })[0];
|
||||
audit(userId, 'assign', 'asset', body.asset_id, null, assignment);
|
||||
return assignment || one('SELECT * FROM asset_assignments WHERE id = ?', [result.lastInsertRowid]);
|
||||
});
|
||||
}
|
||||
|
||||
function releaseAssignment(id, body, userId) {
|
||||
const before = one('SELECT * FROM asset_assignments WHERE id = ?', [id]);
|
||||
if (!before) return null;
|
||||
const timestamp = now();
|
||||
run(
|
||||
`UPDATE asset_assignments
|
||||
SET released_at = ?, status = 'released', release_reason = ?, notes = COALESCE(?, notes), updated_at = ?
|
||||
WHERE id = ?`,
|
||||
[timestamp, body.release_reason || 'Released', body.notes || null, timestamp, id]
|
||||
);
|
||||
audit(userId, 'release_assignment', 'asset_assignment', id, before, { released_at: timestamp, ...body });
|
||||
return assignmentRows({ asset_id: before.asset_id }).find((assignment) => assignment.id === Number(id));
|
||||
}
|
||||
|
||||
function assignmentSummary() {
|
||||
const stats = one(`
|
||||
SELECT
|
||||
COUNT(*) AS active_assignments,
|
||||
COUNT(DISTINCT employee_id) AS assigned_employees,
|
||||
COUNT(DISTINCT location) AS assigned_locations
|
||||
FROM asset_assignments
|
||||
WHERE released_at IS NULL
|
||||
`);
|
||||
const unassigned = one(`
|
||||
SELECT COUNT(*) AS count
|
||||
FROM assets a
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM asset_assignments aa
|
||||
WHERE aa.asset_id = a.id AND aa.released_at IS NULL
|
||||
)
|
||||
`);
|
||||
return { ...stats, unassigned_assets: unassigned.count };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
assignAsset,
|
||||
assignmentRows,
|
||||
assignmentSummary,
|
||||
employeeFromRow,
|
||||
listEmployees,
|
||||
releaseAssignment,
|
||||
upsertEmployee
|
||||
};
|
||||
Reference in New Issue
Block a user