207 lines
7.7 KiB
Markdown
207 lines
7.7 KiB
Markdown
# Live Dashboard — Real-Time Event Specification
|
|
|
|
## Mechanism
|
|
|
|
PHP engine files emit named events to a Node.js process over HTTP after a successful DB commit. Node.js broadcasts the event to all browser tabs in the same company room via Socket.IO. Each dashboard page listens for relevant events and reloads only the sections that changed — without a full page refresh.
|
|
|
|
```
|
|
PHP (engine file)
|
|
└─ notify_node(event, payload, company_id) // fire-and-forget POST to Node
|
|
└─ Node.js /emit endpoint
|
|
└─ io.to('company_{id}').emit(event, payload)
|
|
└─ Browser (dashboard JS)
|
|
└─ targeted section reload + flash effect
|
|
```
|
|
|
|
---
|
|
|
|
## `notify_node()` (`app/assets/utils/notify_node.php`)
|
|
|
|
Fire-and-forget HTTP POST from PHP to the local Node.js `/emit` endpoint. Called **after** `$pdo->commit()` so the event is never sent for rolled-back transactions.
|
|
|
|
```php
|
|
notify_node(string $event, array $payload, int $company_id): void
|
|
```
|
|
|
|
Failure is silently ignored — the DB write is authoritative; the live update is a UX enhancement only.
|
|
|
|
---
|
|
|
|
## Socket.IO Room
|
|
|
|
Each company gets its own room: `company_{company_id}`. The browser joins this room on page load via `window._socket` (initialized in `include_ending.php`, always before page `<script>` blocks).
|
|
|
|
---
|
|
|
|
## Events
|
|
|
|
### `stock_updated`
|
|
|
|
Fired when stock inventory changes.
|
|
|
|
| Trigger file | Action |
|
|
|---|---|
|
|
| `ics/api/engine/approve_stock.php` | Stock In / Out / Transfer approved |
|
|
|
|
Payload: `{ warehouse_id, type }`
|
|
|
|
Dashboard sections reloaded: stock-in total, stock-out total, low stock count, stock movement chart, stock health donut, most-moved list, low-stock list, recent activity list.
|
|
|
|
> `stock_overview.php` does **not** listen — it is an analytical report page, not a live monitor.
|
|
|
|
---
|
|
|
|
### `order_updated`
|
|
|
|
Fired when a sales order is created, confirmed, cancelled, or deleted.
|
|
|
|
| Trigger file | Action |
|
|
|---|---|
|
|
| `order/api/engine/manage_order.php` | Order saved (create or update) |
|
|
| `order/api/engine/confirm_order.php` | Order confirmed |
|
|
| `order/api/engine/cancel_order.php` | Order cancelled |
|
|
| `order/api/engine/delete_order.php` | Order soft-deleted |
|
|
|
|
Payload: `{}`
|
|
|
|
Dashboard sections reloaded: total orders card.
|
|
|
|
---
|
|
|
|
### `invoice_updated`
|
|
|
|
Fired when any invoice-type document changes status or is created/deleted. Also fired as a side-effect from `confirm_order` when `auto_invoice = true`, and from `confirm_return` when `auto_cn = true`.
|
|
|
|
| Trigger file | Action |
|
|
|---|---|
|
|
| `order/api/engine/proceed_to_invoice.php` | Invoice created from order |
|
|
| `order/api/engine/manage_invoice.php` | Invoice saved (create or update) |
|
|
| `order/api/engine/void_invoice.php` | Invoice voided |
|
|
| `order/api/engine/delete_invoice.php` | Invoice soft-deleted |
|
|
| `order/api/engine/update_invoice_status.php` | Invoice status updated |
|
|
| `order/api/engine/confirm_order.php` | Auto-invoice on order confirm (conditional) |
|
|
| `order/api/engine/confirm_return.php` | Auto credit note on return confirm (conditional) |
|
|
| `revenue/api/engine/proceed_to_invoice.php` | Revenue invoice created |
|
|
| `revenue/api/engine/delete_credit_note.php` | Credit note deleted |
|
|
| `expense/api/engine/delete_supplier_credit_note.php` | Supplier credit note deleted |
|
|
|
|
Payload: `{}`
|
|
|
|
Dashboard sections reloaded: total revenue card, unpaid invoices card.
|
|
|
|
---
|
|
|
|
### `return_updated`
|
|
|
|
Fired when a customer return is created, confirmed, cancelled, or deleted.
|
|
|
|
| Trigger file | Action |
|
|
|---|---|
|
|
| `order/api/engine/manage_return.php` | Return saved (create or update) |
|
|
| `order/api/engine/confirm_return.php` | Return confirmed |
|
|
| `order/api/engine/cancel_return.php` | Return cancelled |
|
|
| `order/api/engine/delete_return.php` | Return soft-deleted |
|
|
|
|
Payload: `{}`
|
|
|
|
Dashboard sections reloaded: pending returns card.
|
|
|
|
---
|
|
|
|
### `gl_posted`
|
|
|
|
Fired when a GL entry is posted, replaced, or saved as a manual journal.
|
|
|
|
| Trigger file | Action |
|
|
|---|---|
|
|
| `accounting/api/engine/post_gl_entry.php` | Source document GL posted or replaced |
|
|
| `accounting/api/engine/save_manual_journal.php` | Manual journal saved or replaced |
|
|
| `order/api/engine/issue_invoice.php` | Invoice issued and posted to GL |
|
|
| `finance/api/engine/manage_receipt.php` | Receipt saved and posted to GL |
|
|
| `finance/api/engine/manage_payment.php` | Payment saved and posted to GL |
|
|
|
|
Payload is built by `gl_posted_payload()` for most emitters and includes document type, source id, action, and journal lines where available.
|
|
|
|
Accounting dashboard sections reloaded: dashboard cards and charts handled by `ac_dashboard/index.php`.
|
|
|
|
---
|
|
|
|
### `receipt_posted` and `payment_posted`
|
|
|
|
Fired as finance-specific signals immediately after receipt or payment creation.
|
|
|
|
| Event | Trigger file | Payload |
|
|
|---|---|---|
|
|
| `receipt_posted` | `finance/api/engine/manage_receipt.php` | `{ id }` |
|
|
| `payment_posted` | `finance/api/engine/manage_payment.php` | `{ id }` |
|
|
|
|
The current accounting dashboard refresh is driven by `gl_posted`; these finance events are available for targeted listeners if a page adds them later.
|
|
|
|
---
|
|
|
|
## WMS Dashboard Section Map (`dashboard/index.php`)
|
|
|
|
| Event | Sections updated | DOM IDs | Reload function |
|
|
|---|---|---|---|
|
|
| `stock_updated` | Stock in/out cards, low stock card, movement chart, health donut, most-moved list, low-stock list, activity list | `stat_total_in`, `stat_total_out`, `stat_low_stock_count`, `#ics_movementChart`, `#overallChart`, `#list_most_moved`, `#list_low_stock`, `#list_recent_activity` | `retrieve_stock_sections()` |
|
|
| `order_updated` | Total orders card | `stat_total_orders` | `retrieve_order_sections()` |
|
|
| `invoice_updated` | Revenue + unpaid invoices cards | `stat_total_revenue`, `stat_unpaid_invoices` | `retrieve_invoice_sections()` |
|
|
| `return_updated` | Pending returns card | `stat_pending_returns` | `retrieve_return_sections()` |
|
|
|
|
All four section functions call the same API endpoint (`dashboard/api/engine_report/reports_stats.php`) but update only the relevant DOM elements.
|
|
|
|
---
|
|
|
|
## Accounting Dashboard (`ac_dashboard/index.php`)
|
|
|
|
The accounting dashboard listens for `gl_posted` and reloads its accounting KPI sections. It does not listen to the WMS events unless a future page explicitly adds those listeners.
|
|
|
|
---
|
|
|
|
## Flash Card Effect
|
|
|
|
When a silent socket-triggered section reload completes, each updated card receives a 1-second yellow glow animation to signal the change without disrupting the user.
|
|
|
|
**CSS:**
|
|
```css
|
|
@keyframes card-flash {
|
|
0% { box-shadow: 0 0 0 3px rgba(250,204,21,0.8); }
|
|
100% { box-shadow: none; }
|
|
}
|
|
.card-flash { animation: card-flash 1s ease-out; }
|
|
```
|
|
|
|
**JS helper (`flash_card`):**
|
|
```javascript
|
|
function flash_card(el) {
|
|
if (!el) return;
|
|
el.classList.remove('card-flash');
|
|
void el.offsetWidth; // force reflow so re-trigger works
|
|
el.classList.add('card-flash');
|
|
setTimeout(function() { el.classList.remove('card-flash'); }, 1000);
|
|
}
|
|
```
|
|
|
|
Called immediately after the overlay `.hide()` for each updated card element. Flash does **not** fire on the initial full-page load — only on silent socket-triggered background refreshes.
|
|
|
|
---
|
|
|
|
## Pages That Do NOT Listen
|
|
|
|
| Page | Reason |
|
|
|---|---|
|
|
| `ics/stock_overview.php` | Analytical report — user loads on demand, no live monitoring needed |
|
|
| All other non-dashboard pages | Point-in-time views; manual refresh is sufficient |
|
|
|
|
---
|
|
|
|
## Key Files
|
|
|
|
| File | Role |
|
|
|---|---|
|
|
| `app/assets/utils/notify_node.php` | PHP → Node.js HTTP POST helper |
|
|
| `nodejs/server.js` | Node.js Socket.IO server, `/emit` endpoint, room broadcast |
|
|
| `app/include_ending.php` | Initializes `window._socket` before page scripts run |
|
|
| `app/dashboard/index.php` | WMS dashboard: 4 socket listeners, 4 section-reload functions, `flash_card()` |
|
|
| `app/ac_dashboard/index.php` | Accounting dashboard: own listener pattern, `flash_card()` |
|