migration changes donde for create request in approval table
This commit is contained in:
parent
cd6a71b804
commit
84bf6e3dfc
@ -51,7 +51,10 @@ AI_MAX_TOKENS=500
|
||||
LOG_LEVEL=info
|
||||
LOG_FILE_PATH=./logs
|
||||
|
||||
# CORS
|
||||
# CORS - Comma-separated list of allowed origins
|
||||
# Local: http://localhost:3000
|
||||
# Production: https://your-frontend-domain.com
|
||||
# Multiple: http://localhost:3000,http://localhost:5173,https://your-frontend-domain.com
|
||||
CORS_ORIGIN=http://localhost:3000
|
||||
|
||||
# Rate Limiting
|
||||
|
||||
76
src/migrations/2025110501-alter-tat-days-to-generated.ts
Normal file
76
src/migrations/2025110501-alter-tat-days-to-generated.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { QueryInterface } from 'sequelize';
|
||||
|
||||
/**
|
||||
* Migration: Convert tat_days to GENERATED STORED column
|
||||
*
|
||||
* This ensures tat_days is auto-calculated from tat_hours across all environments.
|
||||
* Production already has this as a generated column, this migration makes other environments consistent.
|
||||
*/
|
||||
export async function up(queryInterface: QueryInterface): Promise<void> {
|
||||
// Check if tat_days is already a generated column
|
||||
const result = await queryInterface.sequelize.query(`
|
||||
SELECT
|
||||
a.attname as column_name,
|
||||
a.attgenerated as is_generated
|
||||
FROM pg_attribute a
|
||||
JOIN pg_class c ON a.attrelid = c.oid
|
||||
WHERE c.relname = 'approval_levels'
|
||||
AND a.attname = 'tat_days'
|
||||
AND NOT a.attisdropped;
|
||||
`, { type: 'SELECT' });
|
||||
|
||||
const column = result[0] as any;
|
||||
|
||||
if (column && column.is_generated === 's') {
|
||||
console.log('✅ tat_days is already a GENERATED STORED column - skipping migration');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('📝 Converting tat_days to GENERATED STORED column...');
|
||||
|
||||
// Step 1: Drop the existing regular column
|
||||
await queryInterface.sequelize.query(`
|
||||
ALTER TABLE approval_levels DROP COLUMN IF EXISTS tat_days;
|
||||
`);
|
||||
|
||||
// Step 2: Add it back as a GENERATED STORED column
|
||||
// Formula: CEIL(tat_hours / 24.0) - rounds up to nearest day
|
||||
await queryInterface.sequelize.query(`
|
||||
ALTER TABLE approval_levels
|
||||
ADD COLUMN tat_days INTEGER
|
||||
GENERATED ALWAYS AS (CAST(CEIL(tat_hours / 24.0) AS INTEGER)) STORED;
|
||||
`);
|
||||
|
||||
console.log('✅ tat_days is now a GENERATED STORED column - will auto-calculate from tat_hours');
|
||||
}
|
||||
|
||||
export async function down(queryInterface: QueryInterface): Promise<void> {
|
||||
console.log('⚠️ Rolling back: Converting tat_days from GENERATED to regular column');
|
||||
|
||||
// Drop the generated column
|
||||
await queryInterface.sequelize.query(`
|
||||
ALTER TABLE approval_levels DROP COLUMN IF EXISTS tat_days;
|
||||
`);
|
||||
|
||||
// Add it back as a regular column (with default calculation for existing rows)
|
||||
await queryInterface.sequelize.query(`
|
||||
ALTER TABLE approval_levels
|
||||
ADD COLUMN tat_days INTEGER;
|
||||
`);
|
||||
|
||||
// Populate existing rows with calculated values
|
||||
await queryInterface.sequelize.query(`
|
||||
UPDATE approval_levels
|
||||
SET tat_days = CAST(CEIL(tat_hours / 24.0) AS INTEGER)
|
||||
WHERE tat_days IS NULL;
|
||||
`);
|
||||
|
||||
// Make it NOT NULL after populating
|
||||
await queryInterface.sequelize.query(`
|
||||
ALTER TABLE approval_levels
|
||||
ALTER COLUMN tat_days SET NOT NULL;
|
||||
`);
|
||||
|
||||
console.log('✅ Rolled back to regular INTEGER column');
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ interface ApprovalLevelAttributes {
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
interface ApprovalLevelCreationAttributes extends Optional<ApprovalLevelAttributes, 'levelId' | 'levelName' | 'levelStartTime' | 'levelEndTime' | 'actionDate' | 'comments' | 'rejectionReason' | 'tat50AlertSent' | 'tat75AlertSent' | 'tatBreached' | 'tatStartTime' | 'createdAt' | 'updatedAt'> {}
|
||||
interface ApprovalLevelCreationAttributes extends Optional<ApprovalLevelAttributes, 'levelId' | 'levelName' | 'levelStartTime' | 'levelEndTime' | 'actionDate' | 'comments' | 'rejectionReason' | 'tat50AlertSent' | 'tat75AlertSent' | 'tatBreached' | 'tatStartTime' | 'tatDays' | 'createdAt' | 'updatedAt'> {}
|
||||
|
||||
class ApprovalLevel extends Model<ApprovalLevelAttributes, ApprovalLevelCreationAttributes> implements ApprovalLevelAttributes {
|
||||
public levelId!: string;
|
||||
@ -119,8 +119,10 @@ ApprovalLevel.init(
|
||||
},
|
||||
tatDays: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
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'),
|
||||
|
||||
@ -306,7 +306,7 @@ export class WorkflowService {
|
||||
approverEmail: email.toLowerCase(),
|
||||
approverName: userName,
|
||||
tatHours,
|
||||
tatDays: Math.ceil(tatHours / 24),
|
||||
// tatDays is auto-calculated by database as a generated column
|
||||
status: targetLevel === (workflow as any).currentLevel ? ApprovalStatus.IN_PROGRESS : ApprovalStatus.PENDING,
|
||||
isFinalApprover: targetLevel === allLevels.length + 1,
|
||||
levelStartTime: targetLevel === (workflow as any).currentLevel ? new Date() : null,
|
||||
@ -702,7 +702,7 @@ export class WorkflowService {
|
||||
approverEmail: levelData.approverEmail,
|
||||
approverName: levelData.approverName,
|
||||
tatHours: levelData.tatHours,
|
||||
tatDays: calculateTATDays(levelData.tatHours),
|
||||
// tatDays is auto-calculated by database as a generated column
|
||||
status: ApprovalStatus.PENDING,
|
||||
elapsedHours: 0,
|
||||
remainingHours: levelData.tatHours,
|
||||
@ -1050,7 +1050,7 @@ export class WorkflowService {
|
||||
approverEmail: levelData.approverEmail,
|
||||
approverName: levelData.approverName,
|
||||
tatHours: levelData.tatHours,
|
||||
tatDays: calculateTATDays(levelData.tatHours),
|
||||
// tatDays is auto-calculated by database as a generated column
|
||||
status: ApprovalStatus.PENDING,
|
||||
elapsedHours: 0,
|
||||
remainingHours: levelData.tatHours,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user