diff --git a/app/accounting/api/engine/acquire_op_lock.php b/app/accounting/api/engine/acquire_op_lock.php index 3c61654..3009691 100644 --- a/app/accounting/api/engine/acquire_op_lock.php +++ b/app/accounting/api/engine/acquire_op_lock.php @@ -3,6 +3,9 @@ require_once __DIR__ . '/../../../session.php'; require_once '../../../assets/utils/db_auth.php'; require_once '../../../assets/utils/classes/OperationLockManager.php'; +// Batch GL posting takes this lock; posting itself is owner/admin only. +require_role($user_role, ['owner', 'admin']); + try { $lock_manager = new OperationLockManager($pdo2, $company_id, $user_id); $result = $lock_manager->acquire( @@ -10,7 +13,15 @@ try { (int)($data['ttl_minutes'] ?? 120) ); $answer = array_merge($answer, $result); + if (empty($result['success'])) { + http_response_code(409); // another tab or user holds the lock + } +} catch (PDOException $e) { + error_log('[acquire_op_lock] ' . $e->getMessage()); + http_response_code(500); + $answer['message'] = 'Database error, please try again.'; } catch (Exception $e) { + http_response_code(400); $answer['message'] = $e->getMessage(); } diff --git a/app/accounting/api/engine/get_gl_by_source.php b/app/accounting/api/engine/get_gl_by_source.php index e991094..0d1b675 100644 --- a/app/accounting/api/engine/get_gl_by_source.php +++ b/app/accounting/api/engine/get_gl_by_source.php @@ -8,6 +8,7 @@ $doc_type = trim((string)($data['doc_type'] ?? '')); $source_id = (int)($data['source_id'] ?? 0); if (!$doc_type || $source_id <= 0) { + http_response_code(400); $answer['message'] = 'doc_type and source_id required.'; exit(json_encode($answer)); } @@ -27,8 +28,13 @@ try { $answer['success'] = 1; $answer['output'] = $detail; +} catch (PDOException $e) { + error_log('[' . basename(__FILE__) . '] ' . $e->getMessage()); + $answer['message'] = 'Database error, please try again.'; + http_response_code(500); } catch (Exception $e) { $answer['message'] = $e->getMessage(); + http_response_code(400); } exit(json_encode($answer)); diff --git a/app/accounting/api/engine/get_gl_documents.php b/app/accounting/api/engine/get_gl_documents.php index e50e25c..efda76e 100644 --- a/app/accounting/api/engine/get_gl_documents.php +++ b/app/accounting/api/engine/get_gl_documents.php @@ -12,8 +12,13 @@ try { (int)($data['formula_id'] ?? 0) ); $answer['success'] = 1; +} catch (PDOException $e) { + error_log('[' . basename(__FILE__) . '] ' . $e->getMessage()); + $answer['message'] = 'Database error, please try again.'; + http_response_code(500); } catch (Exception $e) { $answer['message'] = $e->getMessage(); + http_response_code(400); } exit(json_encode($answer)); diff --git a/app/accounting/api/engine/get_gl_movement.php b/app/accounting/api/engine/get_gl_movement.php index c92652a..0d18de7 100644 --- a/app/accounting/api/engine/get_gl_movement.php +++ b/app/accounting/api/engine/get_gl_movement.php @@ -12,6 +12,7 @@ $to_date = trim((string)($data['to_date'] ?? ($data['to_period'] ?? $dept_id = (int)($data['department_id'] ?? 0); if ($account_code === '') { + http_response_code(400); $answer['message'] = 'account_code is required.'; exit(json_encode($answer)); } diff --git a/app/accounting/api/engine/get_journal_detail.php b/app/accounting/api/engine/get_journal_detail.php index 9904885..a087c0d 100644 --- a/app/accounting/api/engine/get_journal_detail.php +++ b/app/accounting/api/engine/get_journal_detail.php @@ -7,8 +7,13 @@ try { $gl_query = new GlQueryManager($pdo2, $company_id); $answer['output'] = $gl_query->getJournalDetail((int)($data['gl_id'] ?? 0)); $answer['success'] = 1; +} catch (PDOException $e) { + error_log('[' . basename(__FILE__) . '] ' . $e->getMessage()); + $answer['message'] = 'Database error, please try again.'; + http_response_code(500); } catch (Exception $e) { $answer['message'] = $e->getMessage(); + http_response_code(400); } exit(json_encode($answer)); diff --git a/app/accounting/api/engine/get_journal_listing.php b/app/accounting/api/engine/get_journal_listing.php index 86e44c0..9da1377 100644 --- a/app/accounting/api/engine/get_journal_listing.php +++ b/app/accounting/api/engine/get_journal_listing.php @@ -11,8 +11,13 @@ try { trim((string)($data['date_to'] ?? '')) ); $answer['success'] = 1; +} catch (PDOException $e) { + error_log('[' . basename(__FILE__) . '] ' . $e->getMessage()); + $answer['message'] = 'Database error, please try again.'; + http_response_code(500); } catch (Exception $e) { $answer['message'] = $e->getMessage(); + http_response_code(400); } exit(json_encode($answer)); diff --git a/app/accounting/api/engine/log_batch_action.php b/app/accounting/api/engine/log_batch_action.php index 5e3e7ed..de26a40 100644 --- a/app/accounting/api/engine/log_batch_action.php +++ b/app/accounting/api/engine/log_batch_action.php @@ -3,12 +3,20 @@ require_once __DIR__ . '/../../../session.php'; require_once '../../../assets/utils/db_auth.php'; require_once '../../../assets/utils/classes/BatchActionManager.php'; +// Logged at the end of a batch GL posting run, which is owner/admin only. +require_role($user_role, ['owner', 'admin']); + try { $batch_action = new BatchActionManager($pdo2, $company_id, $user_id); $batch_action->log($data); $answer['success'] = 1; $answer['message'] = 'Batch action logged.'; +} catch (PDOException $e) { + error_log('[log_batch_action] ' . $e->getMessage()); + http_response_code(500); + $answer['message'] = 'Database error, please try again.'; } catch (Exception $e) { + http_response_code(400); $answer['message'] = $e->getMessage(); } diff --git a/app/accounting/api/engine/manage_account.php b/app/accounting/api/engine/manage_account.php index d795e68..7ccc72b 100644 --- a/app/accounting/api/engine/manage_account.php +++ b/app/accounting/api/engine/manage_account.php @@ -6,6 +6,7 @@ require_once '../../../assets/utils/classes_ac/ChartOfAccounts.php'; require_role($user_role, ['owner', 'admin']); if (empty($data['account_code']) || empty($data['account_name']) || empty($data['account_type'])) { + http_response_code(400); $answer['message'] = 'Account code, name, and type are required'; exit(json_encode($answer)); } diff --git a/app/accounting/api/engine/manage_department.php b/app/accounting/api/engine/manage_department.php index d22e968..7e49dd9 100644 --- a/app/accounting/api/engine/manage_department.php +++ b/app/accounting/api/engine/manage_department.php @@ -6,6 +6,7 @@ require_once '../../../assets/utils/classes_ac/DepartmentManager.php'; require_role($user_role, ['owner', 'admin']); if (empty($data['dept_code']) || empty($data['dept_name'])) { + http_response_code(400); $answer['message'] = 'Department code and name are required'; exit(json_encode($answer)); } diff --git a/app/accounting/api/engine/post_gl_entry.php b/app/accounting/api/engine/post_gl_entry.php index 8ac4c85..84e7dc9 100644 --- a/app/accounting/api/engine/post_gl_entry.php +++ b/app/accounting/api/engine/post_gl_entry.php @@ -30,6 +30,7 @@ $posting_map = [ ]; if (!isset($posting_map[$doc_type]) || $id <= 0) { + http_response_code(400); $answer['message'] = 'Invalid doc_type or id.'; exit(json_encode($answer)); } @@ -73,9 +74,15 @@ try { 'has_expense' => $has_expense, ], $company_id); +} catch (PDOException $e) { + if ($pdo2->inTransaction()) $pdo2->rollBack(); + error_log('[' . basename(__FILE__) . '] ' . $e->getMessage()); + $answer['message'] = 'Database error, please try again.'; + http_response_code(500); } catch (Exception $e) { if ($pdo2->inTransaction()) $pdo2->rollBack(); $answer['message'] = $e->getMessage(); + http_response_code(400); } exit(json_encode($answer)); diff --git a/app/accounting/api/engine/posting_window.php b/app/accounting/api/engine/posting_window.php index b4ed19c..5b10426 100644 --- a/app/accounting/api/engine/posting_window.php +++ b/app/accounting/api/engine/posting_window.php @@ -30,14 +30,17 @@ if ($data['action'] === 'save') { $to = trim((string)($data['open_to'] ?? '')); if ($from !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $from)) { + http_response_code(400); $answer['message'] = 'Invalid open_from date. Use YYYY-MM-DD.'; exit(json_encode($answer)); } if ($to !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $to)) { + http_response_code(400); $answer['message'] = 'Invalid open_to date. Use YYYY-MM-DD.'; exit(json_encode($answer)); } if ($from && $to && $from > $to) { + http_response_code(400); $answer['message'] = 'Open From must be on or before Open To.'; exit(json_encode($answer)); } @@ -50,5 +53,6 @@ if ($data['action'] === 'save') { exit(json_encode($answer)); } +http_response_code(400); $answer['message'] = 'Invalid action.'; exit(json_encode($answer)); diff --git a/app/accounting/api/engine/release_op_lock.php b/app/accounting/api/engine/release_op_lock.php index a63fa52..3e5ecc5 100644 --- a/app/accounting/api/engine/release_op_lock.php +++ b/app/accounting/api/engine/release_op_lock.php @@ -3,12 +3,20 @@ require_once __DIR__ . '/../../../session.php'; require_once '../../../assets/utils/db_auth.php'; require_once '../../../assets/utils/classes/OperationLockManager.php'; +// Batch GL posting takes this lock; posting itself is owner/admin only. +require_role($user_role, ['owner', 'admin']); + try { $lock_manager = new OperationLockManager($pdo2, $company_id, $user_id); $lock_manager->release(trim((string)($data['operation_type'] ?? ''))); $answer['success'] = 1; $answer['message'] = 'Lock released.'; +} catch (PDOException $e) { + error_log('[release_op_lock] ' . $e->getMessage()); + http_response_code(500); + $answer['message'] = 'Database error, please try again.'; } catch (Exception $e) { + http_response_code(400); $answer['message'] = $e->getMessage(); } diff --git a/app/accounting/api/engine/remove_account.php b/app/accounting/api/engine/remove_account.php index 6d3909a..7db6434 100644 --- a/app/accounting/api/engine/remove_account.php +++ b/app/accounting/api/engine/remove_account.php @@ -7,6 +7,7 @@ require_role($user_role, ['owner', 'admin']); $id = (int)($data['id'] ?? 0); if (!$id) { + http_response_code(400); $answer['message'] = 'Missing id'; exit(json_encode($answer)); } diff --git a/app/accounting/api/engine/remove_department.php b/app/accounting/api/engine/remove_department.php index d913463..19a856f 100644 --- a/app/accounting/api/engine/remove_department.php +++ b/app/accounting/api/engine/remove_department.php @@ -6,7 +6,7 @@ require_once '../../../assets/utils/classes_ac/DepartmentManager.php'; require_role($user_role, ['owner', 'admin']); $id = (int)($data['id'] ?? 0); -if (!$id) { $answer['message'] = 'Missing id'; exit(json_encode($answer)); } +if (!$id) { http_response_code(400); $answer['message'] = 'Missing id'; exit(json_encode($answer)); } $dept = new DepartmentManager($pdo2, $company_id); $dept->delete($id); diff --git a/app/accounting/api/engine/retrieve_account.php b/app/accounting/api/engine/retrieve_account.php index 68d41b9..6cc0b09 100644 --- a/app/accounting/api/engine/retrieve_account.php +++ b/app/accounting/api/engine/retrieve_account.php @@ -5,6 +5,7 @@ require_once '../../../assets/utils/classes_ac/ChartOfAccounts.php'; $id = (int)($data['id'] ?? 0); if (!$id) { + http_response_code(400); $answer['message'] = 'Missing id'; exit(json_encode($answer)); } @@ -12,6 +13,7 @@ if (!$id) { $coa = new ChartOfAccounts($pdo2, $company_id); $row = $coa->getById($id); if (!$row) { + http_response_code(404); $answer['message'] = 'Account not found'; exit(json_encode($answer)); } diff --git a/app/accounting/api/engine/retrieve_department.php b/app/accounting/api/engine/retrieve_department.php index 485b9ee..a7bacbb 100644 --- a/app/accounting/api/engine/retrieve_department.php +++ b/app/accounting/api/engine/retrieve_department.php @@ -4,11 +4,11 @@ require_once '../../../assets/utils/db_auth.php'; require_once '../../../assets/utils/classes_ac/DepartmentManager.php'; $id = (int)($data['id'] ?? 0); -if (!$id) { $answer['message'] = 'Missing id'; exit(json_encode($answer)); } +if (!$id) { http_response_code(400); $answer['message'] = 'Missing id'; exit(json_encode($answer)); } $dept = new DepartmentManager($pdo2, $company_id); $row = $dept->getById($id); -if (!$row) { $answer['message'] = 'Department not found'; exit(json_encode($answer)); } +if (!$row) { http_response_code(404); $answer['message'] = 'Department not found'; exit(json_encode($answer)); } $answer['output'] = $row; $answer['success'] = 1; diff --git a/app/accounting/api/engine/save_manual_journal.php b/app/accounting/api/engine/save_manual_journal.php index 73ff8e8..da70da6 100644 --- a/app/accounting/api/engine/save_manual_journal.php +++ b/app/accounting/api/engine/save_manual_journal.php @@ -25,6 +25,7 @@ if (preg_match('#^(\d{2})/(\d{2})/(\d{4})$#', $journal_date, $m)) { } if (!$journal_date || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $journal_date)) { + http_response_code(400); $answer['message'] = 'Valid journal date is required.'; exit(json_encode($answer)); } @@ -51,11 +52,13 @@ foreach ($lines_raw as $l) { } if (count($lines) < 2) { + http_response_code(400); $answer['message'] = 'At least two journal lines are required.'; exit(json_encode($answer)); } if (abs($total_debit - $total_credit) > 0.005) { + http_response_code(400); $answer['message'] = 'Journal is not balanced. Debit ' . number_format($total_debit, 2) . ' ≠ Credit ' . number_format($total_credit, 2) . '.'; exit(json_encode($answer)); } @@ -83,9 +86,15 @@ try { $answer['success'] = 1; $answer['gl_id'] = $gl_id; notify_node('gl_posted', gl_posted_payload('manual', (int)$gl_id, $event_action, $lines), $company_id); +} catch (PDOException $e) { + if ($pdo2->inTransaction()) $pdo2->rollBack(); + error_log('[' . basename(__FILE__) . '] ' . $e->getMessage()); + $answer['message'] = 'Database error, please try again.'; + http_response_code(500); } catch (Exception $e) { if ($pdo2->inTransaction()) $pdo2->rollBack(); $answer['message'] = $e->getMessage(); + http_response_code(400); } exit(json_encode($answer)); diff --git a/app/contact/api/engine/manage_contact.php b/app/contact/api/engine/manage_contact.php index 7b5a273..4adff95 100644 --- a/app/contact/api/engine/manage_contact.php +++ b/app/contact/api/engine/manage_contact.php @@ -20,6 +20,7 @@ $errors = $uploader->upload('contact_image'); if (!empty($errors)) { $answer['success'] = 0; + http_response_code(400); $answer['message'] = $errors[0]; exit(json_encode($answer)); } diff --git a/app/expense/api/engine/convert_purchase_request.php b/app/expense/api/engine/convert_purchase_request.php index 010e0bd..50612ce 100644 --- a/app/expense/api/engine/convert_purchase_request.php +++ b/app/expense/api/engine/convert_purchase_request.php @@ -12,6 +12,7 @@ $discount = (float)($data['discount'] ?? 0); $shipping_fee = (float)($data['shipping_fee'] ?? 0); if (!$request_id) { + http_response_code(400); $answer['message'] = 'Purchase request ID is required.'; exit(json_encode($answer)); } @@ -21,6 +22,7 @@ $prm = new PurchaseRequestManager($pdo2, $company_id); // Load PR — must be Approved $pr = $prm->getById($request_id); if (!$pr || (int)$pr['status'] !== 2) { + http_response_code(400); $answer['message'] = 'Purchase request not found or not in Approved status.'; exit(json_encode($answer)); } @@ -30,17 +32,20 @@ if (!$contact_id) { $contact_id = (int)($pr['contact_id'] ?? 0); } if (!$contact_id) { + http_response_code(400); $answer['message'] = 'Set a Preferred Supplier on this purchase request before converting it to a PO.'; exit(json_encode($answer)); } $pr_items = $pr['items']; if (empty($pr_items)) { + http_response_code(400); $answer['message'] = 'Purchase request has no items.'; exit(json_encode($answer)); } if ((float)$pr['total_remaining'] <= 0.000001) { + http_response_code(400); $answer['message'] = 'Purchase request is already fully converted.'; exit(json_encode($answer)); } @@ -72,6 +77,7 @@ foreach ($convert_items as $ci) { if ($qty <= 0) continue; if (!isset($pr_by_id[$item_id])) { + http_response_code(400); $answer['message'] = "Item #{$item_id} not found in this purchase request."; exit(json_encode($answer)); } @@ -81,6 +87,7 @@ foreach ($convert_items as $ci) { if ($qty - $remaining > 0.000001) { $name = $pi['product_name'] ?: $pi['product_sku']; + http_response_code(400); $answer['message'] = "Cannot convert {$qty} for \"{$name}\": only {$remaining} remaining."; exit(json_encode($answer)); } @@ -105,6 +112,7 @@ foreach ($convert_items as $ci) { } if (empty($po_items)) { + http_response_code(400); $answer['message'] = 'No valid items to convert.'; exit(json_encode($answer)); } diff --git a/app/expense/api/engine/update_purchase_request_status.php b/app/expense/api/engine/update_purchase_request_status.php index 85c7035..c2fb1d2 100644 --- a/app/expense/api/engine/update_purchase_request_status.php +++ b/app/expense/api/engine/update_purchase_request_status.php @@ -8,6 +8,7 @@ $id = (int)($data['id'] ?? 0); $action = $data['action_type'] ?? ''; if (!$id || !$action) { + http_response_code(400); $answer['message'] = 'ID and action are required.'; exit(json_encode($answer)); } diff --git a/app/ics/api/engine_report/warehouse_overview.php b/app/ics/api/engine_report/warehouse_overview.php index 0961245..695c399 100644 --- a/app/ics/api/engine_report/warehouse_overview.php +++ b/app/ics/api/engine_report/warehouse_overview.php @@ -8,6 +8,7 @@ if (!$warehouse_id) { $answer['success'] = 0; + http_response_code(400); $answer['message'] = 'Warehouse is required'; exit(json_encode($answer)); } diff --git a/app/inventory/api/engine/manage_product.php b/app/inventory/api/engine/manage_product.php index b18eb81..49ce5d4 100644 --- a/app/inventory/api/engine/manage_product.php +++ b/app/inventory/api/engine/manage_product.php @@ -37,6 +37,7 @@ $errors = $uploader->upload('product_image'); if (!empty($errors)) { $answer['success'] = 0; + http_response_code(400); $answer['message'] = $errors[0]; exit(json_encode($answer)); } diff --git a/app/inventory/api/engine/manager.php b/app/inventory/api/engine/manager.php index 498d7ac..d5cd82f 100644 --- a/app/inventory/api/engine/manager.php +++ b/app/inventory/api/engine/manager.php @@ -9,8 +9,13 @@ $users = new UserManager($pdo1, $company_id, $user_id); $answer['output'] = $users->getCompanyUsers(); $answer['success'] = 1; + } catch (PDOException $e) { + error_log('[' . basename(__FILE__) . '] ' . $e->getMessage()); + $answer['message'] = 'Database error, please try again.'; + http_response_code(500); } catch (Exception $e) { $answer['message'] = $e->getMessage(); + http_response_code(400); } exit(json_encode($answer)); diff --git a/app/reports/api/engine_report/stock_movement.php b/app/reports/api/engine_report/stock_movement.php index 96e0c8f..9e786a7 100644 --- a/app/reports/api/engine_report/stock_movement.php +++ b/app/reports/api/engine_report/stock_movement.php @@ -12,6 +12,7 @@ $to_month = $data['to_month'] ?? ''; if (!$warehouse_id || !$from_month || !$to_month) { + http_response_code(400); $answer['message'] = 'warehouse_id, from_month, and to_month are required.'; exit(json_encode($answer)); } diff --git a/app/reports/api/engine_report/stock_movement_sku.php b/app/reports/api/engine_report/stock_movement_sku.php index 1b1c7d6..78b5030 100644 --- a/app/reports/api/engine_report/stock_movement_sku.php +++ b/app/reports/api/engine_report/stock_movement_sku.php @@ -13,6 +13,7 @@ $to_month = $data['to_month'] ?? ''; if (!$warehouse_id || !$product_sku || !$from_month || !$to_month) { + http_response_code(400); $answer['message'] = 'warehouse_id, product_sku, from_month, and to_month are required.'; exit(json_encode($answer)); } diff --git a/app/revenue/api/engine/convert_quotation.php b/app/revenue/api/engine/convert_quotation.php index 1451111..61b0a67 100644 --- a/app/revenue/api/engine/convert_quotation.php +++ b/app/revenue/api/engine/convert_quotation.php @@ -10,6 +10,7 @@ $quotation_id = (int)($data['quotation_id'] ?? 0); $convert_items = $data['convert_items'] ?? []; if (!$quotation_id) { + http_response_code(400); $answer['message'] = 'Quotation ID required.'; exit(json_encode($answer)); } @@ -19,17 +20,20 @@ $qm = new QuotationManager($pdo2, $company_id); // Load quotation — must be Accepted $q = $qm->getById($quotation_id); if (!$q || (int)$q['status'] !== 2) { + http_response_code(400); $answer['message'] = 'Quotation not found or not in Accepted status.'; exit(json_encode($answer)); } $qt_items = $q['items']; if (empty($qt_items)) { + http_response_code(400); $answer['message'] = 'Quotation has no items.'; exit(json_encode($answer)); } if ((float)$q['total_remaining'] <= 0.000001) { + http_response_code(400); $answer['message'] = 'Quotation is already fully converted.'; exit(json_encode($answer)); } @@ -61,6 +65,7 @@ foreach ($convert_items as $ci) { if ($qty <= 0) continue; if (!isset($qt_by_id[$item_id])) { + http_response_code(400); $answer['message'] = "Item #{$item_id} not found in this quotation."; exit(json_encode($answer)); } @@ -70,6 +75,7 @@ foreach ($convert_items as $ci) { if ($qty - $remaining > 0.000001) { $name = $qi['product_name'] ?: $qi['product_sku']; + http_response_code(400); $answer['message'] = "Cannot convert {$qty} for \"{$name}\": only {$remaining} remaining."; exit(json_encode($answer)); } @@ -95,6 +101,7 @@ foreach ($convert_items as $ci) { } if (empty($ord_items)) { + http_response_code(400); $answer['message'] = 'No valid items to convert.'; exit(json_encode($answer)); } diff --git a/app/revenue/api/engine/manage_order.php b/app/revenue/api/engine/manage_order.php index 478452e..3d591a2 100644 --- a/app/revenue/api/engine/manage_order.php +++ b/app/revenue/api/engine/manage_order.php @@ -10,11 +10,13 @@ $action = $data['action'] ?? ''; $data['items'] = json_decode($data['items'] ?? '[]', true) ?: []; if (!in_array($action, ['create', 'update'], true)) { + http_response_code(400); $answer['message'] = 'Unknown action'; exit(json_encode($answer)); } if (empty($data['items'])) { + http_response_code(400); $answer['message'] = 'At least one item is required'; exit(json_encode($answer)); } diff --git a/app/revenue/api/engine/quotation_stats.php b/app/revenue/api/engine/quotation_stats.php index d818c02..086be77 100644 --- a/app/revenue/api/engine/quotation_stats.php +++ b/app/revenue/api/engine/quotation_stats.php @@ -7,8 +7,13 @@ try { $quotation = new QuotationManager($pdo2, $company_id); $answer['output'] = $quotation->getStats(); $answer['success'] = 1; +} catch (PDOException $e) { + error_log('[' . basename(__FILE__) . '] ' . $e->getMessage()); + $answer['message'] = 'Database error, please try again.'; + http_response_code(500); } catch (Exception $e) { $answer['message'] = $e->getMessage(); + http_response_code(400); } exit(json_encode($answer)); diff --git a/app/revenue/api/engine/update_quotation_status.php b/app/revenue/api/engine/update_quotation_status.php index 58243f1..7a7867e 100644 --- a/app/revenue/api/engine/update_quotation_status.php +++ b/app/revenue/api/engine/update_quotation_status.php @@ -9,6 +9,7 @@ $id = (int)($data['id'] ?? 0); $action = $data['action_type'] ?? ''; if (!$id || !$action) { + http_response_code(400); $answer['message'] = 'ID and action are required.'; exit(json_encode($answer)); } diff --git a/app/setting/api/engine/document_types.php b/app/setting/api/engine/document_types.php index dd7c576..40b1a26 100644 --- a/app/setting/api/engine/document_types.php +++ b/app/setting/api/engine/document_types.php @@ -38,10 +38,16 @@ try { break; default: + http_response_code(400); $answer['message'] = 'Unknown action.'; } +} catch (PDOException $e) { + error_log('[' . basename(__FILE__) . '] ' . $e->getMessage()); + $answer['message'] = 'Database error, please try again.'; + http_response_code(500); } catch (Exception $e) { $answer['message'] = $e->getMessage(); + http_response_code(400); } exit(json_encode($answer)); diff --git a/app/setting/api/engine/gl_maintenance.php b/app/setting/api/engine/gl_maintenance.php index 4b5d26f..4c75f5d 100644 --- a/app/setting/api/engine/gl_maintenance.php +++ b/app/setting/api/engine/gl_maintenance.php @@ -34,6 +34,7 @@ try { if ($action === 'rebuild_period') { $period = $data['period'] ?? ''; if (!preg_match('/^\d{4}-\d{2}$/', $period)) { + http_response_code(400); $answer['message'] = 'Invalid period.'; exit(json_encode($answer)); } diff --git a/app/setting/api/engine/retrieve_smtp.php b/app/setting/api/engine/retrieve_smtp.php index 5a34ec7..0eada3e 100644 --- a/app/setting/api/engine/retrieve_smtp.php +++ b/app/setting/api/engine/retrieve_smtp.php @@ -3,13 +3,17 @@ require_once '../../../assets/utils/db_auth.php'; require_once '../../../assets/utils/classes/SmtpManager.php'; + // Admin data: owners and admins only; staff and viewers never see it. + require_role($user_role, ['owner', 'admin']); + try { $smtp = new SmtpManager($pdo1, $company_id, $method, $pinkey, $iv); $answer['success'] = 1; $answer['output'] = $smtp->get(); } catch (Exception $e) { - $answer['message'] = $e->getMessage(); + error_log('[retrieve_smtp] ' . $e->getMessage()); + $answer['message'] = 'Could not load SMTP settings.'; http_response_code(500); } diff --git a/app/setting/api/engine/retrieve_users.php b/app/setting/api/engine/retrieve_users.php index e3f9b4e..f7eb8ae 100644 --- a/app/setting/api/engine/retrieve_users.php +++ b/app/setting/api/engine/retrieve_users.php @@ -3,6 +3,9 @@ require_once '../../../assets/utils/db_auth.php'; require_once '../../../assets/utils/classes/UserManager.php'; + // Admin data: owners and admins only; staff and viewers never see it. + require_role($user_role, ['owner', 'admin']); + $um = new UserManager($pdo1, $company_id, $user_id); $answer['success'] = 1; diff --git a/app/setting/api/engine/search_users.php b/app/setting/api/engine/search_users.php index b0c6941..49a51a8 100644 --- a/app/setting/api/engine/search_users.php +++ b/app/setting/api/engine/search_users.php @@ -3,6 +3,9 @@ require_once '../../../assets/utils/db_auth.php'; require_once '../../../assets/utils/classes/UserManager.php'; + // Admin data: owners and admins only; staff and viewers never see it. + require_role($user_role, ['owner', 'admin']); + $keyword = trim($data['keyword'] ?? ''); $um = new UserManager($pdo1, $company_id, $user_id); diff --git a/app/setting/api/engine/stock_maintenance.php b/app/setting/api/engine/stock_maintenance.php index dbd66f9..f8e5ef1 100644 --- a/app/setting/api/engine/stock_maintenance.php +++ b/app/setting/api/engine/stock_maintenance.php @@ -36,6 +36,7 @@ try { if ($action === 'get_periods') { $warehouse_id = (int)($data['warehouse_id'] ?? 0); if (!$warehouse_id) { + http_response_code(400); $answer['message'] = 'warehouse_id is required.'; exit(json_encode($answer)); } @@ -48,6 +49,7 @@ try { $warehouse_id = (int)($data['warehouse_id'] ?? 0); $period = $data['period'] ?? ''; if (!$warehouse_id || !preg_match('/^\d{4}-\d{2}$/', $period)) { + http_response_code(400); $answer['message'] = 'Valid warehouse_id and period (YYYY-MM) are required.'; exit(json_encode($answer)); }