|
- >
+ | >
false, 'error' => 'Invalid request'];
+// Function to send a consistent JSON response, aggressively cleaning any output.
+function send_json_response($data) {
+ // Ensure no stray output is included.
+ if (ob_get_level() > 0) {
+ ob_end_clean();
+ }
+ header('Content-Type: application/json');
+ echo json_encode($data);
+ exit(); // Terminate immediately
+}
-if ($_SERVER['REQUEST_METHOD'] === 'POST') {
- $project_id = $_POST['projectId'] ?? null;
- $month = $_POST['month'] ?? null;
+// Set a global exception handler to catch any uncaught errors
+set_exception_handler(function($exception) {
+ error_log($exception->getMessage());
+ send_json_response(['success' => false, 'message' => 'A server error occurred: ' . $exception->getMessage()]);
+});
- $metrics = [
- 'wip' => $_POST['wip'] ?? null,
- 'opening_balance' => $_POST['openingBalance'] ?? null,
- 'billings' => $_POST['billings'] ?? null,
- 'expenses' => $_POST['expenses'] ?? null,
- 'cost' => $_POST['cost'] ?? null,
+$response_data = [
+ 'success' => false,
+ 'message' => 'An unknown error occurred.',
+ // Initialize all fields JS expects to avoid 'undefined' issues
+ 'wip' => 0,
+ 'openingBalance' => 0,
+ 'billings' => 0,
+ 'expenses' => 0,
+ 'cost' => 0,
+ 'nsr' => 0,
+ 'margin' => 0,
+];
+
+ob_clean(); // Clear any pre-existing output
+try {
+ $data = $_POST;
+
+ $projectId = $data['projectId'] ?? null;
+ $month = $data['month'] ?? null;
+
+ if (!$projectId || !$month) {
+ send_json_response(['success' => false, 'message' => 'Missing project ID or month.']);
+ }
+
+ $pdo = db();
+
+ $metrics_from_form = [
+ 'wip' => $data['wip'] ?? 0,
+ 'opening_balance' => $data['openingBalance'] ?? 0,
+ 'billing' => $data['billings'] ?? 0,
+ 'expenses' => $data['expenses'] ?? 0,
+ 'cost' => $data['cost'] ?? 0,
];
- if ($project_id && $month && !in_array(null, $metrics, true)) {
- try {
- $pdo = db();
- $sql = "INSERT INTO projectFinanceMonthly (projectId, month, metricName, value, is_overridden)
- VALUES (:pid, :m, :metric, :value, 1)
- ON DUPLICATE KEY UPDATE value = :value, is_overridden = 1";
- $stmt = $pdo->prepare($sql);
+ $pdo->beginTransaction();
- $metric_map = [
- 'wip' => 'wip',
- 'opening_balance' => 'opening_balance',
- 'billings' => 'billing',
- 'expenses' => 'expenses',
- 'cost' => 'cost'
- ];
+ // Check if records exist, if not, INSERT them. Otherwise, UPDATE.
+ $select_sql = "SELECT COUNT(*) FROM projectFinanceMonthly WHERE projectId = :projectId AND month = :month AND metricName = :metricName";
+ $select_stmt = $pdo->prepare($select_sql);
- foreach ($metrics as $metricKey => $value) {
- if (isset($metric_map[$metricKey])) {
- $metricName = $metric_map[$metricKey];
- $stmt->execute([
- ':pid' => $project_id,
- ':m' => $month,
- ':metric' => $metricName,
- ':value' => $value
- ]);
- }
- }
+ $update_sql = "UPDATE projectFinanceMonthly SET value = :value, is_overridden = 1 WHERE projectId = :projectId AND month = :month AND metricName = :metricName";
+ $update_stmt = $pdo->prepare($update_sql);
- // 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);
+ $insert_sql = "INSERT INTO projectFinanceMonthly (projectId, month, metricName, value, is_overridden) VALUES (:projectId, :month, :metricName, :value, 1)";
+ $insert_stmt = $pdo->prepare($insert_sql);
- // Calculate NSR
- $nsr = $wip + $billings - $opening_balance - $expenses;
+ foreach ($metrics_from_form as $metricName => $value) {
+ $select_stmt->execute([
+ 'projectId' => $projectId,
+ 'month' => $month,
+ 'metricName' => $metricName
+ ]);
+ $exists = $select_stmt->fetchColumn() > 0;
- // Calculate Margin
- $margin = ($nsr != 0) ? (($nsr - $cost) / $nsr) : 0;
-
- // Save NSR and Margin
- $stmt->execute([
- ':pid' => $project_id,
- ':m' => $month,
- ':metric' => 'nsr',
- ':value' => $nsr
+ if ($exists) {
+ $update_stmt->execute([
+ 'value' => $value,
+ 'projectId' => $projectId,
+ 'month' => $month,
+ 'metricName' => $metricName,
]);
-
- $stmt->execute([
- ':pid' => $project_id,
- ':m' => $month,
- ':metric' => 'margin',
- ':value' => $margin
+ } else {
+ $insert_stmt->execute([
+ 'projectId' => $projectId,
+ 'month' => $month,
+ 'metricName' => $metricName,
+ 'value' => $value,
]);
-
- $response = [
- 'success' => true,
- 'wip' => $wip,
- 'openingBalance' => $opening_balance,
- 'billings' => $billings,
- 'expenses' => $expenses,
- 'cost' => $cost,
- 'nsr' => $nsr,
- 'margin' => $margin
- ];
-
- } catch (PDOException $e) {
- $response['error'] = 'Database error: ' . $e->getMessage();
- error_log($e->getMessage());
}
- } else {
- $response['error'] = 'Missing required fields.';
}
-}
-try {
- echo json_encode($response, JSON_THROW_ON_ERROR);
-} catch (JsonException $e) {
- error_log('JSON encoding error: ' . $e->getMessage());
- http_response_code(500);
- echo json_encode(['success' => false, 'error' => 'Internal server error during JSON encoding.']);
+ $pdo->commit();
+
+ // Recalculate NSR and Margin
+ $billing = floatval($metrics_from_form['billing']);
+ $expenses = floatval($metrics_from_form['expenses']);
+ $cost = floatval($metrics_from_form['cost']);
+ $wip = floatval($metrics_from_form['wip']);
+ $opening_balance = floatval($metrics_from_form['opening_balance']);
+
+ $nsr = $wip + $billing - $opening_balance - $expenses;
+ $margin = ($nsr > 0) ? (($nsr - $cost) / $nsr) : 0;
+
+
+ // Fetch the updated data to send back
+ $stmt = $pdo->prepare("SELECT metricName, value FROM projectFinanceMonthly WHERE projectId = ? AND month = ?");
+ $stmt->execute([$projectId, $month]);
+ $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ $response_data['success'] = true;
+ $response_data['message'] = 'Override saved successfully.';
+
+ foreach ($rows as $row) {
+ // Map db snake_case to form's camelCase
+ if ($row['metricName'] === 'opening_balance') {
+ $response_data['openingBalance'] = $row['value'];
+ } else if ($row['metricName'] === 'billing') {
+ $response_data['billings'] = $row['value'];
+ } else {
+ // Directly map other metrics like 'wip', 'expenses'
+ $response_data[$row['metricName']] = $row['value'];
+ }
+ }
+
+ $response_data['nsr'] = $nsr;
+ $response_data['margin'] = $margin;
+
+ send_json_response($response_data);
+
+} catch (Exception $e) {
+ if (isset($pdo) && $pdo->inTransaction()) {
+ $pdo->rollBack();
+ }
+ // Log the error and send a JSON response directly
+ error_log($e->getMessage());
+ send_json_response(['success' => false, 'message' => 'A server error occurred: ' . $e->getMessage()]);
}
-exit;
\ No newline at end of file
|