now chat implementation menhanced ui created for FDD from he can upload the rlevenat documents dedicated documents creted for each service
This commit is contained in:
parent
28580b7fb0
commit
115e978eb8
@ -38,6 +38,13 @@ const policies = [
|
|||||||
approvalMode: 'ROLE_MANDATORY',
|
approvalMode: 'ROLE_MANDATORY',
|
||||||
requiredRoles: ['DD Head', 'NBH'],
|
requiredRoles: ['DD Head', 'NBH'],
|
||||||
isActive: true
|
isActive: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stageCode: 'FDD_VERIFICATION',
|
||||||
|
minApprovals: 1,
|
||||||
|
approvalMode: 'ROLE_MANDATORY',
|
||||||
|
requiredRoles: ['FDD'],
|
||||||
|
isActive: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,9 @@ const rolesToSeed = [
|
|||||||
{ roleCode: ROLES.LEGAL_ADMIN, roleName: 'Legal Admin', category: 'DEPARTMENT', description: 'Legal Department' },
|
{ roleCode: ROLES.LEGAL_ADMIN, roleName: 'Legal Admin', category: 'DEPARTMENT', description: 'Legal Department' },
|
||||||
{ roleCode: ROLES.NBH, roleName: 'NBH', category: 'NATIONAL', description: 'National Business Head' },
|
{ roleCode: ROLES.NBH, roleName: 'NBH', category: 'NATIONAL', description: 'National Business Head' },
|
||||||
{ roleCode: ROLES.ASM, roleName: 'ASM', category: 'SALES', description: 'Area Sales Manager' },
|
{ roleCode: ROLES.ASM, roleName: 'ASM', category: 'SALES', description: 'Area Sales Manager' },
|
||||||
{ roleCode: ROLES.DEALER, roleName: 'Dealer', category: 'EXTERNAL', description: 'Dealer Principal' }
|
{ roleCode: ROLES.DEALER, roleName: 'Dealer', category: 'EXTERNAL', description: 'Dealer Principal' },
|
||||||
|
{ roleCode: ROLES.FDD, roleName: 'FDD Team', category: 'EXTERNAL', description: 'Financial Due Diligence Team' },
|
||||||
|
{ roleCode: ROLES.ARCHITECTURE, roleName: 'Architecture Team', category: 'DEPARTMENT', description: 'Architecture & Design Team' }
|
||||||
];
|
];
|
||||||
|
|
||||||
async function seedRoles() {
|
async function seedRoles() {
|
||||||
|
|||||||
@ -19,7 +19,9 @@ async function seedUsers() {
|
|||||||
{ email: 'admin@royalenfield.com', fullName: 'Laxman H', password: hashedPassword, roleCode: ROLES.DD_LEAD, status: 'active' },
|
{ email: 'admin@royalenfield.com', fullName: 'Laxman H', password: hashedPassword, roleCode: ROLES.DD_LEAD, status: 'active' },
|
||||||
{ email: 'yashwin@gmail.com', fullName: 'Yashwin', password: hashedPassword, roleCode: ROLES.ZBH, status: 'active' },
|
{ email: 'yashwin@gmail.com', fullName: 'Yashwin', password: hashedPassword, roleCode: ROLES.ZBH, status: 'active' },
|
||||||
{ email: 'kenil@gmail.com', fullName: 'Kenil', password: hashedPassword, roleCode: ROLES.DD_LEAD, status: 'active' },
|
{ email: 'kenil@gmail.com', fullName: 'Kenil', password: hashedPassword, roleCode: ROLES.DD_LEAD, status: 'active' },
|
||||||
{ email: 'lince@gmail.com', fullName: 'Lince', password: hashedPassword, roleCode: ROLES.DD_ADMIN, status: 'active' }
|
{ email: 'lince@gmail.com', fullName: 'Lince', password: hashedPassword, roleCode: ROLES.DD_ADMIN, status: 'active' },
|
||||||
|
{ email: 'fdd@royalenfield.com', fullName: 'FDD Partner', password: hashedPassword, roleCode: ROLES.FDD, status: 'active' },
|
||||||
|
{ email: 'architecture@royalenfield.com', fullName: 'RE Architect', password: hashedPassword, roleCode: ROLES.ARCHITECTURE, status: 'active' }
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const u of usersToSeed) {
|
for (const u of usersToSeed) {
|
||||||
|
|||||||
@ -26,7 +26,8 @@ async function seed() {
|
|||||||
{ roleCode: 'Finance', roleName: 'Finance', category: 'DEPARTMENT' },
|
{ roleCode: 'Finance', roleName: 'Finance', category: 'DEPARTMENT' },
|
||||||
{ roleCode: 'Dealer', roleName: 'Dealer', category: 'EXTERNAL' },
|
{ roleCode: 'Dealer', roleName: 'Dealer', category: 'EXTERNAL' },
|
||||||
{ roleCode: 'DD Admin', roleName: 'DD Admin', category: 'ADMIN' },
|
{ roleCode: 'DD Admin', roleName: 'DD Admin', category: 'ADMIN' },
|
||||||
{ roleCode: 'ARCHITECTURE', roleName: 'Architecture Team', category: 'DEPARTMENT' }
|
{ roleCode: 'ARCHITECTURE', roleName: 'Architecture Team', category: 'DEPARTMENT' },
|
||||||
|
{ roleCode: 'FDD', roleName: 'FDD Team', category: 'EXTERNAL' }
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const r of roles) {
|
for (const r of roles) {
|
||||||
@ -98,7 +99,9 @@ async function seed() {
|
|||||||
{ email: 'ddhead@royalenfield.com', name: 'Vikram Singh', role: 'DD Head' },
|
{ email: 'ddhead@royalenfield.com', name: 'Vikram Singh', role: 'DD Head' },
|
||||||
{ email: 'finance@royalenfield.com', name: 'Rahul Verma', role: 'Finance' },
|
{ email: 'finance@royalenfield.com', name: 'Rahul Verma', role: 'Finance' },
|
||||||
{ email: 'admin@royalenfield.com', name: 'Laxman H', role: 'Super Admin' },
|
{ email: 'admin@royalenfield.com', name: 'Laxman H', role: 'Super Admin' },
|
||||||
{ email: 'lince@gmail.com', name: 'Lince', role: 'DD Admin' }
|
{ email: 'lince@gmail.com', name: 'Lince', role: 'DD Admin' },
|
||||||
|
{ email: 'fdd@royalenfield.com', name: 'FDD Partner', role: 'FDD' },
|
||||||
|
{ email: 'architecture@royalenfield.com', name: 'RE Architect', role: 'ARCHITECTURE' }
|
||||||
];
|
];
|
||||||
for (const u of nationalUsers) {
|
for (const u of nationalUsers) {
|
||||||
const [user] = await User.findOrCreate({
|
const [user] = await User.findOrCreate({
|
||||||
|
|||||||
@ -14,7 +14,8 @@ export const ROLES = {
|
|||||||
ASM: 'ASM',
|
ASM: 'ASM',
|
||||||
FINANCE: 'Finance',
|
FINANCE: 'Finance',
|
||||||
DEALER: 'Dealer',
|
DEALER: 'Dealer',
|
||||||
ARCHITECTURE: 'ARCHITECTURE'
|
ARCHITECTURE: 'ARCHITECTURE',
|
||||||
|
FDD: 'FDD'
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// Regions
|
// Regions
|
||||||
|
|||||||
@ -245,11 +245,9 @@ export default (sequelize: Sequelize) => {
|
|||||||
Application.hasMany(models.ApplicationStatusHistory, { foreignKey: 'applicationId', as: 'statusHistory' });
|
Application.hasMany(models.ApplicationStatusHistory, { foreignKey: 'applicationId', as: 'statusHistory' });
|
||||||
Application.hasMany(models.ApplicationProgress, { foreignKey: 'applicationId', as: 'progressTracking' });
|
Application.hasMany(models.ApplicationProgress, { foreignKey: 'applicationId', as: 'progressTracking' });
|
||||||
|
|
||||||
Application.hasMany(models.Document, {
|
Application.hasMany(models.OnboardingDocument, {
|
||||||
foreignKey: 'requestId',
|
foreignKey: 'applicationId',
|
||||||
as: 'uploadedDocuments',
|
as: 'uploadedDocuments'
|
||||||
scope: { requestType: 'application' },
|
|
||||||
constraints: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Application.hasMany(models.QuestionnaireResponse, { foreignKey: 'applicationId', as: 'questionnaireResponses' });
|
Application.hasMany(models.QuestionnaireResponse, { foreignKey: 'applicationId', as: 'questionnaireResponses' });
|
||||||
@ -268,6 +266,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
Application.hasMany(models.StageApprovalAction, { foreignKey: 'applicationId', as: 'stageApprovals' });
|
Application.hasMany(models.StageApprovalAction, { foreignKey: 'applicationId', as: 'stageApprovals' });
|
||||||
Application.hasOne(models.Dealer, { foreignKey: 'applicationId', as: 'dealer' });
|
Application.hasOne(models.Dealer, { foreignKey: 'applicationId', as: 'dealer' });
|
||||||
Application.hasMany(models.SecurityDeposit, { foreignKey: 'applicationId', as: 'securityDeposits' });
|
Application.hasMany(models.SecurityDeposit, { foreignKey: 'applicationId', as: 'securityDeposits' });
|
||||||
|
Application.hasOne(models.EorChecklist, { foreignKey: 'applicationId', as: 'eorChecklist' });
|
||||||
};
|
};
|
||||||
|
|
||||||
return Application;
|
return Application;
|
||||||
|
|||||||
@ -93,6 +93,10 @@ export default (sequelize: Sequelize) => {
|
|||||||
foreignKey: 'dealerId',
|
foreignKey: 'dealerId',
|
||||||
as: 'dealer'
|
as: 'dealer'
|
||||||
});
|
});
|
||||||
|
ConstitutionalChange.hasMany(models.ConstitutionalDocument, {
|
||||||
|
foreignKey: 'constitutionalChangeId',
|
||||||
|
as: 'uploadedDocuments'
|
||||||
|
});
|
||||||
ConstitutionalChange.hasMany(models.Worknote, {
|
ConstitutionalChange.hasMany(models.Worknote, {
|
||||||
foreignKey: 'requestId',
|
foreignKey: 'requestId',
|
||||||
as: 'worknotes',
|
as: 'worknotes',
|
||||||
|
|||||||
93
src/database/models/ConstitutionalDocument.ts
Normal file
93
src/database/models/ConstitutionalDocument.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { Model, DataTypes, Sequelize } from 'sequelize';
|
||||||
|
|
||||||
|
export interface ConstitutionalDocumentAttributes {
|
||||||
|
id: string;
|
||||||
|
constitutionalChangeId: string;
|
||||||
|
documentType: string;
|
||||||
|
fileName: string;
|
||||||
|
filePath: string;
|
||||||
|
fileSize: number | null;
|
||||||
|
mimeType: string | null;
|
||||||
|
stage: string | null;
|
||||||
|
status: string;
|
||||||
|
uploadedBy: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConstitutionalDocumentInstance extends Model<ConstitutionalDocumentAttributes>, ConstitutionalDocumentAttributes { }
|
||||||
|
|
||||||
|
export default (sequelize: Sequelize) => {
|
||||||
|
const ConstitutionalDocument = sequelize.define<ConstitutionalDocumentInstance>('ConstitutionalDocument', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
constitutionalChangeId: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: false,
|
||||||
|
references: {
|
||||||
|
model: 'constitutional_changes',
|
||||||
|
key: 'id'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
documentType: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
fileName: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
filePath: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
fileSize: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
mimeType: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
stage: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
defaultValue: 'active'
|
||||||
|
},
|
||||||
|
uploadedBy: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: true,
|
||||||
|
references: {
|
||||||
|
model: 'users',
|
||||||
|
key: 'id'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
tableName: 'constitutional_documents',
|
||||||
|
timestamps: true,
|
||||||
|
indexes: [
|
||||||
|
{ fields: ['constitutionalChangeId'] },
|
||||||
|
{ fields: ['status'] }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
(ConstitutionalDocument as any).associate = (models: any) => {
|
||||||
|
ConstitutionalDocument.belongsTo(models.User, { foreignKey: 'uploadedBy', as: 'uploader' });
|
||||||
|
ConstitutionalDocument.belongsTo(models.ConstitutionalChange, { foreignKey: 'constitutionalChangeId', as: 'constitutionalChange' });
|
||||||
|
|
||||||
|
ConstitutionalDocument.hasMany(models.DocumentVersion, { foreignKey: 'documentId', as: 'versions', constraints: false });
|
||||||
|
ConstitutionalDocument.belongsToMany(models.Worknote, {
|
||||||
|
through: models.WorkNoteAttachment,
|
||||||
|
foreignKey: 'documentId',
|
||||||
|
otherKey: 'noteId',
|
||||||
|
as: 'worknotes',
|
||||||
|
constraints: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return ConstitutionalDocument;
|
||||||
|
};
|
||||||
@ -77,13 +77,26 @@ export default (sequelize: Sequelize) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
(Dealer as any).associate = (models: any) => {
|
(Dealer as any).associate = (models: any) => {
|
||||||
Dealer.belongsTo(models.Application, { foreignKey: 'applicationId', as: 'application' });
|
const { Application, DealerCode, OnboardingDocument, Resignation, TerminationRequest, User } = models;
|
||||||
Dealer.belongsTo(models.DealerCode, { foreignKey: 'dealerCodeId', as: 'dealerCode' });
|
|
||||||
|
Dealer.belongsTo(Application, { foreignKey: 'applicationId', as: 'application' });
|
||||||
|
Dealer.belongsTo(DealerCode, { foreignKey: 'dealerCodeId', as: 'dealerCode' });
|
||||||
|
|
||||||
Dealer.hasMany(models.Document, { foreignKey: 'dealerId', as: 'documents' });
|
if (OnboardingDocument) {
|
||||||
Dealer.hasMany(models.Resignation, { foreignKey: 'dealerId', as: 'resignations' });
|
Dealer.hasMany(OnboardingDocument, { foreignKey: 'dealerId', as: 'onboardingDocuments' });
|
||||||
Dealer.hasMany(models.TerminationRequest, { foreignKey: 'dealerId', as: 'terminationRequests' });
|
}
|
||||||
Dealer.hasOne(models.User, { foreignKey: 'dealerId', as: 'user' });
|
|
||||||
|
if (Resignation) {
|
||||||
|
Dealer.hasMany(Resignation, { foreignKey: 'dealerId', as: 'resignations' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TerminationRequest) {
|
||||||
|
Dealer.hasMany(TerminationRequest, { foreignKey: 'dealerId', as: 'terminationRequests' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (User) {
|
||||||
|
Dealer.hasOne(User, { foreignKey: 'dealerId', as: 'user' });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return Dealer;
|
return Dealer;
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { Model, DataTypes, Sequelize } from 'sequelize';
|
|||||||
export interface DocumentVersionAttributes {
|
export interface DocumentVersionAttributes {
|
||||||
id: string;
|
id: string;
|
||||||
documentId: string;
|
documentId: string;
|
||||||
|
documentType: string;
|
||||||
versionNumber: number;
|
versionNumber: number;
|
||||||
filePath: string;
|
filePath: string;
|
||||||
uploadedBy: string | null;
|
uploadedBy: string | null;
|
||||||
@ -19,11 +20,11 @@ export default (sequelize: Sequelize) => {
|
|||||||
},
|
},
|
||||||
documentId: {
|
documentId: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: false
|
||||||
references: {
|
},
|
||||||
model: 'documents',
|
documentType: {
|
||||||
key: 'id'
|
type: DataTypes.STRING,
|
||||||
}
|
allowNull: false
|
||||||
},
|
},
|
||||||
versionNumber: {
|
versionNumber: {
|
||||||
type: DataTypes.INTEGER,
|
type: DataTypes.INTEGER,
|
||||||
@ -48,7 +49,6 @@ export default (sequelize: Sequelize) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
(DocumentVersion as any).associate = (models: any) => {
|
(DocumentVersion as any).associate = (models: any) => {
|
||||||
DocumentVersion.belongsTo(models.Document, { foreignKey: 'documentId', as: 'document' });
|
|
||||||
DocumentVersion.belongsTo(models.User, { foreignKey: 'uploadedBy', as: 'uploader' });
|
DocumentVersion.belongsTo(models.User, { foreignKey: 'uploadedBy', as: 'uploader' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -45,11 +45,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
},
|
},
|
||||||
proofDocumentId: {
|
proofDocumentId: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: true,
|
allowNull: true
|
||||||
references: {
|
|
||||||
model: 'documents',
|
|
||||||
key: 'id'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
tableName: 'eor_checklist_items',
|
tableName: 'eor_checklist_items',
|
||||||
@ -58,7 +54,6 @@ export default (sequelize: Sequelize) => {
|
|||||||
|
|
||||||
(EorChecklistItem as any).associate = (models: any) => {
|
(EorChecklistItem as any).associate = (models: any) => {
|
||||||
EorChecklistItem.belongsTo(models.EorChecklist, { foreignKey: 'checklistId', as: 'checklist' });
|
EorChecklistItem.belongsTo(models.EorChecklist, { foreignKey: 'checklistId', as: 'checklist' });
|
||||||
EorChecklistItem.belongsTo(models.Document, { foreignKey: 'proofDocumentId', as: 'proofDocument' });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return EorChecklistItem;
|
return EorChecklistItem;
|
||||||
|
|||||||
@ -31,7 +31,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
references: {
|
references: {
|
||||||
model: 'documents',
|
model: 'onboarding_documents',
|
||||||
key: 'id'
|
key: 'id'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -62,7 +62,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
|
|
||||||
(FddReport as any).associate = (models: any) => {
|
(FddReport as any).associate = (models: any) => {
|
||||||
FddReport.belongsTo(models.FddAssignment, { foreignKey: 'assignmentId', as: 'assignment' });
|
FddReport.belongsTo(models.FddAssignment, { foreignKey: 'assignmentId', as: 'assignment' });
|
||||||
FddReport.belongsTo(models.Document, { foreignKey: 'reportDocumentId', as: 'reportDocument' });
|
FddReport.belongsTo(models.OnboardingDocument, { foreignKey: 'reportDocumentId', as: 'reportDocument' });
|
||||||
FddReport.belongsTo(models.User, { foreignKey: 'verifiedBy', as: 'verifier' });
|
FddReport.belongsTo(models.User, { foreignKey: 'verifiedBy', as: 'verifier' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
references: {
|
references: {
|
||||||
model: 'documents',
|
model: 'onboarding_documents',
|
||||||
key: 'id'
|
key: 'id'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -49,7 +49,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
|
|
||||||
(LoaDocumentGenerated as any).associate = (models: any) => {
|
(LoaDocumentGenerated as any).associate = (models: any) => {
|
||||||
LoaDocumentGenerated.belongsTo(models.LoaRequest, { foreignKey: 'requestId', as: 'request' });
|
LoaDocumentGenerated.belongsTo(models.LoaRequest, { foreignKey: 'requestId', as: 'request' });
|
||||||
LoaDocumentGenerated.belongsTo(models.Document, { foreignKey: 'documentId', as: 'document' });
|
LoaDocumentGenerated.belongsTo(models.OnboardingDocument, { foreignKey: 'documentId', as: 'document' });
|
||||||
LoaDocumentGenerated.hasMany(models.LoaAcknowledgement, { foreignKey: 'loaDocId', as: 'acknowledgements' });
|
LoaDocumentGenerated.hasMany(models.LoaAcknowledgement, { foreignKey: 'loaDocId', as: 'acknowledgements' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
references: {
|
references: {
|
||||||
model: 'documents',
|
model: 'onboarding_documents',
|
||||||
key: 'id'
|
key: 'id'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -49,7 +49,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
|
|
||||||
(LoiDocumentGenerated as any).associate = (models: any) => {
|
(LoiDocumentGenerated as any).associate = (models: any) => {
|
||||||
LoiDocumentGenerated.belongsTo(models.LoiRequest, { foreignKey: 'requestId', as: 'request' });
|
LoiDocumentGenerated.belongsTo(models.LoiRequest, { foreignKey: 'requestId', as: 'request' });
|
||||||
LoiDocumentGenerated.belongsTo(models.Document, { foreignKey: 'documentId', as: 'document' });
|
LoiDocumentGenerated.belongsTo(models.OnboardingDocument, { foreignKey: 'documentId', as: 'document' });
|
||||||
LoiDocumentGenerated.hasMany(models.LoiAcknowledgement, { foreignKey: 'loiDocId', as: 'acknowledgements' });
|
LoiDocumentGenerated.hasMany(models.LoiAcknowledgement, { foreignKey: 'loiDocId', as: 'acknowledgements' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -17,10 +17,10 @@ export interface DocumentAttributes {
|
|||||||
uploadedBy: string | null;
|
uploadedBy: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DocumentInstance extends Model<DocumentAttributes>, DocumentAttributes { }
|
export interface OnboardingDocumentInstance extends Model<DocumentAttributes>, DocumentAttributes { }
|
||||||
|
|
||||||
export default (sequelize: Sequelize) => {
|
export default (sequelize: Sequelize) => {
|
||||||
const Document = sequelize.define<DocumentInstance>('Document', {
|
const OnboardingDocument = sequelize.define<OnboardingDocumentInstance>('OnboardingDocument', {
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
defaultValue: DataTypes.UUIDV4,
|
defaultValue: DataTypes.UUIDV4,
|
||||||
@ -87,7 +87,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
tableName: 'documents',
|
tableName: 'onboarding_documents',
|
||||||
timestamps: true,
|
timestamps: true,
|
||||||
indexes: [
|
indexes: [
|
||||||
{ fields: ['applicationId'] },
|
{ fields: ['applicationId'] },
|
||||||
@ -96,19 +96,20 @@ export default (sequelize: Sequelize) => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
(Document as any).associate = (models: any) => {
|
(OnboardingDocument as any).associate = (models: any) => {
|
||||||
Document.belongsTo(models.User, { foreignKey: 'uploadedBy', as: 'uploader' });
|
OnboardingDocument.belongsTo(models.User, { foreignKey: 'uploadedBy', as: 'uploader' });
|
||||||
Document.belongsTo(models.Application, { foreignKey: 'applicationId', as: 'application' });
|
OnboardingDocument.belongsTo(models.Application, { foreignKey: 'applicationId', as: 'application' });
|
||||||
Document.belongsTo(models.Dealer, { foreignKey: 'dealerId', as: 'dealer' });
|
OnboardingDocument.belongsTo(models.Dealer, { foreignKey: 'dealerId', as: 'dealer' });
|
||||||
|
|
||||||
Document.hasMany(models.DocumentVersion, { foreignKey: 'documentId', as: 'versions' });
|
OnboardingDocument.hasMany(models.DocumentVersion, { foreignKey: 'documentId', as: 'versions', constraints: false });
|
||||||
Document.belongsToMany(models.Worknote, {
|
OnboardingDocument.belongsToMany(models.Worknote, {
|
||||||
through: models.WorkNoteAttachment,
|
through: models.WorkNoteAttachment,
|
||||||
foreignKey: 'documentId',
|
foreignKey: 'documentId',
|
||||||
otherKey: 'noteId',
|
otherKey: 'noteId',
|
||||||
as: 'workNotes'
|
as: 'workNotes',
|
||||||
|
constraints: false
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return Document;
|
return OnboardingDocument;
|
||||||
};
|
};
|
||||||
93
src/database/models/RelocationDocument.ts
Normal file
93
src/database/models/RelocationDocument.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { Model, DataTypes, Sequelize } from 'sequelize';
|
||||||
|
|
||||||
|
export interface RelocationDocumentAttributes {
|
||||||
|
id: string;
|
||||||
|
relocationId: string;
|
||||||
|
documentType: string;
|
||||||
|
fileName: string;
|
||||||
|
filePath: string;
|
||||||
|
fileSize: number | null;
|
||||||
|
mimeType: string | null;
|
||||||
|
stage: string | null;
|
||||||
|
status: string;
|
||||||
|
uploadedBy: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RelocationDocumentInstance extends Model<RelocationDocumentAttributes>, RelocationDocumentAttributes { }
|
||||||
|
|
||||||
|
export default (sequelize: Sequelize) => {
|
||||||
|
const RelocationDocument = sequelize.define<RelocationDocumentInstance>('RelocationDocument', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
relocationId: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: false,
|
||||||
|
references: {
|
||||||
|
model: 'relocation_requests',
|
||||||
|
key: 'id'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
documentType: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
fileName: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
filePath: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
fileSize: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
mimeType: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
stage: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
defaultValue: 'active'
|
||||||
|
},
|
||||||
|
uploadedBy: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: true,
|
||||||
|
references: {
|
||||||
|
model: 'users',
|
||||||
|
key: 'id'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
tableName: 'relocation_documents',
|
||||||
|
timestamps: true,
|
||||||
|
indexes: [
|
||||||
|
{ fields: ['relocationId'] },
|
||||||
|
{ fields: ['status'] }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
(RelocationDocument as any).associate = (models: any) => {
|
||||||
|
RelocationDocument.belongsTo(models.User, { foreignKey: 'uploadedBy', as: 'uploader' });
|
||||||
|
RelocationDocument.belongsTo(models.RelocationRequest, { foreignKey: 'relocationId', as: 'request' });
|
||||||
|
|
||||||
|
RelocationDocument.hasMany(models.DocumentVersion, { foreignKey: 'documentId', as: 'versions', constraints: false });
|
||||||
|
RelocationDocument.belongsToMany(models.Worknote, {
|
||||||
|
through: models.WorkNoteAttachment,
|
||||||
|
foreignKey: 'documentId',
|
||||||
|
otherKey: 'noteId',
|
||||||
|
as: 'workNotes',
|
||||||
|
constraints: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return RelocationDocument;
|
||||||
|
};
|
||||||
@ -140,11 +140,9 @@ export default (sequelize: Sequelize) => {
|
|||||||
scope: { requestType: 'relocation' },
|
scope: { requestType: 'relocation' },
|
||||||
constraints: false
|
constraints: false
|
||||||
});
|
});
|
||||||
RelocationRequest.hasMany(models.Document, {
|
RelocationRequest.hasMany(models.RelocationDocument, {
|
||||||
foreignKey: 'requestId',
|
foreignKey: 'relocationId',
|
||||||
as: 'uploadedDocuments',
|
as: 'uploadedDocuments'
|
||||||
scope: { requestType: 'relocation' },
|
|
||||||
constraints: false
|
|
||||||
});
|
});
|
||||||
RelocationRequest.hasOne(models.EorChecklist, {
|
RelocationRequest.hasOne(models.EorChecklist, {
|
||||||
foreignKey: 'relocationId',
|
foreignKey: 'relocationId',
|
||||||
|
|||||||
@ -134,6 +134,10 @@ export default (sequelize: Sequelize) => {
|
|||||||
foreignKey: 'dealerId',
|
foreignKey: 'dealerId',
|
||||||
as: 'dealer'
|
as: 'dealer'
|
||||||
});
|
});
|
||||||
|
Resignation.hasMany(models.ResignationDocument, {
|
||||||
|
foreignKey: 'resignationId',
|
||||||
|
as: 'uploadedDocuments'
|
||||||
|
});
|
||||||
Resignation.hasMany(models.Worknote, {
|
Resignation.hasMany(models.Worknote, {
|
||||||
foreignKey: 'requestId',
|
foreignKey: 'requestId',
|
||||||
as: 'worknotes',
|
as: 'worknotes',
|
||||||
|
|||||||
93
src/database/models/ResignationDocument.ts
Normal file
93
src/database/models/ResignationDocument.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { Model, DataTypes, Sequelize } from 'sequelize';
|
||||||
|
|
||||||
|
export interface ResignationDocumentAttributes {
|
||||||
|
id: string;
|
||||||
|
resignationId: string;
|
||||||
|
documentType: string;
|
||||||
|
fileName: string;
|
||||||
|
filePath: string;
|
||||||
|
fileSize: number | null;
|
||||||
|
mimeType: string | null;
|
||||||
|
stage: string | null;
|
||||||
|
status: string;
|
||||||
|
uploadedBy: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResignationDocumentInstance extends Model<ResignationDocumentAttributes>, ResignationDocumentAttributes { }
|
||||||
|
|
||||||
|
export default (sequelize: Sequelize) => {
|
||||||
|
const ResignationDocument = sequelize.define<ResignationDocumentInstance>('ResignationDocument', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
resignationId: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: false,
|
||||||
|
references: {
|
||||||
|
model: 'resignations',
|
||||||
|
key: 'id'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
documentType: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
fileName: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
filePath: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
fileSize: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
mimeType: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
stage: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
defaultValue: 'active'
|
||||||
|
},
|
||||||
|
uploadedBy: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: true,
|
||||||
|
references: {
|
||||||
|
model: 'users',
|
||||||
|
key: 'id'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
tableName: 'resignation_documents',
|
||||||
|
timestamps: true,
|
||||||
|
indexes: [
|
||||||
|
{ fields: ['resignationId'] },
|
||||||
|
{ fields: ['status'] }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
(ResignationDocument as any).associate = (models: any) => {
|
||||||
|
ResignationDocument.belongsTo(models.User, { foreignKey: 'uploadedBy', as: 'uploader' });
|
||||||
|
ResignationDocument.belongsTo(models.Resignation, { foreignKey: 'resignationId', as: 'resignation' });
|
||||||
|
|
||||||
|
ResignationDocument.hasMany(models.DocumentVersion, { foreignKey: 'documentId', as: 'versions', constraints: false });
|
||||||
|
ResignationDocument.belongsToMany(models.Worknote, {
|
||||||
|
through: models.WorkNoteAttachment,
|
||||||
|
foreignKey: 'documentId',
|
||||||
|
otherKey: 'noteId',
|
||||||
|
as: 'worknotes',
|
||||||
|
constraints: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return ResignationDocument;
|
||||||
|
};
|
||||||
@ -41,7 +41,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
references: {
|
references: {
|
||||||
model: 'documents',
|
model: 'onboarding_documents',
|
||||||
key: 'id'
|
key: 'id'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -73,7 +73,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
|
|
||||||
(SecurityDeposit as any).associate = (models: any) => {
|
(SecurityDeposit as any).associate = (models: any) => {
|
||||||
SecurityDeposit.belongsTo(models.Application, { foreignKey: 'applicationId', as: 'application' });
|
SecurityDeposit.belongsTo(models.Application, { foreignKey: 'applicationId', as: 'application' });
|
||||||
SecurityDeposit.belongsTo(models.Document, { foreignKey: 'proofDocumentId', as: 'proofDocument' });
|
SecurityDeposit.belongsTo(models.OnboardingDocument, { foreignKey: 'proofDocumentId', as: 'proofDocument' });
|
||||||
SecurityDeposit.belongsTo(models.User, { foreignKey: 'verifiedBy', as: 'verifier' });
|
SecurityDeposit.belongsTo(models.User, { foreignKey: 'verifiedBy', as: 'verifier' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
93
src/database/models/TerminationDocument.ts
Normal file
93
src/database/models/TerminationDocument.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { Model, DataTypes, Sequelize } from 'sequelize';
|
||||||
|
|
||||||
|
export interface TerminationDocumentAttributes {
|
||||||
|
id: string;
|
||||||
|
terminationRequestId: string;
|
||||||
|
documentType: string;
|
||||||
|
fileName: string;
|
||||||
|
filePath: string;
|
||||||
|
fileSize: number | null;
|
||||||
|
mimeType: string | null;
|
||||||
|
stage: string | null;
|
||||||
|
status: string;
|
||||||
|
uploadedBy: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TerminationDocumentInstance extends Model<TerminationDocumentAttributes>, TerminationDocumentAttributes { }
|
||||||
|
|
||||||
|
export default (sequelize: Sequelize) => {
|
||||||
|
const TerminationDocument = sequelize.define<TerminationDocumentInstance>('TerminationDocument', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
terminationRequestId: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: false,
|
||||||
|
references: {
|
||||||
|
model: 'termination_requests',
|
||||||
|
key: 'id'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
documentType: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
fileName: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
filePath: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
fileSize: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
mimeType: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
stage: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
defaultValue: 'active'
|
||||||
|
},
|
||||||
|
uploadedBy: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: true,
|
||||||
|
references: {
|
||||||
|
model: 'users',
|
||||||
|
key: 'id'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
tableName: 'termination_documents',
|
||||||
|
timestamps: true,
|
||||||
|
indexes: [
|
||||||
|
{ fields: ['terminationRequestId'] },
|
||||||
|
{ fields: ['status'] }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
(TerminationDocument as any).associate = (models: any) => {
|
||||||
|
TerminationDocument.belongsTo(models.User, { foreignKey: 'uploadedBy', as: 'uploader' });
|
||||||
|
TerminationDocument.belongsTo(models.TerminationRequest, { foreignKey: 'terminationRequestId', as: 'terminationRequest' });
|
||||||
|
|
||||||
|
TerminationDocument.hasMany(models.DocumentVersion, { foreignKey: 'documentId', as: 'versions', constraints: false });
|
||||||
|
TerminationDocument.belongsToMany(models.Worknote, {
|
||||||
|
through: models.WorkNoteAttachment,
|
||||||
|
foreignKey: 'documentId',
|
||||||
|
otherKey: 'noteId',
|
||||||
|
as: 'worknotes',
|
||||||
|
constraints: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return TerminationDocument;
|
||||||
|
};
|
||||||
@ -80,6 +80,16 @@ export default (sequelize: Sequelize) => {
|
|||||||
TerminationRequest.belongsTo(models.Dealer, { foreignKey: 'dealerId', as: 'dealer' });
|
TerminationRequest.belongsTo(models.Dealer, { foreignKey: 'dealerId', as: 'dealer' });
|
||||||
TerminationRequest.belongsTo(models.User, { foreignKey: 'initiatedBy', as: 'initiator' });
|
TerminationRequest.belongsTo(models.User, { foreignKey: 'initiatedBy', as: 'initiator' });
|
||||||
TerminationRequest.hasOne(models.FnF, { foreignKey: 'terminationRequestId', as: 'fnfSettlement' });
|
TerminationRequest.hasOne(models.FnF, { foreignKey: 'terminationRequestId', as: 'fnfSettlement' });
|
||||||
|
TerminationRequest.hasMany(models.TerminationDocument, {
|
||||||
|
foreignKey: 'terminationRequestId',
|
||||||
|
as: 'uploadedDocuments'
|
||||||
|
});
|
||||||
|
TerminationRequest.hasMany(models.Worknote, {
|
||||||
|
foreignKey: 'requestId',
|
||||||
|
as: 'worknotes',
|
||||||
|
scope: { requestType: 'termination' },
|
||||||
|
constraints: false
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return TerminationRequest;
|
return TerminationRequest;
|
||||||
|
|||||||
@ -4,6 +4,7 @@ export interface WorkNoteAttachmentAttributes {
|
|||||||
id: string;
|
id: string;
|
||||||
noteId: string;
|
noteId: string;
|
||||||
documentId: string;
|
documentId: string;
|
||||||
|
documentType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkNoteAttachmentInstance extends Model<WorkNoteAttachmentAttributes>, WorkNoteAttachmentAttributes { }
|
export interface WorkNoteAttachmentInstance extends Model<WorkNoteAttachmentAttributes>, WorkNoteAttachmentAttributes { }
|
||||||
@ -25,11 +26,11 @@ export default (sequelize: Sequelize) => {
|
|||||||
},
|
},
|
||||||
documentId: {
|
documentId: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: false
|
||||||
references: {
|
},
|
||||||
model: 'documents',
|
documentType: {
|
||||||
key: 'id'
|
type: DataTypes.STRING,
|
||||||
}
|
allowNull: false
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
tableName: 'work_note_attachments',
|
tableName: 'work_note_attachments',
|
||||||
@ -39,7 +40,6 @@ export default (sequelize: Sequelize) => {
|
|||||||
|
|
||||||
(WorkNoteAttachment as any).associate = (models: any) => {
|
(WorkNoteAttachment as any).associate = (models: any) => {
|
||||||
WorkNoteAttachment.belongsTo(models.Worknote, { foreignKey: 'noteId', as: 'workNote' });
|
WorkNoteAttachment.belongsTo(models.Worknote, { foreignKey: 'noteId', as: 'workNote' });
|
||||||
WorkNoteAttachment.belongsTo(models.Document, { foreignKey: 'documentId', as: 'document' });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return WorkNoteAttachment;
|
return WorkNoteAttachment;
|
||||||
|
|||||||
@ -71,12 +71,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
Worknote.belongsTo(models.Application, { foreignKey: 'applicationId', as: 'application' });
|
Worknote.belongsTo(models.Application, { foreignKey: 'applicationId', as: 'application' });
|
||||||
|
|
||||||
Worknote.hasMany(models.WorkNoteTag, { foreignKey: 'noteId', as: 'tags' });
|
Worknote.hasMany(models.WorkNoteTag, { foreignKey: 'noteId', as: 'tags' });
|
||||||
Worknote.belongsToMany(models.Document, {
|
Worknote.hasMany(models.WorkNoteAttachment, { foreignKey: 'noteId', as: 'attachments' });
|
||||||
through: models.WorkNoteAttachment,
|
|
||||||
foreignKey: 'noteId',
|
|
||||||
otherKey: 'documentId',
|
|
||||||
as: 'attachments'
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return Worknote;
|
return Worknote;
|
||||||
|
|||||||
@ -9,9 +9,13 @@ import createConstitutionalChange from './ConstitutionalChange.js';
|
|||||||
import createRelocationRequest from './RelocationRequest.js';
|
import createRelocationRequest from './RelocationRequest.js';
|
||||||
import createOutlet from './Outlet.js';
|
import createOutlet from './Outlet.js';
|
||||||
import createWorknote from './Worknote.js';
|
import createWorknote from './Worknote.js';
|
||||||
import createDocument from './Document.js';
|
import createOnboardingDocument from './OnboardingDocument.js';
|
||||||
import createAuditLog from './AuditLog.js';
|
import createAuditLog from './AuditLog.js';
|
||||||
import createFinancePayment from './FinancePayment.js';
|
import createFinancePayment from './FinancePayment.js';
|
||||||
|
import createRelocationDocument from './RelocationDocument.js';
|
||||||
|
import createResignationDocument from './ResignationDocument.js';
|
||||||
|
import createConstitutionalDocument from './ConstitutionalDocument.js';
|
||||||
|
import createTerminationDocument from './TerminationDocument.js';
|
||||||
import createFnF from './FnF.js';
|
import createFnF from './FnF.js';
|
||||||
import createFnFLineItem from './FnFLineItem.js';
|
import createFnFLineItem from './FnFLineItem.js';
|
||||||
import createSLAConfiguration from './SLAConfiguration.js';
|
import createSLAConfiguration from './SLAConfiguration.js';
|
||||||
@ -115,9 +119,13 @@ db.ConstitutionalChange = createConstitutionalChange(sequelize);
|
|||||||
db.RelocationRequest = createRelocationRequest(sequelize);
|
db.RelocationRequest = createRelocationRequest(sequelize);
|
||||||
db.Outlet = createOutlet(sequelize);
|
db.Outlet = createOutlet(sequelize);
|
||||||
db.Worknote = createWorknote(sequelize);
|
db.Worknote = createWorknote(sequelize);
|
||||||
db.Document = createDocument(sequelize);
|
db.OnboardingDocument = createOnboardingDocument(sequelize);
|
||||||
db.AuditLog = createAuditLog(sequelize);
|
db.AuditLog = createAuditLog(sequelize);
|
||||||
db.FinancePayment = createFinancePayment(sequelize);
|
db.FinancePayment = createFinancePayment(sequelize);
|
||||||
|
db.RelocationDocument = createRelocationDocument(sequelize);
|
||||||
|
db.ResignationDocument = createResignationDocument(sequelize);
|
||||||
|
db.ConstitutionalDocument = createConstitutionalDocument(sequelize);
|
||||||
|
db.TerminationDocument = createTerminationDocument(sequelize);
|
||||||
db.FnF = createFnF(sequelize);
|
db.FnF = createFnF(sequelize);
|
||||||
db.FnFLineItem = createFnFLineItem(sequelize);
|
db.FnFLineItem = createFnFLineItem(sequelize);
|
||||||
db.SLAConfiguration = createSLAConfiguration(sequelize);
|
db.SLAConfiguration = createSLAConfiguration(sequelize);
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import db from '../../database/models/index.js';
|
import db from '../../database/models/index.js';
|
||||||
const { Worknote, User, WorkNoteTag, WorkNoteAttachment, Document, DocumentVersion, RequestParticipant, Application, AuditLog } = db;
|
const {
|
||||||
|
Worknote, User, WorkNoteTag, WorkNoteAttachment, DocumentVersion, RequestParticipant, Application, AuditLog,
|
||||||
|
OnboardingDocument, RelocationDocument, ResignationDocument, ConstitutionalDocument, TerminationDocument
|
||||||
|
} = db;
|
||||||
import { AuthRequest } from '../../types/express.types.js';
|
import { AuthRequest } from '../../types/express.types.js';
|
||||||
import { AUDIT_ACTIONS } from '../../common/config/constants.js';
|
import { AUDIT_ACTIONS } from '../../common/config/constants.js';
|
||||||
import * as EmailService from '../../common/utils/email.service.js';
|
import * as EmailService from '../../common/utils/email.service.js';
|
||||||
@ -8,6 +11,59 @@ import { getIO } from '../../common/utils/socket.js';
|
|||||||
import * as NotificationService from '../../common/utils/notification.service.js';
|
import * as NotificationService from '../../common/utils/notification.service.js';
|
||||||
import logger from '../../common/utils/logger.js';
|
import logger from '../../common/utils/logger.js';
|
||||||
|
|
||||||
|
// --- Helpers ---
|
||||||
|
const getDocumentModel = (requestType: string) => {
|
||||||
|
switch (requestType?.toLowerCase()) {
|
||||||
|
case 'relocation': return RelocationDocument;
|
||||||
|
case 'resignation': return ResignationDocument;
|
||||||
|
case 'constitutional': return ConstitutionalDocument;
|
||||||
|
case 'termination': return TerminationDocument;
|
||||||
|
case 'onboarding':
|
||||||
|
case 'application': return OnboardingDocument;
|
||||||
|
default: return OnboardingDocument;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const stitchWorknoteAttachments = async (worknotes: any[]) => {
|
||||||
|
const notePromises = worknotes.map(async (note: any) => {
|
||||||
|
const noteObj = note.toJSON ? note.toJSON() : note;
|
||||||
|
if (noteObj.attachments && noteObj.attachments.length > 0) {
|
||||||
|
// Group by documentType for batch fetching
|
||||||
|
const typeGroups: Record<string, string[]> = {};
|
||||||
|
noteObj.attachments.forEach((att: any) => {
|
||||||
|
const type = att.documentType || 'onboarding';
|
||||||
|
if (!typeGroups[type]) typeGroups[type] = [];
|
||||||
|
typeGroups[type].push(att.documentId);
|
||||||
|
});
|
||||||
|
|
||||||
|
const attachmentsWithFiles = [];
|
||||||
|
for (const [type, ids] of Object.entries(typeGroups)) {
|
||||||
|
const DocModel = getDocumentModel(type);
|
||||||
|
if (DocModel) {
|
||||||
|
const docs = await (DocModel as any).findAll({ where: { id: ids } });
|
||||||
|
const docsByid = new Map(docs.map((d: any) => [d.id, d.toJSON()]));
|
||||||
|
attachmentsWithFiles.push(...noteObj.attachments
|
||||||
|
.filter((att: any) => (att.documentType || 'onboarding') === type)
|
||||||
|
.map((att: any) => {
|
||||||
|
const doc = docsByid.get(att.documentId) as any;
|
||||||
|
return {
|
||||||
|
...att,
|
||||||
|
fileName: doc?.fileName || 'Unknown',
|
||||||
|
filePath: doc?.filePath || '',
|
||||||
|
mimeType: doc?.mimeType || 'application/octet-stream',
|
||||||
|
fileSize: doc?.fileSize
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
noteObj.attachments = attachmentsWithFiles;
|
||||||
|
}
|
||||||
|
return noteObj;
|
||||||
|
});
|
||||||
|
return Promise.all(notePromises);
|
||||||
|
};
|
||||||
|
|
||||||
// --- Worknotes ---
|
// --- Worknotes ---
|
||||||
|
|
||||||
export const addWorknote = async (req: AuthRequest, res: Response) => {
|
export const addWorknote = async (req: AuthRequest, res: Response) => {
|
||||||
@ -40,7 +96,11 @@ export const addWorknote = async (req: AuthRequest, res: Response) => {
|
|||||||
|
|
||||||
if (attachmentDocIds && attachmentDocIds.length > 0) {
|
if (attachmentDocIds && attachmentDocIds.length > 0) {
|
||||||
for (const docId of attachmentDocIds) {
|
for (const docId of attachmentDocIds) {
|
||||||
await WorkNoteAttachment.create({ noteId: worknote.id, documentId: docId });
|
await WorkNoteAttachment.create({
|
||||||
|
noteId: worknote.id,
|
||||||
|
documentId: docId,
|
||||||
|
documentType: requestType || 'onboarding'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,14 +124,16 @@ export const addWorknote = async (req: AuthRequest, res: Response) => {
|
|||||||
include: [
|
include: [
|
||||||
{ model: User, as: 'author', attributes: [['fullName', 'name'], 'email', ['roleCode', 'role']] },
|
{ model: User, as: 'author', attributes: [['fullName', 'name'], 'email', ['roleCode', 'role']] },
|
||||||
{ model: WorkNoteTag, as: 'tags' },
|
{ model: WorkNoteTag, as: 'tags' },
|
||||||
{ model: Document, as: 'attachments' }
|
{ model: WorkNoteAttachment, as: 'attachments' }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [stitchedNote] = await stitchWorknoteAttachments([fullWorknote]);
|
||||||
|
|
||||||
// --- Real-time & Notifications ---
|
// --- Real-time & Notifications ---
|
||||||
try {
|
try {
|
||||||
const io = getIO();
|
const io = getIO();
|
||||||
io.to(requestId).emit('new_worknote', fullWorknote);
|
io.to(requestId).emit('new_worknote', stitchedNote);
|
||||||
|
|
||||||
// Handle Mentions/Notifications
|
// Handle Mentions/Notifications
|
||||||
const notifiedUserIds = new Set<string>();
|
const notifiedUserIds = new Set<string>();
|
||||||
@ -131,7 +193,7 @@ export const addWorknote = async (req: AuthRequest, res: Response) => {
|
|||||||
newData: { noteType: noteType || 'General', hasAttachments: !!(attachmentDocIds?.length) }
|
newData: { noteType: noteType || 'General', hasAttachments: !!(attachmentDocIds?.length) }
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(201).json({ success: true, message: 'Worknote added', data: fullWorknote });
|
res.status(201).json({ success: true, message: 'Worknote added', data: stitchedNote });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Add worknote error:', error);
|
console.error('Add worknote error:', error);
|
||||||
res.status(500).json({ success: false, message: 'Error adding worknote' });
|
res.status(500).json({ success: false, message: 'Error adding worknote' });
|
||||||
@ -147,12 +209,13 @@ export const getWorknotes = async (req: AuthRequest, res: Response) => {
|
|||||||
include: [
|
include: [
|
||||||
{ model: User, as: 'author', attributes: [['fullName', 'name'], 'email', ['roleCode', 'role']] },
|
{ model: User, as: 'author', attributes: [['fullName', 'name'], 'email', ['roleCode', 'role']] },
|
||||||
{ model: WorkNoteTag, as: 'tags' },
|
{ model: WorkNoteTag, as: 'tags' },
|
||||||
{ model: Document, as: 'attachments' }
|
{ model: WorkNoteAttachment, as: 'attachments' }
|
||||||
],
|
],
|
||||||
order: [['createdAt', 'DESC']]
|
order: [['createdAt', 'DESC']]
|
||||||
});
|
});
|
||||||
|
|
||||||
res.json({ success: true, data: worknotes });
|
const finalWorknotes = await stitchWorknoteAttachments(worknotes);
|
||||||
|
res.json({ success: true, data: finalWorknotes });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: 'Error fetching worknotes' });
|
res.status(500).json({ success: false, message: 'Error fetching worknotes' });
|
||||||
}
|
}
|
||||||
@ -167,9 +230,9 @@ export const uploadWorknoteAttachment = async (req: any, res: Response) => {
|
|||||||
return res.status(400).json({ success: false, message: 'No file uploaded' });
|
return res.status(400).json({ success: false, message: 'No file uploaded' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const document = await Document.create({
|
const DocModel = getDocumentModel(requestType);
|
||||||
requestId: requestId || null,
|
|
||||||
requestType: requestType || null,
|
let createData: any = {
|
||||||
documentType: 'Worknote Attachment',
|
documentType: 'Worknote Attachment',
|
||||||
fileName: file.originalname,
|
fileName: file.originalname,
|
||||||
filePath: file.path,
|
filePath: file.path,
|
||||||
@ -177,11 +240,21 @@ export const uploadWorknoteAttachment = async (req: any, res: Response) => {
|
|||||||
fileSize: file.size,
|
fileSize: file.size,
|
||||||
uploadedBy: req.user?.id,
|
uploadedBy: req.user?.id,
|
||||||
status: 'active'
|
status: 'active'
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// Assign correct FK based on model
|
||||||
|
if (DocModel === RelocationDocument) createData.relocationId = requestId;
|
||||||
|
else if (DocModel === ResignationDocument) createData.resignationId = requestId;
|
||||||
|
else if (DocModel === ConstitutionalDocument) createData.constitutionalChangeId = requestId;
|
||||||
|
else if (DocModel === TerminationDocument) createData.terminationRequestId = requestId;
|
||||||
|
else createData.applicationId = requestId;
|
||||||
|
|
||||||
|
const document = await DocModel.create(createData);
|
||||||
|
|
||||||
// Create initial version
|
// Create initial version
|
||||||
await DocumentVersion.create({
|
await DocumentVersion.create({
|
||||||
documentId: document.id,
|
documentId: document.id,
|
||||||
|
documentType: requestType || 'onboarding',
|
||||||
versionNumber: 1,
|
versionNumber: 1,
|
||||||
filePath: file.path,
|
filePath: file.path,
|
||||||
uploadedBy: req.user?.id,
|
uploadedBy: req.user?.id,
|
||||||
@ -221,10 +294,10 @@ export const uploadDocument = async (req: AuthRequest, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
const { applicationId, dealerId, docType, fileName, fileUrl, mimeType } = req.body;
|
const { applicationId, dealerId, docType, fileName, fileUrl, mimeType } = req.body;
|
||||||
|
|
||||||
const document = await Document.create({
|
const document = await OnboardingDocument.create({
|
||||||
applicationId,
|
applicationId,
|
||||||
dealerId,
|
dealerId,
|
||||||
docType,
|
documentType: docType,
|
||||||
fileName,
|
fileName,
|
||||||
filePath: fileUrl, // Assuming URL from cloud storage
|
filePath: fileUrl, // Assuming URL from cloud storage
|
||||||
mimeType,
|
mimeType,
|
||||||
@ -275,7 +348,8 @@ export const uploadNewVersion = async (req: AuthRequest, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Update main document pointer if needed (usually main doc points to latest or metadata)
|
// Update main document pointer if needed (usually main doc points to latest or metadata)
|
||||||
await Document.update({ filePath: fileUrl }, { where: { id: documentId } });
|
// For simplicity assuming onboarding if not specified
|
||||||
|
await OnboardingDocument.update({ filePath: fileUrl }, { where: { id: documentId } });
|
||||||
|
|
||||||
res.status(201).json({ success: true, message: 'New version uploaded' });
|
res.status(201).json({ success: true, message: 'New version uploaded' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import db from '../../database/models/index.js';
|
import db from '../../database/models/index.js';
|
||||||
const { EorChecklist, EorChecklistItem, Document } = db;
|
const { EorChecklist, EorChecklistItem, OnboardingDocument, RelocationDocument } = db;
|
||||||
import { AuthRequest } from '../../types/express.types.js';
|
import { AuthRequest } from '../../types/express.types.js';
|
||||||
|
|
||||||
export const getChecklist = async (req: Request, res: Response) => {
|
export const getChecklist = async (req: Request, res: Response) => {
|
||||||
@ -8,7 +8,8 @@ export const getChecklist = async (req: Request, res: Response) => {
|
|||||||
const { applicationId, relocationId } = req.params;
|
const { applicationId, relocationId } = req.params;
|
||||||
let checklist = await EorChecklist.findOne({
|
let checklist = await EorChecklist.findOne({
|
||||||
where: relocationId ? { relocationId } : { applicationId },
|
where: relocationId ? { relocationId } : { applicationId },
|
||||||
include: [{ model: EorChecklistItem, as: 'items', include: ['proofDocument'] }]
|
// proofDocument is now polymorphic, would need manual stitch or sub-selects
|
||||||
|
include: [{ model: EorChecklistItem, as: 'items' }]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!checklist) {
|
if (!checklist) {
|
||||||
@ -16,6 +17,27 @@ export const getChecklist = async (req: Request, res: Response) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const items = checklist.items || [];
|
||||||
|
const proofDocIds = items.map((i: any) => i.proofDocumentId).filter(Boolean);
|
||||||
|
|
||||||
|
if (proofDocIds.length > 0) {
|
||||||
|
// Find documents from the relevant table
|
||||||
|
let docs = [];
|
||||||
|
if (relocationId) {
|
||||||
|
docs = await RelocationDocument.findAll({ where: { id: proofDocIds } });
|
||||||
|
} else {
|
||||||
|
docs = await OnboardingDocument.findAll({ where: { id: proofDocIds } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map docs to items
|
||||||
|
const docsMap = new Map(docs.map((d: any) => [d.id, d]));
|
||||||
|
checklist = checklist.toJSON();
|
||||||
|
checklist.items = checklist.items.map((item: any) => ({
|
||||||
|
...item,
|
||||||
|
proofDocument: docsMap.get(item.proofDocumentId) || null
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
res.json({ success: true, data: checklist });
|
res.json({ success: true, data: checklist });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Get EOR checklist error:', error);
|
console.error('Get EOR checklist error:', error);
|
||||||
|
|||||||
@ -139,8 +139,8 @@ export const approveRequest = async (req: AuthRequest, res: Response) => {
|
|||||||
// MANDATORY DOCUMENT CHECK (SRS Requirement)
|
// MANDATORY DOCUMENT CHECK (SRS Requirement)
|
||||||
// Level 2+ requires minimum set of documents uploaded by applicant
|
// Level 2+ requires minimum set of documents uploaded by applicant
|
||||||
if (currentApproval.level === 1 && action === 'Approved') {
|
if (currentApproval.level === 1 && action === 'Approved') {
|
||||||
const docCount = await db.Document.count({
|
const docCount = await db.OnboardingDocument.count({
|
||||||
where: { requestId: request.applicationId, requestType: 'application' }
|
where: { applicationId: request.applicationId }
|
||||||
});
|
});
|
||||||
if (docCount < 5) { // SRS requires 18, using 5 for functional demo
|
if (docCount < 5) { // SRS requires 18, using 5 for functional demo
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
|
|||||||
@ -136,6 +136,16 @@ export const getApplications = async (req: AuthRequest, res: Response) => {
|
|||||||
whereClause.email = req.user.email;
|
whereClause.email = req.user.email;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Security Check: If FDD user, only show applications where they are a participant
|
||||||
|
if (req.user?.roleCode === 'FDD') {
|
||||||
|
const participantApps = await db.RequestParticipant.findAll({
|
||||||
|
where: { userId: req.user.id, requestType: 'application' },
|
||||||
|
attributes: ['requestId']
|
||||||
|
});
|
||||||
|
const appIds = participantApps.map((p: any) => p.requestId);
|
||||||
|
whereClause.id = { [Op.in]: appIds };
|
||||||
|
}
|
||||||
|
|
||||||
const applications = await Application.findAll({
|
const applications = await Application.findAll({
|
||||||
where: whereClause,
|
where: whereClause,
|
||||||
include: [
|
include: [
|
||||||
@ -191,9 +201,10 @@ export const getApplicationById = async (req: AuthRequest, res: Response) => {
|
|||||||
include: [{ model: db.User, as: 'user', attributes: ['id', ['fullName', 'name'], 'email', ['roleCode', 'role']] }]
|
include: [{ model: db.User, as: 'user', attributes: ['id', ['fullName', 'name'], 'email', ['roleCode', 'role']] }]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
model: db.Document,
|
model: db.OnboardingDocument,
|
||||||
as: 'uploadedDocuments',
|
as: 'uploadedDocuments',
|
||||||
separate: true,
|
separate: true,
|
||||||
|
include: [{ model: db.User, as: 'uploader', attributes: ['fullName', 'roleCode'] }],
|
||||||
order: [['createdAt', 'DESC']]
|
order: [['createdAt', 'DESC']]
|
||||||
},
|
},
|
||||||
{ model: db.StageApprovalAction, as: 'stageApprovals', separate: true },
|
{ model: db.StageApprovalAction, as: 'stageApprovals', separate: true },
|
||||||
@ -206,6 +217,41 @@ export const getApplicationById = async (req: AuthRequest, res: Response) => {
|
|||||||
return res.status(404).json({ success: false, message: 'Application not found' });
|
return res.status(404).json({ success: false, message: 'Application not found' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Security Check for FDD: If user is FDD, only return restricted data
|
||||||
|
if (req.user?.roleCode === 'FDD') {
|
||||||
|
const isParticipant = await db.RequestParticipant.findOne({
|
||||||
|
where: { requestId: application.id, userId: req.user.id, requestType: 'application' }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isParticipant) {
|
||||||
|
return res.status(403).json({ success: false, message: 'Access denied. You are not assigned to this application.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip sensitive internal data for FDD
|
||||||
|
const restrictedData = application.toJSON();
|
||||||
|
delete (restrictedData as any).questionnaireResponses;
|
||||||
|
delete (restrictedData as any).stageApprovals;
|
||||||
|
delete (restrictedData as any).score;
|
||||||
|
|
||||||
|
// FDD should only see relevant documents for security
|
||||||
|
// FDD should only see relevant documents for security
|
||||||
|
// OR documents they uploaded themselves
|
||||||
|
const fddRelevantDocs = [
|
||||||
|
'GST Certificate', 'PAN Card', 'Bank Statement', 'Cancelled Check',
|
||||||
|
'Partnership Deed', 'LLP Agreement', 'Certificate of Incorporation', 'MOA', 'AOA',
|
||||||
|
'Property Documents', 'Rental Agreement', 'Firm Registration', 'CIBIL Report',
|
||||||
|
'FDD Final Audit Report', 'FDD Audit Report'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (restrictedData.uploadedDocuments) {
|
||||||
|
restrictedData.uploadedDocuments = (restrictedData.uploadedDocuments as any[]).filter(
|
||||||
|
(doc: any) => fddRelevantDocs.includes(doc.documentType) || (req.user && doc.uploadedBy === req.user.id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json({ success: true, data: restrictedData });
|
||||||
|
}
|
||||||
|
|
||||||
// Security Check: Ensure prospective dealer controls data ownership
|
// Security Check: Ensure prospective dealer controls data ownership
|
||||||
if (req.user?.roleCode === 'Prospective Dealer' && application.email !== req.user.email) {
|
if (req.user?.roleCode === 'Prospective Dealer' && application.email !== req.user.email) {
|
||||||
return res.status(403).json({ success: false, message: 'Forbidden: You do not have permission to view this application' });
|
return res.status(403).json({ success: false, message: 'Forbidden: You do not have permission to view this application' });
|
||||||
@ -268,17 +314,14 @@ export const uploadDocuments = async (req: any, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create Document Record
|
// Create Document Record
|
||||||
const newDoc = await db.Document.create({
|
const newDoc = await db.OnboardingDocument.create({
|
||||||
applicationId: application.id,
|
applicationId: application.id,
|
||||||
requestId: application.id,
|
|
||||||
requestType: 'application',
|
|
||||||
documentType,
|
documentType,
|
||||||
stage: stage || null,
|
stage: stage || null,
|
||||||
fileName: file.originalname,
|
fileName: file.originalname,
|
||||||
filePath: file.path, // Store relative path or full path as needed by your storage strategy
|
filePath: file.path,
|
||||||
fileSize: file.size,
|
fileSize: file.size,
|
||||||
mimeType: file.mimetype,
|
mimeType: file.mimetype,
|
||||||
// For prospective users (who are applications, not in Users table), set uploadedBy to null to avoid FK violation
|
|
||||||
uploadedBy: req.user?.roleCode === 'Prospective Dealer' ? null : req.user?.id,
|
uploadedBy: req.user?.roleCode === 'Prospective Dealer' ? null : req.user?.id,
|
||||||
status: 'active'
|
status: 'active'
|
||||||
});
|
});
|
||||||
@ -360,10 +403,9 @@ export const getApplicationDocuments = async (req: AuthRequest, res: Response) =
|
|||||||
return res.status(404).json({ success: false, message: 'Application not found' });
|
return res.status(404).json({ success: false, message: 'Application not found' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const documents = await db.Document.findAll({
|
const documents = await db.OnboardingDocument.findAll({
|
||||||
where: {
|
where: {
|
||||||
requestId: application.id,
|
applicationId: application.id,
|
||||||
requestType: 'application',
|
|
||||||
status: 'active'
|
status: 'active'
|
||||||
},
|
},
|
||||||
include: [
|
include: [
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import db from '../../database/models/index.js';
|
import db from '../../database/models/index.js';
|
||||||
import { RelocationWorkflowService } from '../../services/RelocationWorkflowService.js';
|
import { RelocationWorkflowService } from '../../services/RelocationWorkflowService.js';
|
||||||
const { RelocationRequest, Outlet, User, Worknote, District, Region, Zone, Document } = db;
|
const { RelocationRequest, Outlet, User, Worknote, District, Region, Zone, RelocationDocument } = db;
|
||||||
import { AUDIT_ACTIONS, ROLES, RELOCATION_STAGES } from '../../common/config/constants.js';
|
import { AUDIT_ACTIONS, ROLES, RELOCATION_STAGES } from '../../common/config/constants.js';
|
||||||
import { Op, Transaction } from 'sequelize';
|
import { Op, Transaction } from 'sequelize';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
@ -236,7 +236,7 @@ export const getRequests = async (req: AuthRequest, res: Response) => {
|
|||||||
const userRoleCode = req.user?.roleCode;
|
const userRoleCode = req.user?.roleCode;
|
||||||
|
|
||||||
// National roles see all requests
|
// National roles see all requests
|
||||||
const nationalRoles = ['NBH', 'DD Lead', 'DD Head', 'Legal Admin'];
|
const nationalRoles = ['NBH', 'DD Lead', 'DD Head', 'Legal Admin', 'Super Admin', 'DD Admin'];
|
||||||
if (userRoleCode && nationalRoles.includes(userRoleCode)) {
|
if (userRoleCode && nationalRoles.includes(userRoleCode)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -426,6 +426,7 @@ export const takeAction = async (req: AuthRequest, res: Response) => {
|
|||||||
|
|
||||||
const stageFlow: Record<string, string> = {
|
const stageFlow: Record<string, string> = {
|
||||||
[RELOCATION_STAGES.ASM_REVIEW]: RELOCATION_STAGES.RBM_REVIEW,
|
[RELOCATION_STAGES.ASM_REVIEW]: RELOCATION_STAGES.RBM_REVIEW,
|
||||||
|
'DD Admin Review': RELOCATION_STAGES.RBM_REVIEW, // Legacy support
|
||||||
[RELOCATION_STAGES.RBM_REVIEW]: RELOCATION_STAGES.DD_ZM_REVIEW,
|
[RELOCATION_STAGES.RBM_REVIEW]: RELOCATION_STAGES.DD_ZM_REVIEW,
|
||||||
[RELOCATION_STAGES.DD_ZM_REVIEW]: RELOCATION_STAGES.ZBH_REVIEW,
|
[RELOCATION_STAGES.DD_ZM_REVIEW]: RELOCATION_STAGES.ZBH_REVIEW,
|
||||||
[RELOCATION_STAGES.ZBH_REVIEW]: RELOCATION_STAGES.DD_LEAD_REVIEW,
|
[RELOCATION_STAGES.ZBH_REVIEW]: RELOCATION_STAGES.DD_LEAD_REVIEW,
|
||||||
@ -526,9 +527,8 @@ export const uploadDocuments = async (req: AuthRequest, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create Document Record
|
// Create Document Record
|
||||||
const newDoc = await Document.create({
|
const newDoc = await RelocationDocument.create({
|
||||||
requestId: request.id,
|
relocationId: request.id,
|
||||||
requestType: 'relocation',
|
|
||||||
documentType,
|
documentType,
|
||||||
stage: stage || request.currentStage,
|
stage: stage || request.currentStage,
|
||||||
fileName: file.originalname,
|
fileName: file.originalname,
|
||||||
@ -596,7 +596,7 @@ export const verifyDocument = async (req: AuthRequest, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find and update the Document record
|
// Find and update the Document record
|
||||||
const docRecord = await Document.findByPk(documentId);
|
const docRecord = await RelocationDocument.findByPk(documentId);
|
||||||
if (docRecord) {
|
if (docRecord) {
|
||||||
await docRecord.update({ status: 'Verified' });
|
await docRecord.update({ status: 'Verified' });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -66,6 +66,7 @@ export class RelocationWorkflowService {
|
|||||||
|
|
||||||
const stageMapping: Record<string, string> = {
|
const stageMapping: Record<string, string> = {
|
||||||
[RELOCATION_STAGES.ASM_REVIEW]: ROLES.ASM,
|
[RELOCATION_STAGES.ASM_REVIEW]: ROLES.ASM,
|
||||||
|
'DD Admin Review': ROLES.ASM, // Legacy/alias mapping for older requests
|
||||||
[RELOCATION_STAGES.RBM_REVIEW]: ROLES.RBM,
|
[RELOCATION_STAGES.RBM_REVIEW]: ROLES.RBM,
|
||||||
[RELOCATION_STAGES.DD_ZM_REVIEW]: ROLES.DD_ZM,
|
[RELOCATION_STAGES.DD_ZM_REVIEW]: ROLES.DD_ZM,
|
||||||
[RELOCATION_STAGES.ZBH_REVIEW]: ROLES.ZBH,
|
[RELOCATION_STAGES.ZBH_REVIEW]: ROLES.ZBH,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user