beginTransaction(); $header = fgetcsv($handle); if ($header === false) { redirect_with_message('error', 'Could not read the CSV header.'); } // Normalize headers to camelCase $normalized_header = array_map(function($h) { $h = trim($h); $h = preg_replace('/[^a-zA-Z0-9_\s]/', '', $h); // Remove special chars $h = preg_replace('/\s+/', ' ', $h); // Normalize whitespace $h = str_replace(' ', '', ucwords(strtolower($h))); // To PascalCase return lcfirst($h); // To camelCase }, $header); $expected_headers = [ 'sapCode', 'fullNameEn', 'legalEntity', 'functionBusinessUnit', 'costCenterCode', 'level', 'newAmendedSalary', 'employerContributions', 'cars', 'ticketRestaurant', 'metlife', 'topusPerMonth' ]; $col_map = array_flip($normalized_header); foreach ($expected_headers as $expected) { if (!isset($col_map[$expected])) { redirect_with_message('error', "Missing required column: '{$expected}'. Please check the CSV file header."); } } $sql = "INSERT INTO roster (sapCode, fullNameEn, legalEntity, functionBusinessUnit, costCenterCode, `level`, newAmendedSalary, employerContributions, cars, ticketRestaurant, metlife, topusPerMonth, totalSalaryCostWithLabor, totalMonthlyCost, totalAnnualCost) VALUES (:sapCode, :fullNameEn, :legalEntity, :functionBusinessUnit, :costCenterCode, :level, :newAmendedSalary, :employerContributions, :cars, :ticketRestaurant, :metlife, :topusPerMonth, :totalSalaryCostWithLabor, :totalMonthlyCost, :totalAnnualCost) ON DUPLICATE KEY UPDATE fullNameEn = VALUES(fullNameEn), legalEntity = VALUES(legalEntity), functionBusinessUnit = VALUES(functionBusinessUnit), costCenterCode = VALUES(costCenterCode), `level` = VALUES(`level`), newAmendedSalary = VALUES(newAmendedSalary), employerContributions = VALUES(employerContributions), cars = VALUES(cars), ticketRestaurant = VALUES(ticketRestaurant), metlife = VALUES(metlife), topusPerMonth = VALUES(topusPerMonth), totalSalaryCostWithLabor = VALUES(totalSalaryCostWithLabor), totalMonthlyCost = VALUES(totalMonthlyCost), totalAnnualCost = VALUES(totalAnnualCost)"; $stmt = $pdo->prepare($sql); $rows_processed = 0; while (($row = fgetcsv($handle)) !== false) { $data = array_combine(array_keys($col_map), $row); $sapCode = $data['sapCode'] ?? null; if (empty($sapCode)) { continue; // Skip rows without a SAP Code } // Prepare data and perform calculations $newAmendedSalary = (float)($data['newAmendedSalary'] ?? 0); $employerContributions = (float)($data['employerContributions'] ?? 0); $cars = (float)($data['cars'] ?? 0); $ticketRestaurant = (float)($data['ticketRestaurant'] ?? 0); $metlife = (float)($data['metlife'] ?? 0); $topusPerMonth = (float)($data['topusPerMonth'] ?? 0); $totalSalaryCostWithLabor = $newAmendedSalary + $employerContributions; $totalMonthlyCost = $totalSalaryCostWithLabor + $cars + $ticketRestaurant + $metlife + $topusPerMonth; $totalAnnualCost = $totalMonthlyCost * 14; $stmt->execute([ ':sapCode' => $sapCode, ':fullNameEn' => $data['fullNameEn'] ?? null, ':legalEntity' => $data['legalEntity'] ?? null, ':functionBusinessUnit' => $data['functionBusinessUnit'] ?? null, ':costCenterCode' => $data['costCenterCode'] ?? null, ':level' => $data['level'] ?? null, ':newAmendedSalary' => $newAmendedSalary, ':employerContributions' => $employerContributions, ':cars' => $cars, ':ticketRestaurant' => $ticketRestaurant, ':metlife' => $metlife, ':topusPerMonth' => $topusPerMonth, ':totalSalaryCostWithLabor' => $totalSalaryCostWithLabor, ':totalMonthlyCost' => $totalMonthlyCost, ':totalAnnualCost' => $totalAnnualCost ]); $rows_processed++; } $pdo->commit(); fclose($handle); redirect_with_message('success', "Successfully imported {$rows_processed} records."); } catch (Exception $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); } fclose($handle); error_log("Import Error: " . $e->getMessage()); redirect_with_message('error', 'An error occurred during the import process. Details: ' . $e->getMessage()); }