few more bugs fixed and dealer ide ui alignmen and visibibity chabges done F& F made manuall trigger
This commit is contained in:
parent
fb07f7ab61
commit
e99a28b7f7
@ -35,6 +35,8 @@ console.log('✓ Termination stage resolution passed.');
|
|||||||
console.log('Testing getPreviousStage (Resignation)...');
|
console.log('Testing getPreviousStage (Resignation)...');
|
||||||
assert.equal(getPreviousStage(REQUEST_TYPES.RESIGNATION, RESIGNATION_STAGES.RBM), RESIGNATION_STAGES.ASM);
|
assert.equal(getPreviousStage(REQUEST_TYPES.RESIGNATION, RESIGNATION_STAGES.RBM), RESIGNATION_STAGES.ASM);
|
||||||
assert.equal(getPreviousStage(REQUEST_TYPES.RESIGNATION, RESIGNATION_STAGES.ZBH), RESIGNATION_STAGES.RBM);
|
assert.equal(getPreviousStage(REQUEST_TYPES.RESIGNATION, RESIGNATION_STAGES.ZBH), RESIGNATION_STAGES.RBM);
|
||||||
|
assert.equal(getPreviousStage(REQUEST_TYPES.RESIGNATION, RESIGNATION_STAGES.FNF_INITIATED), RESIGNATION_STAGES.AWAITING_FNF);
|
||||||
|
assert.equal(getPreviousStage(REQUEST_TYPES.RESIGNATION, RESIGNATION_STAGES.DD_ADMIN), RESIGNATION_STAGES.LEGAL);
|
||||||
assert.equal(getPreviousStage(REQUEST_TYPES.RESIGNATION, RESIGNATION_STAGES.COMPLETED), RESIGNATION_STAGES.FNF_INITIATED);
|
assert.equal(getPreviousStage(REQUEST_TYPES.RESIGNATION, RESIGNATION_STAGES.COMPLETED), RESIGNATION_STAGES.FNF_INITIATED);
|
||||||
console.log('✓ Resignation stage resolution passed.');
|
console.log('✓ Resignation stage resolution passed.');
|
||||||
|
|
||||||
|
|||||||
@ -200,6 +200,8 @@ export const RESIGNATION_STAGES = {
|
|||||||
DD_LEAD: 'DD Lead',
|
DD_LEAD: 'DD Lead',
|
||||||
NBH: 'NBH',
|
NBH: 'NBH',
|
||||||
DD_ADMIN: 'DD Admin',
|
DD_ADMIN: 'DD Admin',
|
||||||
|
/** Post DD Admin — workflow paused until an authorized user runs Push to F&F (no automatic F&F). */
|
||||||
|
AWAITING_FNF: 'Awaiting F&F',
|
||||||
LEGAL: 'Legal',
|
LEGAL: 'Legal',
|
||||||
SPARES_CLEARANCE: 'Spares Clearance',
|
SPARES_CLEARANCE: 'Spares Clearance',
|
||||||
SERVICE_CLEARANCE: 'Service Clearance',
|
SERVICE_CLEARANCE: 'Service Clearance',
|
||||||
|
|||||||
@ -30,6 +30,8 @@ export const getResignationStatusForStage = (stage: string): string => {
|
|||||||
case RESIGNATION_STAGES.NBH:
|
case RESIGNATION_STAGES.NBH:
|
||||||
case RESIGNATION_STAGES.DD_ADMIN:
|
case RESIGNATION_STAGES.DD_ADMIN:
|
||||||
return `${stage} Review`;
|
return `${stage} Review`;
|
||||||
|
case RESIGNATION_STAGES.AWAITING_FNF:
|
||||||
|
return 'Awaiting F&F — manual initiation';
|
||||||
case RESIGNATION_STAGES.LEGAL:
|
case RESIGNATION_STAGES.LEGAL:
|
||||||
return 'Legal - Resignation Letter';
|
return 'Legal - Resignation Letter';
|
||||||
case RESIGNATION_STAGES.FNF_INITIATED:
|
case RESIGNATION_STAGES.FNF_INITIATED:
|
||||||
|
|||||||
@ -39,9 +39,10 @@ export const getPreviousStage = (requestType: string, currentStage: string): str
|
|||||||
[RESIGNATION_STAGES.ZBH]: RESIGNATION_STAGES.RBM,
|
[RESIGNATION_STAGES.ZBH]: RESIGNATION_STAGES.RBM,
|
||||||
[RESIGNATION_STAGES.DD_LEAD]: RESIGNATION_STAGES.ZBH,
|
[RESIGNATION_STAGES.DD_LEAD]: RESIGNATION_STAGES.ZBH,
|
||||||
[RESIGNATION_STAGES.NBH]: RESIGNATION_STAGES.DD_LEAD,
|
[RESIGNATION_STAGES.NBH]: RESIGNATION_STAGES.DD_LEAD,
|
||||||
[RESIGNATION_STAGES.DD_ADMIN]: RESIGNATION_STAGES.NBH,
|
[RESIGNATION_STAGES.LEGAL]: RESIGNATION_STAGES.NBH,
|
||||||
[RESIGNATION_STAGES.LEGAL]: RESIGNATION_STAGES.DD_ADMIN,
|
[RESIGNATION_STAGES.DD_ADMIN]: RESIGNATION_STAGES.LEGAL,
|
||||||
[RESIGNATION_STAGES.FNF_INITIATED]: RESIGNATION_STAGES.LEGAL,
|
[RESIGNATION_STAGES.AWAITING_FNF]: RESIGNATION_STAGES.DD_ADMIN,
|
||||||
|
[RESIGNATION_STAGES.FNF_INITIATED]: RESIGNATION_STAGES.AWAITING_FNF,
|
||||||
[RESIGNATION_STAGES.COMPLETED]: RESIGNATION_STAGES.FNF_INITIATED
|
[RESIGNATION_STAGES.COMPLETED]: RESIGNATION_STAGES.FNF_INITIATED
|
||||||
};
|
};
|
||||||
return flow[currentStage] || null;
|
return flow[currentStage] || null;
|
||||||
|
|||||||
@ -247,6 +247,7 @@ export async function resolveNextActors(requestId: string, requestType: string,
|
|||||||
|
|
||||||
// --- Resignation Specific ---
|
// --- Resignation Specific ---
|
||||||
'DD Admin': [ROLES.DD_ADMIN],
|
'DD Admin': [ROLES.DD_ADMIN],
|
||||||
|
'Awaiting F&F': [ROLES.DD_LEAD, ROLES.DD_HEAD, ROLES.NBH, ROLES.DD_ADMIN],
|
||||||
'Spares Clearance': [ROLES.SPARES_MANAGER],
|
'Spares Clearance': [ROLES.SPARES_MANAGER],
|
||||||
'Service Clearance': [ROLES.SERVICE_MANAGER],
|
'Service Clearance': [ROLES.SERVICE_MANAGER],
|
||||||
'Accounts Clearance': [ROLES.ACCOUNTS_MANAGER],
|
'Accounts Clearance': [ROLES.ACCOUNTS_MANAGER],
|
||||||
|
|||||||
@ -397,7 +397,8 @@ export const approveResignation = async (req: AuthRequest, res: Response, next:
|
|||||||
[RESIGNATION_STAGES.DD_LEAD]: RESIGNATION_STAGES.NBH,
|
[RESIGNATION_STAGES.DD_LEAD]: RESIGNATION_STAGES.NBH,
|
||||||
[RESIGNATION_STAGES.NBH]: RESIGNATION_STAGES.LEGAL,
|
[RESIGNATION_STAGES.NBH]: RESIGNATION_STAGES.LEGAL,
|
||||||
[RESIGNATION_STAGES.LEGAL]: RESIGNATION_STAGES.DD_ADMIN,
|
[RESIGNATION_STAGES.LEGAL]: RESIGNATION_STAGES.DD_ADMIN,
|
||||||
[RESIGNATION_STAGES.DD_ADMIN]: RESIGNATION_STAGES.FNF_INITIATED, // DD Admin approval moves to F&F initiation
|
// DD Admin approval completes internal review; F&F is started only via explicit Push to F&F.
|
||||||
|
[RESIGNATION_STAGES.DD_ADMIN]: RESIGNATION_STAGES.AWAITING_FNF,
|
||||||
[RESIGNATION_STAGES.FNF_INITIATED]: RESIGNATION_STAGES.COMPLETED
|
[RESIGNATION_STAGES.FNF_INITIATED]: RESIGNATION_STAGES.COMPLETED
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -407,16 +408,16 @@ export const approveResignation = async (req: AuthRequest, res: Response, next:
|
|||||||
return res.status(400).json({ success: false, message: 'Cannot move to next stage from current state' });
|
return res.status(400).json({ success: false, message: 'Cannot move to next stage from current state' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guard before transition: F&F initiation is allowed only on/after LWD as per SRS §4.2.2.8
|
// F&F records are created only from explicit Push to F&F (targetStage), not from sequential approvals.
|
||||||
|
// LWD gate applies to that manual push (SRS §4.2.2.8).
|
||||||
let shouldTriggerFnF = false;
|
let shouldTriggerFnF = false;
|
||||||
if (nextStage === RESIGNATION_STAGES.FNF_INITIATED) {
|
if (nextStage === RESIGNATION_STAGES.FNF_INITIATED && targetOverride) {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const lwdString = resignation.lastOperationalDateServices || resignation.lastOperationalDateSales;
|
const lwdString = resignation.lastOperationalDateServices || resignation.lastOperationalDateSales;
|
||||||
const { force } = req.body;
|
const { force } = req.body;
|
||||||
|
|
||||||
const lwd = lwdString ? new Date(lwdString) : null;
|
const lwd = lwdString ? new Date(lwdString) : null;
|
||||||
if (lwd) {
|
if (lwd) {
|
||||||
// Clear time for date-only comparison
|
|
||||||
today.setHours(0, 0, 0, 0);
|
today.setHours(0, 0, 0, 0);
|
||||||
lwd.setHours(0, 0, 0, 0);
|
lwd.setHours(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
@ -535,9 +536,14 @@ export const approveResignation = async (req: AuthRequest, res: Response, next:
|
|||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
|
|
||||||
const message = (sourceStage === RESIGNATION_STAGES.LEGAL && nextStage === RESIGNATION_STAGES.LEGAL)
|
let message = 'Resignation approved successfully';
|
||||||
? 'Legal stage approved successfully. Use Push to F&F to initiate settlement as per LWD rules.'
|
if (nextStage === RESIGNATION_STAGES.AWAITING_FNF) {
|
||||||
: 'Resignation approved successfully';
|
message =
|
||||||
|
'DD Admin approval recorded. Use Push to F&F when ready to create the Full & Final settlement (Last Working Day rules apply).';
|
||||||
|
} else if (sourceStage === RESIGNATION_STAGES.LEGAL && nextStage === RESIGNATION_STAGES.DD_ADMIN) {
|
||||||
|
message =
|
||||||
|
'Legal stage approved successfully. After DD Admin review, use Push to F&F to start settlement when ready.';
|
||||||
|
}
|
||||||
|
|
||||||
res.json({ success: true, message, nextStage, resignation });
|
res.json({ success: true, message, nextStage, resignation });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -608,6 +614,7 @@ export const withdrawResignation = async (req: AuthRequest, res: Response, next:
|
|||||||
const restrictedStages = [
|
const restrictedStages = [
|
||||||
RESIGNATION_STAGES.NBH,
|
RESIGNATION_STAGES.NBH,
|
||||||
RESIGNATION_STAGES.DD_ADMIN,
|
RESIGNATION_STAGES.DD_ADMIN,
|
||||||
|
RESIGNATION_STAGES.AWAITING_FNF,
|
||||||
RESIGNATION_STAGES.LEGAL,
|
RESIGNATION_STAGES.LEGAL,
|
||||||
RESIGNATION_STAGES.FNF_INITIATED,
|
RESIGNATION_STAGES.FNF_INITIATED,
|
||||||
RESIGNATION_STAGES.COMPLETED
|
RESIGNATION_STAGES.COMPLETED
|
||||||
@ -1047,10 +1054,15 @@ export const updateResignationStatus = async (req: AuthRequest, res: Response, n
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SRS-aligned gate: F&F can start only after Legal completion artifacts.
|
// SRS-aligned gate: F&F can start only after Legal completion artifacts.
|
||||||
if (resignation.currentStage !== RESIGNATION_STAGES.LEGAL) {
|
const pushAllowedStages = [
|
||||||
|
RESIGNATION_STAGES.AWAITING_FNF,
|
||||||
|
// Legacy rows: acceptance letter uploaded at Legal before DD Admin step completed in older builds
|
||||||
|
RESIGNATION_STAGES.LEGAL
|
||||||
|
];
|
||||||
|
if (!pushAllowedStages.includes(resignation.currentStage as any)) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: `Cannot trigger F&F from ${resignation.currentStage}. Move request to Legal stage first.`
|
message: `Cannot trigger F&F from ${resignation.currentStage}. Complete DD Admin review first (stage must be Awaiting F&F), or use Legal only for legacy cases.`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -618,24 +618,18 @@ export const updateTerminationStatus = async (req: AuthRequest, res: Response, n
|
|||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
// If Terminated, trigger F&F initiation via Workflow Service
|
// F&F is never started automatically on termination; authorized users run Push to F&F when ready.
|
||||||
// SRS REQUIREMENT: F&F settlement process is triggered only on the Last Working Day (LWD)
|
|
||||||
if (nextStage === TERMINATION_STAGES.TERMINATED) {
|
if (nextStage === TERMINATION_STAGES.TERMINATED) {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const lwd = new Date(termination.proposedLwd);
|
const lwd = new Date(termination.proposedLwd);
|
||||||
|
|
||||||
// Clear time components for date-only comparison
|
|
||||||
today.setHours(0, 0, 0, 0);
|
today.setHours(0, 0, 0, 0);
|
||||||
lwd.setHours(0, 0, 0, 0);
|
lwd.setHours(0, 0, 0, 0);
|
||||||
|
const statusAfterTerm =
|
||||||
if (today >= lwd) {
|
today < lwd ? 'Awaiting F&F (LWD Pending)' : 'Awaiting F&F';
|
||||||
logger.info(`[TerminationController] LWD reached or passed (${termination.proposedLwd}). Initiating F&F.`);
|
await termination.update({ status: statusAfterTerm }, { transaction });
|
||||||
await TerminationWorkflowService.initiateFnF(termination, req.user.id, transaction);
|
logger.info(
|
||||||
} else {
|
`[TerminationController] Termination reached TERMINATED. F&F must be started manually (Push to F&F). LWD=${termination.proposedLwd}, status=${statusAfterTerm}`
|
||||||
logger.info(`[TerminationController] Termination approved but LWD (${termination.proposedLwd}) not yet reached. F&F will be triggered on LWD.`);
|
);
|
||||||
// Keep parent status aligned while waiting for LWD-triggered F&F
|
|
||||||
await termination.update({ status: 'Awaiting F&F (LWD Pending)' }, { transaction });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -135,6 +135,7 @@ export class ResignationWorkflowService {
|
|||||||
[RESIGNATION_STAGES.NBH]: 65,
|
[RESIGNATION_STAGES.NBH]: 65,
|
||||||
[RESIGNATION_STAGES.LEGAL]: 80,
|
[RESIGNATION_STAGES.LEGAL]: 80,
|
||||||
[RESIGNATION_STAGES.DD_ADMIN]: 90,
|
[RESIGNATION_STAGES.DD_ADMIN]: 90,
|
||||||
|
[RESIGNATION_STAGES.AWAITING_FNF]: 92,
|
||||||
[RESIGNATION_STAGES.FNF_INITIATED]: 95,
|
[RESIGNATION_STAGES.FNF_INITIATED]: 95,
|
||||||
[RESIGNATION_STAGES.COMPLETED]: 100,
|
[RESIGNATION_STAGES.COMPLETED]: 100,
|
||||||
[RESIGNATION_STAGES.REJECTED]: 100
|
[RESIGNATION_STAGES.REJECTED]: 100
|
||||||
@ -157,6 +158,7 @@ export class ResignationWorkflowService {
|
|||||||
[RESIGNATION_STAGES.NBH]: ROLES.NBH,
|
[RESIGNATION_STAGES.NBH]: ROLES.NBH,
|
||||||
[RESIGNATION_STAGES.LEGAL]: ROLES.LEGAL_ADMIN,
|
[RESIGNATION_STAGES.LEGAL]: ROLES.LEGAL_ADMIN,
|
||||||
[RESIGNATION_STAGES.DD_ADMIN]: ROLES.DD_ADMIN,
|
[RESIGNATION_STAGES.DD_ADMIN]: ROLES.DD_ADMIN,
|
||||||
|
[RESIGNATION_STAGES.AWAITING_FNF]: [ROLES.DD_LEAD, ROLES.DD_HEAD, ROLES.NBH, ROLES.DD_ADMIN],
|
||||||
[RESIGNATION_STAGES.FNF_INITIATED]: ROLES.DD_ADMIN
|
[RESIGNATION_STAGES.FNF_INITIATED]: ROLES.DD_ADMIN
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user