From dd81fb557e9cf318dd12d8af00d86445d291fb30 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 28 Nov 2025 17:29:32 +0000 Subject: [PATCH] without DiscountRevenue in Roster --- assets/js/main.js | 2 - ..._remove_discounted_revenue_from_roster.sql | 1 + export_project_finance.php | 52 +++++--- project_details.php | 122 +++++++++++------- roster.php | 27 +--- 5 files changed, 111 insertions(+), 93 deletions(-) create mode 100644 db/migrations/012_remove_discounted_revenue_from_roster.sql diff --git a/assets/js/main.js b/assets/js/main.js index 48bfc0b..959f821 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -33,7 +33,6 @@ document.addEventListener('DOMContentLoaded', function () { document.getElementById('edit-metlife').value = data.metlife; document.getElementById('edit-topusPerMonth').value = data.topusPerMonth; document.getElementById('edit-grossRevenue').value = data.grossRevenue; - document.getElementById('edit-discountedRevenue').value = data.discountedRevenue; editResourceModal.show(); }); @@ -66,7 +65,6 @@ document.addEventListener('DOMContentLoaded', function () { document.getElementById('view-totalMonthlyCost').value = '€' + parseFloat(data.totalMonthlyCost).toFixed(2); document.getElementById('view-totalAnnualCost').value = '€' + parseFloat(data.totalAnnualCost).toFixed(2); document.getElementById('view-grossRevenue').value = '€' + parseFloat(data.grossRevenue).toFixed(2); - document.getElementById('view-discountedRevenue').value = '€' + parseFloat(data.discountedRevenue).toFixed(2); document.getElementById('view-dailyCost').value = '€' + (parseFloat(data.totalMonthlyCost) / 20).toFixed(2); viewResourceModal.show(); diff --git a/db/migrations/012_remove_discounted_revenue_from_roster.sql b/db/migrations/012_remove_discounted_revenue_from_roster.sql new file mode 100644 index 0000000..795d21e --- /dev/null +++ b/db/migrations/012_remove_discounted_revenue_from_roster.sql @@ -0,0 +1 @@ +ALTER TABLE `roster` DROP COLUMN `discountedRevenue`; \ No newline at end of file diff --git a/export_project_finance.php b/export_project_finance.php index 4836f86..04c9ad2 100644 --- a/export_project_finance.php +++ b/export_project_finance.php @@ -56,9 +56,10 @@ try { $monthly_costs = $cost_stmt->fetchAll(PDO::FETCH_KEY_PAIR); // Base Monthly WIP - $wip_sql = "SELECT fa.month, SUM(fa.allocatedDays * r.discountedRevenue) FROM forecastAllocation fa JOIN roster r ON fa.rosterId = r.id WHERE fa.forecastingId = :fid GROUP BY fa.month"; + $recoverability_decimal = $project['recoverability'] / 100; + $wip_sql = "SELECT fa.month, SUM(fa.allocatedDays * (r.grossRevenue * (1 - :recoverability))) as wip FROM forecastAllocation fa JOIN roster r ON fa.rosterId = r.id WHERE fa.forecastingId = :fid GROUP BY fa.month"; $wip_stmt = $pdo->prepare($wip_sql); - $wip_stmt->execute([':fid' => $fid]); + $wip_stmt->execute([':fid' => $fid, ':recoverability' => $recoverability_decimal]); $monthly_wip = $wip_stmt->fetchAll(PDO::FETCH_KEY_PAIR); } @@ -67,41 +68,54 @@ try { $billing_stmt->execute([':pid' => $project_id]); $monthly_billing = $billing_stmt->fetchAll(PDO::FETCH_KEY_PAIR); + // Base Monthly Expenses + $expenses_stmt = $pdo->prepare("SELECT month, amount FROM expensesMonthly WHERE projectId = :pid"); + $expenses_stmt->execute([':pid' => $project_id]); + $monthly_expenses = $expenses_stmt->fetchAll(PDO::FETCH_KEY_PAIR); + // 2. Calculate cumulative values month by month $cumulative_billing = 0; $cumulative_cost = 0; + $cumulative_expenses = 0; $previous_month_wip = 0; foreach ($months as $month) { - // Normalize month keys from fetched data + // --- Opening Balance --- + $opening_balance = $previous_month_wip; + $financial_data['Opening Balance'][$month] = $opening_balance; + + // --- Base Data --- $cost = $monthly_costs[$month] ?? 0; $base_monthly_wip = $monthly_wip[$month] ?? 0; $billing = $monthly_billing[$month] ?? 0; - $expenses = 0; // Placeholder for expenses + $expenses = $monthly_expenses[$month] ?? 0; - // Cumulative calculations + // --- Cumulative Metrics --- $cumulative_billing += $billing; - $cumulative_cost += $cost; - - // WIP Calculation (new formula) - // current month WIP = previous month WIP + Month Expenses + base_monthly_wip - month Billing - $current_wip = $previous_month_wip + $expenses + $base_monthly_wip - $billing; - - $financial_data['WIP'][$month] = $current_wip; $financial_data['Billings'][$month] = $cumulative_billing; - $financial_data['Cost'][$month] = $cumulative_cost; - $financial_data['Opening Balance'][$month] = 0; // Placeholder - $financial_data['Expenses'][$month] = $expenses; - // Calculated metrics (NSR and Margin) + $cumulative_cost += $cost; + $financial_data['Cost'][$month] = $cumulative_cost; + + $cumulative_expenses += $expenses; + $financial_data['Expenses'][$month] = $cumulative_expenses; + + // --- WIP (Closing Balance) --- + $final_opening_balance = $financial_data['Opening Balance'][$month]; + $current_wip = $final_opening_balance + $expenses + $base_monthly_wip - $billing; + $financial_data['WIP'][$month] = $current_wip; + + // --- Calculated Metrics (NSR and Margin) --- $nsr = $financial_data['WIP'][$month] + $financial_data['Billings'][$month] - $financial_data['Opening Balance'][$month] - $financial_data['Expenses'][$month]; $financial_data['NSR'][$month] = $nsr; - $margin = ($nsr != 0) ? (($nsr - $financial_data['Cost'][$month]) / $nsr) : 0; + $final_nsr = $financial_data['NSR'][$month]; + $final_cost = $financial_data['Cost'][$month]; + $margin = ($final_nsr != 0) ? (($final_nsr - $final_cost) / $final_nsr) : 0; $financial_data['Margin'][$month] = $margin; - // Update previous month's WIP for the next iteration - $previous_month_wip = $current_wip; + // --- Carry-over for next iteration --- + $previous_month_wip = $financial_data['WIP'][$month]; } } } catch (PDOException $e) { diff --git a/project_details.php b/project_details.php index 30e5510..cc762bc 100644 --- a/project_details.php +++ b/project_details.php @@ -87,9 +87,10 @@ if ($project_id) { $monthly_costs = $cost_stmt->fetchAll(PDO::FETCH_KEY_PAIR); // Base Monthly WIP - $wip_sql = "SELECT fa.month, SUM(fa.allocatedDays * r.discountedRevenue) as wip FROM forecastAllocation fa JOIN roster r ON fa.rosterId = r.id WHERE fa.forecastingId = :fid GROUP BY fa.month"; + $recoverability_decimal = $project['recoverability'] / 100; + $wip_sql = "SELECT fa.month, SUM(fa.allocatedDays * (r.grossRevenue * (1 - :recoverability))) as wip FROM forecastAllocation fa JOIN roster r ON fa.rosterId = r.id WHERE fa.forecastingId = :fid GROUP BY fa.month"; $wip_stmt = $pdo->prepare($wip_sql); - $wip_stmt->execute([':fid' => $fid]); + $wip_stmt->execute([':fid' => $fid, ':recoverability' => $recoverability_decimal]); $monthly_wip = $wip_stmt->fetchAll(PDO::FETCH_KEY_PAIR); } @@ -103,50 +104,17 @@ if ($project_id) { $expenses_stmt->execute([':pid' => $project_id]); $monthly_expenses = $expenses_stmt->fetchAll(PDO::FETCH_KEY_PAIR); - // 2. Calculate cumulative values month by month + // 2. Calculate and process values month by month, including overrides $cumulative_billing = 0; $cumulative_cost = 0; $cumulative_expenses = 0; - $previous_month_wip = 0; + $previous_month_wip = 0; // This is the closing balance from the prior month. - foreach ($months as $month) { - // Normalize month keys from fetched data - $cost = $monthly_costs[$month] ?? 0; - $base_monthly_wip = $monthly_wip[$month] ?? 0; - $billing = $monthly_billing[$month] ?? 0; - $expenses = $monthly_expenses[$month] ?? 0; - - // Cumulative calculations - $cumulative_billing += $billing; - $cumulative_cost += $cost; - $cumulative_expenses += $expenses; - - // WIP Calculation (new formula) - // current month WIP = previous month WIP + Month Expenses + base_monthly_wip - month Billing - $current_wip = $previous_month_wip + $expenses + $base_monthly_wip - $billing; - - $financial_data['WIP'][$month] = $current_wip; - $financial_data['Billings'][$month] = $cumulative_billing; - $financial_data['Cost'][$month] = $cumulative_cost; - $financial_data['Opening Balance'][$month] = 0; // Placeholder - $financial_data['Expenses'][$month] = $cumulative_expenses; - - // Calculated metrics (NSR and Margin) - $nsr = $financial_data['WIP'][$month] + $financial_data['Billings'][$month] - $financial_data['Opening Balance'][$month] - $financial_data['Expenses'][$month]; - $financial_data['NSR'][$month] = $nsr; - - $margin = ($nsr != 0) ? (($nsr - $financial_data['Cost'][$month]) / $nsr) : 0; - $financial_data['Margin'][$month] = $margin; - - // Update previous month's WIP for the next iteration - $previous_month_wip = $current_wip; - } - - // 3. Fetch and apply overridden data + // Fetch all overridden data at once to avoid querying in a loop $override_stmt = $pdo->prepare("SELECT month, metricName, value FROM projectFinanceMonthly WHERE projectId = :pid AND is_overridden = 1"); $override_stmt->execute([':pid' => $project_id]); - $overridden_data = $override_stmt->fetchAll(PDO::FETCH_ASSOC); - + $overridden_rows = $override_stmt->fetchAll(PDO::FETCH_ASSOC); + $overrides = []; $metric_name_map = [ 'wip' => 'WIP', 'opening_balance' => 'Opening Balance', @@ -156,17 +124,73 @@ if ($project_id) { 'nsr' => 'NSR', 'margin' => 'Margin' ]; - - foreach ($overridden_data as $row) { - $month = $row['month']; - $metric_db_name = $row['metricName']; - $value = $row['value']; - - if (in_array($month, $months) && isset($metric_name_map[$metric_db_name])) { - $metric_display_name = $metric_name_map[$metric_db_name]; - $financial_data[$metric_display_name][$month] = $value; + foreach ($overridden_rows as $row) { + if (isset($metric_name_map[$row['metricName']])) { + $metric_display_name = $metric_name_map[$row['metricName']]; + $overrides[$row['month']][$metric_display_name] = $row['value']; } } + + foreach ($months as $month) { + // --- Opening Balance --- + $opening_balance = $previous_month_wip; + $financial_data['Opening Balance'][$month] = $opening_balance; + if (isset($overrides[$month]['Opening Balance'])) { + $financial_data['Opening Balance'][$month] = $overrides[$month]['Opening Balance']; + } + + // --- Base Data --- + $cost = $monthly_costs[$month] ?? 0; + $base_monthly_wip = $monthly_wip[$month] ?? 0; + $billing = $monthly_billing[$month] ?? 0; + $expenses = $monthly_expenses[$month] ?? 0; + + // --- Cumulative Metrics --- + $cumulative_billing += $billing; + $financial_data['Billings'][$month] = $cumulative_billing; + if (isset($overrides[$month]['Billings'])) { + $financial_data['Billings'][$month] = $overrides[$month]['Billings']; + } + + $cumulative_cost += $cost; + $financial_data['Cost'][$month] = $cumulative_cost; + if (isset($overrides[$month]['Cost'])) { + $financial_data['Cost'][$month] = $overrides[$month]['Cost']; + } + + $cumulative_expenses += $expenses; + $financial_data['Expenses'][$month] = $cumulative_expenses; + if (isset($overrides[$month]['Expenses'])) { + $financial_data['Expenses'][$month] = $overrides[$month]['Expenses']; + } + + // --- WIP (Closing Balance) --- + $final_opening_balance = $financial_data['Opening Balance'][$month]; + $current_wip = $final_opening_balance + $expenses + $base_monthly_wip - $billing; + $financial_data['WIP'][$month] = $current_wip; + if (isset($overrides[$month]['WIP'])) { + $financial_data['WIP'][$month] = $overrides[$month]['WIP']; + } + + // --- Calculated Metrics (NSR and Margin) --- + $nsr = $financial_data['WIP'][$month] + $financial_data['Billings'][$month] - $financial_data['Opening Balance'][$month] - $financial_data['Expenses'][$month]; + $financial_data['NSR'][$month] = $nsr; + if (isset($overrides[$month]['NSR'])) { + $financial_data['NSR'][$month] = $overrides[$month]['NSR']; + } + + $final_nsr = $financial_data['NSR'][$month]; + $final_cost = $financial_data['Cost'][$month]; + $margin = ($final_nsr != 0) ? (($final_nsr - $final_cost) / $final_nsr) : 0; + $financial_data['Margin'][$month] = $margin; + if (isset($overrides[$month]['Margin'])) { + $financial_data['Margin'][$month] = $overrides[$month]['Margin']; + } + + // --- Carry-over for next iteration --- + // Update previous month's WIP with the final, possibly overridden, value. + $previous_month_wip = $financial_data['WIP'][$month]; + } } } diff --git a/roster.php b/roster.php index 76b0527..8dac4cc 100644 --- a/roster.php +++ b/roster.php @@ -20,14 +20,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' $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; - $insert_sql = "INSERT INTO roster (sapCode, fullNameEn, legalEntity, functionBusinessUnit, costCenterCode, `level`, newAmendedSalary, employerContributions, cars, ticketRestaurant, metlife, topusPerMonth, totalSalaryCostWithLabor, totalMonthlyCost, totalAnnualCost, grossRevenue, discountedRevenue) VALUES (:sapCode, :fullNameEn, :legalEntity, :functionBusinessUnit, :costCenterCode, :level, :newAmendedSalary, :employerContributions, :cars, :ticketRestaurant, :metlife, :topusPerMonth, :totalSalaryCostWithLabor, :totalMonthlyCost, :totalAnnualCost, :grossRevenue, :discountedRevenue)"; + $insert_sql = "INSERT INTO roster (sapCode, fullNameEn, legalEntity, functionBusinessUnit, costCenterCode, `level`, newAmendedSalary, employerContributions, cars, ticketRestaurant, metlife, topusPerMonth, totalSalaryCostWithLabor, totalMonthlyCost, totalAnnualCost, grossRevenue) VALUES (:sapCode, :fullNameEn, :legalEntity, :functionBusinessUnit, :costCenterCode, :level, :newAmendedSalary, :employerContributions, :cars, :ticketRestaurant, :metlife, :topusPerMonth, :totalSalaryCostWithLabor, :totalMonthlyCost, :totalAnnualCost, :grossRevenue)"; $stmt = $pdo_form->prepare($insert_sql); $stmt->execute([ @@ -46,8 +45,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' ':totalSalaryCostWithLabor' => $totalSalaryCostWithLabor, ':totalMonthlyCost' => $totalMonthlyCost, ':totalAnnualCost' => $totalAnnualCost, - ':grossRevenue' => $grossRevenue, - ':discountedRevenue' => $discountedRevenue + ':grossRevenue' => $grossRevenue ]); // To prevent form resubmission on refresh, redirect @@ -95,7 +93,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' $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; @@ -118,8 +115,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' totalSalaryCostWithLabor = :totalSalaryCostWithLabor, totalMonthlyCost = :totalMonthlyCost, totalAnnualCost = :totalAnnualCost, - grossRevenue = :grossRevenue, - discountedRevenue = :discountedRevenue + grossRevenue = :grossRevenue WHERE id = :id"; $stmt = $pdo_update->prepare($update_sql); @@ -140,8 +136,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' ':totalSalaryCostWithLabor' => $totalSalaryCostWithLabor, ':totalMonthlyCost' => $totalMonthlyCost, ':totalAnnualCost' => $totalAnnualCost, - ':grossRevenue' => $grossRevenue, - ':discountedRevenue' => $discountedRevenue + ':grossRevenue' => $grossRevenue ]); header("Location: " . $_SERVER['PHP_SELF']); @@ -356,7 +351,6 @@ try { Total Monthly Cost (€) Total Annual Cost (€) Gross Revenue (€) - Discounted Revenue (€) Actions @@ -389,7 +383,6 @@ try { € - €
-
- - -
-
- - -
-
- - -