Enforce roles on admin endpoints and return real status codes
- users, SMTP and batch-lock endpoints are owner/admin only - engines answer 400/403/404/409/500 instead of 200 with an error body; database errors no longer leak to the client
This commit is contained in:
@@ -3,6 +3,9 @@ require_once __DIR__ . '/../../../session.php';
|
|||||||
require_once '../../../assets/utils/db_auth.php';
|
require_once '../../../assets/utils/db_auth.php';
|
||||||
require_once '../../../assets/utils/classes/OperationLockManager.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 {
|
try {
|
||||||
$lock_manager = new OperationLockManager($pdo2, $company_id, $user_id);
|
$lock_manager = new OperationLockManager($pdo2, $company_id, $user_id);
|
||||||
$result = $lock_manager->acquire(
|
$result = $lock_manager->acquire(
|
||||||
@@ -10,7 +13,15 @@ try {
|
|||||||
(int)($data['ttl_minutes'] ?? 120)
|
(int)($data['ttl_minutes'] ?? 120)
|
||||||
);
|
);
|
||||||
$answer = array_merge($answer, $result);
|
$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) {
|
} catch (Exception $e) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = $e->getMessage();
|
$answer['message'] = $e->getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ $doc_type = trim((string)($data['doc_type'] ?? ''));
|
|||||||
$source_id = (int)($data['source_id'] ?? 0);
|
$source_id = (int)($data['source_id'] ?? 0);
|
||||||
|
|
||||||
if (!$doc_type || $source_id <= 0) {
|
if (!$doc_type || $source_id <= 0) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'doc_type and source_id required.';
|
$answer['message'] = 'doc_type and source_id required.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -27,8 +28,13 @@ try {
|
|||||||
|
|
||||||
$answer['success'] = 1;
|
$answer['success'] = 1;
|
||||||
$answer['output'] = $detail;
|
$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) {
|
} catch (Exception $e) {
|
||||||
$answer['message'] = $e->getMessage();
|
$answer['message'] = $e->getMessage();
|
||||||
|
http_response_code(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
|
|||||||
@@ -12,8 +12,13 @@ try {
|
|||||||
(int)($data['formula_id'] ?? 0)
|
(int)($data['formula_id'] ?? 0)
|
||||||
);
|
);
|
||||||
$answer['success'] = 1;
|
$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) {
|
} catch (Exception $e) {
|
||||||
$answer['message'] = $e->getMessage();
|
$answer['message'] = $e->getMessage();
|
||||||
|
http_response_code(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ $to_date = trim((string)($data['to_date'] ?? ($data['to_period'] ??
|
|||||||
$dept_id = (int)($data['department_id'] ?? 0);
|
$dept_id = (int)($data['department_id'] ?? 0);
|
||||||
|
|
||||||
if ($account_code === '') {
|
if ($account_code === '') {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'account_code is required.';
|
$answer['message'] = 'account_code is required.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,13 @@ try {
|
|||||||
$gl_query = new GlQueryManager($pdo2, $company_id);
|
$gl_query = new GlQueryManager($pdo2, $company_id);
|
||||||
$answer['output'] = $gl_query->getJournalDetail((int)($data['gl_id'] ?? 0));
|
$answer['output'] = $gl_query->getJournalDetail((int)($data['gl_id'] ?? 0));
|
||||||
$answer['success'] = 1;
|
$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) {
|
} catch (Exception $e) {
|
||||||
$answer['message'] = $e->getMessage();
|
$answer['message'] = $e->getMessage();
|
||||||
|
http_response_code(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
|
|||||||
@@ -11,8 +11,13 @@ try {
|
|||||||
trim((string)($data['date_to'] ?? ''))
|
trim((string)($data['date_to'] ?? ''))
|
||||||
);
|
);
|
||||||
$answer['success'] = 1;
|
$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) {
|
} catch (Exception $e) {
|
||||||
$answer['message'] = $e->getMessage();
|
$answer['message'] = $e->getMessage();
|
||||||
|
http_response_code(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
|
|||||||
@@ -3,12 +3,20 @@ require_once __DIR__ . '/../../../session.php';
|
|||||||
require_once '../../../assets/utils/db_auth.php';
|
require_once '../../../assets/utils/db_auth.php';
|
||||||
require_once '../../../assets/utils/classes/BatchActionManager.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 {
|
try {
|
||||||
$batch_action = new BatchActionManager($pdo2, $company_id, $user_id);
|
$batch_action = new BatchActionManager($pdo2, $company_id, $user_id);
|
||||||
$batch_action->log($data);
|
$batch_action->log($data);
|
||||||
$answer['success'] = 1;
|
$answer['success'] = 1;
|
||||||
$answer['message'] = 'Batch action logged.';
|
$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) {
|
} catch (Exception $e) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = $e->getMessage();
|
$answer['message'] = $e->getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ require_once '../../../assets/utils/classes_ac/ChartOfAccounts.php';
|
|||||||
require_role($user_role, ['owner', 'admin']);
|
require_role($user_role, ['owner', 'admin']);
|
||||||
|
|
||||||
if (empty($data['account_code']) || empty($data['account_name']) || empty($data['account_type'])) {
|
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';
|
$answer['message'] = 'Account code, name, and type are required';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ require_once '../../../assets/utils/classes_ac/DepartmentManager.php';
|
|||||||
require_role($user_role, ['owner', 'admin']);
|
require_role($user_role, ['owner', 'admin']);
|
||||||
|
|
||||||
if (empty($data['dept_code']) || empty($data['dept_name'])) {
|
if (empty($data['dept_code']) || empty($data['dept_name'])) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Department code and name are required';
|
$answer['message'] = 'Department code and name are required';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ $posting_map = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
if (!isset($posting_map[$doc_type]) || $id <= 0) {
|
if (!isset($posting_map[$doc_type]) || $id <= 0) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Invalid doc_type or id.';
|
$answer['message'] = 'Invalid doc_type or id.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -73,9 +74,15 @@ try {
|
|||||||
'has_expense' => $has_expense,
|
'has_expense' => $has_expense,
|
||||||
], $company_id);
|
], $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) {
|
} catch (Exception $e) {
|
||||||
if ($pdo2->inTransaction()) $pdo2->rollBack();
|
if ($pdo2->inTransaction()) $pdo2->rollBack();
|
||||||
$answer['message'] = $e->getMessage();
|
$answer['message'] = $e->getMessage();
|
||||||
|
http_response_code(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
|
|||||||
@@ -30,14 +30,17 @@ if ($data['action'] === 'save') {
|
|||||||
$to = trim((string)($data['open_to'] ?? ''));
|
$to = trim((string)($data['open_to'] ?? ''));
|
||||||
|
|
||||||
if ($from !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $from)) {
|
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.';
|
$answer['message'] = 'Invalid open_from date. Use YYYY-MM-DD.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
if ($to !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $to)) {
|
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.';
|
$answer['message'] = 'Invalid open_to date. Use YYYY-MM-DD.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
if ($from && $to && $from > $to) {
|
if ($from && $to && $from > $to) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Open From must be on or before Open To.';
|
$answer['message'] = 'Open From must be on or before Open To.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -50,5 +53,6 @@ if ($data['action'] === 'save') {
|
|||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Invalid action.';
|
$answer['message'] = 'Invalid action.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
|
|||||||
@@ -3,12 +3,20 @@ require_once __DIR__ . '/../../../session.php';
|
|||||||
require_once '../../../assets/utils/db_auth.php';
|
require_once '../../../assets/utils/db_auth.php';
|
||||||
require_once '../../../assets/utils/classes/OperationLockManager.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 {
|
try {
|
||||||
$lock_manager = new OperationLockManager($pdo2, $company_id, $user_id);
|
$lock_manager = new OperationLockManager($pdo2, $company_id, $user_id);
|
||||||
$lock_manager->release(trim((string)($data['operation_type'] ?? '')));
|
$lock_manager->release(trim((string)($data['operation_type'] ?? '')));
|
||||||
$answer['success'] = 1;
|
$answer['success'] = 1;
|
||||||
$answer['message'] = 'Lock released.';
|
$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) {
|
} catch (Exception $e) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = $e->getMessage();
|
$answer['message'] = $e->getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ require_role($user_role, ['owner', 'admin']);
|
|||||||
|
|
||||||
$id = (int)($data['id'] ?? 0);
|
$id = (int)($data['id'] ?? 0);
|
||||||
if (!$id) {
|
if (!$id) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Missing id';
|
$answer['message'] = 'Missing id';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ require_once '../../../assets/utils/classes_ac/DepartmentManager.php';
|
|||||||
require_role($user_role, ['owner', 'admin']);
|
require_role($user_role, ['owner', 'admin']);
|
||||||
|
|
||||||
$id = (int)($data['id'] ?? 0);
|
$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 = new DepartmentManager($pdo2, $company_id);
|
||||||
$dept->delete($id);
|
$dept->delete($id);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ require_once '../../../assets/utils/classes_ac/ChartOfAccounts.php';
|
|||||||
|
|
||||||
$id = (int)($data['id'] ?? 0);
|
$id = (int)($data['id'] ?? 0);
|
||||||
if (!$id) {
|
if (!$id) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Missing id';
|
$answer['message'] = 'Missing id';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -12,6 +13,7 @@ if (!$id) {
|
|||||||
$coa = new ChartOfAccounts($pdo2, $company_id);
|
$coa = new ChartOfAccounts($pdo2, $company_id);
|
||||||
$row = $coa->getById($id);
|
$row = $coa->getById($id);
|
||||||
if (!$row) {
|
if (!$row) {
|
||||||
|
http_response_code(404);
|
||||||
$answer['message'] = 'Account not found';
|
$answer['message'] = 'Account not found';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ require_once '../../../assets/utils/db_auth.php';
|
|||||||
require_once '../../../assets/utils/classes_ac/DepartmentManager.php';
|
require_once '../../../assets/utils/classes_ac/DepartmentManager.php';
|
||||||
|
|
||||||
$id = (int)($data['id'] ?? 0);
|
$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 = new DepartmentManager($pdo2, $company_id);
|
||||||
$row = $dept->getById($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['output'] = $row;
|
||||||
$answer['success'] = 1;
|
$answer['success'] = 1;
|
||||||
|
|||||||
@@ -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)) {
|
if (!$journal_date || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $journal_date)) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Valid journal date is required.';
|
$answer['message'] = 'Valid journal date is required.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -51,11 +52,13 @@ foreach ($lines_raw as $l) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (count($lines) < 2) {
|
if (count($lines) < 2) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'At least two journal lines are required.';
|
$answer['message'] = 'At least two journal lines are required.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abs($total_debit - $total_credit) > 0.005) {
|
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) . '.';
|
$answer['message'] = 'Journal is not balanced. Debit ' . number_format($total_debit, 2) . ' ≠ Credit ' . number_format($total_credit, 2) . '.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -83,9 +86,15 @@ try {
|
|||||||
$answer['success'] = 1;
|
$answer['success'] = 1;
|
||||||
$answer['gl_id'] = $gl_id;
|
$answer['gl_id'] = $gl_id;
|
||||||
notify_node('gl_posted', gl_posted_payload('manual', (int)$gl_id, $event_action, $lines), $company_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) {
|
} catch (Exception $e) {
|
||||||
if ($pdo2->inTransaction()) $pdo2->rollBack();
|
if ($pdo2->inTransaction()) $pdo2->rollBack();
|
||||||
$answer['message'] = $e->getMessage();
|
$answer['message'] = $e->getMessage();
|
||||||
|
http_response_code(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
$errors = $uploader->upload('contact_image');
|
$errors = $uploader->upload('contact_image');
|
||||||
if (!empty($errors)) {
|
if (!empty($errors)) {
|
||||||
$answer['success'] = 0;
|
$answer['success'] = 0;
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = $errors[0];
|
$answer['message'] = $errors[0];
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ $discount = (float)($data['discount'] ?? 0);
|
|||||||
$shipping_fee = (float)($data['shipping_fee'] ?? 0);
|
$shipping_fee = (float)($data['shipping_fee'] ?? 0);
|
||||||
|
|
||||||
if (!$request_id) {
|
if (!$request_id) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Purchase request ID is required.';
|
$answer['message'] = 'Purchase request ID is required.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -21,6 +22,7 @@ $prm = new PurchaseRequestManager($pdo2, $company_id);
|
|||||||
// Load PR — must be Approved
|
// Load PR — must be Approved
|
||||||
$pr = $prm->getById($request_id);
|
$pr = $prm->getById($request_id);
|
||||||
if (!$pr || (int)$pr['status'] !== 2) {
|
if (!$pr || (int)$pr['status'] !== 2) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Purchase request not found or not in Approved status.';
|
$answer['message'] = 'Purchase request not found or not in Approved status.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -30,17 +32,20 @@ if (!$contact_id) {
|
|||||||
$contact_id = (int)($pr['contact_id'] ?? 0);
|
$contact_id = (int)($pr['contact_id'] ?? 0);
|
||||||
}
|
}
|
||||||
if (!$contact_id) {
|
if (!$contact_id) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Set a Preferred Supplier on this purchase request before converting it to a PO.';
|
$answer['message'] = 'Set a Preferred Supplier on this purchase request before converting it to a PO.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|
||||||
$pr_items = $pr['items'];
|
$pr_items = $pr['items'];
|
||||||
if (empty($pr_items)) {
|
if (empty($pr_items)) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Purchase request has no items.';
|
$answer['message'] = 'Purchase request has no items.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((float)$pr['total_remaining'] <= 0.000001) {
|
if ((float)$pr['total_remaining'] <= 0.000001) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Purchase request is already fully converted.';
|
$answer['message'] = 'Purchase request is already fully converted.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -72,6 +77,7 @@ foreach ($convert_items as $ci) {
|
|||||||
|
|
||||||
if ($qty <= 0) continue;
|
if ($qty <= 0) continue;
|
||||||
if (!isset($pr_by_id[$item_id])) {
|
if (!isset($pr_by_id[$item_id])) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = "Item #{$item_id} not found in this purchase request.";
|
$answer['message'] = "Item #{$item_id} not found in this purchase request.";
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -81,6 +87,7 @@ foreach ($convert_items as $ci) {
|
|||||||
|
|
||||||
if ($qty - $remaining > 0.000001) {
|
if ($qty - $remaining > 0.000001) {
|
||||||
$name = $pi['product_name'] ?: $pi['product_sku'];
|
$name = $pi['product_name'] ?: $pi['product_sku'];
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = "Cannot convert {$qty} for \"{$name}\": only {$remaining} remaining.";
|
$answer['message'] = "Cannot convert {$qty} for \"{$name}\": only {$remaining} remaining.";
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -105,6 +112,7 @@ foreach ($convert_items as $ci) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (empty($po_items)) {
|
if (empty($po_items)) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'No valid items to convert.';
|
$answer['message'] = 'No valid items to convert.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ $id = (int)($data['id'] ?? 0);
|
|||||||
$action = $data['action_type'] ?? '';
|
$action = $data['action_type'] ?? '';
|
||||||
|
|
||||||
if (!$id || !$action) {
|
if (!$id || !$action) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'ID and action are required.';
|
$answer['message'] = 'ID and action are required.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
if (!$warehouse_id) {
|
if (!$warehouse_id) {
|
||||||
$answer['success'] = 0;
|
$answer['success'] = 0;
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Warehouse is required';
|
$answer['message'] = 'Warehouse is required';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
$errors = $uploader->upload('product_image');
|
$errors = $uploader->upload('product_image');
|
||||||
if (!empty($errors)) {
|
if (!empty($errors)) {
|
||||||
$answer['success'] = 0;
|
$answer['success'] = 0;
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = $errors[0];
|
$answer['message'] = $errors[0];
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,13 @@
|
|||||||
$users = new UserManager($pdo1, $company_id, $user_id);
|
$users = new UserManager($pdo1, $company_id, $user_id);
|
||||||
$answer['output'] = $users->getCompanyUsers();
|
$answer['output'] = $users->getCompanyUsers();
|
||||||
$answer['success'] = 1;
|
$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) {
|
} catch (Exception $e) {
|
||||||
$answer['message'] = $e->getMessage();
|
$answer['message'] = $e->getMessage();
|
||||||
|
http_response_code(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
$to_month = $data['to_month'] ?? '';
|
$to_month = $data['to_month'] ?? '';
|
||||||
|
|
||||||
if (!$warehouse_id || !$from_month || !$to_month) {
|
if (!$warehouse_id || !$from_month || !$to_month) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'warehouse_id, from_month, and to_month are required.';
|
$answer['message'] = 'warehouse_id, from_month, and to_month are required.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
$to_month = $data['to_month'] ?? '';
|
$to_month = $data['to_month'] ?? '';
|
||||||
|
|
||||||
if (!$warehouse_id || !$product_sku || !$from_month || !$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.';
|
$answer['message'] = 'warehouse_id, product_sku, from_month, and to_month are required.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ $quotation_id = (int)($data['quotation_id'] ?? 0);
|
|||||||
$convert_items = $data['convert_items'] ?? [];
|
$convert_items = $data['convert_items'] ?? [];
|
||||||
|
|
||||||
if (!$quotation_id) {
|
if (!$quotation_id) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Quotation ID required.';
|
$answer['message'] = 'Quotation ID required.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -19,17 +20,20 @@ $qm = new QuotationManager($pdo2, $company_id);
|
|||||||
// Load quotation — must be Accepted
|
// Load quotation — must be Accepted
|
||||||
$q = $qm->getById($quotation_id);
|
$q = $qm->getById($quotation_id);
|
||||||
if (!$q || (int)$q['status'] !== 2) {
|
if (!$q || (int)$q['status'] !== 2) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Quotation not found or not in Accepted status.';
|
$answer['message'] = 'Quotation not found or not in Accepted status.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|
||||||
$qt_items = $q['items'];
|
$qt_items = $q['items'];
|
||||||
if (empty($qt_items)) {
|
if (empty($qt_items)) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Quotation has no items.';
|
$answer['message'] = 'Quotation has no items.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((float)$q['total_remaining'] <= 0.000001) {
|
if ((float)$q['total_remaining'] <= 0.000001) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Quotation is already fully converted.';
|
$answer['message'] = 'Quotation is already fully converted.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -61,6 +65,7 @@ foreach ($convert_items as $ci) {
|
|||||||
|
|
||||||
if ($qty <= 0) continue;
|
if ($qty <= 0) continue;
|
||||||
if (!isset($qt_by_id[$item_id])) {
|
if (!isset($qt_by_id[$item_id])) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = "Item #{$item_id} not found in this quotation.";
|
$answer['message'] = "Item #{$item_id} not found in this quotation.";
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -70,6 +75,7 @@ foreach ($convert_items as $ci) {
|
|||||||
|
|
||||||
if ($qty - $remaining > 0.000001) {
|
if ($qty - $remaining > 0.000001) {
|
||||||
$name = $qi['product_name'] ?: $qi['product_sku'];
|
$name = $qi['product_name'] ?: $qi['product_sku'];
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = "Cannot convert {$qty} for \"{$name}\": only {$remaining} remaining.";
|
$answer['message'] = "Cannot convert {$qty} for \"{$name}\": only {$remaining} remaining.";
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -95,6 +101,7 @@ foreach ($convert_items as $ci) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (empty($ord_items)) {
|
if (empty($ord_items)) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'No valid items to convert.';
|
$answer['message'] = 'No valid items to convert.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,13 @@ $action = $data['action'] ?? '';
|
|||||||
$data['items'] = json_decode($data['items'] ?? '[]', true) ?: [];
|
$data['items'] = json_decode($data['items'] ?? '[]', true) ?: [];
|
||||||
|
|
||||||
if (!in_array($action, ['create', 'update'], true)) {
|
if (!in_array($action, ['create', 'update'], true)) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Unknown action';
|
$answer['message'] = 'Unknown action';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($data['items'])) {
|
if (empty($data['items'])) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'At least one item is required';
|
$answer['message'] = 'At least one item is required';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,13 @@ try {
|
|||||||
$quotation = new QuotationManager($pdo2, $company_id);
|
$quotation = new QuotationManager($pdo2, $company_id);
|
||||||
$answer['output'] = $quotation->getStats();
|
$answer['output'] = $quotation->getStats();
|
||||||
$answer['success'] = 1;
|
$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) {
|
} catch (Exception $e) {
|
||||||
$answer['message'] = $e->getMessage();
|
$answer['message'] = $e->getMessage();
|
||||||
|
http_response_code(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ $id = (int)($data['id'] ?? 0);
|
|||||||
$action = $data['action_type'] ?? '';
|
$action = $data['action_type'] ?? '';
|
||||||
|
|
||||||
if (!$id || !$action) {
|
if (!$id || !$action) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'ID and action are required.';
|
$answer['message'] = 'ID and action are required.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,10 +38,16 @@ try {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Unknown action.';
|
$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) {
|
} catch (Exception $e) {
|
||||||
$answer['message'] = $e->getMessage();
|
$answer['message'] = $e->getMessage();
|
||||||
|
http_response_code(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ try {
|
|||||||
if ($action === 'rebuild_period') {
|
if ($action === 'rebuild_period') {
|
||||||
$period = $data['period'] ?? '';
|
$period = $data['period'] ?? '';
|
||||||
if (!preg_match('/^\d{4}-\d{2}$/', $period)) {
|
if (!preg_match('/^\d{4}-\d{2}$/', $period)) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'Invalid period.';
|
$answer['message'] = 'Invalid period.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,17 @@
|
|||||||
require_once '../../../assets/utils/db_auth.php';
|
require_once '../../../assets/utils/db_auth.php';
|
||||||
require_once '../../../assets/utils/classes/SmtpManager.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 {
|
try {
|
||||||
$smtp = new SmtpManager($pdo1, $company_id, $method, $pinkey, $iv);
|
$smtp = new SmtpManager($pdo1, $company_id, $method, $pinkey, $iv);
|
||||||
$answer['success'] = 1;
|
$answer['success'] = 1;
|
||||||
$answer['output'] = $smtp->get();
|
$answer['output'] = $smtp->get();
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$answer['message'] = $e->getMessage();
|
error_log('[retrieve_smtp] ' . $e->getMessage());
|
||||||
|
$answer['message'] = 'Could not load SMTP settings.';
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
require_once '../../../assets/utils/db_auth.php';
|
require_once '../../../assets/utils/db_auth.php';
|
||||||
require_once '../../../assets/utils/classes/UserManager.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);
|
$um = new UserManager($pdo1, $company_id, $user_id);
|
||||||
|
|
||||||
$answer['success'] = 1;
|
$answer['success'] = 1;
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
require_once '../../../assets/utils/db_auth.php';
|
require_once '../../../assets/utils/db_auth.php';
|
||||||
require_once '../../../assets/utils/classes/UserManager.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'] ?? '');
|
$keyword = trim($data['keyword'] ?? '');
|
||||||
$um = new UserManager($pdo1, $company_id, $user_id);
|
$um = new UserManager($pdo1, $company_id, $user_id);
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ try {
|
|||||||
if ($action === 'get_periods') {
|
if ($action === 'get_periods') {
|
||||||
$warehouse_id = (int)($data['warehouse_id'] ?? 0);
|
$warehouse_id = (int)($data['warehouse_id'] ?? 0);
|
||||||
if (!$warehouse_id) {
|
if (!$warehouse_id) {
|
||||||
|
http_response_code(400);
|
||||||
$answer['message'] = 'warehouse_id is required.';
|
$answer['message'] = 'warehouse_id is required.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
@@ -48,6 +49,7 @@ try {
|
|||||||
$warehouse_id = (int)($data['warehouse_id'] ?? 0);
|
$warehouse_id = (int)($data['warehouse_id'] ?? 0);
|
||||||
$period = $data['period'] ?? '';
|
$period = $data['period'] ?? '';
|
||||||
if (!$warehouse_id || !preg_match('/^\d{4}-\d{2}$/', $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.';
|
$answer['message'] = 'Valid warehouse_id and period (YYYY-MM) are required.';
|
||||||
exit(json_encode($answer));
|
exit(json_encode($answer));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user