Seal live dashboard event gaps
This commit is contained in:
@@ -3,10 +3,12 @@ session_start();
|
||||
require_once '../../../assets/utils/db_auth.php';
|
||||
require_once '../../../assets/utils/classes_ac/PostingWindowGuard.php';
|
||||
require_once '../../../assets/utils/classes_ac/GlManager.php';
|
||||
require_once '../../../assets/utils/notify_node.php';
|
||||
|
||||
require_role($user_role, ['owner', 'admin']);
|
||||
|
||||
$gl_id = (int)($data['gl_id'] ?? 0);
|
||||
$event_action = $gl_id > 0 ? 'replaced' : 'posted';
|
||||
$reference = trim((string)($data['reference'] ?? ''));
|
||||
$description = trim((string)($data['description'] ?? ''));
|
||||
$journal_date = trim((string)($data['journal_date'] ?? ''));
|
||||
@@ -73,6 +75,7 @@ try {
|
||||
$pdo2->commit();
|
||||
$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 (Exception $e) {
|
||||
if ($pdo2->inTransaction()) $pdo2->rollBack();
|
||||
$answer['message'] = $e->getMessage();
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../db_helpers.php';
|
||||
require_once __DIR__ . '/../notify_node.php';
|
||||
|
||||
/**
|
||||
* StockManager
|
||||
*
|
||||
@@ -856,6 +859,11 @@ class StockManager {
|
||||
(int)$inbound_row['id'], $inbound_row['source'] ?? '', (int)($inbound_row['source_id'] ?? 0), $inbound_row['date'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
$notify_company_id = $this->company_id;
|
||||
db_after_commit(function() use ($warehouse_id, $type, $notify_company_id) {
|
||||
notify_node("stock_updated", ["warehouse_id" => $warehouse_id, "type" => $type], $notify_company_id);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -69,17 +69,43 @@ function db_check($sth, &$answer, $options = []) {
|
||||
|
||||
|
||||
|
||||
// Run a callback after the surrounding dbTransaction() commits. If no managed
|
||||
// transaction is active, run immediately. Used for non-authoritative side effects
|
||||
// such as live dashboard notifications.
|
||||
function db_after_commit(callable $callback): void {
|
||||
if (!isset($GLOBALS['_db_after_commit_stack']) || empty($GLOBALS['_db_after_commit_stack'])) {
|
||||
$callback();
|
||||
return;
|
||||
}
|
||||
|
||||
$idx = count($GLOBALS['_db_after_commit_stack']) - 1;
|
||||
$GLOBALS['_db_after_commit_stack'][$idx][] = $callback;
|
||||
}
|
||||
|
||||
// transaction wrapper with retry logic for deadlocks
|
||||
function dbTransaction($pdo, $callback, $maxRetries = 3) {
|
||||
for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
|
||||
$GLOBALS['_db_after_commit_stack'][] = [];
|
||||
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
$result = $callback($pdo);
|
||||
$pdo->commit();
|
||||
|
||||
$callbacks = array_pop($GLOBALS['_db_after_commit_stack']);
|
||||
foreach ($callbacks as $after_commit) {
|
||||
try {
|
||||
$after_commit();
|
||||
} catch (Throwable $e) {
|
||||
error_log("[AFTER COMMIT ERROR] " . $e->getMessage() . " | " . $e->getFile() . ":" . $e->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (PDOException $e) {
|
||||
$pdo->rollBack();
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
array_pop($GLOBALS['_db_after_commit_stack']);
|
||||
|
||||
if ($e->getCode() == '40001' && $attempt < $maxRetries - 1) {
|
||||
usleep(600000 * ($attempt + 1));
|
||||
@@ -90,7 +116,8 @@ function dbTransaction($pdo, $callback, $maxRetries = 3) {
|
||||
throw $e;
|
||||
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
array_pop($GLOBALS['_db_after_commit_stack']);
|
||||
error_log("[ERROR] " . $e->getMessage() . " | " . $e->getFile() . ":" . $e->getLine());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -34,3 +34,23 @@ function notify_node(string $event, array $data, int $company_id): void
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
}
|
||||
|
||||
function gl_posted_payload(string $doc_type, int $id, string $action, array $lines): array
|
||||
{
|
||||
$has_revenue = false;
|
||||
$has_expense = false;
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$code = (int)($line['account_code'] ?? 0);
|
||||
if ($code >= 4000 && $code < 5000) $has_revenue = true;
|
||||
if ($code >= 5000) $has_expense = true;
|
||||
}
|
||||
|
||||
return [
|
||||
'doc_type' => $doc_type,
|
||||
'id' => $id,
|
||||
'action' => $action,
|
||||
'has_revenue' => $has_revenue,
|
||||
'has_expense' => $has_expense,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../../../assets/utils/db_auth.php';
|
||||
require_role($user_role, ['owner', 'admin']);
|
||||
require_once '../../../assets/utils/classes/InvoiceManager.php';
|
||||
require_once '../../../assets/utils/notify_node.php';
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
|
||||
if (!$id) {
|
||||
$answer['message'] = 'Invalid supplier credit note ID.';
|
||||
http_response_code(400);
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
try {
|
||||
dbTransaction($pdo2, function($pdo) use ($id, $company_id) {
|
||||
$mgr = new InvoiceManager($pdo, $company_id);
|
||||
$mgr->softDelete($id);
|
||||
});
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Supplier credit note deleted.';
|
||||
notify_node('invoice_updated', [], $company_id);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
$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));
|
||||
@@ -69,7 +69,7 @@
|
||||
$pdo2->beginTransaction();
|
||||
$gl->post('payment', $new_id, $built['formula_id'], $built['period'], $built['lines'], ['journal_date' => $built['doc_date']]);
|
||||
$pdo2->commit();
|
||||
notify_node('gl_posted', ['doc_type' => 'payment', 'id' => $new_id, 'action' => 'posted'], $company_id);
|
||||
notify_node('gl_posted', gl_posted_payload('payment', (int)$new_id, 'posted', $built['lines'] ?? []), $company_id);
|
||||
$answer['gl_posted'] = true;
|
||||
} catch (Exception $e) {
|
||||
if ($pdo2->inTransaction()) $pdo2->rollBack();
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
$pdo2->beginTransaction();
|
||||
$gl->post('receipt', $new_id, $built['formula_id'], $built['period'], $built['lines'], ['journal_date' => $built['doc_date']]);
|
||||
$pdo2->commit();
|
||||
notify_node('gl_posted', ['doc_type' => 'receipt', 'id' => $new_id, 'action' => 'posted'], $company_id);
|
||||
notify_node('gl_posted', gl_posted_payload('receipt', (int)$new_id, 'posted', $built['lines'] ?? []), $company_id);
|
||||
$answer['gl_posted'] = true;
|
||||
} catch (Exception $e) {
|
||||
if ($pdo2->inTransaction()) $pdo2->rollBack();
|
||||
|
||||
@@ -15,7 +15,6 @@ session_start();
|
||||
require_once '../../../assets/utils/db_auth.php';
|
||||
require_once '../../../assets/utils/classes/StockManager.php';
|
||||
require_once '../../../assets/utils/classes/WarehouseManager.php';
|
||||
require_once '../../../assets/utils/notify_node.php';
|
||||
|
||||
require_role($user_role, ['owner', 'admin']);
|
||||
|
||||
@@ -29,24 +28,18 @@ if (!$id || !$warehouse_id || !in_array($type, ['in', 'out', 'transfer'])) {
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
$stock = new StockManager($pdo2, $company_id);
|
||||
$whMgmt = new WarehouseManager($pdo2, $company_id);
|
||||
|
||||
try {
|
||||
$pdo2->beginTransaction();
|
||||
|
||||
$result = $stock->approveStock($id, $warehouse_id, $type, $whMgmt);
|
||||
|
||||
$pdo2->commit();
|
||||
|
||||
notify_node('stock_updated', ['warehouse_id' => $warehouse_id, 'type' => $type], $company_id);
|
||||
dbTransaction($pdo2, function($pdo) use ($id, $warehouse_id, $type, $company_id) {
|
||||
$stock = new StockManager($pdo, $company_id);
|
||||
$whMgmt = new WarehouseManager($pdo, $company_id);
|
||||
$stock->approveStock($id, $warehouse_id, $type, $whMgmt);
|
||||
});
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Stock approved.';
|
||||
exit(json_encode($answer));
|
||||
|
||||
} catch (Exception $e) {
|
||||
$pdo2->rollBack();
|
||||
http_response_code(400);
|
||||
$answer['message'] = $e->getMessage();
|
||||
exit(json_encode($answer));
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
$answer['message'] = 'Return confirmed.';
|
||||
$answer['auto_cn'] = $auto_cn;
|
||||
notify_node('return_updated', [], $company_id);
|
||||
if ($auto_approve) {
|
||||
notify_node('stock_updated', ['warehouse_id' => 0, 'type' => 'in'], $company_id);
|
||||
}
|
||||
if ($auto_cn) {
|
||||
notify_node('invoice_updated', [], $company_id);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
}
|
||||
$pdo2->commit();
|
||||
|
||||
notify_node('gl_posted', ['doc_type' => $doc['doc_type'], 'id' => $id, 'action' => 'posted'], $company_id);
|
||||
notify_node('gl_posted', gl_posted_payload((string)$doc['doc_type'], (int)$id, $existing ? 'replaced' : 'posted', $built['lines'] ?? []), $company_id);
|
||||
$answer['gl_posted'] = true;
|
||||
} catch (Exception $e) {
|
||||
if ($pdo2->inTransaction()) $pdo2->rollBack();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
require_once '../../../assets/utils/classes/WarehouseManager.php';
|
||||
require_once '../../../assets/utils/classes/InvoiceManager.php';
|
||||
require_once '../../../assets/utils/classes/CompanySettingManager.php';
|
||||
require_once '../../../assets/utils/notify_node.php';
|
||||
|
||||
$return_id = (int)($data['id'] ?? 0);
|
||||
|
||||
@@ -29,6 +30,9 @@
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Supplier return confirmed.';
|
||||
if ($auto_approve) {
|
||||
notify_node('stock_updated', ['warehouse_id' => 0, 'type' => 'out'], $company_id);
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
$answer['message'] = 'Database error, please try again.';
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../../../assets/utils/db_auth.php';
|
||||
require_role($user_role, ['owner', 'admin']);
|
||||
require_once '../../../assets/utils/classes/InvoiceManager.php';
|
||||
require_once '../../../assets/utils/notify_node.php';
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
|
||||
if (!$id) {
|
||||
$answer['message'] = 'Invalid credit note ID.';
|
||||
http_response_code(400);
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
try {
|
||||
dbTransaction($pdo2, function($pdo) use ($id, $company_id) {
|
||||
$mgr = new InvoiceManager($pdo, $company_id);
|
||||
$mgr->softDelete($id);
|
||||
});
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Credit note deleted.';
|
||||
notify_node('invoice_updated', [], $company_id);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
$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));
|
||||
@@ -4,6 +4,7 @@
|
||||
require_role($user_role, ['owner', 'admin', 'staff']);
|
||||
require_once '../../../assets/utils/classes/OrderManager.php';
|
||||
require_once '../../../assets/utils/classes/InvoiceManager.php';
|
||||
require_once '../../../assets/utils/notify_node.php';
|
||||
|
||||
$order_id = (int)($data['order_id'] ?? 0);
|
||||
|
||||
@@ -26,6 +27,7 @@
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['new_id'] = $new_id;
|
||||
notify_node('invoice_updated', [], $company_id);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
$answer['message'] = 'Database error, please try again.';
|
||||
|
||||
Reference in New Issue
Block a user