Re_Backend/src/models/ApprovalLevel.ts

308 lines
7.5 KiB
TypeScript

import { DataTypes, Model, Optional } from 'sequelize';
import { sequelize } from '@config/database';
import { User } from './User';
import { WorkflowRequest } from './WorkflowRequest';
import { ApprovalStatus } from '../types/common.types';
interface ApprovalLevelAttributes {
levelId: string;
requestId: string;
levelNumber: number;
levelName?: string;
approverId: string;
approverEmail: string;
approverName: string;
tatHours: number;
tatDays: number;
status: ApprovalStatus;
levelStartTime?: Date;
levelEndTime?: Date;
actionDate?: Date;
comments?: string;
rejectionReason?: string;
breachReason?: string;
isFinalApprover: boolean;
elapsedHours: number;
remainingHours: number;
tatPercentageUsed: number;
tat50AlertSent: boolean;
tat75AlertSent: boolean;
tatBreached: boolean;
tatStartTime?: Date;
isPaused: boolean;
pausedAt?: Date;
pausedBy?: string;
pauseReason?: string;
pauseResumeDate?: Date;
pauseTatStartTime?: Date;
pauseElapsedHours?: number;
createdAt: Date;
updatedAt: Date;
}
interface ApprovalLevelCreationAttributes extends Optional<ApprovalLevelAttributes, 'levelId' | 'levelName' | 'levelStartTime' | 'levelEndTime' | 'actionDate' | 'comments' | 'rejectionReason' | 'breachReason' | 'tat50AlertSent' | 'tat75AlertSent' | 'tatBreached' | 'tatStartTime' | 'tatDays' | 'isPaused' | 'pausedAt' | 'pausedBy' | 'pauseReason' | 'pauseResumeDate' | 'pauseTatStartTime' | 'pauseElapsedHours' | 'createdAt' | 'updatedAt'> {}
class ApprovalLevel extends Model<ApprovalLevelAttributes, ApprovalLevelCreationAttributes> implements ApprovalLevelAttributes {
public levelId!: string;
public requestId!: string;
public levelNumber!: number;
public levelName?: string;
public approverId!: string;
public approverEmail!: string;
public approverName!: string;
public tatHours!: number;
public tatDays!: number;
public status!: ApprovalStatus;
public levelStartTime?: Date;
public levelEndTime?: Date;
public actionDate?: Date;
public comments?: string;
public rejectionReason?: string;
public breachReason?: string;
public isFinalApprover!: boolean;
public elapsedHours!: number;
public remainingHours!: number;
public tatPercentageUsed!: number;
public tat50AlertSent!: boolean;
public tat75AlertSent!: boolean;
public tatBreached!: boolean;
public tatStartTime?: Date;
public isPaused!: boolean;
public pausedAt?: Date;
public pausedBy?: string;
public pauseReason?: string;
public pauseResumeDate?: Date;
public pauseTatStartTime?: Date;
public pauseElapsedHours?: number;
public createdAt!: Date;
public updatedAt!: Date;
// Associations
public request?: WorkflowRequest;
public approver?: User;
}
ApprovalLevel.init(
{
levelId: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
field: 'level_id'
},
requestId: {
type: DataTypes.UUID,
allowNull: false,
field: 'request_id',
references: {
model: 'workflow_requests',
key: 'request_id'
}
},
levelNumber: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'level_number'
},
levelName: {
type: DataTypes.STRING(100),
allowNull: true,
field: 'level_name'
},
approverId: {
type: DataTypes.UUID,
allowNull: false,
field: 'approver_id',
references: {
model: 'users',
key: 'user_id'
}
},
approverEmail: {
type: DataTypes.STRING(255),
allowNull: false,
field: 'approver_email'
},
approverName: {
type: DataTypes.STRING(200),
allowNull: false,
field: 'approver_name'
},
tatHours: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
field: 'tat_hours'
},
tatDays: {
type: DataTypes.INTEGER,
allowNull: true,
field: 'tat_days'
// This is a GENERATED STORED column in production DB (calculated as CEIL(tat_hours / 24.0))
// Database will auto-calculate this value - do NOT pass it during INSERT/UPDATE operations
},
status: {
type: DataTypes.ENUM('PENDING', 'IN_PROGRESS', 'APPROVED', 'REJECTED', 'SKIPPED', 'PAUSED'),
defaultValue: 'PENDING'
},
levelStartTime: {
type: DataTypes.DATE,
allowNull: true,
field: 'level_start_time'
},
levelEndTime: {
type: DataTypes.DATE,
allowNull: true,
field: 'level_end_time'
},
actionDate: {
type: DataTypes.DATE,
allowNull: true,
field: 'action_date'
},
comments: {
type: DataTypes.TEXT,
allowNull: true
},
rejectionReason: {
type: DataTypes.TEXT,
allowNull: true,
field: 'rejection_reason'
},
breachReason: {
type: DataTypes.TEXT,
allowNull: true,
field: 'breach_reason',
comment: 'Reason for TAT breach - can contain paragraph-length text'
},
isFinalApprover: {
type: DataTypes.BOOLEAN,
defaultValue: false,
field: 'is_final_approver'
},
elapsedHours: {
type: DataTypes.DECIMAL(10, 2),
defaultValue: 0,
field: 'elapsed_hours'
},
remainingHours: {
type: DataTypes.DECIMAL(10, 2),
defaultValue: 0,
field: 'remaining_hours'
},
tatPercentageUsed: {
type: DataTypes.DECIMAL(5, 2),
defaultValue: 0,
field: 'tat_percentage_used'
},
tat50AlertSent: {
type: DataTypes.BOOLEAN,
defaultValue: false,
field: 'tat50_alert_sent'
},
tat75AlertSent: {
type: DataTypes.BOOLEAN,
defaultValue: false,
field: 'tat75_alert_sent'
},
tatBreached: {
type: DataTypes.BOOLEAN,
defaultValue: false,
field: 'tat_breached'
},
tatStartTime: {
type: DataTypes.DATE,
allowNull: true,
field: 'tat_start_time'
},
isPaused: {
type: DataTypes.BOOLEAN,
defaultValue: false,
field: 'is_paused'
},
pausedAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'paused_at'
},
pausedBy: {
type: DataTypes.UUID,
allowNull: true,
field: 'paused_by',
references: {
model: 'users',
key: 'user_id'
}
},
pauseReason: {
type: DataTypes.TEXT,
allowNull: true,
field: 'pause_reason'
},
pauseResumeDate: {
type: DataTypes.DATE,
allowNull: true,
field: 'pause_resume_date'
},
pauseTatStartTime: {
type: DataTypes.DATE,
allowNull: true,
field: 'pause_tat_start_time'
},
pauseElapsedHours: {
type: DataTypes.DECIMAL(10, 2),
allowNull: true,
field: 'pause_elapsed_hours'
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
field: 'created_at'
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
field: 'updated_at'
}
},
{
sequelize,
modelName: 'ApprovalLevel',
tableName: 'approval_levels',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
indexes: [
{
fields: ['request_id']
},
{
fields: ['approver_id']
},
{
fields: ['status']
},
{
unique: true,
fields: ['request_id', 'level_number']
}
]
}
);
// Associations
ApprovalLevel.belongsTo(WorkflowRequest, {
as: 'request',
foreignKey: 'requestId',
targetKey: 'requestId'
});
ApprovalLevel.belongsTo(User, {
as: 'approver',
foreignKey: 'approverId',
targetKey: 'userId'
});
export { ApprovalLevel };