Auto commit: 2025-11-27T11:18:43.259Z
This commit is contained in:
parent
3965ab4f7a
commit
09ebb5bdee
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
const overrideBtn = document.getElementById('override-btn');
|
const overrideBtn = document.getElementById('override-btn');
|
||||||
|
|
||||||
@ -16,12 +15,14 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
makeEditable('Opening-Balance', month);
|
makeEditable('Opening-Balance', month);
|
||||||
makeEditable('Billings', month);
|
makeEditable('Billings', month);
|
||||||
makeEditable('Expenses', month);
|
makeEditable('Expenses', month);
|
||||||
|
makeEditable('Cost', month);
|
||||||
|
|
||||||
} else if (this.textContent.trim() === 'Save') {
|
} else if (this.textContent.trim() === 'Save') {
|
||||||
const wip = document.getElementById(`wip-${month}-input`).value;
|
const wip = document.getElementById(`wip-${month}-input`).value;
|
||||||
const openingBalance = document.getElementById(`opening-balance-${month}-input`).value;
|
const openingBalance = document.getElementById(`opening-balance-${month}-input`).value;
|
||||||
const billings = document.getElementById(`billings-${month}-input`).value;
|
const billings = document.getElementById(`billings-${month}-input`).value;
|
||||||
const expenses = document.getElementById(`expenses-${month}-input`).value;
|
const expenses = document.getElementById(`expenses-${month}-input`).value;
|
||||||
|
const cost = document.getElementById(`cost-${month}-input`).value;
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('projectId', projectId);
|
formData.append('projectId', projectId);
|
||||||
@ -30,6 +31,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
formData.append('openingBalance', openingBalance);
|
formData.append('openingBalance', openingBalance);
|
||||||
formData.append('billings', billings);
|
formData.append('billings', billings);
|
||||||
formData.append('expenses', expenses);
|
formData.append('expenses', expenses);
|
||||||
|
formData.append('cost', cost);
|
||||||
|
|
||||||
fetch('save_override.php', {
|
fetch('save_override.php', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -43,10 +45,14 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
this.classList.add('btn-secondary');
|
this.classList.add('btn-secondary');
|
||||||
this.disabled = true;
|
this.disabled = true;
|
||||||
|
|
||||||
updateCell('WIP', month, wip);
|
updateMetricCell('WIP', month, wip);
|
||||||
updateCell('Opening-Balance', month, openingBalance);
|
updateMetricCell('Opening-Balance', month, openingBalance);
|
||||||
updateCell('Billings', month, billings);
|
updateMetricCell('Billings', month, billings);
|
||||||
updateCell('Expenses', month, expenses);
|
updateMetricCell('Expenses', month, expenses);
|
||||||
|
updateMetricCell('Cost', month, cost);
|
||||||
|
updateMetricCell('NSR', month, data.nsr);
|
||||||
|
updateMetricCell('Margin', month, data.margin);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
alert('Error saving override: ' + data.error);
|
alert('Error saving override: ' + data.error);
|
||||||
}
|
}
|
||||||
@ -60,22 +66,31 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function makeEditable(metric, month) {
|
function makeEditable(metric, month) {
|
||||||
const cell = document.getElementById(`${metric.toLowerCase().replace(/\s/g, '-')}-${month}`);
|
const cellId = `${metric.toLowerCase().replace(/\s/g, '-')}-${month}`;
|
||||||
|
const cell = document.getElementById(cellId);
|
||||||
if (cell) {
|
if (cell) {
|
||||||
let value = cell.textContent.replace(/€/g, '').replace(/,/g, '');
|
let value = cell.textContent.replace(/[€%]/g, '').replace(/,/g, '').trim();
|
||||||
let numericValue = parseFloat(value);
|
let numericValue = parseFloat(value);
|
||||||
if (isNaN(numericValue)) {
|
if (isNaN(numericValue)) {
|
||||||
numericValue = 0;
|
numericValue = 0;
|
||||||
}
|
}
|
||||||
cell.innerHTML = `<input type="number" id="${metric.toLowerCase().replace(/\s/g, '-')}-${month}-input" class="form-control" value="${numericValue.toFixed(2)}">`;
|
if (metric === 'Margin') {
|
||||||
|
numericValue = numericValue / 100;
|
||||||
|
}
|
||||||
|
cell.innerHTML = `<input type="number" step="0.01" id="${cellId}-input" class="form-control" value="${numericValue.toFixed(2)}">`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCell(metric, month, value) {
|
function updateMetricCell(metric, month, value) {
|
||||||
const cell = document.getElementById(`${metric.toLowerCase().replace(/\s/g, '-')}-${month}`);
|
const cellId = `${metric.toLowerCase().replace(/\s/g, '-')}-${month}`;
|
||||||
|
const cell = document.getElementById(cellId);
|
||||||
if (cell) {
|
if (cell) {
|
||||||
const numericValue = value === '' ? 0 : parseFloat(value);
|
const numericValue = value === '' ? 0 : parseFloat(value);
|
||||||
cell.innerHTML = `€${numericValue.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
if (metric === 'Margin') {
|
||||||
|
cell.innerHTML = `${(numericValue * 100).toFixed(2)}%`;
|
||||||
|
} else {
|
||||||
|
cell.innerHTML = `€${numericValue.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -143,27 +143,30 @@ if ($project_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. Fetch and apply overridden data
|
// 3. Fetch and apply overridden data
|
||||||
$override_stmt = $pdo->prepare("SELECT month, MAX(CASE WHEN metricName = 'wip' THEN value ELSE 0 END) as wip, MAX(CASE WHEN metricName = 'opening_balance' THEN value ELSE 0 END) as opening_balance, MAX(CASE WHEN metricName = 'billing' THEN value ELSE 0 END) as billing, MAX(CASE WHEN metricName = 'expenses' THEN value ELSE 0 END) as expenses FROM projectFinanceMonthly WHERE projectId = :pid AND is_overridden = 1 GROUP BY month");
|
$override_stmt = $pdo->prepare("SELECT month, metricName, value FROM projectFinanceMonthly WHERE projectId = :pid AND is_overridden = 1");
|
||||||
$override_stmt->execute([':pid' => $project_id]);
|
$override_stmt->execute([':pid' => $project_id]);
|
||||||
|
$overridden_data = $override_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
while ($row = $override_stmt->fetch(PDO::FETCH_ASSOC)) {
|
$metric_name_map = [
|
||||||
|
'wip' => 'WIP',
|
||||||
|
'opening_balance' => 'Opening Balance',
|
||||||
|
'billing' => 'Billings',
|
||||||
|
'expenses' => 'Expenses',
|
||||||
|
'cost' => 'Cost',
|
||||||
|
'nsr' => 'NSR',
|
||||||
|
'margin' => 'Margin'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($overridden_data as $row) {
|
||||||
$month = $row['month'];
|
$month = $row['month'];
|
||||||
if (in_array($month, $months)) {
|
$metric_db_name = $row['metricName'];
|
||||||
$financial_data['WIP'][$month] = $row['wip'];
|
$value = $row['value'];
|
||||||
$financial_data['Opening Balance'][$month] = $row['opening_balance'];
|
|
||||||
$financial_data['Billings'][$month] = $row['billing'];
|
if (in_array($month, $months) && isset($metric_name_map[$metric_db_name])) {
|
||||||
$financial_data['Expenses'][$month] = $row['expenses'];
|
$metric_display_name = $metric_name_map[$metric_db_name];
|
||||||
|
$financial_data[$metric_display_name][$month] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Recalculate dependent metrics (NSR and Margin) after override
|
|
||||||
foreach ($months as $month) {
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,7 +279,7 @@ if (!$project) {
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php
|
||||||
$editable_metrics = ['WIP', 'Opening Balance', 'Billings', 'Expenses'];
|
$editable_metrics = ['WIP', 'Opening Balance', 'Billings', 'Expenses', 'Cost'];
|
||||||
foreach ($metrics as $metric): ?>
|
foreach ($metrics as $metric): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="fw-bold bg-body-tertiary"><?php echo $metric; ?></td>
|
<td class="fw-bold bg-body-tertiary"><?php echo $metric; ?></td>
|
||||||
|
|||||||
@ -14,6 +14,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
'opening_balance' => $_POST['openingBalance'] ?? null,
|
'opening_balance' => $_POST['openingBalance'] ?? null,
|
||||||
'billings' => $_POST['billings'] ?? null,
|
'billings' => $_POST['billings'] ?? null,
|
||||||
'expenses' => $_POST['expenses'] ?? null,
|
'expenses' => $_POST['expenses'] ?? null,
|
||||||
|
'cost' => $_POST['cost'] ?? null,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($project_id && $month && !in_array(null, $metrics, true)) {
|
if ($project_id && $month && !in_array(null, $metrics, true)) {
|
||||||
@ -28,7 +29,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
'wip' => 'wip',
|
'wip' => 'wip',
|
||||||
'opening_balance' => 'opening_balance',
|
'opening_balance' => 'opening_balance',
|
||||||
'billings' => 'billing',
|
'billings' => 'billing',
|
||||||
'expenses' => 'expenses'
|
'expenses' => 'expenses',
|
||||||
|
'cost' => 'cost'
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($metrics as $metricKey => $value) {
|
foreach ($metrics as $metricKey => $value) {
|
||||||
@ -43,7 +45,35 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = ['success' => true];
|
// Use the newly submitted values from POST as the source of truth for calculation
|
||||||
|
$wip = (float)($metrics['wip'] ?? 0);
|
||||||
|
$billings = (float)($metrics['billings'] ?? 0);
|
||||||
|
$opening_balance = (float)($metrics['opening_balance'] ?? 0);
|
||||||
|
$expenses = (float)($metrics['expenses'] ?? 0);
|
||||||
|
$cost = (float)($metrics['cost'] ?? 0);
|
||||||
|
|
||||||
|
// Calculate NSR
|
||||||
|
$nsr = $wip + $billings - $opening_balance - $expenses;
|
||||||
|
|
||||||
|
// Calculate Margin
|
||||||
|
$margin = ($nsr != 0) ? (($nsr - $cost) / $nsr) : 0;
|
||||||
|
|
||||||
|
// Save NSR and Margin
|
||||||
|
$stmt->execute([
|
||||||
|
':pid' => $project_id,
|
||||||
|
':m' => $month,
|
||||||
|
':metric' => 'nsr',
|
||||||
|
':value' => $nsr
|
||||||
|
]);
|
||||||
|
|
||||||
|
$stmt->execute([
|
||||||
|
':pid' => $project_id,
|
||||||
|
':m' => $month,
|
||||||
|
':metric' => 'margin',
|
||||||
|
':value' => $margin
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = ['success' => true, 'nsr' => $nsr, 'margin' => $margin];
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
$response['error'] = 'Database error: ' . $e->getMessage();
|
$response['error'] = 'Database error: ' . $e->getMessage();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user