Commit c23742ac authored by Mahmoud Aglan's avatar Mahmoud Aglan

fixhr3

parent ba5df702
......@@ -80,7 +80,7 @@ return function (Database $db): void {
if ($hasColumn('hr_salary_components', 'component_type') && !$hasColumn('hr_salary_components', 'type')) {
// Must drop CHECK constraint before renaming column (MySQL 3959)
if ($hasCheck('hr_salary_components', 'chk_hr_salary_comp_type')) {
$safeAlter("ALTER TABLE `hr_salary_components` DROP CONSTRAINT `chk_hr_salary_comp_type`");
$safeAlter("ALTER TABLE `hr_salary_components` DROP CHECK `chk_hr_salary_comp_type`");
}
$safeAlter("ALTER TABLE `hr_salary_components` CHANGE COLUMN `component_type` `type` VARCHAR(20) NOT NULL DEFAULT 'earning'");
$safeAlter("ALTER TABLE `hr_salary_components` ADD CONSTRAINT `chk_hr_salary_comp_type` CHECK (`type` IN ('earning', 'deduction'))");
......@@ -215,7 +215,7 @@ return function (Database $db): void {
if ($hasColumn('hr_holidays', 'religion_specific') && !$hasColumn('hr_holidays', 'religion')) {
// Must drop CHECK constraint before renaming column (MySQL 3959)
if ($hasCheck('hr_holidays', 'chk_hr_holidays_religion')) {
$safeAlter("ALTER TABLE `hr_holidays` DROP CONSTRAINT `chk_hr_holidays_religion`");
$safeAlter("ALTER TABLE `hr_holidays` DROP CHECK `chk_hr_holidays_religion`");
}
$safeAlter("ALTER TABLE `hr_holidays` CHANGE COLUMN `religion_specific` `religion` VARCHAR(20) NULL DEFAULT NULL");
echo " ✔ hr_holidays: religion_specific → religion\n";
......@@ -277,7 +277,7 @@ return function (Database $db): void {
if ($hasColumn('hr_payroll_components_log', 'component_type') && !$hasColumn('hr_payroll_components_log', 'type')) {
// Must drop CHECK constraint before renaming column (MySQL 3959)
if ($hasCheck('hr_payroll_components_log', 'chk_hr_payroll_comp_type')) {
$safeAlter("ALTER TABLE `hr_payroll_components_log` DROP CONSTRAINT `chk_hr_payroll_comp_type`");
$safeAlter("ALTER TABLE `hr_payroll_components_log` DROP CHECK `chk_hr_payroll_comp_type`");
}
$safeAlter("ALTER TABLE `hr_payroll_components_log` CHANGE COLUMN `component_type` `type` VARCHAR(20) NOT NULL");
$safeAlter("ALTER TABLE `hr_payroll_components_log` ADD CONSTRAINT `chk_hr_payroll_comp_type` CHECK (`type` IN ('earning', 'deduction'))");
......@@ -424,7 +424,7 @@ return function (Database $db): void {
$safeAlter("ALTER TABLE `hr_attendance` ADD COLUMN `is_archived` TINYINT(1) NOT NULL DEFAULT 0, ADD COLUMN `archived_at` TIMESTAMP NULL DEFAULT NULL, ADD COLUMN `archived_by` BIGINT UNSIGNED NULL");
}
// Update CHECK constraint to include 'late' and 'rest_day' statuses
$safeAlter("ALTER TABLE `hr_attendance` DROP CONSTRAINT `chk_hr_attendance_status`");
$safeAlter("ALTER TABLE `hr_attendance` DROP CHECK `chk_hr_attendance_status`");
$safeAlter("ALTER TABLE `hr_attendance` ADD CONSTRAINT `chk_hr_attendance_status` CHECK (`status` IN ('present', 'absent', 'late', 'leave', 'holiday', 'rest_day', 'half_day'))");
// ─── 15. hr_leave_balances: employee_id → employee_profile_id ───
......@@ -476,7 +476,7 @@ return function (Database $db): void {
if ($hasColumn('hr_employee_loans', 'amount') && !$hasColumn('hr_employee_loans', 'loan_amount')) {
// Must drop CHECK constraint before renaming column (MySQL 3959)
if ($hasCheck('hr_employee_loans', 'chk_hr_loans_amount')) {
$safeAlter("ALTER TABLE `hr_employee_loans` DROP CONSTRAINT `chk_hr_loans_amount`");
$safeAlter("ALTER TABLE `hr_employee_loans` DROP CHECK `chk_hr_loans_amount`");
}
$safeAlter("ALTER TABLE `hr_employee_loans` CHANGE COLUMN `amount` `loan_amount` DECIMAL(15,2) NOT NULL");
$safeAlter("ALTER TABLE `hr_employee_loans` ADD CONSTRAINT `chk_hr_loans_amount` CHECK (`loan_amount` > 0)");
......
<?php
declare(strict_types=1);
use App\Core\Database;
/**
* Phase 32: Fix issues from Phase 31 deployment.
*
* Phase 31 used DROP CONSTRAINT (not supported for CHECK constraints in
* this MySQL version), so 4 column renames were silently blocked.
* Additionally, sections 21-25 of Phase 31 were not present in the
* deployed code, so those changes need to be applied here.
*
* This migration is fully idempotent — safe to run on any DB state.
*/
return function (Database $db): void {
$hasColumn = function (string $table, string $column) use ($db): bool {
$row = $db->selectOne(
"SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?",
[$table, $column]
);
return $row !== null;
};
$safeAlter = function (string $sql) use ($db): void {
try {
$db->raw($sql);
} catch (\Throwable $e) {
echo " ⚠ " . $e->getMessage() . "\n";
}
};
$findFkName = function (string $table, string $column) use ($db): ?string {
$row = $db->selectOne(
"SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?
AND REFERENCED_TABLE_NAME IS NOT NULL
LIMIT 1",
[$table, $column]
);
return $row ? $row['CONSTRAINT_NAME'] : null;
};
/**
* Find FK constraint pointing a column to a SPECIFIC referenced table.
* Used to find and drop orphaned FKs pointing to wrong table.
*/
$findFkToTable = function (string $table, string $column, string $referencedTable) use ($db): ?string {
$row = $db->selectOne(
"SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?
AND REFERENCED_TABLE_NAME = ?
LIMIT 1",
[$table, $column, $referencedTable]
);
return $row ? $row['CONSTRAINT_NAME'] : null;
};
$hasCheck = function (string $table, string $constraintName) use ($db): bool {
$row = $db->selectOne(
"SELECT 1 FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND CONSTRAINT_NAME = ? AND CONSTRAINT_TYPE = 'CHECK'",
[$table, $constraintName]
);
return $row !== null;
};
echo " Phase 32: Fixing Phase 31 deployment issues...\n";
// ─── 1. hr_salary_components: component_type → type (CHECK blocked in Phase 31) ───
if ($hasColumn('hr_salary_components', 'component_type') && !$hasColumn('hr_salary_components', 'type')) {
if ($hasCheck('hr_salary_components', 'chk_hr_salary_comp_type')) {
$safeAlter("ALTER TABLE `hr_salary_components` DROP CHECK `chk_hr_salary_comp_type`");
}
$safeAlter("ALTER TABLE `hr_salary_components` CHANGE COLUMN `component_type` `type` VARCHAR(20) NOT NULL DEFAULT 'earning'");
$safeAlter("ALTER TABLE `hr_salary_components` ADD CONSTRAINT `chk_hr_salary_comp_type` CHECK (`type` IN ('earning', 'deduction'))");
echo " ✔ hr_salary_components: component_type → type\n";
}
// ─── 2. hr_holidays: religion_specific → religion (CHECK blocked in Phase 31) ───
if ($hasColumn('hr_holidays', 'religion_specific') && !$hasColumn('hr_holidays', 'religion')) {
if ($hasCheck('hr_holidays', 'chk_hr_holidays_religion')) {
$safeAlter("ALTER TABLE `hr_holidays` DROP CHECK `chk_hr_holidays_religion`");
}
$safeAlter("ALTER TABLE `hr_holidays` CHANGE COLUMN `religion_specific` `religion` VARCHAR(20) NULL DEFAULT NULL");
echo " ✔ hr_holidays: religion_specific → religion\n";
}
// ─── 3. hr_payroll_components_log: component_type → type (CHECK blocked in Phase 31) ───
if ($hasColumn('hr_payroll_components_log', 'component_type') && !$hasColumn('hr_payroll_components_log', 'type')) {
if ($hasCheck('hr_payroll_components_log', 'chk_hr_payroll_comp_type')) {
$safeAlter("ALTER TABLE `hr_payroll_components_log` DROP CHECK `chk_hr_payroll_comp_type`");
}
$safeAlter("ALTER TABLE `hr_payroll_components_log` CHANGE COLUMN `component_type` `type` VARCHAR(20) NOT NULL");
$safeAlter("ALTER TABLE `hr_payroll_components_log` ADD CONSTRAINT `chk_hr_payroll_comp_type` CHECK (`type` IN ('earning', 'deduction'))");
echo " ✔ hr_payroll_components_log: component_type → type\n";
}
// ─── 4. hr_employee_loans: amount → loan_amount (CHECK blocked in Phase 31) ───
if ($hasColumn('hr_employee_loans', 'amount') && !$hasColumn('hr_employee_loans', 'loan_amount')) {
if ($hasCheck('hr_employee_loans', 'chk_hr_loans_amount')) {
$safeAlter("ALTER TABLE `hr_employee_loans` DROP CHECK `chk_hr_loans_amount`");
}
$safeAlter("ALTER TABLE `hr_employee_loans` CHANGE COLUMN `amount` `loan_amount` DECIMAL(15,2) NOT NULL");
$safeAlter("ALTER TABLE `hr_employee_loans` ADD CONSTRAINT `chk_hr_loans_amount` CHECK (`loan_amount` > 0)");
echo " ✔ hr_employee_loans: amount → loan_amount\n";
}
// ─── 5. Fix orphaned FKs on hr_employee_documents ───
// Phase 31 failed to drop old FK (wrong hardcoded name), so employee_profile_id may have
// two FKs: one to employees(id) and one to hr_employee_profiles(id). Drop the wrong one.
$orphanFk = $findFkToTable('hr_employee_documents', 'employee_profile_id', 'employees');
if ($orphanFk) {
$safeAlter("ALTER TABLE `hr_employee_documents` DROP FOREIGN KEY `{$orphanFk}`");
echo " ✔ hr_employee_documents: dropped orphan FK '{$orphanFk}' → employees\n";
}
// ─── 6. Fix orphaned FKs on hr_disciplinary_actions ───
$orphanFk = $findFkToTable('hr_disciplinary_actions', 'employee_profile_id', 'employees');
if ($orphanFk) {
$safeAlter("ALTER TABLE `hr_disciplinary_actions` DROP FOREIGN KEY `{$orphanFk}`");
echo " ✔ hr_disciplinary_actions: dropped orphan FK '{$orphanFk}' → employees\n";
}
// ─── 7. Add missing FK on hr_payroll_runs (Phase 31 dropped old FK but never re-added) ───
if ($hasColumn('hr_payroll_runs', 'employee_profile_id')) {
$existingFk = $findFkName('hr_payroll_runs', 'employee_profile_id');
if (!$existingFk) {
$safeAlter("ALTER TABLE `hr_payroll_runs` ADD CONSTRAINT `fk_hr_payroll_run_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`)");
echo " ✔ hr_payroll_runs: added FK → hr_employee_profiles\n";
}
}
// ─── 8. Add missing FK on hr_insurance_records (same issue) ───
if ($hasColumn('hr_insurance_records', 'employee_profile_id')) {
// Also check for orphan FK to employees table
$orphanFk = $findFkToTable('hr_insurance_records', 'employee_profile_id', 'employees');
if ($orphanFk) {
$safeAlter("ALTER TABLE `hr_insurance_records` DROP FOREIGN KEY `{$orphanFk}`");
echo " ✔ hr_insurance_records: dropped orphan FK → employees\n";
}
$existingFk = $findFkToTable('hr_insurance_records', 'employee_profile_id', 'hr_employee_profiles');
if (!$existingFk) {
$safeAlter("ALTER TABLE `hr_insurance_records` ADD CONSTRAINT `fk_hr_insurance_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`)");
echo " ✔ hr_insurance_records: added FK → hr_employee_profiles\n";
}
}
// ─── 9. hr_end_of_service: employee_id → employee_profile_id ───
if ($hasColumn('hr_end_of_service', 'employee_id') && !$hasColumn('hr_end_of_service', 'employee_profile_id')) {
$fkName = $findFkName('hr_end_of_service', 'employee_id');
if ($fkName) {
$safeAlter("ALTER TABLE `hr_end_of_service` DROP FOREIGN KEY `{$fkName}`");
}
$safeAlter("ALTER TABLE `hr_end_of_service` CHANGE COLUMN `employee_id` `employee_profile_id` BIGINT UNSIGNED NOT NULL");
$safeAlter("ALTER TABLE `hr_end_of_service` ADD CONSTRAINT `fk_hr_eos_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`)");
echo " ✔ hr_end_of_service: employee_id → employee_profile_id\n";
}
// ─── 10. hr_performance_reviews: employee_id → employee_profile_id ───
if ($hasColumn('hr_performance_reviews', 'employee_id') && !$hasColumn('hr_performance_reviews', 'employee_profile_id')) {
$fkName = $findFkName('hr_performance_reviews', 'employee_id');
if ($fkName) {
$safeAlter("ALTER TABLE `hr_performance_reviews` DROP FOREIGN KEY `{$fkName}`");
}
$safeAlter("ALTER TABLE `hr_performance_reviews` CHANGE COLUMN `employee_id` `employee_profile_id` BIGINT UNSIGNED NOT NULL");
$safeAlter("ALTER TABLE `hr_performance_reviews` ADD CONSTRAINT `fk_hr_perf_review_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`)");
echo " ✔ hr_performance_reviews: employee_id → employee_profile_id\n";
}
// ─── 11. hr_salary_adjustments: employee_id → employee_profile_id ───
if ($hasColumn('hr_salary_adjustments', 'employee_id') && !$hasColumn('hr_salary_adjustments', 'employee_profile_id')) {
$fkName = $findFkName('hr_salary_adjustments', 'employee_id');
if ($fkName) {
$safeAlter("ALTER TABLE `hr_salary_adjustments` DROP FOREIGN KEY `{$fkName}`");
}
$safeAlter("ALTER TABLE `hr_salary_adjustments` CHANGE COLUMN `employee_id` `employee_profile_id` BIGINT UNSIGNED NOT NULL");
$safeAlter("ALTER TABLE `hr_salary_adjustments` ADD CONSTRAINT `fk_hr_adj_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`)");
echo " ✔ hr_salary_adjustments: employee_id → employee_profile_id\n";
}
// ─── 12. hr_sick_leave_records: employee_id → employee_profile_id ───
if ($hasColumn('hr_sick_leave_records', 'employee_id') && !$hasColumn('hr_sick_leave_records', 'employee_profile_id')) {
$fkName = $findFkName('hr_sick_leave_records', 'employee_id');
if ($fkName) {
$safeAlter("ALTER TABLE `hr_sick_leave_records` DROP FOREIGN KEY `{$fkName}`");
}
$safeAlter("ALTER TABLE `hr_sick_leave_records` CHANGE COLUMN `employee_id` `employee_profile_id` BIGINT UNSIGNED NOT NULL");
$safeAlter("ALTER TABLE `hr_sick_leave_records` ADD CONSTRAINT `fk_hr_sick_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`)");
echo " ✔ hr_sick_leave_records: employee_id → employee_profile_id\n";
}
// ─── 13. Add missing is_archived columns ───
$archiveTables = [
'hr_payroll_periods', 'hr_salary_components', 'hr_performance_cycles',
'hr_performance_reviews', 'hr_salary_adjustments',
];
foreach ($archiveTables as $tbl) {
if (!$hasColumn($tbl, 'is_archived')) {
$safeAlter("ALTER TABLE `{$tbl}` ADD COLUMN `is_archived` TINYINT(1) NOT NULL DEFAULT 0, ADD COLUMN `archived_at` TIMESTAMP NULL DEFAULT NULL, ADD COLUMN `archived_by` BIGINT UNSIGNED NULL");
echo " ✔ {$tbl}: added is_archived columns\n";
}
}
// ─── 14. Fix hr_attendance CHECK constraint (use DROP CHECK not DROP CONSTRAINT) ───
if ($hasCheck('hr_attendance', 'chk_hr_attendance_status')) {
$safeAlter("ALTER TABLE `hr_attendance` DROP CHECK `chk_hr_attendance_status`");
$safeAlter("ALTER TABLE `hr_attendance` ADD CONSTRAINT `chk_hr_attendance_status` CHECK (`status` IN ('present', 'absent', 'late', 'leave', 'holiday', 'rest_day', 'half_day'))");
echo " ✔ hr_attendance: updated status CHECK constraint\n";
}
echo " Phase 32: Deployment fixes complete.\n";
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment