Stop tracking docs/ — already in .gitignore
This commit is contained in:
@@ -1,53 +0,0 @@
|
||||
# MN3 WMS Feature Documentation
|
||||
|
||||
This folder documents the main product features discovered from the current PHP codebase.
|
||||
|
||||
The app is split into three operating areas:
|
||||
|
||||
- WMS: warehouse operations, stock movement, sales and purchase workflows.
|
||||
- Accounting: revenue, expense, finance, journals, batch GL posting, and financial reports.
|
||||
- Master Data: shared setup for products, locations, contacts, chart of accounts, departments, formulas, and posting rules.
|
||||
|
||||
## Documents
|
||||
|
||||
- [Coverage Matrix](coverage-matrix.md) — codebase surface mapped to documentation status
|
||||
- [WMS Features](../reviewing/wms.md)
|
||||
- [Accounting Features](../reviewing/accounting.md)
|
||||
- [Master Data Features](../reviewing/master-data.md)
|
||||
- [System And Settings Features](../reviewing/system-settings.md)
|
||||
- [Runtime Architecture](../reviewing/runtime-architecture.md)
|
||||
- [Setup And Landing Pages](../reviewing/setup-landing.md)
|
||||
- [Helper Endpoint Contracts](../reviewing/helper-endpoints.md)
|
||||
- [Document Lifecycle And Status](../reviewing/document-lifecycle.md)
|
||||
- [Transaction Limits](../reviewing/transaction-limits.md) — package tiers, daily/weekly quotas, report gating
|
||||
|
||||
## Architecture
|
||||
|
||||
- Architecture coverage is currently split between [System And Settings Features](../reviewing/system-settings.md), [Accounting Features](../reviewing/accounting.md), and [Coverage Matrix](coverage-matrix.md).
|
||||
|
||||
## Cross-Cutting Specs
|
||||
|
||||
- [Running Number](running-number.md) — document number format, sequences, manual entry, gap policy
|
||||
- [Session Concurrency](session-concurrency.md) — single-session enforcement, heartbeat, PHP GC stale detection, single-factor auth
|
||||
- [Live Dashboard](live-dashboard.md) — real-time Socket.IO events, section reload map, flash card effect
|
||||
- [Role Guards](role-guards.md) — CRUD access matrix per role
|
||||
- [Soft Delete](soft-delete.md) — mechanism, downstream blocks, stock/GL side effects, deletion order
|
||||
|
||||
## Core Architecture
|
||||
|
||||
The UI is mostly PHP pages under `app/`, with AJAX endpoints under each module's `api/engine` or `api/engine_report` folder.
|
||||
|
||||
Common backend behavior is concentrated in manager classes:
|
||||
|
||||
- `app/assets/utils/classes/*Manager.php` handles WMS, document, contact, product, finance, and report logic.
|
||||
- `app/assets/utils/classes_ac/*` handles accounting setup, GL posting, financial statements, posting windows, and tax reports.
|
||||
- `app/assets/js/custom.js` contains shared AJAX, pagination, account autocomplete, locks, batch processing, and display formatting helpers.
|
||||
|
||||
## Cross-Cutting Rules
|
||||
|
||||
- Company scoping is applied through `company_id` from the authenticated session.
|
||||
- Most write APIs load `app/assets/utils/db_auth.php`, which validates session, OTP freshness, CSRF token, and request payload.
|
||||
- Role checks use `require_role()` where a route is limited to owners/admins.
|
||||
- Audit logs are stored as JSON in many business tables through the `$logging` object from `db_auth.php`.
|
||||
- Numeric display uses shared frontend formatting to suppress floating point noise and avoid scientific notation in the UI.
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
# Documentation Coverage Matrix
|
||||
|
||||
This matrix compares the current codebase surface area with the feature specs in `docs/`.
|
||||
|
||||
Status legend:
|
||||
|
||||
- `Covered` - behavior is described by a reviewed spec.
|
||||
- `Draft-covered` - behavior is described in `docs/reviewing/` and still needs review sign-off.
|
||||
- `Partial` - behavior is mentioned, but important routes, events, or edge cases are missing.
|
||||
- `Missing` - no product/spec coverage was found.
|
||||
- `Internal` - infrastructure or shared code that is not a user-facing feature by itself.
|
||||
|
||||
## Summary
|
||||
|
||||
| Area | Code surface | Coverage |
|
||||
|---|---:|---|
|
||||
| App PHP pages/routes | 108 | Mostly draft-covered |
|
||||
| App API endpoints | 192 | Mostly draft-covered |
|
||||
| Reviewed specs | 6 | Cross-cutting behavior only |
|
||||
| Reviewing specs | 10 | Main feature families plus runtime/setup/helper coverage |
|
||||
|
||||
The docs cover the main product domains, but the full feature set is not all reviewed. The main business specs remain in `docs/reviewing/`.
|
||||
|
||||
## Specification Map
|
||||
|
||||
| Spec | Status | Primary coverage |
|
||||
|---|---|---|
|
||||
| `docs/reviewing/wms.md` | Draft-covered | WMS dashboard, stock overview, stock in/out/transfer, labels, sales orders/returns/invoices, purchase orders/supplier returns/purchase invoices, stock reports |
|
||||
| `docs/reviewing/accounting.md` | Draft-covered | Accounting dashboard, revenue/expense documents, finance, chart of accounts, departments, formulas, product account mapping, manual journals, GL posting, financial reports |
|
||||
| `docs/reviewing/master-data.md` | Draft-covered | Products, categories, warehouses/storage, contacts, chart of accounts, departments, formulas, posting window |
|
||||
| `docs/reviewing/system-settings.md` | Draft-covered | Authentication, sessions/security, users, profile/password, company settings, SMTP, branch switching, shared JS, locks, ETL maintenance, file uploads |
|
||||
| `docs/reviewing/document-lifecycle.md` | Draft-covered | Document statuses, void strategy, soft delete strategy, GL versioning |
|
||||
| `docs/reviewing/transaction-limits.md` | Draft-covered | Package tiers, quota counting, report/dashboard gating |
|
||||
| `docs/reviewing/invited-onboarding.md` | Draft-covered | Invited user activation flow |
|
||||
| `docs/reviewed/role-guards.md` | Covered | Role access matrix |
|
||||
| `docs/reviewed/soft-delete.md` | Covered | Soft delete and downstream deletion guards |
|
||||
| `docs/reviewed/session-concurrency.md` | Covered | Single-session login enforcement and heartbeat |
|
||||
| `docs/reviewed/live-dashboard.md` | Covered | Socket.IO dashboard event behavior |
|
||||
| `docs/reviewed/running-number.md` | Covered | Document numbering configuration and generation |
|
||||
| `docs/reviewing/runtime-architecture.md` | Draft-covered | App shell, config, database connections, Node.js realtime server, cron scheduler, setup script |
|
||||
| `docs/reviewing/setup-landing.md` | Draft-covered | Root redirect, landing page, legal pages, CLI setup behavior |
|
||||
| `docs/reviewing/helper-endpoints.md` | Draft-covered | Helper/search/retrieve/stats endpoint contracts |
|
||||
|
||||
## Page Coverage
|
||||
|
||||
| Code area | Pages | Coverage | Spec |
|
||||
|---|---|---|---|
|
||||
| WMS dashboard | `app/dashboard/index.php`, `app/dashboard/low_stock_products.php` | Draft-covered | `docs/reviewing/wms.md`, `docs/reviewed/live-dashboard.md` |
|
||||
| Stock operations | `app/ics/stock_in.php`, `app/ics/manage_stock_in.php`, `app/ics/stock_out.php`, `app/ics/manage_stock_out.php`, `app/ics/stock_transfer.php`, `app/ics/manage_stock_transfer.php`, `app/ics/stock_overview.php` | Draft-covered | `docs/reviewing/wms.md`, `docs/reviewing/document-lifecycle.md` |
|
||||
| Barcode labels | `app/ics/sku_barcode_label.php`, `app/ics/location_barcode_label.php` | Draft-covered | `docs/reviewing/wms.md` |
|
||||
| Inventory master data | `app/inventory/product.php`, `app/inventory/manage_product.php`, `app/inventory/warehouse.php`, `app/inventory/manage_warehouse.php`, `app/inventory/manage_category.php`, `app/inventory/manage_storage.php` | Draft-covered | `docs/reviewing/master-data.md` |
|
||||
| Contacts | `app/contact/contact.php`, `app/contact/manage_contact.php`, `app/contact/manage_contact_type.php` | Draft-covered | `docs/reviewing/master-data.md` |
|
||||
| WMS sales | `app/order/order.php`, `app/order/manage_order.php`, `app/order/confirm_order.php`, `app/order/return.php`, `app/order/manage_return.php`, `app/order/invoice.php`, `app/order/manage_invoice.php`, `app/order/print_invoice.php` | Draft-covered | `docs/reviewing/wms.md`, `docs/reviewing/document-lifecycle.md` |
|
||||
| WMS purchase | `app/po/po.php`, `app/po/manage_po.php`, `app/po/supplier_returns.php`, `app/po/manage_supplier_return.php`, `app/po/invoice.php`, `app/po/manage_purchase_invoice.php` | Draft-covered | `docs/reviewing/wms.md`, `docs/reviewing/document-lifecycle.md` |
|
||||
| Revenue documents | `app/revenue/quotation.php`, `app/revenue/manage_quotation.php`, `app/revenue/order.php`, `app/revenue/manage_order.php`, `app/revenue/invoice.php`, `app/revenue/manage_invoice.php`, `app/revenue/manage_credit_note.php`, `app/revenue/view_invoice.php`, `app/revenue/receipt.php` | Draft-covered | `docs/reviewing/accounting.md` |
|
||||
| Expense documents | `app/expense/purchase_request.php`, `app/expense/manage_purchase_request.php`, `app/expense/purchase_order.php`, `app/expense/manage_purchase_order.php`, `app/expense/purchase_invoice.php`, `app/expense/manage_purchase_invoice.php`, `app/expense/manage_supplier_credit_note.php`, `app/expense/payment.php` | Draft-covered | `docs/reviewing/accounting.md` |
|
||||
| Finance | `app/finance/receipt_billing.php`, `app/finance/manage_receipt_billing.php`, `app/finance/receipt.php`, `app/finance/manage_receipt.php`, `app/finance/payment_billing.php`, `app/finance/manage_payment_billing.php`, `app/finance/payment.php`, `app/finance/manage_payment.php` | Draft-covered | `docs/reviewing/accounting.md` |
|
||||
| Accounting dashboard | `app/ac_dashboard/index.php` | Draft-covered | `docs/reviewing/accounting.md`, `docs/reviewed/live-dashboard.md` |
|
||||
| Accounting setup | `app/accounting/chart_of_accounts.php`, `app/accounting/manage_account.php`, `app/accounting/departments.php`, `app/accounting/manage_department.php`, `app/accounting/account_formulas.php`, `app/accounting/posting_window.php` | Draft-covered | `docs/reviewing/accounting.md`, `docs/reviewing/master-data.md` |
|
||||
| Accounting journals and reports | `app/accounting/gl_entries.php`, `app/accounting/journal_listing.php`, `app/journal/index.php`, `app/journal/new.php`, `app/accounting/trial_balance.php`, `app/accounting/pl_statement.php`, `app/accounting/balance_sheet.php`, `app/accounting/gl_movement.php`, `app/accounting/vat_report.php` | Draft-covered | `docs/reviewing/accounting.md` |
|
||||
| WMS reports | `app/reports/stock_movement.php`, `app/reports/expired_stock.php`, `app/reports/occupy_rack.php`, `app/reports/product_lot.php` | Draft-covered | `docs/reviewing/wms.md`, `docs/reviewing/transaction-limits.md` |
|
||||
| Authentication | `app/login/index.php`, `app/login/register.php`, `app/login/verify.php`, `app/login/forgot_password.php`, `app/login/onboarding.php`, `app/login/invited_onboarding.php` | Draft-covered | `docs/reviewing/system-settings.md`, `docs/reviewing/invited-onboarding.md`, `docs/reviewed/session-concurrency.md` |
|
||||
| Settings | `app/setting/users.php`, `app/setting/profile.php`, `app/setting/company.php`, `app/setting/system_config.php`, `app/setting/smtp.php`, `app/setting/document_types.php`, `app/setting/gl_maintenance.php`, `app/setting/stock_maintenance.php` | Draft-covered | `docs/reviewing/system-settings.md`, `docs/reviewed/running-number.md` |
|
||||
| Cron | `app/cron/etl_gl_maintenance.php`, `app/cron/etl_stock_maintenance.php` | Draft-covered | `docs/reviewing/system-settings.md` |
|
||||
| Shared app shell | `app/index.php`, `app/session.php`, `app/preset.php`, `app/dbconn.php`, `app/config.php`, `app/config.example.php`, `app/include_*.php` | Draft-covered | `docs/reviewing/system-settings.md`, `docs/reviewing/runtime-architecture.md` |
|
||||
| Landing/setup | `index.php`, `setup.php`, `landing/index.php`, `landing/privacy.php`, `landing/terms.php`, `SESSION.php` | Draft-covered | `docs/reviewing/setup-landing.md`, `docs/reviewing/runtime-architecture.md` |
|
||||
|
||||
## API Coverage By Module
|
||||
|
||||
| Module | Endpoints | Coverage | Notes |
|
||||
|---|---:|---|---|
|
||||
| `ac_dashboard/api/engine` | 6 | Draft-covered | Dashboard APIs are `by_source`, `journals`, `pl`, `posting_window`, `recent`, `trend` |
|
||||
| `accounting/api/engine` | 25 | Draft-covered | CRUD, journal, posting, report, lock, and batch-log APIs are described by accounting/system specs |
|
||||
| `contact/api/engine` | 9 | Draft-covered | Contact and type CRUD plus stats are covered at feature level |
|
||||
| `dashboard/api/engine_report` | 2 | Draft-covered | WMS dashboard stats and low-stock APIs |
|
||||
| `expense/api/engine` | 6 | Draft-covered | Purchase request and supplier credit note APIs; purchase invoice behavior is shared through invoice managers/routes |
|
||||
| `finance/api/engine` | 8 | Draft-covered | Receipt/payment billing and receipt/payment CRUD/delete APIs |
|
||||
| `ics/api/engine` | 27 | Draft-covered | Core stock and label flows are covered by WMS; helper lookup endpoints are covered by `helper-endpoints.md` |
|
||||
| `ics/api/engine_report` | 7 | Draft-covered | Stock overview and activity/report APIs |
|
||||
| `inventory/api/engine` | 17 | Draft-covered | Product/category/warehouse/storage CRUD and stats |
|
||||
| `login/api/engine` | 9 | Draft-covered | Login, OTP, registration, onboarding, password reset, logout |
|
||||
| `order/api/engine` | 18 | Draft-covered | Sales order, return, invoice lifecycle |
|
||||
| `po/api/engine` | 17 | Draft-covered | PO, receive, supplier return, purchase invoice/credit note lifecycle |
|
||||
| `reports/api/engine_report` | 7 | Draft-covered | Stock reports and quota gating |
|
||||
| `revenue/api/engine` | 10 | Draft-covered | Quotation, revenue order, invoice conversion, credit note APIs |
|
||||
| `setting/api/engine` | 18 | Draft-covered | Users, company/profile/SMTP/system settings, document types, ETL maintenance, branch switching |
|
||||
|
||||
## API Helper Endpoint Coverage
|
||||
|
||||
These endpoints are implemented and used by forms. They are now covered at contract level by `docs/reviewing/helper-endpoints.md`:
|
||||
|
||||
- `app/ics/api/engine/contact_search.php`
|
||||
- `app/ics/api/engine/product_search.php`
|
||||
- `app/ics/api/engine/retrieve_active_lot.php`
|
||||
- `app/ics/api/engine/retrieve_active_serial.php`
|
||||
- `app/ics/api/engine/retrieve_aisle.php`
|
||||
- `app/ics/api/engine/retrieve_lot.php`
|
||||
- `app/ics/api/engine/retrieve_rack.php`
|
||||
- `app/ics/api/engine/retrieve_warehouse.php`
|
||||
- `app/ics/api/engine/retrieve_zone.php`
|
||||
- `app/ics/api/engine/validate_scan_location.php`
|
||||
- `app/inventory/api/engine/manager.php`
|
||||
- `app/accounting/api/engine/account_stats.php`
|
||||
- `app/accounting/api/engine/department_stats.php`
|
||||
- `app/contact/api/engine/contact_stats.php`
|
||||
- `app/inventory/api/engine/product_stats.php`
|
||||
- `app/inventory/api/engine/warehouse_stats.php`
|
||||
|
||||
## Cross-Cutting Coverage
|
||||
|
||||
| Behavior | Coverage | Spec |
|
||||
|---|---|---|
|
||||
| Role authorization | Covered | `docs/reviewed/role-guards.md` |
|
||||
| Soft delete and deletion order | Covered | `docs/reviewed/soft-delete.md` |
|
||||
| Running numbers | Covered | `docs/reviewed/running-number.md` |
|
||||
| Session concurrency | Covered | `docs/reviewed/session-concurrency.md` |
|
||||
| Dashboard socket events | Covered | `docs/reviewed/live-dashboard.md` |
|
||||
| Transaction quotas | Draft-covered | `docs/reviewing/transaction-limits.md` |
|
||||
| Posting window | Draft-covered | `docs/reviewing/accounting.md`, `docs/reviewing/master-data.md` |
|
||||
| Document statuses | Draft-covered | `docs/reviewing/document-lifecycle.md` |
|
||||
| Operation locks | Draft-covered | `docs/reviewing/system-settings.md`, `docs/reviewing/accounting.md` |
|
||||
| ETL maintenance | Draft-covered | `docs/reviewing/system-settings.md` |
|
||||
| File uploads | Draft-covered | `docs/reviewing/system-settings.md` |
|
||||
| Node.js server and cron runtime | Draft-covered | `docs/reviewed/live-dashboard.md`, `docs/reviewing/system-settings.md`, `docs/reviewing/runtime-architecture.md` |
|
||||
| Installation/setup and landing site | Draft-covered | `docs/reviewing/setup-landing.md`, `docs/reviewing/runtime-architecture.md` |
|
||||
|
||||
## Known Stale Or Corrected References
|
||||
|
||||
- `docs/reviewed/README.md` previously linked reviewing specs as if they lived in `docs/reviewed/`.
|
||||
- `docs/reviewed/README.md` previously referenced `docs/architecture.md`, which is not present.
|
||||
- `docs/reviewing/accounting.md` previously referenced `app/ac_dashboard/api/engine/stats.php`; the actual accounting dashboard endpoints are split by section.
|
||||
- `docs/reviewing/system-settings.md` previously referenced `migrations/*.sql`; schema setup wording now reflects this repository snapshot.
|
||||
|
||||
## Remaining Documentation Work
|
||||
|
||||
1. Promote or revise the `docs/reviewing/` specs after review.
|
||||
2. Add exact request/response payload tables for helper endpoints where frontend code depends on field-level contracts.
|
||||
3. Reconcile `schema_migrations` usage with the absence of a `migrations/` directory if file-based migrations are reintroduced.
|
||||
@@ -1,148 +0,0 @@
|
||||
# Document Lifecycle And Status
|
||||
|
||||
## Status Values
|
||||
|
||||
Document status is stored as an integer column on each transaction table. The meaning is consistent across most document types but not guaranteed to be identical everywhere — always read the manager before assuming.
|
||||
|
||||
### Sales and Purchase Invoices (`td_invoice`, all `doc_type` values)
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| `0` | Draft |
|
||||
| `1` | Issued / Active |
|
||||
| `2` | Paid / Settled (fully allocated) |
|
||||
| `4` | Void |
|
||||
|
||||
`doc_type` values on `td_invoice`: `invoice`, `credit_note`, `purchase_invoice`, `supplier_credit_note`.
|
||||
|
||||
Voiding is handled by a shared status endpoint. When a document is voided, `InvoiceManager` calls `GlManager::delete()` to remove any posted GL entry. Void is blocked if settlement documents (receipts or payments) are still allocated to the invoice — those must be voided first.
|
||||
|
||||
### Receipts and Payments (`td_receipt`, `td_payment`)
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| `1` | Active / posted business document |
|
||||
| `4` | Void |
|
||||
|
||||
Void calls `GlManager::delete()` through `ReceiptManager` and `PaymentManager` respectively. Posting window is enforced on both save and void.
|
||||
|
||||
### Receipt Billing Notes and Payment Billing Notes (`td_receipt_billing`, `td_payment_billing`)
|
||||
|
||||
Billing notes group one or more invoices into a single collection document sent to a customer (receipt billing) or supplier (payment billing). Each line in `td_receipt_billing_item` / `td_payment_billing_item` references a `td_invoice.id` and carries an allocated amount.
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| `1` | Open — no receipts / payments posted against it yet |
|
||||
| `2` | Fully received / paid — allocated receipts or payments equal the billing total |
|
||||
| `3` | Partially received / paid — some but not all receipts or payments have been posted |
|
||||
| `4` | Void |
|
||||
|
||||
Status is never set manually — `ReceiptBillingManager::refreshBillingStatus()` and `PaymentBillingManager::refreshBillingStatus()` derive and write the correct value after each receipt or payment change.
|
||||
|
||||
Void is blocked if any posted receipt (`td_receipt.status = 1`) or payment (`td_payment.status = 1`) is allocated to the billing note — those must be voided first. Voiding a billing note does **not** void the invoices it references. Invoices cannot be voided while an active billing note (`status` ∈ `{1, 2, 3}`) references them.
|
||||
|
||||
### Quotations (`td_quotation`)
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| `-1` | Cancelled |
|
||||
| `0` | Draft |
|
||||
| `1` | Sent |
|
||||
| `2` | Accepted |
|
||||
| `3` | Rejected |
|
||||
| `5` | Converted |
|
||||
|
||||
Status transitions are enforced by `QuotationManager::updateStatus()`:
|
||||
|
||||
- `0 → 1` (send), `1 → 2` (accept), `1 → 3` (reject)
|
||||
- `1 / 2 / 3 → 0` (reopen) — blocked if any item already has `converted_qty > 0`
|
||||
- `0 / 1 → -1` (cancel)
|
||||
|
||||
`status = 5` is never set via `updateStatus()`. It is written automatically by `QuotationManager::incrementConvertedQty()` once all line items are fully converted to a sales order. `OrderManager::linkQuotationToOrder()` calls this after a successful order creation. When the linked order is later cancelled or soft-deleted, `OrderManager` reverts the quotation back to `status = 2` (Accepted).
|
||||
|
||||
### Sales Orders (`td_order`)
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| `-2` | Pending warehouse selection or source completion |
|
||||
| `-1` | Cancelled |
|
||||
| `0` | Draft |
|
||||
| `1` | Confirmed |
|
||||
|
||||
Confirmation can create stock-out rows. Cancellation reverses stock-out rows where required and is blocked when active invoices or returns exist.
|
||||
|
||||
### Purchase Orders (`td_purchase_order`)
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| `-2` | Pending warehouse selection or source completion |
|
||||
| `-1` | Cancelled |
|
||||
| `0` | Draft |
|
||||
| `1` | Confirmed |
|
||||
|
||||
`td_purchase_order.status` only ever holds the values above. **Partially received** and **fully received** are not stored statuses — they are derived on the fly by `PurchaseOrderManager::deriveReceiptStatus()` into a virtual `receipt_status` field returned alongside the PO row. This derived value reflects the ratio of approved stock-in rows to ordered quantities and is never written back to `td_purchase_order.status`.
|
||||
|
||||
Receiving creates stock-in rows. Close and cancel rules apply based on receiving progress and approved stock-in rows.
|
||||
|
||||
### Purchase Requests (`td_purchase_request`)
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| `-1` | Cancelled |
|
||||
| `0` | Draft |
|
||||
| `1` | Submitted |
|
||||
| `2` | Approved / reopened after conversion rollback |
|
||||
| `3` | Rejected |
|
||||
| `5` | Converted |
|
||||
|
||||
Converted requests have a linked purchase order.
|
||||
|
||||
### Returns and Supplier Returns (`td_return`, `td_supplier_return`)
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| `-1` | Cancelled |
|
||||
| `0` | Draft |
|
||||
| `1` | Confirmed |
|
||||
|
||||
Confirmation creates stock-in (sales return) or stock-out (supplier return) rows. Conversion creates a credit note or supplier credit note document via `InvoiceManager`.
|
||||
|
||||
### Stock Entries (`td_stock_<warehouse_id>`)
|
||||
|
||||
Draft stock entries await approval. Approved entries update warehouse balances. Approved entries cannot be deleted without reversal; editing and deletion are also blocked outside the posting window.
|
||||
|
||||
## Void And Soft Delete Strategy
|
||||
|
||||
The current strategy (as of 2026-05-19):
|
||||
|
||||
- **Draft or unposted documents** may be soft-deleted or cancelled freely.
|
||||
- **Posted or issued documents** use a void workflow. The source document is preserved in audit history. Void removes the linked GL entry through `GlManager::delete()` and enforces the posting window.
|
||||
- **Master data** is soft-deleted by setting `status = 0`. Deletion is blocked when active documents reference the record.
|
||||
|
||||
Void convention uses `status = 4` for `td_invoice` document types (covers `invoice`, `credit_note`, `purchase_invoice`, `supplier_credit_note`). Credit notes are considered inactive at `status = 4`, not `status = -1`.
|
||||
|
||||
### Soft-delete tombstone mechanism
|
||||
|
||||
Draft transaction documents (invoices, receipts, payments, billing notes, orders, quotations, returns, etc.) are soft-deleted by **negating `company_id`** on the header row and all child item rows:
|
||||
|
||||
```sql
|
||||
UPDATE td_invoice SET company_id = company_id * -1 WHERE id = :id AND company_id = :cid
|
||||
UPDATE td_invoice_item SET company_id = company_id * -1 WHERE invoice_id = :id AND company_id = :cid
|
||||
```
|
||||
|
||||
This is the tombstone convention used by every `softDelete()` method across all managers. Because every read query filters `WHERE company_id = :cid` (positive), tombstoned rows are automatically excluded without any additional flag. Any query that omits the `company_id` filter will expose tombstoned rows — treat this filter as mandatory.
|
||||
|
||||
`deleted_at`, `deleted_by`, and `delete_reason` audit columns are not yet implemented.
|
||||
|
||||
## GL Versioning
|
||||
|
||||
When a source document is posted to the GL more than once (re-post after edit), `GlManager::replace()` is called instead of `GlManager::post()`. The replace path:
|
||||
|
||||
1. Reads the current `td_gl` and `td_gl_item` rows.
|
||||
2. Appends a snapshot of the current lines as JSON into the `td_gl.history` column.
|
||||
3. Increments `td_gl.current_version`.
|
||||
4. Writes the new lines into `td_gl_item`.
|
||||
|
||||
This means the full posting history for a document is preserved in `td_gl.history` as a versioned JSON array. `GlManager::delete()` creates a **reversal journal entry** (debits and credits swapped, lines prefixed with `VOID:`) linked to the original `source_id`, then tombstones the original `td_gl` row. No rows are physically deleted — the full audit trail is preserved in the database.
|
||||
|
||||
Manual journal entries use `GlManager::postManual()` and `GlManager::replaceManual()` which follow the same versioning pattern.
|
||||
@@ -1,206 +0,0 @@
|
||||
# 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()` |
|
||||
@@ -1,308 +0,0 @@
|
||||
# Accounting Features
|
||||
|
||||
## Accounting Dashboard
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/ac_dashboard/index.php`
|
||||
- `app/ac_dashboard/api/engine/by_source.php`
|
||||
- `app/ac_dashboard/api/engine/journals.php`
|
||||
- `app/ac_dashboard/api/engine/pl.php`
|
||||
- `app/ac_dashboard/api/engine/posting_window.php`
|
||||
- `app/ac_dashboard/api/engine/recent.php`
|
||||
- `app/ac_dashboard/api/engine/trend.php`
|
||||
|
||||
The accounting dashboard summarizes accounting KPIs, posting window status, and high-level financial indicators.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `ac_dashboard/api/engine/*`
|
||||
- `FinancialReports`
|
||||
- `CompanySettingManager`
|
||||
|
||||
## Revenue Documents
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/revenue/quotation.php`
|
||||
- `app/revenue/manage_quotation.php`
|
||||
- `app/revenue/order.php`
|
||||
- `app/revenue/manage_order.php`
|
||||
- `app/revenue/invoice.php`
|
||||
- `app/revenue/manage_invoice.php`
|
||||
- `app/revenue/manage_credit_note.php`
|
||||
- `app/revenue/view_invoice.php`
|
||||
- `app/revenue/receipt.php`
|
||||
|
||||
Revenue workflows include quotations, sales orders, sales invoices, and customer credit notes. Quotations can be converted forward, sales documents support inline tax, and invoices can be posted to the GL.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `QuotationManager`
|
||||
- `OrderManager`
|
||||
- `InvoiceManager`
|
||||
- `revenue/api/engine/*`
|
||||
|
||||
## Expense Documents
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/expense/purchase_request.php`
|
||||
- `app/expense/manage_purchase_request.php`
|
||||
- `app/expense/purchase_order.php`
|
||||
- `app/expense/manage_purchase_order.php`
|
||||
- `app/expense/purchase_invoice.php`
|
||||
- `app/expense/manage_purchase_invoice.php`
|
||||
- `app/expense/manage_supplier_credit_note.php`
|
||||
- `app/expense/payment.php`
|
||||
|
||||
Expense workflows include purchase requests, purchase orders, purchase invoices, and supplier credit notes. Purchase requests can be submitted, approved, rejected, reopened, cancelled, and converted to purchase orders. Purchase documents support inline tax and accounting formula assignment.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `PurchaseRequestManager`
|
||||
- `PurchaseOrderManager`
|
||||
- `InvoiceManager`
|
||||
- `expense/api/engine/*`
|
||||
|
||||
## Receipt Billing And Receipts
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/finance/receipt_billing.php`
|
||||
- `app/finance/manage_receipt_billing.php`
|
||||
- `app/finance/receipt.php`
|
||||
- `app/finance/manage_receipt.php`
|
||||
|
||||
Receipt Billing groups customer invoices for collection. Receipts allocate cash against open customer documents, refresh invoice settlement status, and can be assigned formulas for GL posting.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `ReceiptBillingManager`
|
||||
- `ReceiptManager`
|
||||
|
||||
## Payment Billing And Payments
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/finance/payment_billing.php`
|
||||
- `app/finance/manage_payment_billing.php`
|
||||
- `app/finance/payment.php`
|
||||
- `app/finance/manage_payment.php`
|
||||
|
||||
Payment Billing groups supplier invoices for payment. Payments allocate cash against open supplier documents, refresh settlement status, and can be assigned formulas for GL posting.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `PaymentBillingManager`
|
||||
- `PaymentManager`
|
||||
|
||||
## Chart Of Accounts
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/chart_of_accounts.php`
|
||||
- `app/accounting/manage_account.php`
|
||||
- `app/accounting/api/engine/account.php`
|
||||
- `app/accounting/api/engine/manage_account.php`
|
||||
|
||||
The chart of accounts manages account code, account name, account type, account category, parent account, posting flag, and status. Account categories are used by reports such as VAT.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `ChartOfAccounts::getAll()`
|
||||
- `ChartOfAccounts::create()`
|
||||
- `ChartOfAccounts::update()`
|
||||
- `ChartOfAccounts::isPostingAccount()`
|
||||
|
||||
## Departments
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/departments.php`
|
||||
- `app/accounting/manage_department.php`
|
||||
- `app/accounting/api/engine/department.php`
|
||||
|
||||
Departments are optional dimensions on journal lines and accounting reports. Reports can filter by department where supported.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `DepartmentManager`
|
||||
|
||||
## Account Formulas
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/account_formulas.php`
|
||||
- `app/accounting/api/engine/account_formula.php`
|
||||
|
||||
Account Formulas define GL posting templates by document type. A formula consists of debit/credit lines, account codes, amount keys, descriptions, status, and default flag.
|
||||
|
||||
Supported amount keys:
|
||||
|
||||
- `grand_total`: final document total.
|
||||
- `total`: pre-tax/subtotal-style amount, split by SKU when Product Account Mapping applies.
|
||||
- `tax`: document tax amount.
|
||||
- `amount`: receipt or payment amount.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `AccountFormulaManager`
|
||||
- `BasePosting::resolveFormula()`
|
||||
|
||||
## Product Account Mapping
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/account_formulas.php`
|
||||
- `app/accounting/api/engine/product_account_mapping.php`
|
||||
- `app/inventory/manage_product.php`
|
||||
|
||||
Product Account Mapping assigns sales and purchase GL accounts to each product/SKU. It is used when an account formula line uses `total`.
|
||||
|
||||
The `Product Accounts` tab inside Account Formulas is an accounting-only maintenance view. The pencil action opens a modal that edits only `sales_account_code` and `purchase_account_code`; full product master data remains in the product maintenance page.
|
||||
|
||||
Posting behavior:
|
||||
|
||||
- Sales invoice and credit note `total` lines use `sales_account_code`.
|
||||
- Purchase invoice and supplier credit note `total` lines use `purchase_account_code`.
|
||||
- If a product mapping is blank, posting falls back to the formula account for that line.
|
||||
- Batch GL posting checks loaded invoice documents and blocks Run when product mappings are missing for formulas that use `total`.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `ProductManager::updateAccountMapping()`
|
||||
- `BasePosting::buildSkuTotalLines()`
|
||||
- `InvoicePosting`
|
||||
- `CreditNotePosting`
|
||||
- `PurchaseInvoicePosting`
|
||||
- `SupplierCreditNotePosting`
|
||||
|
||||
## Manual GL Journal
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/journal_listing.php`
|
||||
- `app/journal/index.php`
|
||||
- `app/journal/new.php`
|
||||
- `app/accounting/api/engine/save_manual_journal.php`
|
||||
- `app/accounting/api/engine/get_journal_listing.php`
|
||||
- `app/accounting/api/engine/get_journal_detail.php`
|
||||
|
||||
Manual GL Journals allow users to create or edit journal entries outside source documents. Journal lines include account code, department, debit, credit, and description.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `GlManager::postManual()`
|
||||
- `GlManager::replaceManual()`
|
||||
|
||||
## Source Document GL Posting
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/api/engine/post_gl_entry.php`
|
||||
|
||||
Source document posting builds journal lines from a source document and selected/default formula, then writes or replaces `td_gl` and `td_gl_item`.
|
||||
|
||||
Supported source types:
|
||||
|
||||
- `invoice`
|
||||
- `credit_note`
|
||||
- `purchase_invoice`
|
||||
- `supplier_credit_note`
|
||||
- `receipt`
|
||||
- `payment`
|
||||
|
||||
Posting behavior:
|
||||
|
||||
- First post: `GlManager::post()` creates `td_gl` and `td_gl_item` rows.
|
||||
- Re-post: `GlManager::replace()` snapshots the current lines as JSON into `td_gl.history`, increments `td_gl.current_version`, then writes new lines. Full version history is preserved.
|
||||
- Void: calls `GlManager::delete()` which hard-deletes the `td_gl` and `td_gl_item` rows. No reversal entry is created (future work).
|
||||
|
||||
Backend support:
|
||||
|
||||
- `BasePosting`
|
||||
- `InvoicePosting`
|
||||
- `CreditNotePosting`
|
||||
- `PurchaseInvoicePosting`
|
||||
- `SupplierCreditNotePosting`
|
||||
- `ReceiptPosting`
|
||||
- `PaymentPosting`
|
||||
- `GlManager::post()`
|
||||
- `GlManager::replace()`
|
||||
|
||||
> **Planned — not yet implemented:** `PostingManager` (`app/assets/utils/classes/PostingManager.php`) is a stub intended as the sole bridge between WMS engine files and the accounting engine. WMS endpoints would call `PostingManager::postInvoice()` etc. instead of importing accounting classes directly. All methods are TODO — GL posting is currently triggered manually from `gl_entries.php`, not automatically from WMS saves.
|
||||
|
||||
## Batch GL Entries
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/gl_entries.php`
|
||||
- `app/accounting/api/engine/get_gl_documents.php`
|
||||
- `app/accounting/api/engine/post_gl_entry.php`
|
||||
- `app/accounting/api/engine/acquire_op_lock.php`
|
||||
- `app/accounting/api/engine/release_op_lock.php`
|
||||
- `app/accounting/api/engine/log_batch_action.php`
|
||||
|
||||
Batch GL Entries load documents by date range and formula, then post or replace GL entries one document at a time. A DB lock prevents overlapping batch posting jobs across tabs/users.
|
||||
|
||||
Current safeguards:
|
||||
|
||||
- Requires formula selection.
|
||||
- Uses operation locks for `gl_post` via `OperationLockManager` — prevents two users running overlapping batch jobs.
|
||||
- Uses retry behavior through `batch_process()`.
|
||||
- Blocks Run when Product Account Mapping is incomplete for selected formula/document set.
|
||||
- Shows Product Accounts status per loaded invoice document.
|
||||
- Logs batch totals and failed IDs via `BatchActionManager` — records start time, end time, total processed, failed IDs, and result message into `batch_action_log`.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `OperationLockManager`
|
||||
- `BatchActionManager::log(array $data)`
|
||||
|
||||
## Posting Window
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/posting_window.php`
|
||||
- `app/accounting/api/engine/posting_window.php`
|
||||
|
||||
The posting window controls which dates are open for accounting postings and approved inventory entries. It supports open-from and open-to boundaries.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `CompanySettingManager`
|
||||
- `PostingWindowGuard::assertOpenDate()`
|
||||
- `GlManager` posting validation
|
||||
- `ReceiptManager` and `PaymentManager` date validation
|
||||
|
||||
## Financial Reports
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/trial_balance.php`
|
||||
- `app/accounting/pl_statement.php`
|
||||
- `app/accounting/balance_sheet.php`
|
||||
- `app/accounting/gl_movement.php`
|
||||
- `app/accounting/vat_report.php`
|
||||
- `app/accounting/api/engine/get_trial_balance.php`
|
||||
- `app/accounting/api/engine/get_pl_statement.php`
|
||||
- `app/accounting/api/engine/get_balance_sheet.php`
|
||||
- `app/accounting/api/engine/get_gl_movement.php`
|
||||
- `app/accounting/api/engine/get_vat_report.php`
|
||||
|
||||
Reports are generated from `td_gl`, `td_gl_item`, `md_account`, and optional departments.
|
||||
|
||||
Report behavior:
|
||||
|
||||
- Date filters use full dates (`YYYY-MM-DD` payload, `dd/mm/yyyy` UI).
|
||||
- Trial Balance shows opening, movement, and closing balances.
|
||||
- P&L shows revenue, expenses, and net profit.
|
||||
- Balance Sheet shows asset, liability, equity, and retained earnings.
|
||||
- GL Movement shows account-level journal movement.
|
||||
- VAT Report uses account categories such as `sales_tax` and `purchase_tax`.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `FinancialReports`
|
||||
- `TaxReportManager`
|
||||
@@ -1,97 +0,0 @@
|
||||
# Helper Endpoint Contracts
|
||||
|
||||
This spec covers helper, lookup, search, and stats endpoints that support forms and dashboards. The main business save/delete/status endpoints are covered in the WMS, Accounting, Master Data, System Settings, and Document Lifecycle specs.
|
||||
|
||||
## Common Contract
|
||||
|
||||
Unless a route explicitly differs, helper endpoints:
|
||||
|
||||
- Load authenticated context through `db_auth.php`.
|
||||
- Scope reads by the active `company_id`.
|
||||
- Return JSON.
|
||||
- Are consumed by AJAX from PHP pages.
|
||||
- Should not mutate business documents unless their name says `manage`, `remove`, `delete`, `approve`, `confirm`, `cancel`, `void`, `issue`, `post`, `receive`, or `update`.
|
||||
|
||||
## ICS Search And Lookup
|
||||
|
||||
| Endpoint | Purpose | Main consumers |
|
||||
|---|---|---|
|
||||
| `app/ics/api/engine/contact_search.php` | Search contacts for stock/document forms. | Stock in/out/transfer and document source forms. |
|
||||
| `app/ics/api/engine/product_search.php` | Search active products/SKUs for stock forms. | Stock in/out/transfer forms. |
|
||||
| `app/ics/api/engine/barcode_lookup.php` | Resolve scanned barcode into product, lot/serial, or location context. | Scanner workflows and barcode-enabled forms. |
|
||||
| `app/ics/api/engine/validate_scan_location.php` | Validate scanned warehouse/zone/aisle/rack context. | Scanner workflows. |
|
||||
| `app/ics/api/engine/retrieve_stock_by_source.php` | Return stock rows linked to a source document. | WMS document detail and approval/deletion context. |
|
||||
|
||||
## ICS Location Helpers
|
||||
|
||||
| Endpoint | Purpose |
|
||||
|---|---|
|
||||
| `app/ics/api/engine/retrieve_warehouse.php` | Return active warehouses available to the company. |
|
||||
| `app/ics/api/engine/retrieve_zone.php` | Return zones for a warehouse. |
|
||||
| `app/ics/api/engine/retrieve_aisle.php` | Return aisles for a warehouse/zone. |
|
||||
| `app/ics/api/engine/retrieve_rack.php` | Return racks for a warehouse/zone/aisle. |
|
||||
|
||||
These endpoints are driven by warehouse/storage setup and company location mode. They are used to populate dependent dropdowns and validate manual location selection.
|
||||
|
||||
## ICS Lot And Serial Helpers
|
||||
|
||||
| Endpoint | Purpose |
|
||||
|---|---|
|
||||
| `app/ics/api/engine/retrieve_lot.php` | Return known lots for a product/SKU. |
|
||||
| `app/ics/api/engine/retrieve_active_lot.php` | Return lots with active stock availability. |
|
||||
| `app/ics/api/engine/retrieve_active_serial.php` | Return serial numbers with active stock availability. |
|
||||
| `app/ics/api/engine/sku_label_lots.php` | Return lots available for SKU label generation. |
|
||||
| `app/ics/api/engine/sku_label_products.php` | Return products available for SKU label generation. |
|
||||
|
||||
## Stats Endpoints
|
||||
|
||||
| Endpoint | Purpose |
|
||||
|---|---|
|
||||
| `app/accounting/api/engine/account_stats.php` | Chart of accounts summary cards/statistics. |
|
||||
| `app/accounting/api/engine/department_stats.php` | Department summary cards/statistics. |
|
||||
| `app/contact/api/engine/contact_stats.php` | Contact summary cards/statistics. |
|
||||
| `app/inventory/api/engine/product_stats.php` | Product summary cards/statistics. |
|
||||
| `app/inventory/api/engine/warehouse_stats.php` | Warehouse summary cards/statistics. |
|
||||
| `app/ics/api/engine_report/product_stats.php` | Stock/product operational statistics. |
|
||||
| `app/ics/api/engine_report/warehouse_stats.php` | Stock overview warehouse statistics. |
|
||||
| `app/dashboard/api/engine_report/reports_stats.php` | WMS dashboard cards and charts; gated by transaction limits. |
|
||||
|
||||
Stats endpoints are read-only dashboard/list-page support routes. They should follow the same quota behavior as their parent dashboard/report area where `UsageGuard` is applied.
|
||||
|
||||
## Retrieve Endpoints
|
||||
|
||||
Retrieve endpoints return one record or a bounded list needed by an edit form.
|
||||
|
||||
| Module | Endpoints |
|
||||
|---|---|
|
||||
| Contact | `retrieve_contact.php`, `retrieve_contact_type.php` |
|
||||
| Inventory | `retrieve_category.php`, `retrieve_product.php`, `retrieve_storage.php`, `retrieve_warehouse.php` |
|
||||
| Stock | `retrieve_stock_in.php`, `retrieve_stock_out.php`, `retrieve_stock_transfer.php` |
|
||||
| Order | `retrieve_order.php`, `retrieve_invoice.php`, `retrieve_return.php` |
|
||||
| PO | `retrieve_po.php`, `retrieve_supplier_return.php` |
|
||||
| Revenue | `retrieve_quotation.php` |
|
||||
| Expense | `retrieve_purchase_request.php` |
|
||||
| Accounting | `retrieve_account.php`, `retrieve_department.php`, `get_journal_detail.php` |
|
||||
| Settings | `retrieve_company.php`, `retrieve_profile.php`, `retrieve_smtp.php`, `retrieve_users.php` |
|
||||
|
||||
Expected behavior:
|
||||
|
||||
- Validate active company scope.
|
||||
- Return only active positive-company rows unless the feature explicitly needs historical/deleted records.
|
||||
- Avoid exposing rows from another company even when an ID is guessed.
|
||||
|
||||
## Generic Inventory Manager Endpoint
|
||||
|
||||
Endpoint: `app/inventory/api/engine/manager.php`
|
||||
|
||||
This route is an inventory utility endpoint guarded to owner/admin roles. It belongs with product/category/warehouse/storage maintenance and should not be used for unauthenticated or cross-module access.
|
||||
|
||||
## Endpoint Documentation Rule
|
||||
|
||||
When adding a new helper endpoint, update this file if the endpoint is not already clearly covered by a feature spec. Include:
|
||||
|
||||
- Endpoint path.
|
||||
- Read/write behavior.
|
||||
- Main consumer page.
|
||||
- Important company/role/limit guards.
|
||||
- Payload shape when frontend code depends on exact fields.
|
||||
@@ -1,158 +0,0 @@
|
||||
# Master Data Features
|
||||
|
||||
## Products
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/inventory/product.php`
|
||||
- `app/inventory/manage_product.php`
|
||||
- `app/inventory/manage_category.php`
|
||||
- `app/inventory/api/engine/product.php`
|
||||
- `app/inventory/api/engine/manage_product.php`
|
||||
- `app/inventory/api/engine/product_category.php`
|
||||
|
||||
Products store SKU, product name, barcode, UOM, price, cost price, min stock, reorder point, category, image, status, and GL mapping fields. Margin is a derived display value (price − cost price) computed on the frontend and is not stored.
|
||||
|
||||
Product accounting fields:
|
||||
|
||||
- `sales_account_code`
|
||||
- `purchase_account_code`
|
||||
|
||||
These fields support Product Account Mapping for GL posting.
|
||||
|
||||
Accounting users can maintain only these two GL mapping fields from `app/accounting/account_formulas.php` without opening the full product edit page.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `ProductManager::getProductList()`
|
||||
- `ProductManager::saveProduct()`
|
||||
- `ProductManager::deleteProduct()`
|
||||
- `ProductManager::updateAccountMapping()`
|
||||
|
||||
## Product Categories
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/inventory/manage_category.php`
|
||||
- `app/inventory/api/engine/manage_category.php`
|
||||
- `app/inventory/api/engine/product_category.php`
|
||||
|
||||
Product categories organize products and are protected from deletion when active products or stock depend on them.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `ProductManager::getCategoryList()`
|
||||
- `ProductManager::saveCategory()`
|
||||
- `ProductManager::deleteCategory()`
|
||||
|
||||
## Warehouse Locations
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/inventory/warehouse.php`
|
||||
- `app/inventory/manage_warehouse.php`
|
||||
- `app/inventory/manage_storage.php`
|
||||
- `app/inventory/api/engine/warehouse.php`
|
||||
- `app/inventory/api/engine/manage_warehouse.php`
|
||||
- `app/inventory/api/engine/storage.php`
|
||||
- `app/inventory/api/engine/manage_storage.php`
|
||||
|
||||
Warehouse setup manages warehouses and storage hierarchy. WMS forms use warehouse, zone, aisle, and rack data for stock movement, barcode labels, validation, and capacity reporting.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `WarehouseManager`
|
||||
- `ReportManager` capacity and occupancy methods
|
||||
|
||||
## Contacts
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/contact/contact.php`
|
||||
- `app/contact/manage_contact.php`
|
||||
- `app/contact/manage_contact_type.php`
|
||||
- `app/contact/api/engine/contact.php`
|
||||
- `app/contact/api/engine/manage_contact.php`
|
||||
- `app/contact/api/engine/contact_type.php`
|
||||
|
||||
Contacts represent customers, suppliers, or other counterparties. Contact records support type/category, image upload, status, search, and transaction references from sales, purchase, receipts, and payments.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `ContactManager::getContactList()`
|
||||
- `ContactManager::searchContact()`
|
||||
- `ContactManager::saveContact()`
|
||||
- `ContactManager::deleteContact()`
|
||||
- `ContactManager::saveContactType()`
|
||||
|
||||
## Chart Of Accounts
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/chart_of_accounts.php`
|
||||
- `app/accounting/manage_account.php`
|
||||
|
||||
Chart of Accounts is master data for accounting. Each account has code, name, type, category, parent, posting flag, and status.
|
||||
|
||||
Important account categories:
|
||||
|
||||
- `sales_tax`
|
||||
- `purchase_tax`
|
||||
|
||||
These categories feed VAT reporting.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `ChartOfAccounts`
|
||||
|
||||
## Departments
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/departments.php`
|
||||
- `app/accounting/manage_department.php`
|
||||
|
||||
Departments are accounting dimensions used by formulas, journals, and reports. They can be active or inactive.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `DepartmentManager`
|
||||
|
||||
## Account Formulas
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/account_formulas.php`
|
||||
|
||||
Account Formulas are master setup for automated GL posting. They are maintained separately from actual journal entries.
|
||||
|
||||
`AccountFormulaManager::delete()` is an **archive operation**, not a physical delete. It sets `status = 0` and clears the `is_default` flag on `md_account_formula`. The formula row and its line items remain in the database. Historical GL entries in `td_gl` that reference the formula via `formula_id` continue to point to the archived row — this is intentional, as the snapshot is preserved for audit and re-post replay. An archived formula can no longer be selected for new postings (`isPostingAccount()` requires `status = 1`) but existing GL history is unaffected.
|
||||
|
||||
Formula setup includes:
|
||||
|
||||
- Document type.
|
||||
- Formula name.
|
||||
- Default flag.
|
||||
- Status.
|
||||
- Debit/credit formula lines.
|
||||
- Amount key per line.
|
||||
- Posting account per line.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `AccountFormulaManager`
|
||||
|
||||
## Posting Window
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/posting_window.php`
|
||||
|
||||
Posting Window is master configuration for accounting/inventory date control. It can define no restriction, a lower date, an upper date, or a bounded posting period.
|
||||
|
||||
The posting window restricts **transaction document dates** only — GL postings, stock movements, receipts, payments, and invoice issuance. It does **not** restrict saves or deletes of master data records (products, contacts, warehouses, chart of accounts, departments). Master data edits are always permitted regardless of the posting window.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `CompanySettingManager` — stores and retrieves the window bounds as company settings (`posting_open_from`, `posting_open_to`)
|
||||
- `PostingWindowGuard` — enforces the window; called by `GlManager` and `WarehouseManager` stock-movement paths, not by master-data managers
|
||||
@@ -1,108 +0,0 @@
|
||||
# Runtime Architecture
|
||||
|
||||
This spec covers runtime files that are not owned by a single business module.
|
||||
|
||||
## Entry Points
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `index.php` | Root redirect to `landing/` using `$base_url` from `app/config.php`. |
|
||||
| `app/index.php` | Authenticated app landing/router entry after login. |
|
||||
| `app/session.php` | Session bootstrap and session guard helpers. |
|
||||
| `app/dbconn.php` | Database connection setup for auth and business databases. |
|
||||
| `app/preset.php` | Shared app initialization values used by pages. |
|
||||
| `app/include_header.php` | Shared HTML head, CSS/JS includes, and page shell dependencies. |
|
||||
| `app/include_topbar.php` | Topbar, branch/company switcher, usage warning badge, and session-visible user context. |
|
||||
| `app/include_sidebar.php` | WMS navigation. |
|
||||
| `app/include_sidebar_ac.php` | Accounting navigation. |
|
||||
| `app/include_master_sidebar.php` | Master-data navigation. |
|
||||
| `app/include_setting_sidebar.php` | Settings navigation. |
|
||||
| `app/include_ending.php` | Shared closing scripts and Socket.IO initialization. |
|
||||
|
||||
## Configuration
|
||||
|
||||
`app/config.php` defines database credentials, base URLs, encryption settings, package tiers, and runtime secrets. `app/config.example.php` is the template for new environments.
|
||||
|
||||
Important runtime values:
|
||||
|
||||
- `$base_url` - public app base URL used for redirects and links.
|
||||
- `$server_url` - app URL prefix used by PHP pages for AJAX routes.
|
||||
- `$db_database` - auth/identity database.
|
||||
- `$db_database2` - business data database.
|
||||
- `$packages` - package tier limits used by `UsageGuard`.
|
||||
- Node emit/cron secrets - shared with the Node.js runtime through environment variables.
|
||||
|
||||
## Database Connections
|
||||
|
||||
The app uses two MySQL databases:
|
||||
|
||||
| Database | Purpose |
|
||||
|---|---|
|
||||
| Auth database | Users, companies, company-user mapping, package usage, SMTP, company settings, schema migration records. |
|
||||
| Business database | Products, warehouses, contacts, stock, sales/purchase/finance documents, GL, reports, barcode labels, operation locks. |
|
||||
|
||||
Most authenticated endpoints load `app/assets/utils/db_auth.php`, which establishes session context, validates company access, validates CSRF for writes, and exposes `$pdo1`, `$pdo2`, `$company_id`, `$user_id`, and `$user_role`.
|
||||
|
||||
## Node.js Realtime Server
|
||||
|
||||
File: `nodejs/server.js`
|
||||
|
||||
The Node.js process hosts the Socket.IO server and a protected HTTP `/emit` endpoint.
|
||||
|
||||
Flow:
|
||||
|
||||
1. PHP calls `/emit` after a successful database commit.
|
||||
2. The request must include the `x-emit-secret` header matching `EMIT_SECRET`.
|
||||
3. Body contains `{ event, data, company_id }`.
|
||||
4. Node broadcasts to room `company_{company_id}`.
|
||||
5. Browser tabs join their company room through the Socket.IO client initialized in `app/include_ending.php`.
|
||||
|
||||
Environment variables:
|
||||
|
||||
| Variable | Purpose |
|
||||
|---|---|
|
||||
| `PORT` | Node HTTP/Socket.IO port; defaults to `3000`. |
|
||||
| `EMIT_SECRET` | Shared secret for PHP-to-Node event emission and cron calls. |
|
||||
|
||||
## Node.js Cron Scheduler
|
||||
|
||||
File: `nodejs/scheduler.js`
|
||||
|
||||
The scheduler runs alongside the realtime server process and calls PHP cron endpoints over HTTP. It authenticates with the `x-cron-secret` header using `EMIT_SECRET`.
|
||||
|
||||
Environment variables:
|
||||
|
||||
| Variable | Purpose |
|
||||
|---|---|
|
||||
| `PHP_BASE_URL` | Base URL for the PHP host; defaults to `http://127.0.0.1`. |
|
||||
| `PHP_WEBROOT` | PHP app path; defaults to `/mn3wms/app`. |
|
||||
| `EMIT_SECRET` | Shared cron secret. |
|
||||
|
||||
Schedules:
|
||||
|
||||
| Schedule | Endpoint | Purpose |
|
||||
|---|---|---|
|
||||
| `0 * * * *` | `/cron/etl_gl_maintenance.php` | Repair GL summary gaps for companies in the current hourly slot. |
|
||||
| `30 * * * *` | `/cron/etl_stock_maintenance.php` | Repair stock summary gaps for companies in the current hourly slot. |
|
||||
|
||||
Both jobs use `company_id % 24` slotting so each company is checked at most once per hour and work is spread across the day.
|
||||
|
||||
## Setup Script
|
||||
|
||||
File: `setup.php`
|
||||
|
||||
`setup.php` is a CLI-only production bootstrap script. HTTP requests receive `403` and the script exits.
|
||||
|
||||
Behavior:
|
||||
|
||||
1. Loads database credentials from `app/config.php`.
|
||||
2. Connects to MySQL without selecting a database.
|
||||
3. Creates the auth and business databases if they do not exist.
|
||||
4. Creates core tables with `CREATE TABLE IF NOT EXISTS`.
|
||||
5. Prints a terminal status report.
|
||||
|
||||
Dynamic stock tables named `td_stock_<warehouse_id>` are not created by `setup.php`; the app creates them when a warehouse is used.
|
||||
|
||||
## Migration State
|
||||
|
||||
The auth database includes a `schema_migrations` table, but this repository snapshot does not contain a `migrations/` directory. Schema setup is currently embedded in `setup.php` and runtime manager code.
|
||||
@@ -1,75 +0,0 @@
|
||||
# Setup And Landing Pages
|
||||
|
||||
This spec covers public entry, setup, and static marketing/legal pages outside the authenticated app.
|
||||
|
||||
## Root Redirect
|
||||
|
||||
Route: `index.php`
|
||||
|
||||
The repository root index loads `app/config.php`, reads `$base_url`, and redirects users to `landing/`.
|
||||
|
||||
## Landing Home
|
||||
|
||||
Route: `landing/index.php`
|
||||
|
||||
The landing page is a public marketing page for MN3 WMS. It uses shared landing includes and assets under `landing/includes`, `landing/css`, `landing/js`, and `landing/images`.
|
||||
|
||||
Primary behavior:
|
||||
|
||||
- Builds `$base` from `$base_url . '/landing'`.
|
||||
- Builds `$app_url` from `$base_url . '/app'`.
|
||||
- Shows product positioning for warehouse, stock, fulfillment, barcode, and reporting workflows.
|
||||
- Links primary calls to action to `app/login/index.php`.
|
||||
- Includes public navigation, footer, and landing scripts.
|
||||
|
||||
## Legal Pages
|
||||
|
||||
Routes:
|
||||
|
||||
- `landing/privacy.php`
|
||||
- `landing/terms.php`
|
||||
|
||||
Both pages load `app/config.php`, landing head/nav/footer includes, and render static legal copy.
|
||||
|
||||
Privacy page sections:
|
||||
|
||||
- Information collected.
|
||||
- How information is used.
|
||||
- Session/authentication data.
|
||||
- Company data isolation.
|
||||
- Data retention.
|
||||
- Security.
|
||||
- Cookies.
|
||||
- User rights.
|
||||
- Policy changes.
|
||||
- Contact email.
|
||||
|
||||
Terms page sections:
|
||||
|
||||
- Use of service.
|
||||
- Account security.
|
||||
- Data ownership.
|
||||
- Acceptable use.
|
||||
- Service availability.
|
||||
- Limitation of liability.
|
||||
- Term changes.
|
||||
- Contact email.
|
||||
|
||||
## Setup Script
|
||||
|
||||
Route/file: `setup.php`
|
||||
|
||||
`setup.php` is not a web page. It is intentionally CLI-only:
|
||||
|
||||
```bash
|
||||
php setup.php
|
||||
```
|
||||
|
||||
If accessed over HTTP, it returns `403` with the message `Run via CLI only: php setup.php`.
|
||||
|
||||
The setup script creates the configured databases and core tables if they do not already exist. It is safe to rerun for table creation because it uses `CREATE TABLE IF NOT EXISTS`, but it is not a migration runner for arbitrary schema changes.
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
- Authenticated app routing is covered by `runtime-architecture.md` and the feature specs for each module.
|
||||
- Business setup after registration/onboarding is covered by `system-settings.md` and the master-data/accounting specs.
|
||||
@@ -1,224 +0,0 @@
|
||||
# System And Settings Features
|
||||
|
||||
## Authentication
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/login/index.php`
|
||||
- `app/login/register.php`
|
||||
- `app/login/verify.php`
|
||||
- `app/login/forgot_password.php`
|
||||
- `app/login/onboarding.php`
|
||||
- `app/login/api/engine/*`
|
||||
|
||||
Authentication supports registration, login, OTP confirmation, password reset, onboarding, and app branch selection.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `PasswordManager`
|
||||
- `PasswordResetManager`
|
||||
- Login APIs under `app/login/api/engine`
|
||||
|
||||
## Session And Security
|
||||
|
||||
Core files:
|
||||
|
||||
- `app/session.php`
|
||||
- `app/assets/utils/db_auth.php`
|
||||
- `app/assets/utils/db_helpers.php`
|
||||
|
||||
Security behavior:
|
||||
|
||||
- Authenticated session required for normal API routes.
|
||||
- POST routes validate CSRF token.
|
||||
- OTP freshness is checked against the user's current password.
|
||||
- Company access is validated through `company_map_user`.
|
||||
- API payloads are accepted as JSON-wrapped POST or FormData.
|
||||
- Role checks use `require_role()`.
|
||||
|
||||
## User Management
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/setting/users.php`
|
||||
- `app/setting/api/engine/manage_users.php`
|
||||
- `app/setting/api/engine/search_users.php`
|
||||
- `app/setting/api/engine/retrieve_users.php`
|
||||
|
||||
User management supports company users, invitation, role updates, and user removal.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `UserManager::getCompanyUsers()`
|
||||
- `UserManager::searchUsers()`
|
||||
- `UserManager::inviteUser()`
|
||||
- `UserManager::updateRole()`
|
||||
- `UserManager::removeUser()`
|
||||
|
||||
## Profile And Password
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/setting/profile.php`
|
||||
- `app/setting/api/engine/manage_profile.php`
|
||||
- `app/setting/api/engine/change_password.php`
|
||||
- `app/setting/api/engine/request_reset_otp.php`
|
||||
- `app/setting/api/engine/reset_password_otp.php`
|
||||
|
||||
Users can maintain profile details and change/reset passwords.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `UserManager::getProfile()`
|
||||
- Password reset managers and setting APIs
|
||||
|
||||
## Company Settings
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/setting/company.php`
|
||||
- `app/setting/system_config.php`
|
||||
- `app/setting/api/engine/company_setting.php`
|
||||
- `app/setting/api/engine/manage_company.php`
|
||||
- `app/setting/api/engine/retrieve_company.php`
|
||||
|
||||
Company settings store transaction configuration, posting windows, system behavior toggles, branch/company details, and setup values.
|
||||
|
||||
System configuration includes:
|
||||
|
||||
- Advanced Location: simple location mode or three-level zone/aisle/rack mode.
|
||||
- Location Labels: custom labels for warehouse location levels.
|
||||
- Default Stock Status: draft or auto-approved stock entries.
|
||||
- Auto-Complete on Shipped: whether shipped orders become completed automatically.
|
||||
- Invoice and Credit Note Generation: automatic invoice on order confirmation and credit note on return confirmation.
|
||||
- Setting Locks: selected settings become locked after dependent transactions exist.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `CompanySettingManager`
|
||||
|
||||
## SMTP Settings
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/setting/smtp.php`
|
||||
- `app/setting/api/engine/manage_smtp.php`
|
||||
- `app/setting/api/engine/retrieve_smtp.php`
|
||||
- `app/setting/api/engine/test_smtp.php`
|
||||
- `app/assets/utils/module/mailer.php`
|
||||
|
||||
SMTP settings configure outbound email and include a test route.
|
||||
|
||||
## Branch Switching
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/setting/api/engine/switch_branch.php`
|
||||
- `app/include_topbar.php`
|
||||
|
||||
Branch switching changes the active company/context for a logged-in user when they have access to more than one company.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `UserManager::getCompanyList()`
|
||||
|
||||
## Shared JavaScript Utilities
|
||||
|
||||
File:
|
||||
|
||||
- `app/assets/js/custom.js`
|
||||
|
||||
Shared frontend utilities include:
|
||||
|
||||
- `ajax_request()` with CSRF headers, loading overlay, queue lock, JSON payload preparation, and FormData handling.
|
||||
- `generate_pagination()` for table paging.
|
||||
- `batch_process()` for retryable sequential batch jobs.
|
||||
- Operation lock helpers (`acquire_op_lock` / `release_op_lock`) for long-running cross-tab operations — wraps `OperationLockManager` (see below).
|
||||
- Account autocomplete for posting-account selection.
|
||||
- Number formatting that avoids scientific notation and suppresses tiny floating point noise.
|
||||
|
||||
## Operation Locks
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/accounting/api/engine/acquire_op_lock.php`
|
||||
- `app/accounting/api/engine/release_op_lock.php`
|
||||
|
||||
Operation locks prevent two users (or two browser tabs) from running the same long-running operation simultaneously. A lock is a DB row in `operation_locks` with a `company_id`, `operation_type`, `user_id`, and expiry timestamp.
|
||||
|
||||
Behavior:
|
||||
|
||||
- `acquire()` inserts or refreshes a lock row. Returns `{ acquired: true }` if successful or `{ acquired: false, held_by: '...' }` if another user holds it.
|
||||
- `release()` deletes the lock row for the calling user.
|
||||
- Locks have a configurable TTL (default 120 minutes). Expired locks are treated as unowned — any user can acquire them even without an explicit release.
|
||||
|
||||
Currently used for:
|
||||
|
||||
- `gl_post` — Batch GL posting in `accounting/gl_entries.php`. Prevents two users from running overlapping batch jobs against the same company's GL.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `OperationLockManager::acquire(string $operation_type, int $ttl_minutes = 120)`
|
||||
- `OperationLockManager::release(string $operation_type)`
|
||||
|
||||
## ETL Summary Maintenance
|
||||
|
||||
Routes:
|
||||
|
||||
- `app/setting/gl_maintenance.php`
|
||||
- `app/setting/stock_maintenance.php`
|
||||
- `app/setting/api/engine/gl_maintenance.php`
|
||||
- `app/setting/api/engine/stock_maintenance.php`
|
||||
- `app/cron/etl_gl_maintenance.php`
|
||||
- `app/cron/etl_stock_maintenance.php`
|
||||
|
||||
Two pre-aggregated summary tables accelerate dashboard and report queries:
|
||||
|
||||
- `etl_gl_summary` — monthly GL totals per account, fed by every GL post/delete. Used by the accounting dashboard.
|
||||
- `etl_stock_summary` — monthly stock in/out totals per warehouse/product, fed by `WarehouseManager::adjustBalance()`. Used by the WMS dashboard and stock overview.
|
||||
|
||||
Each summary table is maintained write-through on every transaction. The maintenance system provides a safety net for periods that drift out of sync due to crashes, rollbacks, or manual DB edits.
|
||||
|
||||
**Gap detection** compares `MAX(source_updated_at)` in the raw source table against the summary table's `source_updated_at` per period. A mismatch means the summary is stale and needs repair.
|
||||
|
||||
**Automatic repair** runs via the Node.js cron scheduler once per hour. Companies are spread across 24 hourly slots using `company_id % 24` to distribute load. GL runs at `:00` and stock runs at `:30` past each hour.
|
||||
|
||||
**Manual rebuild** is available in the maintenance UI. The page shows the cron schedule slot, last run result, and a live count of current gaps. A full rebuild drops and regenerates all periods for the company.
|
||||
|
||||
Backend support:
|
||||
|
||||
- `EtlManager` — GL ETL (gap detection, period repair, full rebuild, status)
|
||||
- `EtlStockManager` — Stock ETL (same interface, works across dynamic `td_stock_<warehouse_id>` tables)
|
||||
|
||||
## Node.js Cron Scheduler
|
||||
|
||||
File:
|
||||
|
||||
- `nodejs/scheduler.js`
|
||||
|
||||
The scheduler runs inside the Node.js process alongside the Socket.IO server. It uses `node-cron` to call PHP cron endpoints over HTTP on a fixed schedule. The shared `x-cron-secret` header authenticates each call.
|
||||
|
||||
Current schedules:
|
||||
|
||||
| Schedule | Endpoint | Purpose |
|
||||
|---|---|---|
|
||||
| `0 * * * *` (every hour, `:00`) | `/cron/etl_gl_maintenance.php` | GL summary gap repair for slot = current hour |
|
||||
| `30 * * * *` (every hour, `:30`) | `/cron/etl_stock_maintenance.php` | Stock summary gap repair for slot = current hour |
|
||||
|
||||
Each cron run processes only the companies whose `company_id % 24` equals the current hour slot — no company is processed more than once per hour.
|
||||
|
||||
## Database Schema Setup
|
||||
|
||||
Files:
|
||||
|
||||
- `setup.php`
|
||||
|
||||
The auth database includes a `schema_migrations` table, but this repository snapshot does not contain a `migrations/` directory or `migrate.php`. Fresh installation schema creation is currently handled by the CLI-only `setup.php` script, with some runtime-created structures such as dynamic `td_stock_<warehouse_id>` tables.
|
||||
|
||||
## File Uploads
|
||||
|
||||
File:
|
||||
|
||||
- `app/assets/utils/classes/FileUploader.php`
|
||||
|
||||
File upload support is used by product/contact/company/profile flows. It handles directory creation, uploaded file lists, cleanup of replaced files, and rollback on failure.
|
||||
@@ -1,143 +0,0 @@
|
||||
# Transaction Limits
|
||||
|
||||
## Overview
|
||||
|
||||
Each company is assigned a package tier that defines how many documents they can create per day and per week. When a quota is hit, report endpoints return HTTP 402 — operations (create, edit, approve) are never blocked.
|
||||
|
||||
---
|
||||
|
||||
## Package Tiers
|
||||
|
||||
Defined in `app/config.php` under `$packages`. Edit the array directly to adjust quotas or add tiers — no migration required.
|
||||
|
||||
| Package | Daily limit | Weekly limit | Locked on limit |
|
||||
|----------|-------------|--------------|---------------------|
|
||||
| `free` | 10 | 30 | dashboard, reports |
|
||||
| `starter`| 30 | 100 | dashboard, reports |
|
||||
| `growth` | 150 | 500 | dashboard, reports |
|
||||
| `pro` | unlimited | unlimited | nothing |
|
||||
|
||||
`lock_on_limit` is an array of feature group keys. When either limit is hit, any endpoint that calls `assertFeatureAccessible('<key>')` returns 402 for that company until the quota resets.
|
||||
|
||||
A company's package is stored in `wms.company_list.package` (default `'starter'`). Change it with:
|
||||
|
||||
```sql
|
||||
UPDATE company_list SET package = 'growth' WHERE company_id = 1;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What Counts as a Transaction
|
||||
|
||||
One document created = one count. Updates to existing documents do not count.
|
||||
|
||||
| Endpoint | Document type |
|
||||
|---|---|
|
||||
| `ics/api/engine/manage_stock_in.php` | Stock In |
|
||||
| `ics/api/engine/manage_stock_out.php` | Stock Out |
|
||||
| `ics/api/engine/manage_stock_transfer.php` | Stock Transfer |
|
||||
| `order/api/engine/manage_order.php` | WMS Sales Order |
|
||||
| `order/api/engine/confirm_order.php` | Auto-created WMS Invoice on confirm |
|
||||
| `order/api/engine/manage_invoice.php` | WMS Invoice / Credit Note |
|
||||
| `order/api/engine/proceed_to_invoice.php` | WMS Invoice from Sales Order |
|
||||
| `order/api/engine/manage_return.php` | Customer Return |
|
||||
| `order/api/engine/confirm_return.php` | Auto-created Credit Note on return confirm |
|
||||
| `order/api/engine/proceed_to_credit_note.php` | Credit Note from Customer Return |
|
||||
| `po/api/engine/manage_po.php` | Purchase Order |
|
||||
| `po/api/engine/manage_supplier_return.php` | Supplier Return |
|
||||
| `po/api/engine/confirm_supplier_return.php` | Auto-created Supplier Credit Note on supplier return confirm |
|
||||
| `po/api/engine/proceed_to_purchase_invoice.php` | Purchase Invoice from Purchase Order |
|
||||
| `po/api/engine/proceed_to_supplier_credit_note.php` | Supplier Credit Note from Supplier Return |
|
||||
| `revenue/api/engine/manage_order.php` | Revenue Sales Order |
|
||||
| `revenue/api/engine/proceed_to_invoice.php` | Revenue Invoice from Sales Order |
|
||||
| `revenue/api/engine/manage_credit_note.php` | Customer Credit Note |
|
||||
| `revenue/api/engine/manage_quotation.php` | Quotation |
|
||||
| `finance/api/engine/manage_receipt_billing.php` | Receipt Billing |
|
||||
| `finance/api/engine/manage_receipt.php` | Receipt |
|
||||
| `finance/api/engine/manage_payment_billing.php` | Payment Billing |
|
||||
| `finance/api/engine/manage_payment.php` | Payment |
|
||||
| `expense/api/engine/manage_purchase_request.php` | Purchase Request |
|
||||
| `expense/api/engine/manage_supplier_credit_note.php` | Supplier Credit Note |
|
||||
|
||||
---
|
||||
|
||||
## What Gets Locked
|
||||
|
||||
Two feature group keys are defined:
|
||||
|
||||
**`dashboard`** — gated on dashboard/report summary endpoints. The low-stock detail endpoint and accounting posting-window endpoint are not gated:
|
||||
- `dashboard/api/engine_report/reports_stats.php` (WMS dashboard)
|
||||
- `ac_dashboard/api/engine/by_source.php`
|
||||
- `ac_dashboard/api/engine/journals.php`
|
||||
- `ac_dashboard/api/engine/pl.php`
|
||||
- `ac_dashboard/api/engine/recent.php`
|
||||
- `ac_dashboard/api/engine/trend.php`
|
||||
|
||||
**`reports`** — gated on all report endpoints:
|
||||
- `accounting/api/engine/get_trial_balance.php`
|
||||
- `accounting/api/engine/get_pl_statement.php`
|
||||
- `accounting/api/engine/get_balance_sheet.php`
|
||||
- `accounting/api/engine/get_gl_movement.php`
|
||||
- `accounting/api/engine/get_vat_report.php`
|
||||
- `reports/api/engine_report/stock_movement.php`
|
||||
- `reports/api/engine_report/stock_movement_sku.php`
|
||||
- `reports/api/engine_report/expired_stock.php`
|
||||
- `reports/api/engine_report/lot_stock_log.php`
|
||||
- `reports/api/engine_report/product_lot.php`
|
||||
- `reports/api/engine_report/rack_log.php`
|
||||
- `reports/api/engine_report/rack_occupancy.php`
|
||||
|
||||
---
|
||||
|
||||
## How Quotas Reset
|
||||
|
||||
- **Daily** — resets at midnight each day. `company_usage` stores one row per company per `day_date`; a new day is a new row with count 0.
|
||||
- **Weekly** — resets at 00:00 Monday. The weekly count is the SUM of `daily_count` for all rows from Monday to today.
|
||||
|
||||
---
|
||||
|
||||
## Database
|
||||
|
||||
`wms.company_usage` — one row per company per day:
|
||||
|
||||
```sql
|
||||
CREATE TABLE company_usage (
|
||||
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
company_id INT(11) NOT NULL,
|
||||
day_date DATE NOT NULL,
|
||||
daily_count INT(11) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uq_company_day (company_id, day_date),
|
||||
KEY idx_company_id (company_id)
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `app/config.php` | `$packages` array — all tier definitions |
|
||||
| `app/assets/utils/classes/UsageGuard.php` | `increment()`, `assertFeatureAccessible()`, `getStatus()` |
|
||||
| `app/include_topbar.php` | Reads `getStatus()` on every page load; renders warning badge |
|
||||
| `app/assets/js/custom.js` | Global 402 handler in `ajax_request` — shows usage detail alert |
|
||||
|
||||
---
|
||||
|
||||
## Topbar Warning Badge
|
||||
|
||||
`include_topbar.php` calls `UsageGuard::getStatus()` on every page load and renders a badge in the nav bar:
|
||||
|
||||
- **≥ 80% of either limit** — amber badge showing percentage
|
||||
- **≥ 100%** — red badge "Limit reached — reports locked"
|
||||
|
||||
Hovering the badge shows raw counts: `Daily: 28/30 | Weekly: 87/100`.
|
||||
|
||||
---
|
||||
|
||||
## Adding a New Gated Feature
|
||||
|
||||
1. Pick a key name (e.g. `'export'`).
|
||||
2. Add it to `lock_on_limit` arrays in the relevant package tiers in `config.php`.
|
||||
3. Add `(new UsageGuard($pdo1, $company_id, $packages))->assertFeatureAccessible('export');` at the top of the target endpoint(s).
|
||||
Reference in New Issue
Block a user