# 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('')` 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).