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_LEVEL=info
|
||||||
LOG_FILE_PATH=./logs
|
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
|
CORS_ORIGIN=http://localhost:3000
|
||||||
|
|
||||||
# Rate Limiting
|
# 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;
|
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 {
|
class ApprovalLevel extends Model<ApprovalLevelAttributes, ApprovalLevelCreationAttributes> implements ApprovalLevelAttributes {
|
||||||
public levelId!: string;
|
public levelId!: string;
|
||||||
@ -119,8 +119,10 @@ ApprovalLevel.init(
|
|||||||
},
|
},
|
||||||
tatDays: {
|
tatDays: {
|
||||||
type: DataTypes.INTEGER,
|
type: DataTypes.INTEGER,
|
||||||
allowNull: false,
|
allowNull: true,
|
||||||
field: 'tat_days'
|
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: {
|
status: {
|
||||||
type: DataTypes.ENUM('PENDING', 'IN_PROGRESS', 'APPROVED', 'REJECTED', 'SKIPPED'),
|
type: DataTypes.ENUM('PENDING', 'IN_PROGRESS', 'APPROVED', 'REJECTED', 'SKIPPED'),
|
||||||
|
|||||||
@ -306,7 +306,7 @@ export class WorkflowService {
|
|||||||
approverEmail: email.toLowerCase(),
|
approverEmail: email.toLowerCase(),
|
||||||
approverName: userName,
|
approverName: userName,
|
||||||
tatHours,
|
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,
|
status: targetLevel === (workflow as any).currentLevel ? ApprovalStatus.IN_PROGRESS : ApprovalStatus.PENDING,
|
||||||
isFinalApprover: targetLevel === allLevels.length + 1,
|
isFinalApprover: targetLevel === allLevels.length + 1,
|
||||||
levelStartTime: targetLevel === (workflow as any).currentLevel ? new Date() : null,
|
levelStartTime: targetLevel === (workflow as any).currentLevel ? new Date() : null,
|
||||||
@ -702,7 +702,7 @@ export class WorkflowService {
|
|||||||
approverEmail: levelData.approverEmail,
|
approverEmail: levelData.approverEmail,
|
||||||
approverName: levelData.approverName,
|
approverName: levelData.approverName,
|
||||||
tatHours: levelData.tatHours,
|
tatHours: levelData.tatHours,
|
||||||
tatDays: calculateTATDays(levelData.tatHours),
|
// tatDays is auto-calculated by database as a generated column
|
||||||
status: ApprovalStatus.PENDING,
|
status: ApprovalStatus.PENDING,
|
||||||
elapsedHours: 0,
|
elapsedHours: 0,
|
||||||
remainingHours: levelData.tatHours,
|
remainingHours: levelData.tatHours,
|
||||||
@ -1050,7 +1050,7 @@ export class WorkflowService {
|
|||||||
approverEmail: levelData.approverEmail,
|
approverEmail: levelData.approverEmail,
|
||||||
approverName: levelData.approverName,
|
approverName: levelData.approverName,
|
||||||
tatHours: levelData.tatHours,
|
tatHours: levelData.tatHours,
|
||||||
tatDays: calculateTATDays(levelData.tatHours),
|
// tatDays is auto-calculated by database as a generated column
|
||||||
status: ApprovalStatus.PENDING,
|
status: ApprovalStatus.PENDING,
|
||||||
elapsedHours: 0,
|
elapsedHours: 0,
|
||||||
remainingHours: levelData.tatHours,
|
remainingHours: levelData.tatHours,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user