diff --git a/assets/js/project_details.js b/assets/js/project_details.js
deleted file mode 100644
index 7925843..0000000
--- a/assets/js/project_details.js
+++ /dev/null
@@ -1,295 +0,0 @@
-document.addEventListener('DOMContentLoaded', () => {
- const dataScript = document.getElementById('financial-data');
- if (!dataScript) return;
-
- const appData = JSON.parse(dataScript.textContent);
- const { projectId, months, metrics, initialFinancialData, baseData, overrides } = appData;
-
- let state = {
- isOverrideActive: false,
- overrideMonth: null,
- originalTableState: {},
- currentFinancialData: JSON.parse(JSON.stringify(initialFinancialData)) // Deep copy
- };
-
- const table = document.getElementById('financials-table');
- if (!table) return;
-
- // UTILITY FUNCTIONS
- const formatCurrency = (value) => `€${(value || 0).toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
- const formatMargin = (value) => `${((value || 0) * 100).toFixed(2).replace('.', ',')}%`;
- const parseLocaleNumber = (stringNumber) => {
- if (typeof stringNumber !== 'string') return stringNumber;
- // Remove thousands separators (.), then replace decimal comma with a period.
- return Number(String(stringNumber).replace(/\./g, '').replace(',', '.'));
- };
-
- function debounce(func, wait) {
- let timeout;
- return function executedFunction(...args) {
- const later = () => {
- clearTimeout(timeout);
- func(...args);
- };
- clearTimeout(timeout);
- timeout = setTimeout(later, wait);
- };
- }
-
-
- // MAIN LOGIC
- function recalculateFinancials(overrideMonth, overrideValues) {
- const newData = JSON.parse(JSON.stringify(state.currentFinancialData));
- const overrideMonthIndex = months.indexOf(overrideMonth);
-
- if (overrideMonthIndex === -1) {
- console.error("Override month not found in months array!");
- return state.currentFinancialData;
- }
-
- // Step 1: Apply the override values for the specific override month.
- for (const key in overrideValues) {
- overrideValues[key] = parseFloat(overrideValues[key] || 0);
- }
-
- newData['Opening Balance'][overrideMonth] = overrideValues['Opening Balance'];
- newData.Billings[overrideMonth] = overrideValues.Billings;
- newData.WIP[overrideMonth] = overrideValues.WIP;
- newData.Expenses[overrideMonth] = overrideValues.Expenses;
- newData.Cost[overrideMonth] = overrideValues.Cost;
- newData.Payment[overrideMonth] = overrideValues.Payment;
-
- let nsr = newData.WIP[overrideMonth] + newData.Billings[overrideMonth] - newData['Opening Balance'][overrideMonth] - newData.Expenses[overrideMonth];
- newData.NSR[overrideMonth] = nsr;
- let margin = (nsr !== 0) ? ((nsr - newData.Cost[overrideMonth]) / nsr) : 0;
- newData.Margin[overrideMonth] = margin;
-
- // Step 2: Recalculate all subsequent months
- for (let i = overrideMonthIndex + 1; i < months.length; i++) {
- const month = months[i];
- const prevMonth = months[i - 1];
-
- const prevCumulativeBilling = parseFloat(newData.Billings[prevMonth] || 0);
- const prevCumulativeCost = parseFloat(newData.Cost[prevMonth] || 0);
- const prevCumulativeExpenses = parseFloat(newData.Expenses[prevMonth] || 0);
- const prevWIP = parseFloat(newData.WIP[prevMonth] || 0);
-
- const monthlyCost = parseFloat(baseData.monthly_costs[month] || 0);
- const monthlyBilling = parseFloat(baseData.monthly_billing[month] || 0);
- const monthlyExpenses = parseFloat(baseData.monthly_expenses[month] || 0);
- const monthlyWIPChange = parseFloat(baseData.monthly_wip[month] || 0);
-
- const newCumulativeBilling = prevCumulativeBilling + monthlyBilling;
- const newCumulativeCost = prevCumulativeCost + monthlyCost;
- const newCumulativeExpenses = prevCumulativeExpenses + monthlyExpenses;
- const newWIP = prevWIP + monthlyExpenses + monthlyWIPChange - monthlyBilling;
-
- newData.Billings[month] = newCumulativeBilling;
- newData.Cost[month] = newCumulativeCost;
- newData.Expenses[month] = newCumulativeExpenses;
- newData.WIP[month] = newWIP;
-
- // THE FIX: Carry over the previous month's Net Service Revenue as the next month's Opening Balance.
- newData['Opening Balance'][month] = newData.NSR[prevMonth] || 0;
- newData.Payment[month] = 0;
-
- nsr = newWIP + newCumulativeBilling - newData['Opening Balance'][month] - newCumulativeExpenses;
- newData.NSR[month] = nsr;
- margin = (nsr !== 0) ? ((nsr - newCumulativeCost) / nsr) : 0;
- newData.Margin[month] = margin;
- }
-
- return newData;
- }
-
- function updateTable(newData) {
- state.currentFinancialData = newData;
- for (const metric of metrics) {
- for (const month of months) {
- const cell = table.querySelector(`td[data-month="${month}"][data-metric="${metric}"]`);
- if (cell && cell.firstElementChild?.tagName !== 'INPUT') {
- const value = newData[metric][month];
- cell.textContent = metric === 'Margin' ? formatMargin(value) : formatCurrency(value);
- }
- }
- }
- }
-
- function recalculateForOverrideMonth(overrideMonth, overrideValues) {
- const newData = JSON.parse(JSON.stringify(state.currentFinancialData)); // Deep copy
-
- // Use the user's input values for the override month
- newData['Opening Balance'][overrideMonth] = overrideValues['Opening Balance'];
- newData.Billings[overrideMonth] = overrideValues.Billings;
- newData.WIP[overrideMonth] = overrideValues.WIP;
- newData.Expenses[overrideMonth] = overrideValues.Expenses;
- newData.Cost[overrideMonth] = overrideValues.Cost;
- newData.Payment[overrideMonth] = overrideValues.Payment;
-
- // Recalculate dependent metrics for the override month
- const nsr = newData.WIP[overrideMonth] + newData.Billings[overrideMonth] - newData['Opening Balance'][overrideMonth] - newData.Expenses[overrideMonth];
- newData.NSR[overrideMonth] = nsr;
- const margin = (nsr !== 0) ? ((nsr - newData.Cost[overrideMonth]) / nsr) : 0;
- newData.Margin[overrideMonth] = margin;
-
- return newData;
- }
-
- function handleInputChange() {
- const overrideValues = {};
- const editableMetrics = ['Opening Balance', 'Billings', 'WIP', 'Expenses', 'Cost', 'Payment'];
- editableMetrics.forEach(metric => {
- const input = table.querySelector(`td[data-month="${state.overrideMonth}"][data-metric="${metric}"] input`);
- overrideValues[metric] = parseLocaleNumber(input.value) || 0;
- });
-
- const recalculatedData = recalculateForOverrideMonth(state.overrideMonth, overrideValues);
- updateTable(recalculatedData);
- }
-
- function enterOverrideMode(month) {
- if (state.isOverrideActive) return;
-
- state.isOverrideActive = true;
- state.overrideMonth = month;
-
- const editableMetrics = ['Opening Balance', 'Billings', 'WIP', 'Expenses', 'Cost', 'Payment'];
-
- metrics.forEach(metric => {
- const cell = table.querySelector(`td[data-month="${month}"][data-metric="${metric}"]`);
- if (!cell) return;
-
- const originalValue = state.currentFinancialData[metric][month];
- state.originalTableState[metric] = cell.innerHTML;
-
- if (editableMetrics.includes(metric)) {
- // Ensure originalValue is a number before formatting
- const numericValue = (typeof originalValue === 'number') ? originalValue : 0;
- // Format to locale string with comma decimal separator for the input value
- const localeValue = numericValue.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
- cell.innerHTML = ``;
- }
- });
-
- const debouncedRecalc = debounce(handleInputChange, 200);
- table.querySelectorAll(`td[data-month="${month}"] input`).forEach(input => {
- input.addEventListener('input', debouncedRecalc);
- });
-
- const buttonCell = table.querySelector(`th button[data-month="${month}"]`).parentElement;
- state.originalTableState['button'] = buttonCell.innerHTML;
- buttonCell.innerHTML = `
-
-
-
-
- `;
- }
-
- async function confirmOverride() {
- try {
- const overrideValues = {};
- const editableMetrics = ['Opening Balance', 'Billings', 'WIP', 'Expenses', 'Cost', 'Payment'];
- editableMetrics.forEach(metric => {
- const input = table.querySelector(`td[data-month="${state.overrideMonth}"][data-metric="${metric}"] input`);
- if (!input) {
- throw new Error(`Could not find input for metric: ${metric} in month ${state.overrideMonth}`);
- }
- overrideValues[metric] = parseLocaleNumber(input.value) || 0;
- });
-
- const finalData = recalculateFinancials(state.overrideMonth, overrideValues);
-
- const monthsToSave = months.slice(months.indexOf(state.overrideMonth));
- const dataToSave = {};
- monthsToSave.forEach(month => {
- dataToSave[month] = {};
- metrics.forEach(metric => {
- const rawValue = finalData[metric][month];
- let cleanValue = typeof rawValue === 'string' ? parseLocaleNumber(rawValue) : rawValue;
-
- if (!isFinite(cleanValue)) {
- console.warn(`Invalid number for ${metric} in ${month}. Resetting to 0. Original:`, rawValue);
- cleanValue = 0;
- }
- dataToSave[month][metric] = cleanValue;
- });
- });
-
- const payload = {
- project_id: projectId,
- overrides: Object.entries(dataToSave).map(([month, values]) => ({
- month: month,
- opening_balance: values['Opening Balance'],
- payment: values.Payment,
- wip: values.WIP,
- expenses: values.Expenses,
- cost: values.Cost,
- nsr: values.NSR,
- margin: values.Margin,
- is_confirmed: month === state.overrideMonth ? 1 : 0
- }))
- };
-
-
-
- const response = await fetch('save_override.php?t=' + new Date().getTime(), {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(payload)
- });
-
- if (!response.ok) {
- let errorMsg = `HTTP error! status: ${response.status}`;
- try {
- const errorData = await response.json();
- errorMsg = errorData.message || errorMsg;
- } catch (e) {
- // Not a JSON response
- }
- throw new Error(errorMsg);
- }
-
- const result = await response.json();
- if (result.success) {
- alert(result.message || 'Override confirmed successfully!');
- window.location.reload();
- } else {
- throw new Error(result.message || 'Failed to save override.');
- }
- } catch (error) {
- console.error('Error confirming override:', error);
- alert(`Error: ${error.message}`);
- }
- }
-
- function exitOverrideMode() {
- metrics.forEach(metric => {
- const cell = table.querySelector(`td[data-month="${state.overrideMonth}"][data-metric="${metric}"]`);
- if (cell) {
- cell.innerHTML = state.originalTableState[metric];
- }
- });
-
- const buttonCell = table.querySelector(`th .btn-group`).parentElement;
- buttonCell.innerHTML = state.originalTableState['button'];
-
- updateTable(initialFinancialData);
-
- state.isOverrideActive = false;
- state.overrideMonth = null;
- state.originalTableState = {};
- }
-
- table.addEventListener('click', (e) => {
- if (e.target.classList.contains('override-btn')) {
- enterOverrideMode(e.target.dataset.month);
- } else if (e.target.classList.contains('confirm-override')) {
- confirmOverride();
- } else if (e.target.classList.contains('cancel-override')) {
- exitOverrideMode();
- }
- });
-});
\ No newline at end of file
diff --git a/db/migrate.php b/db/migrate.php
deleted file mode 100644
index b3df702..0000000
--- a/db/migrate.php
+++ /dev/null
@@ -1,81 +0,0 @@
-setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
-
- // Create migrations table if it doesn't exist
- $pdo->exec("CREATE TABLE IF NOT EXISTS migrations (
- id INT AUTO_INCREMENT PRIMARY KEY,
- migration_name VARCHAR(255) NOT NULL,
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
- )");
-
- // Get all migration files
- $migrationFiles = glob(__DIR__ . '/migrations/*.sql');
-
- // Get already run migrations
- $stmt = $pdo->query("SELECT migration_name FROM migrations");
- $runMigrations = $stmt->fetchAll(PDO::FETCH_COLUMN);
-
- echo 'Database Migrations';
- echo '';
- echo '';
- echo '';
- echo '
Database Migrations
';
-
- $migrationsApplied = false;
-
- foreach ($migrationFiles as $file) {
- $migrationName = basename($file);
- if (!in_array($migrationName, $runMigrations)) {
- echo '
Applying migration: ' . htmlspecialchars($migrationName) . '...
';
- $sql = file_get_contents($file);
- $statements = array_filter(array_map('trim', explode(';', $sql)));
- $fileHasError = false;
-
- foreach ($statements as $statement) {
- if (empty($statement)) continue;
- try {
- $pdo->exec($statement);
- } catch (PDOException $e) {
- // 1060 is the specific error code for "Duplicate column name"
- if (strpos($e->getMessage(), '1060') !== false) {
- // It's a duplicate column error, we can ignore it.
- echo '
Ignoring existing column in ' . htmlspecialchars($migrationName) . '.
';
- } else {
- // It's another error, so we should stop.
- echo '
Error applying migration ' . htmlspecialchars($migrationName) . ': ' . htmlspecialchars($e->getMessage()) . '
';
- $fileHasError = true;
- break; // break from statements loop
- }
- }
- }
-
- if (!$fileHasError) {
- // Record migration
- $stmt = $pdo->prepare("INSERT INTO migrations (migration_name) VALUES (?)");
- $stmt->execute([$migrationName]);
-
- echo '
Successfully applied ' . htmlspecialchars($migrationName) . '
';
- $migrationsApplied = true;
- } else {
- // Stop on error
- break; // break from files loop
- }
- }
- }
-
- if (!$migrationsApplied) {
- echo '
Database is already up to date.
';
- }
-
- echo '
Back to Home';
- echo '
';
-
-} catch (PDOException $e) {
- http_response_code(500);
- die("Database connection failed: " . $e->getMessage());
-}
\ No newline at end of file
diff --git a/db/migrations/005_create_project_finance_monthly_table.sql b/db/migrations/005_create_project_finance_monthly_table.sql
index e8d9d00..e0d9ee7 100644
--- a/db/migrations/005_create_project_finance_monthly_table.sql
+++ b/db/migrations/005_create_project_finance_monthly_table.sql
@@ -1,19 +1,11 @@
-DROP TABLE IF EXISTS `projectFinanceMonthly`;
-
CREATE TABLE IF NOT EXISTS `projectFinanceMonthly` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`projectId` INT NOT NULL,
+ `metricName` VARCHAR(255) NOT NULL,
`month` DATE NOT NULL,
- `opening_balance` DECIMAL(15, 2) DEFAULT 0,
- `payment` DECIMAL(15, 2) DEFAULT 0,
- `wip` DECIMAL(15, 2) DEFAULT 0,
- `expenses` DECIMAL(15, 2) DEFAULT 0,
- `cost` DECIMAL(15, 2) DEFAULT 0,
- `nsr` DECIMAL(15, 2) DEFAULT 0,
- `margin` DECIMAL(15, 5) DEFAULT 0,
- `is_confirmed` TINYINT(1) NOT NULL DEFAULT 0,
+ `amount` DECIMAL(15, 2) NOT NULL,
`createdAt` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updatedAt` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (`projectId`) REFERENCES `projects`(`id`) ON DELETE CASCADE,
- UNIQUE KEY `unique_project_month` (`projectId`, `month`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
\ No newline at end of file
+ UNIQUE KEY `project_metric_month` (`projectId`, `metricName`, `month`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
diff --git a/db/migrations/007_add_revenue_fields_to_roster.sql b/db/migrations/007_add_revenue_fields_to_roster.sql
index 0143998..8590309 100644
--- a/db/migrations/007_add_revenue_fields_to_roster.sql
+++ b/db/migrations/007_add_revenue_fields_to_roster.sql
@@ -1,2 +1,3 @@
-ALTER TABLE `roster` ADD COLUMN `grossRevenue` DECIMAL(10, 2) NOT NULL DEFAULT 0.00;
-ALTER TABLE `roster` ADD COLUMN `discountedRevenue` DECIMAL(10, 2) NOT NULL DEFAULT 0.00;
+ALTER TABLE `roster`
+ADD COLUMN `grossRevenue` DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
+ADD COLUMN `discountedRevenue` DECIMAL(10, 2) NOT NULL DEFAULT 0.00;
diff --git a/db/migrations/009_create_project_finance_monthly_override_table.sql b/db/migrations/009_create_project_finance_monthly_override_table.sql
deleted file mode 100644
index fdf98b6..0000000
--- a/db/migrations/009_create_project_finance_monthly_override_table.sql
+++ /dev/null
@@ -1,10 +0,0 @@
-CREATE TABLE IF NOT EXISTS `project_finance_monthly_override` (
- `project_id` INT NOT NULL,
- `nsr_override` DECIMAL(15, 2) DEFAULT NULL,
- `cost_override` DECIMAL(15, 2) DEFAULT NULL,
- `hours_override` DECIMAL(10, 2) DEFAULT NULL,
- `createdAt` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- `updatedAt` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (`project_id`),
- FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
\ No newline at end of file
diff --git a/db/migrations/010_alter_override_table.sql b/db/migrations/010_alter_override_table.sql
deleted file mode 100644
index 5159dc3..0000000
--- a/db/migrations/010_alter_override_table.sql
+++ /dev/null
@@ -1,14 +0,0 @@
--- Step 1: Drop the foreign key constraint
-ALTER TABLE project_finance_monthly_override DROP FOREIGN KEY project_finance_monthly_override_ibfk_1;
-
--- Step 2: Drop the old primary key
-ALTER TABLE project_finance_monthly_override DROP PRIMARY KEY;
-
--- Step 3: Add the new month column
-ALTER TABLE project_finance_monthly_override ADD COLUMN month VARCHAR(7) NOT NULL;
-
--- Step 4: Add the new composite primary key
-ALTER TABLE project_finance_monthly_override ADD PRIMARY KEY (project_id, month);
-
--- Step 5: Re-add the foreign key constraint with a more descriptive name
-ALTER TABLE project_finance_monthly_override ADD CONSTRAINT fk_override_project_id FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
diff --git a/describe_table.php b/describe_table.php
deleted file mode 100644
index 2fd72e0..0000000
--- a/describe_table.php
+++ /dev/null
@@ -1,12 +0,0 @@
-query("DESCRIBE project_finance_monthly_override");
- $columns = $stmt->fetchAll(PDO::FETCH_ASSOC);
- print_r($columns);
-} catch (Exception $e) {
- echo "Error: " . $e->getMessage();
-}
-?>
\ No newline at end of file
diff --git a/logs/php_errors.log b/logs/php_errors.log
deleted file mode 100644
index 6fc7ef1..0000000
--- a/logs/php_errors.log
+++ /dev/null
@@ -1,20 +0,0 @@
-[26-Nov-2025 13:29:03 UTC] save_override.php: Script start
-[26-Nov-2025 13:29:03 UTC] save_override.php: Invalid request method
-[26-Nov-2025 13:29:08 UTC] save_override.php: Script start
-[26-Nov-2025 13:29:08 UTC] save_override.php: Invalid request method
-[26-Nov-2025 13:29:13 UTC] save_override.php: Script start
-[26-Nov-2025 13:29:13 UTC] save_override.php: Invalid request method
-[26-Nov-2025 13:29:19 UTC] save_override.php: Script start
-[26-Nov-2025 13:29:19 UTC] save_override.php: Invalid request method
-[26-Nov-2025 13:29:24 UTC] save_override.php: Script start
-[26-Nov-2025 13:29:24 UTC] save_override.php: Invalid request method
-[26-Nov-2025 13:29:29 UTC] save_override.php: Script start
-[26-Nov-2025 13:29:29 UTC] save_override.php: Invalid request method
-[26-Nov-2025 13:29:29 UTC] save_override.php: Script start
-[26-Nov-2025 13:29:29 UTC] save_override.php: JSON data decoded
-[26-Nov-2025 13:29:29 UTC] save_override.php: Database connection successful
-[26-Nov-2025 13:29:29 UTC] save_override.php: Starting database transaction
-[26-Nov-2025 13:29:29 UTC] save_override.php: SQL statement prepared
-[26-Nov-2025 13:29:29 UTC] save_override.php: Processing month: 2025-11-01
-[26-Nov-2025 13:29:29 UTC] save_override.php: An exception occurred: SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'month' at row 1
-[26-Nov-2025 13:29:29 UTC] save_override.php: Rolling back transaction
diff --git a/logs/raw_input.log b/logs/raw_input.log
deleted file mode 100644
index 00786fd..0000000
--- a/logs/raw_input.log
+++ /dev/null
@@ -1,24 +0,0 @@
-{"project_id":25,"overrides":[{"month":"2025-11-01","nsr":27855,"cost":1350},{"month":"2025-12-01","nsr":35545,"cost":2600},{"month":"2026-01-01","nsr":43355,"cost":3225}]}Executing with params: Array
-(
- [:project_id] => 25
- [:month] => 2025-11-01
- [:nsr_override] => 27855
- [:cost_override] => 1350
- [:hours_override] =>
-)
-Executing with params: Array
-(
- [:project_id] => 25
- [:month] => 2025-12-01
- [:nsr_override] => 35545
- [:cost_override] => 2600
- [:hours_override] =>
-)
-Executing with params: Array
-(
- [:project_id] => 25
- [:month] => 2026-01-01
- [:nsr_override] => 43355
- [:cost_override] => 3225
- [:hours_override] =>
-)
diff --git a/project_details.php b/project_details.php
index eb9463f..08fc482 100644
--- a/project_details.php
+++ b/project_details.php
@@ -10,7 +10,7 @@ function execute_sql_from_file($pdo, $filepath) {
return true;
} catch (PDOException $e) {
// Ignore errors about tables/columns that already exist
- if (strpos($e->getMessage(), 'already exists') === false && strpos($e->getMessage(), 'Duplicate column name') === false && strpos($e->getMessage(), 'Duplicate key name') === false) {
+ if (strpos($e->getMessage(), 'already exists') === false && strpos($e->getMessage(), 'Duplicate column name') === false) {
error_log("SQL Execution Error in $filepath: " . $e->getMessage());
}
return false;
@@ -35,7 +35,7 @@ $project = null;
$project_id = $_GET['id'] ?? null;
$financial_data = [];
$months = [];
-$metrics = ["Opening Balance", "Billings", "WIP", "Expenses", "Cost", "NSR", "Margin", "Payment"];
+$metrics = ["Opening Balance", "Billings", "WIP", "Expenses", "Cost", "NSR", "Margin"];
if ($project_id) {
$stmt = $pdo->prepare("SELECT * FROM projects WHERE id = :id");
@@ -103,15 +103,6 @@ if ($project_id) {
$expenses_stmt->execute([':pid' => $project_id]);
$monthly_expenses = $expenses_stmt->fetchAll(PDO::FETCH_KEY_PAIR);
- // Fetch confirmed override data
- $finance_override_stmt = $pdo->prepare("SELECT * FROM projectFinanceMonthly WHERE projectId = :pid ORDER BY month ASC");
- $finance_override_stmt->execute([':pid' => $project_id]);
- $overrides_raw = $finance_override_stmt->fetchAll(PDO::FETCH_ASSOC);
- $finance_overrides = [];
- foreach ($overrides_raw as $row) {
- $finance_overrides[$row['month']] = $row;
- }
-
// 2. Calculate cumulative values month by month
$cumulative_billing = 0;
$cumulative_cost = 0;
@@ -119,54 +110,36 @@ if ($project_id) {
$previous_month_wip = 0;
foreach ($months as $month) {
- if (isset($finance_overrides[$month])) {
- // This month has saved data. Use its values.
- $financial_data['Opening Balance'][$month] = $finance_overrides[$month]['opening_balance'];
- $financial_data['Billings'][$month] = $finance_overrides[$month]['payment'];
- $financial_data['Payment'][$month] = $finance_overrides[$month]['payment'];
- $financial_data['WIP'][$month] = $finance_overrides[$month]['wip'];
- $financial_data['Expenses'][$month] = $finance_overrides[$month]['expenses'];
- $financial_data['Cost'][$month] = $finance_overrides[$month]['cost'];
- $financial_data['NSR'][$month] = $finance_overrides[$month]['nsr'];
- $financial_data['Margin'][$month] = $finance_overrides[$month]['margin'];
+ // 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;
- // Update cumulative trackers for the next potential calculation
- $cumulative_billing = $financial_data['Billings'][$month];
- $cumulative_cost = $financial_data['Cost'][$month];
- $cumulative_expenses= $financial_data['Expenses'][$month];
- $previous_month_wip = $financial_data['WIP'][$month];
- } else {
- // This month has no saved data. Calculate as usual.
- $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;
- // 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;
- // WIP Calculation
- $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;
- $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;
- $financial_data['Payment'][$month] = 0; // Not overridden, so no payment input
+ // 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;
- // 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;
- $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;
- }
+ // Update previous month's WIP for the next iteration
+ $previous_month_wip = $current_wip;
}
}
}
@@ -259,45 +232,21 @@ if (!$project) {
-
-
+
| Metric |
-
-
- Confirmed';
- } elseif ($is_eligible) {
- echo '';
- } else {
- echo '';
- }
- ?>
- |
+ |
-
+
|
-
+ |
-
-
|