tab filters in differnt module changes and amount update from F&F fixed checking for the dues update from two placeds finance & normal departmental response need to finalisre one
This commit is contained in:
parent
96edda54d9
commit
7fbf134cf1
@ -310,7 +310,9 @@ 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.DD_ADMIN,
|
[RESIGNATION_STAGES.NBH]: RESIGNATION_STAGES.DD_ADMIN,
|
||||||
[RESIGNATION_STAGES.DD_ADMIN]: RESIGNATION_STAGES.LEGAL,
|
[RESIGNATION_STAGES.DD_ADMIN]: RESIGNATION_STAGES.LEGAL,
|
||||||
[RESIGNATION_STAGES.LEGAL]: RESIGNATION_STAGES.FNF_INITIATED,
|
// Legal approval should complete only the Legal stage.
|
||||||
|
// F&F initiation is explicitly triggered via `pushfnf` action (with LWD/force gates).
|
||||||
|
[RESIGNATION_STAGES.LEGAL]: RESIGNATION_STAGES.LEGAL,
|
||||||
[RESIGNATION_STAGES.FNF_INITIATED]: RESIGNATION_STAGES.COMPLETED
|
[RESIGNATION_STAGES.FNF_INITIATED]: RESIGNATION_STAGES.COMPLETED
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -320,6 +322,22 @@ 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 unless forced.
|
||||||
|
if (nextStage === RESIGNATION_STAGES.FNF_INITIATED) {
|
||||||
|
const today = new Date();
|
||||||
|
const lwd = resignation.lastOperationalDateServices || resignation.lastOperationalDateSales;
|
||||||
|
const { force } = req.body;
|
||||||
|
|
||||||
|
if (!force && lwd && today < new Date(lwd)) {
|
||||||
|
await transaction.rollback();
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: `F&F can only be initiated on or after the Last Working Day (${lwd}).`,
|
||||||
|
canForce: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sequence guard: resignation can be marked completed only after F&F settlement is complete.
|
// Sequence guard: resignation can be marked completed only after F&F settlement is complete.
|
||||||
if (
|
if (
|
||||||
resignation.currentStage === RESIGNATION_STAGES.FNF_INITIATED &&
|
resignation.currentStage === RESIGNATION_STAGES.FNF_INITIATED &&
|
||||||
@ -340,7 +358,8 @@ export const approveResignation = async (req: AuthRequest, res: Response, next:
|
|||||||
// Transition via Workflow Service
|
// Transition via Workflow Service
|
||||||
await ResignationWorkflowService.transitionResignation(resignation, nextStage, req.user.id, {
|
await ResignationWorkflowService.transitionResignation(resignation, nextStage, req.user.id, {
|
||||||
remarks,
|
remarks,
|
||||||
status: getResignationStatusForStage(nextStage)
|
status: getResignationStatusForStage(nextStage),
|
||||||
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
// Special logic for F&F and Completion
|
// Special logic for F&F and Completion
|
||||||
@ -351,47 +370,37 @@ export const approveResignation = async (req: AuthRequest, res: Response, next:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nextStage === RESIGNATION_STAGES.FNF_INITIATED) {
|
if (nextStage === RESIGNATION_STAGES.FNF_INITIATED) {
|
||||||
const today = new Date();
|
const existingFnF = await db.FnF.findOne({ where: { resignationId: resignation.id }, transaction });
|
||||||
const lwd = resignation.lastOperationalDateServices || resignation.lastOperationalDateSales;
|
if (!existingFnF) {
|
||||||
const { force } = req.body;
|
const sapDues = await ExternalMocksService.mockGetFinancialDuesFromSap((resignation as any).outlet.code);
|
||||||
|
const dealerProfileId = (resignation as any).dealer?.dealerId;
|
||||||
|
|
||||||
if (!force && lwd && today < new Date(lwd)) {
|
const fnf = await db.FnF.create({
|
||||||
await transaction.rollback();
|
settlementId: NomenclatureService.generateFnFId(),
|
||||||
return res.status(400).json({
|
resignationId: resignation.id,
|
||||||
success: false,
|
outletId: resignation.outletId,
|
||||||
message: `F&F can only be initiated on or after the Last Working Day (${lwd}).`,
|
dealerId: dealerProfileId, // Correctly using the Dealer model ID
|
||||||
canForce: true
|
status: 'Initiated',
|
||||||
});
|
totalReceivables: sapDues.data.outstandingInvoices,
|
||||||
|
totalPayables: sapDues.data.securityDeposit,
|
||||||
|
netAmount: sapDues.data.securityDeposit - sapDues.data.outstandingInvoices
|
||||||
|
}, { transaction });
|
||||||
|
|
||||||
|
await db.FnFLineItem.bulkCreate([
|
||||||
|
{ fnfId: fnf.id, itemType: 'Receivable', description: 'Outstanding Invoices from SAP', department: 'Finance', amount: sapDues.data.outstandingInvoices, addedBy: req.user.id },
|
||||||
|
{ fnfId: fnf.id, itemType: 'Payable', description: 'Security Deposit from SAP', department: 'Finance', amount: sapDues.data.securityDeposit, addedBy: req.user.id }
|
||||||
|
], { transaction });
|
||||||
|
|
||||||
|
const { FNF_DEPARTMENTS } = await import('../../common/config/constants.js');
|
||||||
|
await db.FffClearance.bulkCreate(
|
||||||
|
FNF_DEPARTMENTS.map(dept => ({
|
||||||
|
fnfId: fnf.id,
|
||||||
|
department: dept,
|
||||||
|
status: 'Pending'
|
||||||
|
})),
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sapDues = await ExternalMocksService.mockGetFinancialDuesFromSap((resignation as any).outlet.code);
|
|
||||||
const dealerProfileId = (resignation as any).dealer?.dealerId;
|
|
||||||
|
|
||||||
const fnf = await db.FnF.create({
|
|
||||||
settlementId: NomenclatureService.generateFnFId(),
|
|
||||||
resignationId: resignation.id,
|
|
||||||
outletId: resignation.outletId,
|
|
||||||
dealerId: dealerProfileId, // Correctly using the Dealer model ID
|
|
||||||
status: 'Initiated',
|
|
||||||
totalReceivables: sapDues.data.outstandingInvoices,
|
|
||||||
totalPayables: sapDues.data.securityDeposit,
|
|
||||||
netAmount: sapDues.data.securityDeposit - sapDues.data.outstandingInvoices
|
|
||||||
}, { transaction });
|
|
||||||
|
|
||||||
await db.FnFLineItem.bulkCreate([
|
|
||||||
{ fnfId: fnf.id, itemType: 'Receivable', description: 'Outstanding Invoices from SAP', department: 'Finance', amount: sapDues.data.outstandingInvoices, addedBy: req.user.id },
|
|
||||||
{ fnfId: fnf.id, itemType: 'Payable', description: 'Security Deposit from SAP', department: 'Finance', amount: sapDues.data.securityDeposit, addedBy: req.user.id }
|
|
||||||
], { transaction });
|
|
||||||
|
|
||||||
const { FNF_DEPARTMENTS } = await import('../../common/config/constants.js');
|
|
||||||
await db.FffClearance.bulkCreate(
|
|
||||||
FNF_DEPARTMENTS.map(dept => ({
|
|
||||||
fnfId: fnf.id,
|
|
||||||
department: dept,
|
|
||||||
status: 'Pending'
|
|
||||||
})),
|
|
||||||
{ transaction }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
@ -409,7 +418,11 @@ export const approveResignation = async (req: AuthRequest, res: Response, next:
|
|||||||
logger.error('[resignation] workflow worknote (approve):', wnErr);
|
logger.error('[resignation] workflow worknote (approve):', wnErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({ success: true, message: 'Resignation approved successfully', nextStage, resignation });
|
const message = (sourceStage === RESIGNATION_STAGES.LEGAL && nextStage === RESIGNATION_STAGES.LEGAL)
|
||||||
|
? 'Legal stage approved successfully. Use Push to F&F to initiate settlement as per LWD rules.'
|
||||||
|
: 'Resignation approved successfully';
|
||||||
|
|
||||||
|
res.json({ success: true, message, nextStage, resignation });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (transaction) await transaction.rollback();
|
if (transaction) await transaction.rollback();
|
||||||
logger.error('Error approving resignation:', error);
|
logger.error('Error approving resignation:', error);
|
||||||
|
|||||||
@ -385,13 +385,51 @@ export const updateClearance = async (req: AuthRequest, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
const { id, clearanceId } = req.params;
|
const { id, clearanceId } = req.params;
|
||||||
const body = (req.body || {}) as Record<string, any>;
|
const body = (req.body || {}) as Record<string, any>;
|
||||||
const { status, remarks, documentId, supportingDocument } = body;
|
const { status, remarks, documentId, supportingDocument, amount, type } = body;
|
||||||
const clearance = await FffClearance.findOne({ where: { id: clearanceId, fnfId: id } });
|
const clearance = await FffClearance.findOne({ where: { id: clearanceId, fnfId: id } });
|
||||||
if (!clearance) return res.status(404).json({ success: false, message: 'Clearance record not found' });
|
if (!clearance) return res.status(404).json({ success: false, message: 'Clearance record not found' });
|
||||||
|
|
||||||
const uploadedSupportingDocument = req.file ? `/uploads/documents/${req.file.filename}` : undefined;
|
const uploadedSupportingDocument = req.file ? `/uploads/documents/${req.file.filename}` : undefined;
|
||||||
|
const enteredAmount = Math.abs(Number(amount) || 0);
|
||||||
|
const clearanceType = String(type || '').toLowerCase();
|
||||||
|
const itemType = clearanceType === 'payable'
|
||||||
|
? 'Payable'
|
||||||
|
: clearanceType === 'deduction'
|
||||||
|
? 'Deduction'
|
||||||
|
: 'Receivable';
|
||||||
|
const syntheticDescription = 'Department Clearance - Manual Update';
|
||||||
|
|
||||||
const normalizedStatus = normalizeClearanceStatus(status || clearance.status, Number(clearance.amount || 0));
|
// Persist amount/type as F&F line item so UI totals and department amount reflect user input.
|
||||||
|
const existingSyntheticLine = await FnFLineItem.findOne({
|
||||||
|
where: {
|
||||||
|
fnfId: id,
|
||||||
|
department: clearance.department,
|
||||||
|
description: syntheticDescription
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (enteredAmount > 0) {
|
||||||
|
if (existingSyntheticLine) {
|
||||||
|
await existingSyntheticLine.update({
|
||||||
|
itemType,
|
||||||
|
amount: enteredAmount,
|
||||||
|
addedBy: req.user?.id || existingSyntheticLine.addedBy
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await FnFLineItem.create({
|
||||||
|
fnfId: id,
|
||||||
|
itemType,
|
||||||
|
description: syntheticDescription,
|
||||||
|
department: clearance.department,
|
||||||
|
amount: enteredAmount,
|
||||||
|
addedBy: req.user?.id || null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (existingSyntheticLine) {
|
||||||
|
await existingSyntheticLine.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedStatus = normalizeClearanceStatus(status || clearance.status, enteredAmount);
|
||||||
await clearance.update({
|
await clearance.update({
|
||||||
status: normalizedStatus,
|
status: normalizedStatus,
|
||||||
remarks: remarks || clearance.remarks,
|
remarks: remarks || clearance.remarks,
|
||||||
@ -413,7 +451,12 @@ export const updateClearance = async (req: AuthRequest, res: Response) => {
|
|||||||
fnfId: id,
|
fnfId: id,
|
||||||
action: 'CLEARANCE_UPDATED',
|
action: 'CLEARANCE_UPDATED',
|
||||||
remarks: remarks || 'No remarks',
|
remarks: remarks || 'No remarks',
|
||||||
details: { department: clearance.department, status: normalizedStatus }
|
details: {
|
||||||
|
department: clearance.department,
|
||||||
|
status: normalizedStatus,
|
||||||
|
amount: enteredAmount,
|
||||||
|
type: itemType
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (auditError) {
|
} catch (auditError) {
|
||||||
console.error('[SettlementController] Local FnFAudit creation failed:', auditError);
|
console.error('[SettlementController] Local FnFAudit creation failed:', auditError);
|
||||||
@ -433,7 +476,12 @@ export const updateClearance = async (req: AuthRequest, res: Response) => {
|
|||||||
[parentKey]: parentId,
|
[parentKey]: parentId,
|
||||||
action: 'STAKEHOLDER_CLEARANCE_UPDATED',
|
action: 'STAKEHOLDER_CLEARANCE_UPDATED',
|
||||||
remarks: `Automated sync from F&F: ${remarks || 'No remarks'}`,
|
remarks: `Automated sync from F&F: ${remarks || 'No remarks'}`,
|
||||||
details: { department: clearance.department, status: normalizedStatus }
|
details: {
|
||||||
|
department: clearance.department,
|
||||||
|
status: normalizedStatus,
|
||||||
|
amount: enteredAmount,
|
||||||
|
type: itemType
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (parentAuditError) {
|
} catch (parentAuditError) {
|
||||||
console.error('[SettlementController] Parent Audit creation failed:', parentAuditError);
|
console.error('[SettlementController] Parent Audit creation failed:', parentAuditError);
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export class ResignationWorkflowService {
|
|||||||
* Standardized method to transition a resignation request status
|
* Standardized method to transition a resignation request status
|
||||||
*/
|
*/
|
||||||
static async transitionResignation(resignation: any, targetStage: string, userId: string | null = null, metadata: any = {}) {
|
static async transitionResignation(resignation: any, targetStage: string, userId: string | null = null, metadata: any = {}) {
|
||||||
const { action, remarks, status } = metadata;
|
const { action, remarks, status, transaction } = metadata;
|
||||||
const sourceStage = resignation.currentStage;
|
const sourceStage = resignation.currentStage;
|
||||||
|
|
||||||
const updateData: any = {
|
const updateData: any = {
|
||||||
@ -38,7 +38,7 @@ export class ResignationWorkflowService {
|
|||||||
await resignation.update({
|
await resignation.update({
|
||||||
...updateData,
|
...updateData,
|
||||||
timeline: updatedTimeline
|
timeline: updatedTimeline
|
||||||
});
|
}, transaction ? { transaction } : undefined);
|
||||||
|
|
||||||
// 3. Create Audit Log
|
// 3. Create Audit Log
|
||||||
let auditAction: any = AUDIT_ACTIONS.APPROVED;
|
let auditAction: any = AUDIT_ACTIONS.APPROVED;
|
||||||
@ -52,7 +52,7 @@ export class ResignationWorkflowService {
|
|||||||
action: auditAction,
|
action: auditAction,
|
||||||
remarks: remarks || '',
|
remarks: remarks || '',
|
||||||
details: { status: updateData.status, stage: sourceStage, targetStage: targetStage }
|
details: { status: updateData.status, stage: sourceStage, targetStage: targetStage }
|
||||||
});
|
}, transaction ? { transaction } : undefined);
|
||||||
|
|
||||||
console.log(`[ResignationWorkflowService] Transitioned Resignation ${resignation.resignationId} to ${targetStage}`);
|
console.log(`[ResignationWorkflowService] Transitioned Resignation ${resignation.resignationId} to ${targetStage}`);
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ export class ResignationWorkflowService {
|
|||||||
await user.update({
|
await user.update({
|
||||||
status: 'deactivated',
|
status: 'deactivated',
|
||||||
isActive: false
|
isActive: false
|
||||||
});
|
}, transaction ? { transaction } : undefined);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.warn(`[ResignationWorkflowService] No user account found with dealerId ${resignation.dealerId}`);
|
logger.warn(`[ResignationWorkflowService] No user account found with dealerId ${resignation.dealerId}`);
|
||||||
|
|||||||
@ -347,229 +347,229 @@ async function triggerWorkflow() {
|
|||||||
await delay();
|
await delay();
|
||||||
|
|
||||||
// 6.3 FDD ASSIGNMENT
|
// 6.3 FDD ASSIGNMENT
|
||||||
// log(6.3, 'Admin Assigning Application to FDD Agency...');
|
log(6.3, 'Admin Assigning Application to FDD Agency...');
|
||||||
// const fddUser = users.data.find(u => u.email === EMAILS.FDD);
|
const fddUser = users.data.find(u => u.email === EMAILS.FDD);
|
||||||
// await apiRequest('/fdd/assign', 'POST', {
|
await apiRequest('/fdd/assign', 'POST', {
|
||||||
// applicationId: applicationUUID,
|
applicationId: applicationUUID,
|
||||||
// assignedToAgency: fddUser.id
|
assignedToAgency: fddUser.id
|
||||||
// }, adminToken);
|
}, adminToken);
|
||||||
// log(6.3, 'FDD Agency assigned successfully.');
|
log(6.3, 'FDD Agency assigned successfully.');
|
||||||
// await delay();
|
await delay();
|
||||||
|
|
||||||
// // 7. FDD MILESTONE
|
// 7. FDD MILESTONE
|
||||||
// log(7, 'FDD Agency Discovery & Report Upload...');
|
log(7, 'FDD Agency Discovery & Report Upload...');
|
||||||
// const fddToken = await login(EMAILS.FDD);
|
const fddToken = await login(EMAILS.FDD);
|
||||||
|
|
||||||
// // FETCH ASSIGNMENT ID
|
// FETCH ASSIGNMENT ID
|
||||||
// const assignmentRes = await apiRequest(`/fdd/${applicationUUID}`, 'GET', null, fddToken);
|
const assignmentRes = await apiRequest(`/fdd/${applicationUUID}`, 'GET', null, fddToken);
|
||||||
// const assignmentId = assignmentRes.data.id;
|
const assignmentId = assignmentRes.data.id;
|
||||||
// log(7, `Found Assignment ID: ${assignmentId}`);
|
log(7, `Found Assignment ID: ${assignmentId}`);
|
||||||
|
|
||||||
// await apiRequest('/fdd/report', 'POST', {
|
await apiRequest('/fdd/report', 'POST', {
|
||||||
// assignmentId,
|
assignmentId,
|
||||||
// findings: 'Finance records clean.',
|
findings: 'Finance records clean.',
|
||||||
// recommendation: 'Approved'
|
recommendation: 'Approved'
|
||||||
// }, fddToken);
|
}, fddToken);
|
||||||
|
|
||||||
// log(7.1, 'Admin Approving FDD Final Stage...');
|
log(7.1, 'Admin Approving FDD Final Stage...');
|
||||||
// await apiRequest('/assessment/stage-decision', 'POST', {
|
await apiRequest('/assessment/stage-decision', 'POST', {
|
||||||
// applicationId: applicationUUID,
|
applicationId: applicationUUID,
|
||||||
// stageCode: 'FDD_VERIFICATION',
|
stageCode: 'FDD_VERIFICATION',
|
||||||
// decision: 'Approved',
|
decision: 'Approved',
|
||||||
// remarks: 'FDD documents verified.'
|
remarks: 'FDD documents verified.'
|
||||||
// }, adminToken);
|
}, adminToken);
|
||||||
// log(7, 'FDD Milestone Complete.');
|
log(7, 'FDD Milestone Complete.');
|
||||||
// await delay();
|
await delay();
|
||||||
|
|
||||||
// log(7.4, 'Uploading mandatory documents prior to LOI generation...');
|
log(7.4, 'Uploading mandatory documents prior to LOI generation...');
|
||||||
// const requiredDocs = ['CIBIL Report', 'Proposed Site City Map', 'Bank Statement', 'GST Certificate', 'PAN Card'];
|
const requiredDocs = ['CIBIL Report', 'Proposed Site City Map', 'Bank Statement', 'GST Certificate', 'PAN Card'];
|
||||||
// for (const doc of requiredDocs) {
|
for (const doc of requiredDocs) {
|
||||||
// await mockUploadDocument(applicationUUID, adminToken, doc);
|
await mockUploadDocument(applicationUUID, adminToken, doc);
|
||||||
// }
|
}
|
||||||
// await delay(1000);
|
await delay(1000);
|
||||||
|
|
||||||
// // 7.5 LOI APPROVAL
|
// 7.5 LOI APPROVAL
|
||||||
// log(7.5, 'LOI Generation & Approval...');
|
log(7.5, 'LOI Generation & Approval...');
|
||||||
// const loiRes = await apiRequest('/loi/request', 'POST', { applicationId: applicationUUID }, adminToken);
|
const loiRes = await apiRequest('/loi/request', 'POST', { applicationId: applicationUUID }, adminToken);
|
||||||
// const loiRequestId = loiRes.data.id;
|
const loiRequestId = loiRes.data.id;
|
||||||
|
|
||||||
// // Head Approval
|
// Head Approval
|
||||||
// await apiRequest(`/loi/request/${loiRequestId}/approve`, 'POST', {
|
await apiRequest(`/loi/request/${loiRequestId}/approve`, 'POST', {
|
||||||
// action: 'Approved',
|
action: 'Approved',
|
||||||
// remarks: 'Head Authorization for LOI'
|
remarks: 'Head Authorization for LOI'
|
||||||
// }, headToken);
|
}, headToken);
|
||||||
|
|
||||||
// // NBH Approval
|
// NBH Approval
|
||||||
// await apiRequest(`/loi/request/${loiRequestId}/approve`, 'POST', {
|
await apiRequest(`/loi/request/${loiRequestId}/approve`, 'POST', {
|
||||||
// action: 'Approved',
|
action: 'Approved',
|
||||||
// remarks: 'NBH Authorization for LOI'
|
remarks: 'NBH Authorization for LOI'
|
||||||
// }, nbhToken);
|
}, nbhToken);
|
||||||
|
|
||||||
// log(7.5, 'LOI Milestone Complete.');
|
log(7.5, 'LOI Milestone Complete.');
|
||||||
// await delay();
|
await delay();
|
||||||
|
|
||||||
// // 8. PAYMENT GATE (SECURITY DEPOSIT FIRST AS PER CURRENT FLOW)
|
// 8. PAYMENT GATE (SECURITY DEPOSIT FIRST AS PER CURRENT FLOW)
|
||||||
// log(8, 'Finance Verifying SECURITY_DEPOSIT to unlock LOI Issued...');
|
log(8, 'Finance Verifying SECURITY_DEPOSIT to unlock LOI Issued...');
|
||||||
// const financeToken = await login(EMAILS.FINANCE);
|
const financeToken = await login(EMAILS.FINANCE);
|
||||||
// await apiRequest('/loa/security-deposit', 'POST', {
|
await apiRequest('/loa/security-deposit', 'POST', {
|
||||||
// applicationId: applicationUUID,
|
applicationId: applicationUUID,
|
||||||
// amount: 500000,
|
amount: 500000,
|
||||||
// paymentReference: 'PAY-888999',
|
paymentReference: 'PAY-888999',
|
||||||
// depositType: 'SECURITY_DEPOSIT',
|
depositType: 'SECURITY_DEPOSIT',
|
||||||
// status: 'Verified'
|
status: 'Verified'
|
||||||
// }, financeToken);
|
}, financeToken);
|
||||||
// log(8, 'Security Deposit Verified.')
|
log(8, 'Security Deposit Verified.')
|
||||||
// // 9. GENERATE DEALER CODES (align with backend gate: LOI Issued required)
|
// 9. GENERATE DEALER CODES (align with backend gate: LOI Issued required)
|
||||||
// let statusBeforeCodeGen = await getApplicationStatus(applicationUUID, adminToken);
|
let statusBeforeCodeGen = await getApplicationStatus(applicationUUID, adminToken);
|
||||||
// log(9, `Current status before code generation: ${statusBeforeCodeGen}`);
|
log(9, `Current status before code generation: ${statusBeforeCodeGen}`);
|
||||||
// log(9, 'Ensuring mandatory PAN/GST/Bank fields before code generation...');
|
log(9, 'Ensuring mandatory PAN/GST/Bank fields before code generation...');
|
||||||
// await ensureMandatoryCodeGenFields(applicationUUID, adminToken);
|
await ensureMandatoryCodeGenFields(applicationUUID, adminToken);
|
||||||
// await delay(300);
|
await delay(300);
|
||||||
|
|
||||||
// if (statusBeforeCodeGen === 'Security Details') {
|
if (statusBeforeCodeGen === 'Security Details') {
|
||||||
// log(9, 'Status is Security Details; re-verifying Security Deposit to move to LOI Issued...');
|
log(9, 'Status is Security Details; re-verifying Security Deposit to move to LOI Issued...');
|
||||||
// await apiRequest('/loa/security-deposit', 'POST', {
|
await apiRequest('/loa/security-deposit', 'POST', {
|
||||||
// applicationId: applicationUUID,
|
applicationId: applicationUUID,
|
||||||
// amount: 500000,
|
amount: 500000,
|
||||||
// paymentReference: `PAY-RETRY-${Date.now()}`,
|
paymentReference: `PAY-RETRY-${Date.now()}`,
|
||||||
// depositType: 'SECURITY_DEPOSIT',
|
depositType: 'SECURITY_DEPOSIT',
|
||||||
// status: 'Verified'
|
status: 'Verified'
|
||||||
// }, financeToken);
|
}, financeToken);
|
||||||
// await delay();
|
await delay();
|
||||||
// statusBeforeCodeGen = await getApplicationStatus(applicationUUID, adminToken);
|
statusBeforeCodeGen = await getApplicationStatus(applicationUUID, adminToken);
|
||||||
// log(9, `Status after re-verify: ${statusBeforeCodeGen}`);
|
log(9, `Status after re-verify: ${statusBeforeCodeGen}`);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // Current backend flow keeps app at "Security Details" until explicit admin transition.
|
// Current backend flow keeps app at "Security Details" until explicit admin transition.
|
||||||
// if (statusBeforeCodeGen === 'Security Details') {
|
if (statusBeforeCodeGen === 'Security Details') {
|
||||||
// log(9, 'Applying admin transition from Security Details -> LOI Issued...');
|
log(9, 'Applying admin transition from Security Details -> LOI Issued...');
|
||||||
// await apiRequest(`/onboarding/applications/${applicationUUID}/status`, 'PUT', {
|
await apiRequest(`/onboarding/applications/${applicationUUID}/status`, 'PUT', {
|
||||||
// status: 'LOI Issued',
|
status: 'LOI Issued',
|
||||||
// stage: 'LOI',
|
stage: 'LOI',
|
||||||
// reason: 'E2E script alignment: unlock dealer code generation after Security Details checks.'
|
reason: 'E2E script alignment: unlock dealer code generation after Security Details checks.'
|
||||||
// }, adminToken);
|
}, adminToken);
|
||||||
// await delay();
|
await delay();
|
||||||
// statusBeforeCodeGen = await getApplicationStatus(applicationUUID, adminToken);
|
statusBeforeCodeGen = await getApplicationStatus(applicationUUID, adminToken);
|
||||||
// log(9, `Status after admin transition: ${statusBeforeCodeGen}`);
|
log(9, `Status after admin transition: ${statusBeforeCodeGen}`);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (statusBeforeCodeGen !== 'LOI Issued' && statusBeforeCodeGen !== 'Dealer Code Generation') {
|
if (statusBeforeCodeGen !== 'LOI Issued' && statusBeforeCodeGen !== 'Dealer Code Generation') {
|
||||||
// throw new Error(`Cannot generate codes: expected LOI Issued/Dealer Code Generation, got ${statusBeforeCodeGen}`);
|
throw new Error(`Cannot generate codes: expected LOI Issued/Dealer Code Generation, got ${statusBeforeCodeGen}`);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// log(9, 'Admin Generating SAP Dealer Codes...');
|
log(9, 'Admin Generating SAP Dealer Codes...');
|
||||||
// await apiRequest(`/onboarding/applications/${applicationUUID}/generate-codes`, 'POST', {}, adminToken);
|
await apiRequest(`/onboarding/applications/${applicationUUID}/generate-codes`, 'POST', {}, adminToken);
|
||||||
// log(9, 'Dealer Codes Generated.');
|
log(9, 'Dealer Codes Generated.');
|
||||||
// await delay();
|
await delay();
|
||||||
|
|
||||||
// // 10. FIRST FILL (POST CODE-GENERATION)
|
// 10. FIRST FILL (POST CODE-GENERATION)
|
||||||
// log(10, 'Finance Verifying FIRST FILL (₹15L)...');
|
log(10, 'Finance Verifying FIRST FILL (₹15L)...');
|
||||||
// await apiRequest('/loa/security-deposit', 'POST', {
|
await apiRequest('/loa/security-deposit', 'POST', {
|
||||||
// applicationId: applicationUUID,
|
applicationId: applicationUUID,
|
||||||
// amount: 1500000,
|
amount: 1500000,
|
||||||
// paymentReference: 'PAY-FIN-999',
|
paymentReference: 'PAY-FIN-999',
|
||||||
// depositType: 'FIRST_FILL',
|
depositType: 'FIRST_FILL',
|
||||||
// status: 'Verified'
|
status: 'Verified'
|
||||||
// }, financeToken);
|
}, financeToken);
|
||||||
// log(10, 'Final Security Deposit Verified.');
|
log(10, 'Final Security Deposit Verified.');
|
||||||
// await delay();
|
await delay();
|
||||||
|
|
||||||
// // 11. ADMIN UPDATING STATUTORY & BANK DETAILS
|
// 11. ADMIN UPDATING STATUTORY & BANK DETAILS
|
||||||
// log(11, 'Admin Updating Statutory & Bank Details for LOA Approval Gate...');
|
log(11, 'Admin Updating Statutory & Bank Details for LOA Approval Gate...');
|
||||||
// await apiRequest(`/onboarding/applications/${applicationUUID}`, 'PUT', {
|
await apiRequest(`/onboarding/applications/${applicationUUID}`, 'PUT', {
|
||||||
// accountHolderName: 'Ramesh Automobiles Private Limited',
|
accountHolderName: 'Ramesh Automobiles Private Limited',
|
||||||
// panNumber: 'ABCDE1234F',
|
panNumber: 'ABCDE1234F',
|
||||||
// gstNumber: '07ABCDE1234F1Z5',
|
gstNumber: '07ABCDE1234F1Z5',
|
||||||
// bankName: 'HDFC Bank',
|
bankName: 'HDFC Bank',
|
||||||
// accountNumber: '50100223344556',
|
accountNumber: '50100223344556',
|
||||||
// ifscCode: 'HDFC0001234'
|
ifscCode: 'HDFC0001234'
|
||||||
// }, adminToken);
|
}, adminToken);
|
||||||
// log(11, 'Statutory & Bank details updated.');
|
log(11, 'Statutory & Bank details updated.');
|
||||||
// await delay();
|
await delay();
|
||||||
|
|
||||||
// // 12. FINAL LOA APPROVAL
|
// 12. FINAL LOA APPROVAL
|
||||||
// log(12, 'NBH & Head Approving Final LOA...');
|
log(12, 'NBH & Head Approving Final LOA...');
|
||||||
// const loaRes = await apiRequest('/loa/request', 'POST', { applicationId: applicationUUID }, headToken);
|
const loaRes = await apiRequest('/loa/request', 'POST', { applicationId: applicationUUID }, headToken);
|
||||||
// const finalLoaRequestId = loaRes.data.id;
|
const finalLoaRequestId = loaRes.data.id;
|
||||||
|
|
||||||
// await apiRequest(`/loa/request/${finalLoaRequestId}/approve`, 'POST', {
|
await apiRequest(`/loa/request/${finalLoaRequestId}/approve`, 'POST', {
|
||||||
// action: 'Approved',
|
action: 'Approved',
|
||||||
// remarks: 'Head Authorization (Level 1)'
|
remarks: 'Head Authorization (Level 1)'
|
||||||
// }, headToken);
|
}, headToken);
|
||||||
|
|
||||||
// await apiRequest(`/loa/request/${finalLoaRequestId}/approve`, 'POST', {
|
await apiRequest(`/loa/request/${finalLoaRequestId}/approve`, 'POST', {
|
||||||
// action: 'Approved',
|
action: 'Approved',
|
||||||
// remarks: 'NBH Approval (Level 2)'
|
remarks: 'NBH Approval (Level 2)'
|
||||||
// }, nbhToken);
|
}, nbhToken);
|
||||||
// log(12, 'LOA Fully Approved.');
|
log(12, 'LOA Fully Approved.');
|
||||||
// await delay();
|
await delay();
|
||||||
|
|
||||||
// // 13. EOR (EVIDENCE OF READINESS) CHECKLIST VERIFICATION
|
// 13. EOR (EVIDENCE OF READINESS) CHECKLIST VERIFICATION
|
||||||
// log(13, 'Admin Initializing EOR Checklist (100% Readiness Requirement)...');
|
log(13, 'Admin Initializing EOR Checklist (100% Readiness Requirement)...');
|
||||||
// const eorInit = await apiRequest('/eor', 'POST', { applicationId: applicationUUID }, adminToken);
|
const eorInit = await apiRequest('/eor', 'POST', { applicationId: applicationUUID }, adminToken);
|
||||||
// const checklistId = eorInit.data.id;
|
const checklistId = eorInit.data.id;
|
||||||
// log(13, `EOR Checklist Created (ID: ${checklistId})`);
|
log(13, `EOR Checklist Created (ID: ${checklistId})`);
|
||||||
|
|
||||||
// log(13.1, 'Auditor Verifying all 12 mandatory EOR items as COMPLIANT...');
|
log(13.1, 'Auditor Verifying all 12 mandatory EOR items as COMPLIANT...');
|
||||||
// const eorItems = [
|
const eorItems = [
|
||||||
// { itemType: 'Sales', description: 'Sales Standards' },
|
{ itemType: 'Sales', description: 'Sales Standards' },
|
||||||
// { itemType: 'Service', description: 'Service & Spares' },
|
{ itemType: 'Service', description: 'Service & Spares' },
|
||||||
// { itemType: 'IT', description: 'DMS infra' },
|
{ itemType: 'IT', description: 'DMS infra' },
|
||||||
// { itemType: 'Training', description: 'Manpower Training' },
|
{ itemType: 'Training', description: 'Manpower Training' },
|
||||||
// { itemType: 'Statutory', description: 'Trade certificate with test ride bikes registration' },
|
{ itemType: 'Statutory', description: 'Trade certificate with test ride bikes registration' },
|
||||||
// { itemType: 'Statutory', description: 'GST certificate including Accessories & Apparels billing' },
|
{ itemType: 'Statutory', description: 'GST certificate including Accessories & Apparels billing' },
|
||||||
// { itemType: 'Finance', description: 'Inventory Funding' },
|
{ itemType: 'Finance', description: 'Inventory Funding' },
|
||||||
// { itemType: 'IT', description: 'Virtual code availability' },
|
{ itemType: 'IT', description: 'Virtual code availability' },
|
||||||
// { itemType: 'Finance', description: 'Vendor payments' },
|
{ itemType: 'Finance', description: 'Vendor payments' },
|
||||||
// { itemType: 'Marketing', description: 'Details for website submission' },
|
{ itemType: 'Marketing', description: 'Details for website submission' },
|
||||||
// { itemType: 'Insurance', description: 'Infra Insurance both Showroom and Service center' },
|
{ itemType: 'Insurance', description: 'Infra Insurance both Showroom and Service center' },
|
||||||
// { itemType: 'IT', description: 'Auto ordering' }
|
{ itemType: 'IT', description: 'Auto ordering' }
|
||||||
// ];
|
];
|
||||||
|
|
||||||
// for (const item of eorItems) {
|
for (const item of eorItems) {
|
||||||
// process.stdout.write(`.`); // Visual progress
|
process.stdout.write(`.`); // Visual progress
|
||||||
// await apiRequest(`/eor/item/${checklistId}`, 'POST', {
|
await apiRequest(`/eor/item/${checklistId}`, 'POST', {
|
||||||
// ...item,
|
...item,
|
||||||
// isCompliant: true,
|
isCompliant: true,
|
||||||
// remarks: 'Verified by Auditor - Compliant'
|
remarks: 'Verified by Auditor - Compliant'
|
||||||
// }, adminToken);
|
}, adminToken);
|
||||||
// }
|
}
|
||||||
// console.log('\n[STEP 13.1] All EOR items marked as compliant.');
|
console.log('\n[STEP 13.1] All EOR items marked as compliant.');
|
||||||
|
|
||||||
// log(13.2, 'Auditor Submitting Final EOR Audit...');
|
log(13.2, 'Auditor Submitting Final EOR Audit...');
|
||||||
// await apiRequest(`/eor/audit/${checklistId}`, 'POST', {
|
await apiRequest(`/eor/audit/${checklistId}`, 'POST', {
|
||||||
// status: 'Completed',
|
status: 'Completed',
|
||||||
// overallComments: 'Dealer is 100% ready for inauguration. All infra and statutory items verified.'
|
overallComments: 'Dealer is 100% ready for inauguration. All infra and statutory items verified.'
|
||||||
// }, adminToken);
|
}, adminToken);
|
||||||
|
|
||||||
// // Status check
|
// Status check
|
||||||
// const finalAppStatus = await apiRequest(`/onboarding/applications/${applicationUUID}`, 'GET', null, adminToken);
|
const finalAppStatus = await apiRequest(`/onboarding/applications/${applicationUUID}`, 'GET', null, adminToken);
|
||||||
// log(13.2, `Application Status after EOR: ${finalAppStatus.data.overallStatus}`);
|
log(13.2, `Application Status after EOR: ${finalAppStatus.data.overallStatus}`);
|
||||||
// await delay();
|
await delay();
|
||||||
|
|
||||||
// // 14. FINAL ONBOARDING
|
// 14. FINAL ONBOARDING
|
||||||
// log(14, 'Admin Finalizing Dealer Onboarding...');
|
log(14, 'Admin Finalizing Dealer Onboarding...');
|
||||||
// await apiRequest('/dealers', 'POST', { applicationId: applicationUUID }, adminToken);
|
await apiRequest('/dealers', 'POST', { applicationId: applicationUUID }, adminToken);
|
||||||
// await delay();
|
await delay();
|
||||||
|
|
||||||
// // 15. VERIFICATION
|
// 15. VERIFICATION
|
||||||
// log(15, 'Verifying Dealer Record Creation...');
|
log(15, 'Verifying Dealer Record Creation...');
|
||||||
// const dealerRes = await apiRequest(`/dealers/application/${applicationUUID}`, 'GET', null, adminToken);
|
const dealerRes = await apiRequest(`/dealers/application/${applicationUUID}`, 'GET', null, adminToken);
|
||||||
// if (!dealerRes.success || !dealerRes.data) {
|
if (!dealerRes.success || !dealerRes.data) {
|
||||||
// throw new Error('Verification Failed: Dealer record not found after onboarding.');
|
throw new Error('Verification Failed: Dealer record not found after onboarding.');
|
||||||
// }
|
}
|
||||||
// log(15, `Dealer Found: ${dealerRes.data.legalName} (${dealerRes.data.id})`);
|
log(15, `Dealer Found: ${dealerRes.data.legalName} (${dealerRes.data.id})`);
|
||||||
|
|
||||||
// log(15.1, 'Verifying User Account Role Update...');
|
log(15.1, 'Verifying User Account Role Update...');
|
||||||
// const userRes = await apiRequest(`/admin/users`, 'GET', null, adminToken);
|
const userRes = await apiRequest(`/admin/users`, 'GET', null, adminToken);
|
||||||
// const dealerUser = userRes.data.find(u => u.email === PROSPECT_EMAIL);
|
const dealerUser = userRes.data.find(u => u.email === PROSPECT_EMAIL);
|
||||||
// if (!dealerUser || dealerUser.roleCode !== 'Dealer') {
|
if (!dealerUser || dealerUser.roleCode !== 'Dealer') {
|
||||||
// throw new Error(`Verification Failed: User role not updated to 'Dealer'. Current role: ${dealerUser?.roleCode}`);
|
throw new Error(`Verification Failed: User role not updated to 'Dealer'. Current role: ${dealerUser?.roleCode}`);
|
||||||
// }
|
}
|
||||||
// log(15.1, `User role confirmed: ${dealerUser.roleCode}`);
|
log(15.1, `User role confirmed: ${dealerUser.roleCode}`);
|
||||||
|
|
||||||
// log(15.2, '--- WORKFLOW COMPLETED SUCCESSFULLY! ---');
|
log(15.2, '--- WORKFLOW COMPLETED SUCCESSFULLY! ---');
|
||||||
// log(15.2, `The application ${applicationId} is now at 'ONBOARDED' status and Dealer profile is active.`);
|
log(15.2, `The application ${applicationId} is now at 'ONBOARDED' status and Dealer profile is active.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user