135 lines
4.2 KiB
TypeScript
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";');
|
|
}
|
|
|