import { Response } from 'express'; import db from '../../database/models/index.js'; const { ConstitutionalChange, Outlet, User, Worknote } = db; import { Op, Transaction } from 'sequelize'; import { v4 as uuidv4 } from 'uuid'; import { AuthRequest } from '../../types/express.types.js'; import { CONSTITUTIONAL_STAGES, AUDIT_ACTIONS, ROLES } from '../../common/config/constants.js'; import { ConstitutionalWorkflowService } from '../../services/ConstitutionalWorkflowService.js'; export const submitRequest = async (req: AuthRequest, res: Response) => { try { if (!req.user) return res.status(401).json({ success: false, message: 'Unauthorized' }); const { outletId, changeType, reason, currentConstitution, newPartnersDetails, shareholdingPattern } = req.body; const requestId = `CC-${Date.now()}-${uuidv4().substring(0, 4).toUpperCase()}`; // Store extra details in metadata const metadata = { newPartnersDetails, shareholdingPattern, currentConstitution }; const request = await ConstitutionalChange.create({ requestId, outletId: outletId || null, // Optional for dealer-level changes dealerId: req.user.id, changeType, description: reason, currentConstitution: currentConstitution || null, currentStage: CONSTITUTIONAL_STAGES.SUBMITTED, status: 'Submitted', progressPercentage: ConstitutionalWorkflowService.calculateProgress(CONSTITUTIONAL_STAGES.SUBMITTED), metadata, documents: [], timeline: [{ stage: 'Submitted', timestamp: new Date(), user: req.user.fullName, action: 'Request submitted', remarks: reason }] }); await db.AuditLog.create({ userId: req.user.id, action: AUDIT_ACTIONS.CREATED, entityType: 'constitutional_change', entityId: request.id }); res.status(201).json({ success: true, message: 'Constitutional change request submitted successfully', requestId: request.requestId }); } catch (error) { console.error('Submit constitutional change error:', error); res.status(500).json({ success: false, message: 'Error submitting request' }); } }; export const getRequests = async (req: AuthRequest, res: Response) => { try { if (!req.user) return res.status(401).json({ success: false, message: 'Unauthorized' }); const where: any = {}; if (req.user.roleCode === 'Dealer') { where.dealerId = req.user.id; } const requests = await ConstitutionalChange.findAll({ where, include: [ { model: Outlet, as: 'outlet', attributes: ['code', 'name'] }, { model: User, as: 'dealer', attributes: ['fullName'] } ], order: [['createdAt', 'DESC']] }); res.json({ success: true, requests }); } catch (error) { console.error('Get constitutional changes error:', error); res.status(500).json({ success: false, message: 'Error fetching requests' }); } }; export const getRequestById = async (req: AuthRequest, res: Response) => { try { const { id } = req.params; const idStr = String(id); const isUUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(idStr); const request = await ConstitutionalChange.findOne({ where: isUUID ? { [Op.or]: [{ id: idStr }, { requestId: idStr }] } : { requestId: idStr }, include: [ { model: Outlet, as: 'outlet' }, { model: User, as: 'dealer', attributes: ['fullName', 'email'] }, { model: Worknote, as: 'worknotes' } ] }); if (!request) return res.status(404).json({ success: false, message: 'Request not found' }); res.json({ success: true, request }); } catch (error) { console.error('Get constitutional change details error:', error); res.status(500).json({ success: false, message: 'Error fetching details' }); } }; export const takeAction = async (req: AuthRequest, res: Response) => { try { if (!req.user) return res.status(401).json({ success: false, message: 'Unauthorized' }); const { id } = req.params; const idStr = String(id); const { action, comments } = req.body; // Approve, Reject, Send Back const isUUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(idStr); const request = await ConstitutionalChange.findOne({ where: isUUID ? { [Op.or]: [{ id: idStr }, { requestId: idStr }] } : { requestId: idStr } }); if (!request) return res.status(404).json({ success: false, message: 'Request not found' }); if (action === 'Reject') { await ConstitutionalWorkflowService.transitionRequest(request, CONSTITUTIONAL_STAGES.REJECTED, req.user.id, { action: 'Rejected', status: 'Rejected', remarks: comments, userFullName: req.user.fullName }); } else { // Multi-level approval flow as per SRS 12.2.4 const stageFlow: Record = { [CONSTITUTIONAL_STAGES.SUBMITTED]: CONSTITUTIONAL_STAGES.ASM_REVIEW, [CONSTITUTIONAL_STAGES.ASM_REVIEW]: CONSTITUTIONAL_STAGES.ZM_RBM_REVIEW, [CONSTITUTIONAL_STAGES.ZM_RBM_REVIEW]: CONSTITUTIONAL_STAGES.ZBH_REVIEW, [CONSTITUTIONAL_STAGES.ZBH_REVIEW]: CONSTITUTIONAL_STAGES.LEAD_REVIEW, [CONSTITUTIONAL_STAGES.LEAD_REVIEW]: CONSTITUTIONAL_STAGES.HEAD_REVIEW, [CONSTITUTIONAL_STAGES.HEAD_REVIEW]: CONSTITUTIONAL_STAGES.NBH_APPROVAL, [CONSTITUTIONAL_STAGES.NBH_APPROVAL]: CONSTITUTIONAL_STAGES.LEGAL_REVIEW, [CONSTITUTIONAL_STAGES.LEGAL_REVIEW]: CONSTITUTIONAL_STAGES.COMPLETED }; const nextStage = stageFlow[request.currentStage]; if (!nextStage) return res.status(400).json({ success: false, message: 'Cannot move forward from current stage' }); await ConstitutionalWorkflowService.transitionRequest(request, nextStage, req.user.id, { action: action === 'Approve' ? `Approved to ${nextStage}` : action, remarks: comments, userFullName: req.user.fullName }); } res.json({ success: true, message: `Request ${action.toLowerCase()}ed successfully` }); } catch (error) { console.error('Take action error:', error); res.status(500).json({ success: false, message: 'Error processing action' }); } }; /** * Returns document checklist based on target constitution */ export const getChecklist = async (req: AuthRequest, res: Response) => { try { const { targetConstitution } = req.query; if (!targetConstitution) return res.status(400).json({ success: false, message: 'targetConstitution is required' }); const checklist = ConstitutionalWorkflowService.getDocumentChecklist(targetConstitution as string); res.json({ success: true, checklist }); } catch (error) { res.status(500).json({ success: false, message: 'Error fetching checklist' }); } }; export const uploadDocuments = async (req: AuthRequest, res: Response) => { try { const { id } = req.params; const idStr = String(id); const { documents } = req.body; const isUUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(idStr); const request = await ConstitutionalChange.findOne({ where: isUUID ? { [Op.or]: [{ id: idStr }, { requestId: idStr }] } : { requestId: idStr } }); if (!request) { return res.status(404).json({ success: false, message: 'Request not found' }); } await request.update({ documents: documents, updatedAt: new Date() }); res.json({ success: true, message: 'Documents uploaded successfully' }); } catch (error) { console.error('Upload documents error:', error); res.status(500).json({ success: false, message: 'Error uploading documents' }); } };