39268-vm/patch_cases_service.js
2026-03-22 19:46:07 +00:00

102 lines
4.0 KiB
JavaScript

const fs = require('fs');
let casesSvc = fs.readFileSync('backend/src/services/cases.js', 'utf8');
// Add Logger import
if (!casesSvc.includes("const Logger = require('./logger');")) {
casesSvc = casesSvc.replace("const config = require('../config');", "const config = require('../config');\nconst Logger = require('./logger');\n");
}
// Patch create
if (casesSvc.includes('await CasesDBApi.create(')) {
casesSvc = casesSvc.replace(
'await CasesDBApi.create(',
'const newCase = await CasesDBApi.create('
);
casesSvc = casesSvc.replace(
'await transaction.commit();',
`await transaction.commit();\n await Logger.log({\n organizationId: newCase.organizationId,\n caseId: newCase.id,\n actorUserId: currentUser.id,\n actionType: 'case_created',\n message: 'Case was created',\n metadata: { case_number: newCase.case_number }\n });`
);
}
// Patch update
const updateCode = `
let cases = await CasesDBApi\.findBy\({id}, {transaction});
if (!cases) { throw new ValidationError('casesNotFound'); }
const oldStatus = cases.status;
const newStatus = data.status || cases.status;
if (data.status && data.status !== oldStatus) {
const allowedTransitions = {
'intake': ['triage'],
'triage': ['evidence_needed', 'appeal_ready'],
'evidence_needed': ['appeal_ready'],
'appeal_ready': ['submitted'],
'submitted': ['pending_payer'],
'pending_payer': ['won', 'lost']
};
const isReopening = (['won', 'lost'].includes(oldStatus)) && !['won', 'lost'].includes(newStatus);
if (isReopening) {
if (!data.reopenReason) {
throw new ValidationError('reopenReasonRequired', 'A reason is required to reopen a case');
}
} else if (allowedTransitions[oldStatus] && !allowedTransitions[oldStatus].includes(newStatus)) {
// reject invalid transition
throw new ValidationError('invalidStatusTransition', 'Cannot transition case status from ' + oldStatus + ' to ' + newStatus);
}
}
const updatedCases = await CasesDBApi.update(id, data, { currentUser, transaction });
await transaction.commit();
// Logging
if (data.status && data.status !== oldStatus) {
await Logger.log({
organizationId: cases.organizationId,
caseId: id,
actorUserId: currentUser.id,
actionType: 'status_changed',
message: 'Status changed from ' + oldStatus + ' to ' + newStatus,
metadata: { oldStatus, newStatus, reopenReason: data.reopenReason }
});
}
if (data.owner_userId && data.owner_userId !== cases.owner_userId) {
await Logger.log({
organizationId: cases.organizationId,
caseId: id,
actorUserId: currentUser.id,
actionType: 'owner_changed',
message: 'Case owner was changed',
metadata: { oldOwner: cases.owner_userId, newOwner: data.owner_userId }
});
}
if (data.due_at && new Date(data.due_at).getTime() !== new Date(cases.due_at).getTime()) {
await Logger.log({
organizationId: cases.organizationId,
caseId: id,
actorUserId: currentUser.id,
actionType: 'due_date_changed',
message: 'Case due date was changed',
metadata: { oldDueDate: cases.due_at, newDueDate: data.due_at }
});
}
if (!data.status && !data.owner_userId && !data.due_at) {
await Logger.log({
organizationId: cases.organizationId,
caseId: id,
actorUserId: currentUser.id,
actionType: 'case_updated',
message: 'Case details were updated',
metadata: { updatedFields: Object.keys(data) }
});
}
return updatedCases;
`;
casesSvc = casesSvc.replace(/let cases = await CasesDBApi\.findBy\([\s\S]*?return updatedCases;/m, updateCode);
fs.writeFileSync('backend/src/services/cases.js', casesSvc);