UNPKG

adpa-enterprise-framework-automation

Version:

Modular, standards-compliant Node.js/TypeScript automation framework for enterprise requirements, project, and data management. Provides CLI and API for BABOK v3, PMBOK 7th Edition, and DMBOK 2.0 (in progress). Production-ready Express.js API with TypeSpe

280 lines 9.42 kB
/** * Scope Change Model * Database model for scope change requests and tracking * * @description Defines the data structure for scope changes with full * impact analysis and PMBOK compliance tracking * * @version 1.0.0 * @since 3.2.0 */ import mongoose, { Schema } from 'mongoose'; const ScopeImpactSchema = new Schema({ scheduleImpact: { days: { type: Number, required: true, min: 0 }, percentage: { type: Number, required: true, min: 0, max: 100 }, criticalPath: { type: Boolean, required: true } }, costImpact: { amount: { type: Number, required: true, min: 0 }, percentage: { type: Number, required: true, min: 0, max: 100 }, budgetCategory: { type: String, required: true, maxlength: 50 } }, resourceImpact: { additionalResources: [{ type: String, maxlength: 100 }], skillsRequired: [{ type: String, maxlength: 50 }], availabilityImpact: { type: Boolean, required: true } }, qualityImpact: { riskToQuality: { type: Boolean, required: true }, testingImpact: { type: Boolean, required: true }, acceptanceCriteriaChanges: { type: Boolean, required: true } }, stakeholderImpact: { affectedStakeholders: [{ type: String, maxlength: 100 }], communicationRequired: { type: Boolean, required: true }, approvalRequired: { type: Boolean, required: true } } }, { _id: false }); const ScopeChangeSchema = new Schema({ projectId: { type: Schema.Types.ObjectId, ref: 'Project', required: true, index: true }, changeType: { type: String, enum: ['addition', 'reduction', 'modification', 'clarification'], required: true }, description: { type: String, required: true, trim: true, minlength: 10, maxlength: 2000 }, requestedBy: { type: String, required: true, trim: true, maxlength: 100 }, requestDate: { type: Date, required: true, default: Date.now }, impact: { type: ScopeImpactSchema, required: true }, status: { type: String, enum: ['pending', 'approved', 'rejected', 'implemented', 'cancelled'], default: 'pending', required: true, index: true }, approvedBy: { type: String, trim: true, maxlength: 100 }, approvalDate: { type: Date }, implementationDate: { type: Date }, riskLevel: { type: String, enum: ['low', 'medium', 'high', 'critical'], required: true, index: true }, pmbokCompliance: { type: Boolean, required: true, default: false }, priority: { type: String, enum: ['low', 'medium', 'high', 'urgent'], default: 'medium' }, estimatedEffort: { type: Number, min: 0 }, actualEffort: { type: Number, min: 0 }, businessJustification: { type: String, maxlength: 1000 }, alternativesConsidered: [{ type: String, maxlength: 500 }], rollbackPlan: { type: String, maxlength: 1000 }, approvalWorkflow: { level1Approver: { type: String, maxlength: 100 }, level1ApprovalDate: { type: Date }, level2Approver: { type: String, maxlength: 100 }, level2ApprovalDate: { type: Date }, finalApprover: { type: String, maxlength: 100 }, finalApprovalDate: { type: Date } }, implementationPlan: { steps: [{ type: String, maxlength: 200 }], assignedTo: { type: String, maxlength: 100 }, estimatedDuration: { type: Number, min: 0 }, dependencies: [{ type: String, maxlength: 100 }], milestones: [{ name: { type: String, required: true, maxlength: 100 }, targetDate: { type: Date, required: true }, completed: { type: Boolean, default: false }, completedDate: { type: Date } }] }, impactScore: { type: Number, min: 0, max: 1, default: 0 }, complexityScore: { type: Number, min: 0, max: 1, default: 0 }, statusHistory: [{ status: { type: String, required: true }, changedBy: { type: String, required: true, maxlength: 100 }, changedDate: { type: Date, required: true, default: Date.now }, comments: { type: String, maxlength: 500 } }] }, { timestamps: true, toJSON: { transform: function (doc, ret) { ret.id = ret._id; delete ret._id; delete ret.__v; return ret; } } }); // Indexes for better query performance ScopeChangeSchema.index({ projectId: 1, status: 1 }); ScopeChangeSchema.index({ projectId: 1, requestDate: -1 }); ScopeChangeSchema.index({ riskLevel: 1, status: 1 }); ScopeChangeSchema.index({ requestedBy: 1 }); ScopeChangeSchema.index({ approvalDate: -1 }); ScopeChangeSchema.index({ 'impact.scheduleImpact.criticalPath': 1 }); // Virtual for change age in days ScopeChangeSchema.virtual('ageInDays').get(function () { return Math.ceil((Date.now() - this.requestDate.getTime()) / (1000 * 60 * 60 * 24)); }); // Virtual for approval duration ScopeChangeSchema.virtual('approvalDuration').get(function () { if (this.approvalDate && this.requestDate) { return Math.ceil((this.approvalDate.getTime() - this.requestDate.getTime()) / (1000 * 60 * 60 * 24)); } return null; }); // Virtual for implementation duration ScopeChangeSchema.virtual('implementationDuration').get(function () { if (this.implementationDate && this.approvalDate) { return Math.ceil((this.implementationDate.getTime() - this.approvalDate.getTime()) / (1000 * 60 * 60 * 24)); } return null; }); // Pre-save middleware to calculate impact and complexity scores ScopeChangeSchema.pre('save', function (next) { // Calculate impact score if (this.isModified('impact')) { const scheduleWeight = 0.3; const costWeight = 0.3; const qualityWeight = 0.2; const stakeholderWeight = 0.2; const scheduleScore = Math.min(this.impact.scheduleImpact.percentage / 100, 1); const costScore = Math.min(this.impact.costImpact.percentage / 100, 1); const qualityScore = this.impact.qualityImpact.riskToQuality ? 0.5 : 0; const stakeholderScore = Math.min(this.impact.stakeholderImpact.affectedStakeholders.length / 10, 1); this.impactScore = (scheduleScore * scheduleWeight) + (costScore * costWeight) + (qualityScore * qualityWeight) + (stakeholderScore * stakeholderWeight); } // Calculate complexity score if (this.isModified('impact') || this.isModified('description')) { let complexityScore = 0; // Description complexity const descriptionLength = this.description.length; complexityScore += Math.min(descriptionLength / 1000, 0.3); // Resource complexity const resourceCount = this.impact.resourceImpact.additionalResources.length + this.impact.resourceImpact.skillsRequired.length; complexityScore += Math.min(resourceCount / 10, 0.3); // Stakeholder complexity const stakeholderCount = this.impact.stakeholderImpact.affectedStakeholders.length; complexityScore += Math.min(stakeholderCount / 10, 0.2); // Critical path complexity if (this.impact.scheduleImpact.criticalPath) { complexityScore += 0.2; } this.complexityScore = Math.min(complexityScore, 1); } // Add status history entry if status changed if (this.isModified('status') && !this.isNew) { this.statusHistory.push({ status: this.status, changedBy: this.approvedBy || 'system', changedDate: new Date(), comments: `Status changed to ${this.status}` }); } next(); }); // Static method to get changes by project ScopeChangeSchema.statics.getByProject = function (projectId, status) { const query = { projectId }; if (status) { query.status = status; } return this.find(query).sort({ requestDate: -1 }); }; // Static method to get pending changes requiring approval ScopeChangeSchema.statics.getPendingApprovals = function (approver) { const query = { status: 'pending' }; if (approver) { query.$or = [ { 'approvalWorkflow.level1Approver': approver }, { 'approvalWorkflow.level2Approver': approver }, { 'approvalWorkflow.finalApprover': approver } ]; } return this.find(query).sort({ priority: -1, requestDate: 1 }); }; // Static method to get high-risk changes ScopeChangeSchema.statics.getHighRiskChanges = function (projectId) { const query = { riskLevel: { $in: ['high', 'critical'] }, status: { $in: ['pending', 'approved'] } }; if (projectId) { query.projectId = projectId; } return this.find(query).sort({ riskLevel: -1, requestDate: 1 }); }; export const ScopeChange = mongoose.model('ScopeChange', ScopeChangeSchema); //# sourceMappingURL=ScopeChange.js.map