diff --git a/db/migrations/004_add_unique_key_to_forecast_allocation.sql b/db/migrations/004_add_unique_key_to_forecast_allocation.sql
index fc8dc73..7d51880 100644
--- a/db/migrations/004_add_unique_key_to_forecast_allocation.sql
+++ b/db/migrations/004_add_unique_key_to_forecast_allocation.sql
@@ -1 +1 @@
-ALTER TABLE `forecastAllocation` ADD UNIQUE KEY `unique_allocation` (`forecastingId`, `rosterId`, `month`);
+ALTER TABLE `forecastAllocation` DROP INDEX `unique_allocation`, ADD UNIQUE KEY `unique_allocation` (`forecastingId`, `rosterId`, `month`);
\ No newline at end of file
diff --git a/forecasting.php b/forecasting.php
index 0b3c036..99cf62c 100644
--- a/forecasting.php
+++ b/forecasting.php
@@ -143,7 +143,7 @@ $months_headers = $project ? get_months($project['startDate'], $project['endDate
diff --git a/import.php b/import.php
index 42e4821..689d232 100644
--- a/import.php
+++ b/import.php
@@ -4,7 +4,7 @@ session_start();
require_once __DIR__ . '/db/config.php';
function redirect_with_message($status, $message) {
- header("Location: index.php?import_status=$status&import_message=" . urlencode($message));
+ header("Location: roster.php?import_status=$status&import_message=" . urlencode($message));
exit();
}
diff --git a/index.php b/index.php
index 3083d67..df5aabe 100644
--- a/index.php
+++ b/index.php
@@ -1,277 +1,32 @@
prepare($insert_sql);
-
- $stmt->execute([
- ':sapCode' => $_POST['sapCode'],
- ':fullNameEn' => $_POST['fullNameEn'],
- ':legalEntity' => $_POST['legalEntity'] ?? null,
- ':functionBusinessUnit' => $_POST['functionBusinessUnit'] ?? null,
- ':costCenterCode' => $_POST['costCenterCode'] ?? null,
- ':level' => $_POST['level'] ?? null,
- ':newAmendedSalary' => $newAmendedSalary,
- ':employerContributions' => $employerContributions,
- ':cars' => $cars,
- ':ticketRestaurant' => $ticketRestaurant,
- ':metlife' => $metlife,
- ':topusPerMonth' => $topusPerMonth,
- ':totalSalaryCostWithLabor' => $totalSalaryCostWithLabor,
- ':totalMonthlyCost' => $totalMonthlyCost,
- ':totalAnnualCost' => $totalAnnualCost,
- ':grossRevenue' => $grossRevenue,
- ':discountedRevenue' => $discountedRevenue
- ]);
-
- // To prevent form resubmission on refresh, redirect
- header("Location: " . $_SERVER['PHP_SELF']);
- exit();
-
- } catch (PDOException $e) {
- // Check for duplicate entry
- if ($e->errorInfo[1] == 1062) {
- $form_error = "Error: A resource with this SAP Code already exists.";
- } else {
- $form_error = "Database error: " . $e->getMessage();
- }
- }
- }
-}
-
-if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'delete_roster') {
- try {
- require_once __DIR__ . '/db/config.php';
- $pdo_delete = db();
- $delete_sql = "DELETE FROM roster WHERE id = :id";
- $stmt = $pdo_delete->prepare($delete_sql);
- $stmt->execute([':id' => $_POST['id']]);
- header("Location: " . $_SERVER['PHP_SELF']);
- exit();
- } catch (PDOException $e) {
- $form_error = "Database error: " . $e->getMessage();
- }
-}
-
-if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'update_roster') {
- if (empty($_POST['id']) || empty($_POST['sapCode']) || empty($_POST['fullNameEn'])) {
- $form_error = "ID, SAP Code, and Full Name are required for an update.";
- } else {
- try {
- require_once __DIR__ . '/db/config.php';
- $pdo_update = db();
-
- // Prepare data from form
- $newAmendedSalary = (float)($_POST['newAmendedSalary'] ?? 0);
- $employerContributions = (float)($_POST['employerContributions'] ?? 0);
- $cars = (float)($_POST['cars'] ?? 0);
- $ticketRestaurant = (float)($_POST['ticketRestaurant'] ?? 0);
- $metlife = (float)($_POST['metlife'] ?? 0);
- $topusPerMonth = (float)($_POST['topusPerMonth'] ?? 0);
- $grossRevenue = (float)($_POST['grossRevenue'] ?? 0);
- $discountedRevenue = (float)($_POST['discountedRevenue'] ?? 0);
-
- // Auto-calculations
- $totalSalaryCostWithLabor = $newAmendedSalary + $employerContributions;
- $totalMonthlyCost = $totalSalaryCostWithLabor + $cars + $ticketRestaurant + $metlife + $topusPerMonth;
- $totalAnnualCost = $totalMonthlyCost * 14;
-
- $update_sql = "UPDATE roster SET
- sapCode = :sapCode,
- fullNameEn = :fullNameEn,
- legalEntity = :legalEntity,
- functionBusinessUnit = :functionBusinessUnit,
- costCenterCode = :costCenterCode,
- `level` = :level,
- newAmendedSalary = :newAmendedSalary,
- employerContributions = :employerContributions,
- cars = :cars,
- ticketRestaurant = :ticketRestaurant,
- metlife = :metlife,
- topusPerMonth = :topusPerMonth,
- totalSalaryCostWithLabor = :totalSalaryCostWithLabor,
- totalMonthlyCost = :totalMonthlyCost,
- totalAnnualCost = :totalAnnualCost,
- grossRevenue = :grossRevenue,
- discountedRevenue = :discountedRevenue
- WHERE id = :id";
-
- $stmt = $pdo_update->prepare($update_sql);
- $stmt->execute([
- ':id' => $_POST['id'],
- ':sapCode' => $_POST['sapCode'],
- ':fullNameEn' => $_POST['fullNameEn'],
- ':legalEntity' => $_POST['legalEntity'] ?? null,
- ':functionBusinessUnit' => $_POST['functionBusinessUnit'] ?? null,
- ':costCenterCode' => $_POST['costCenterCode'] ?? null,
- ':level' => $_POST['level'] ?? null,
- ':newAmendedSalary' => $newAmendedSalary,
- ':employerContributions' => $employerContributions,
- ':cars' => $cars,
- ':ticketRestaurant' => $ticketRestaurant,
- ':metlife' => $metlife,
- ':topusPerMonth' => $topusPerMonth,
- ':totalSalaryCostWithLabor' => $totalSalaryCostWithLabor,
- ':totalMonthlyCost' => $totalMonthlyCost,
- ':totalAnnualCost' => $totalAnnualCost,
- ':grossRevenue' => $grossRevenue,
- ':discountedRevenue' => $discountedRevenue
- ]);
-
- header("Location: " . $_SERVER['PHP_SELF']);
- exit();
-
- } catch (PDOException $e) {
- if ($e->errorInfo[1] == 1062) {
- $form_error = "Error: A resource with this SAP Code already exists.";
- } else {
- $form_error = "Database error: " . $e->getMessage();
- }
- }
- }
-}
-
-// --- DATABASE INITIALIZATION ---
require_once __DIR__ . '/db/config.php';
-function execute_sql_from_file($pdo, $filepath) {
- try {
- $sql = file_get_contents($filepath);
- $pdo->exec($sql);
- return true;
- } catch (PDOException $e) {
- if (strpos($e->getMessage(), 'already exists') === false) {
- error_log("SQL Execution Error: " . $e->getMessage());
- }
- return false;
- }
-}
-
-function seed_roster_data($pdo) {
- try {
- $stmt = $pdo->query("SELECT COUNT(*) FROM roster");
- if ($stmt->fetchColumn() > 0) {
- return; // Data already exists
- }
-
- $seed_data = [
- [
- 'sapCode' => '1001', 'fullNameEn' => 'John Doe', 'legalEntity' => 'Entity A', 'functionBusinessUnit' => 'Finance',
- 'costCenterCode' => 'CC100', 'level' => 'Senior', 'newAmendedSalary' => 6000, 'employerContributions' => 1500,
- 'cars' => 500, 'ticketRestaurant' => 150, 'metlife' => 50, 'topusPerMonth' => 100
- ],
- [
- 'sapCode' => '1002', 'fullNameEn' => 'Jane Smith', 'legalEntity' => 'Entity B', 'functionBusinessUnit' => 'IT',
- 'costCenterCode' => 'CC200', 'level' => 'Manager', 'newAmendedSalary' => 8000, 'employerContributions' => 2000,
- 'cars' => 600, 'ticketRestaurant' => 150, 'metlife' => 60, 'topusPerMonth' => 120
- ],
- ];
-
- $insert_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)";
- $stmt = $pdo->prepare($insert_sql);
-
- foreach ($seed_data as $row) {
- $totalSalaryCostWithLabor = $row['newAmendedSalary'] + $row['employerContributions'];
- $totalMonthlyCost = $totalSalaryCostWithLabor + $row['cars'] + $row['ticketRestaurant'] + $row['metlife'] + $row['topusPerMonth'];
- $totalAnnualCost = $totalMonthlyCost * 14;
-
- $stmt->execute([
- ':sapCode' => $row['sapCode'],
- ':fullNameEn' => $row['fullNameEn'],
- ':legalEntity' => $row['legalEntity'],
- ':functionBusinessUnit' => $row['functionBusinessUnit'],
- ':costCenterCode' => $row['costCenterCode'],
- ':level' => $row['level'],
- ':newAmendedSalary' => $row['newAmendedSalary'],
- ':employerContributions' => $row['employerContributions'],
- ':cars' => $row['cars'],
- ':ticketRestaurant' => $row['ticketRestaurant'],
- ':metlife' => $row['metlife'],
- ':topusPerMonth' => $row['topusPerMonth'],
- ':totalSalaryCostWithLabor' => $totalSalaryCostWithLabor,
- ':totalMonthlyCost' => $totalMonthlyCost,
- ':totalAnnualCost' => $totalAnnualCost
- ]);
- }
-
- } catch (PDOException $e) {
- error_log("Seeding Error: " . $e->getMessage());
- }
-}
-
-$roster_data = [];
-$search_term = $_GET['search'] ?? '';
-
try {
$pdo = db();
-
- // Apply all migrations
$migration_files = glob(__DIR__ . '/db/migrations/*.sql');
sort($migration_files);
foreach ($migration_files as $file) {
- execute_sql_from_file($pdo, $file);
+ $sql = file_get_contents($file);
+ $pdo->exec($sql);
}
-
- seed_roster_data($pdo);
-
- $sql = "SELECT * FROM roster";
- $params = [];
-
- if (!empty($search_term)) {
- $sql .= " WHERE fullNameEn LIKE :search OR sapCode LIKE :search";
- $params[':search'] = '%' . $search_term . '%';
- }
-
- $sql .= " ORDER BY fullNameEn";
- $stmt = $pdo->prepare($sql);
- $stmt->execute($params);
- $roster_data = $stmt->fetchAll(PDO::FETCH_ASSOC);
-
} catch (PDOException $e) {
- $db_error = "Database connection failed: " . $e->getMessage();
+ // If the table already exists, ignore the error
+ if (strpos($e->getMessage(), 'already exists') === false) {
+ die("Database migration failed: " . $e->getMessage());
+ }
}
-
-// --- RENDER PAGE ---
?>
-
Project Financials
-
-
-
-
+
Project Financial Management
+
+
+
-
@@ -281,416 +36,30 @@ try {
- Project Financials
+ Project Financial Management
-
-
- {$message}
-
-
";
- }
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
- | SAP Code |
- Full Name |
- Legal Entity |
- Business Unit |
- Cost Center |
- Level |
- Salary |
- Contributions |
- Cars |
- Ticket Restaurant |
- Metlife |
- Topus/Month |
- Total Salary Cost |
- Total Monthly Cost |
- Total Annual Cost |
- Gross Revenue |
- Discounted Revenue |
- Daily Cost |
- Actions |
-
-
-
-
-
- | No roster data found. |
-
-
-
-
- |
- |
- |
- |
- |
- |
- € |
- € |
- € |
- € |
- € |
- € |
- € |
- € |
- € |
- € |
- € |
- € |
-
-
-
-
-
-
- |
-
-
-
-
-
+
+
This is the landing page. You can navigate to the Roster or Projects page using the sidebar.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+