Seal transaction limit coverage gaps
This commit is contained in:
@@ -0,0 +1,308 @@
|
||||
# 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`
|
||||
@@ -0,0 +1,97 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,108 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,75 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,224 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,143 @@
|
||||
# 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