Re_Backend/src/migrations/20251104-create-tat-alerts.ts

135 lines
4.2 KiB
TypeScript

import { QueryInterface, DataTypes } from 'sequelize';
/**
* Migration to create TAT alerts/reminders table
* Stores all TAT-related notifications sent (50%, 75%, 100%)
*/
export async function up(queryInterface: QueryInterface): Promise<void> {
await queryInterface.createTable('tat_alerts', {
alert_id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
request_id: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'workflow_requests',
key: 'request_id'
}
},
level_id: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'approval_levels',
key: 'level_id'
}
},
approver_id: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'users',
key: 'user_id'
}
},
alert_type: {
type: DataTypes.ENUM('TAT_50', 'TAT_75', 'TAT_100'),
allowNull: false
},
threshold_percentage: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '50, 75, or 100'
},
tat_hours_allocated: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
comment: 'Total TAT hours for this level'
},
tat_hours_elapsed: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
comment: 'Hours elapsed when alert was sent'
},
tat_hours_remaining: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
comment: 'Hours remaining when alert was sent'
},
level_start_time: {
type: DataTypes.DATE,
allowNull: false,
comment: 'When the approval level started'
},
alert_sent_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: 'When the alert was sent'
},
expected_completion_time: {
type: DataTypes.DATE,
allowNull: false,
comment: 'When the level should be completed'
},
alert_message: {
type: DataTypes.TEXT,
allowNull: false,
comment: 'The notification message sent'
},
notification_sent: {
type: DataTypes.BOOLEAN,
defaultValue: true,
comment: 'Whether notification was successfully sent'
},
notification_channels: {
type: DataTypes.ARRAY(DataTypes.STRING),
defaultValue: [],
comment: 'push, email, sms'
},
is_breached: {
type: DataTypes.BOOLEAN,
defaultValue: false,
comment: 'Whether this was a breach alert (100%)'
},
was_completed_on_time: {
type: DataTypes.BOOLEAN,
allowNull: true,
comment: 'Set when level is completed - was it on time?'
},
completion_time: {
type: DataTypes.DATE,
allowNull: true,
comment: 'When the level was actually completed'
},
metadata: {
type: DataTypes.JSONB,
defaultValue: {},
comment: 'Additional context (priority, request title, etc.)'
},
created_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW
}
});
// Indexes for performance (with IF NOT EXISTS check)
await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "tat_alerts_request_id" ON "tat_alerts" ("request_id");');
await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "tat_alerts_level_id" ON "tat_alerts" ("level_id");');
await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "tat_alerts_approver_id" ON "tat_alerts" ("approver_id");');
await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "tat_alerts_alert_type" ON "tat_alerts" ("alert_type");');
await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "tat_alerts_alert_sent_at" ON "tat_alerts" ("alert_sent_at");');
await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "tat_alerts_is_breached" ON "tat_alerts" ("is_breached");');
await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "tat_alerts_was_completed_on_time" ON "tat_alerts" ("was_completed_on_time");');
}
export async function down(queryInterface: QueryInterface): Promise<void> {
await queryInterface.dropTable('tat_alerts');
await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_tat_alerts_alert_type";');
}