# W1-INVRPT — Reorder dashboard · demand forecast · cross-warehouse lookup · expiry report · perpetual-GL review (full-stack, Inventory)

Migration timestamp range: `2026_11_25_500001…500099`. Read `_COMMON.md` first. Another agent (W1-INVTR) owns everything transfer-related (transfer controller/actions/ship/receive/transfer FE) — don't touch those.

## 1. Reorder in the dashboard (client: «تقرير لحد إعادة الطلب يبقى ظاهر في الداشبورد (المخازن)»)
Today: thresholds per product `products.reorder_point/min_stock_level/max_stock_level`; routes `GET inventory/reorder-alerts`, `reorder-report` (by warehouse, `ReorderAlertController.php:454`), `stats` (`below_reorder_count`), `POST reorder-alerts/notify`; FE page `/core/reorder-alerts`; nothing in FE calls `inventory/stats`; `features/dashboard` has no inventory widget; notify schedule commented out (`InventoryServiceProvider.php:80-84`).
Build: dashboard widget «المخازن — حد إعادة الطلب» (visible with inventory view permission): count below reorder / below min / out of stock, top 10 items (product, warehouse, on hand, reorder point, suggested qty) with link to the full page; also a small «قرب انتهاء الصلاحية» tile (from item 4) and «بضاعة بالطريق» count if W1-INVTR's endpoint exists (skip otherwise). Respect warehouse scope. Re-enable the daily notify schedule behind setting `inventory.reorder.daily_notify` (default false). Per-warehouse thresholds remain out of scope (deliberate in ReorderAlertController:52-53) — leave a note.

## 2. Demand forecast (client: «تقرير بتوقع الطلبات من كل بند لمدة معينة بناءً على المعلومات القديمة عن المبيعات… تقرير تنبؤ»)
Today: only `InventoryCostService::averageDailyUsage` (:220, fixed `DEMAND_WINDOW_DAYS = 90`) feeding EOQ/ROP in `GET inventory/reports/costs` (FE only under production, perm `production.inventory_costs.view`).
Build `GET inventory/reports/demand-forecast?history_days=90&horizon_days=30&warehouse_id=&category_id=&search=` (paginated, perm inventory reports view): per product (and per warehouse when filtered) — sold/issued qty in history window (sales + POS + clinic consumption = Issue movements of sale/consumption types; exclude transfers/adjustments), avg daily demand, trend (least-squares slope over weekly buckets → trend-adjusted daily rate, floored at 0), simple weekday-agnostic forecast for horizon = rate × horizon_days, on hand, on order (open POs), suggested order qty = max(0, forecast + safety stock (use product min_stock_level or rate×lead-time if lead time exists) − on hand − on order), confidence hint (low when < 4 weeks of data). Pure service class with unit tests. FE: new tab «توقع الطلب» in `/core/inventory-reports` with the window/horizon pickers, sortable table, CSV/Excel export like neighbouring tabs, and a button «إنشاء طلب شراء» that pre-fills a purchase request with the suggested qtys if a PR create-from-lines path exists (else skip, note it).

## 3. Cross-warehouse lookup (client: «يمكن لمستخدم مخزن معين البحث في كميات المخازن الأخرى، زي الصيدليات كده»)
Today: `inventory.warehouse_data_scope` = all/assigned/own_records (`Core/app/Support/ResourceScope.php:67-73,83`); every stock read is scoped (`StockBalanceController.php:106,536,582`; `byWarehouse` 404 at :681); FE `WarehouseStockCacheService` uses the same scoped endpoint → under `assigned` other warehouses are invisible.
Build: permission `inventory.stock.lookup_all` («الاستعلام عن أرصدة كل المخازن» — read-only; grant to storekeeper/pharmacist/cashier-type roles + admin via bridge migration). Endpoint `GET inventory/stock-availability?product_ids[]=…|search=` → per product: list of all company warehouses (excluding transit/non-stock types) with available qty (on hand − reserved), branch name, optional nearest expiry; NO cost/value fields. FE: a reusable «الرصيد في المخازن الأخرى» lookup (dialog with product search) reachable from the stock-balances screen header, the product picker rows in issue/transfer/sales/POS line entry (shared line-items component if there is one — one hook in the shared layer), and a global quick-search shortcut if the app has one. Keep it in place: hovering/clicking a stock badge opens the per-warehouse breakdown.

## 4. Expiry report (client: «تقرير بالمنتجات المنتهية خلال فترة معينة (خليها بإعداد أحسن)»)
Today: `GET inventory/reports/expiring-batches` (`before`, `warehouse_id`, `owner`) + `reports/expiry` (serials, `days_ahead`), setting `inventory.near_expiry_days`, FE `/core/inventory-reports` tab 3. Company batch rows use RECEIVED qty from `inventory_receipt_item_batches`, not on-hand (docblock ~InventoryReportController.php:1210 "a fully-issued batch still shows"), though `inventory_lot_balances` exists (used for consignment rows). Only a `before` date.
Build: rebuild company rows from `inventory_lot_balances` on-hand > 0 (per warehouse, lot, expiry, qty, unit cost, value); filters: period from–to, quick chips (منتهي بالفعل / خلال 30 / 60 / 90 يوم / مخصص), warehouse, category, search; summary tiles (count, qty, value, already expired); group by warehouse or product; status colours (expired red, ≤ near_expiry amber); export + print (A4). Keep the consignment section working. Tests on the query (fully issued lot no longer shows; window filter).

## 5. Perpetual-inventory GL review (client: «موضوع القيود في الجرد المستمر… راجعها»)
Today (audit): posting is gated by `inventory.post_to_gl` (default false, `InventoryGlPoster.php:60-63`). Receipts/issues/adjustments/landed cost/returns post; transfers post at receive; stocktake `FinalizeCount` creates a DRAFT adjustment (no GL until a second approval); no automatic stock-vs-GL alert (only `GET inventory/gl/reconciliation`).
Build:
- Do a careful correctness review of every posting path in Modules/Inventory (+ Sales COGS / Purchases GRNI hand-offs) — wrong account, sign, missing reversal, double posting, cost basis, rounding. Write findings to `/home/moonui3/public_html/knowledge-base/plans/client-mtw39-requests/reports/inventory-gl-review.md` (table: path, what posts, verdict, fix). Fix real bugs (P0/P1) with tests; list P2s. Do not change transfer posting (W1-INVTR owns it) — report transfer findings in the file for them.
- Stocktake: setting `inventory.count.auto_approve_adjustment` (default false) — when on, finalizing a count approves its variance adjustment immediately (posting GL). Default off keeps current two-step.
- Dashboard alert: if `post_to_gl` is on and the reconciliation shows a stock-vs-GL difference above a small tolerance, show it on the inventory dashboard widget with a link to the reconciliation screen; if `post_to_gl` is off, show an info hint «القيود المخزنية غير مفعّلة» with a link to the setting (for users who can change settings).

Acceptance: widget shows on dashboard; forecast tab works on dev data; a user scoped to one warehouse with lookup_all sees other warehouses' qty read-only; expiry report reflects on-hand; GL review file written, P0/P1 fixed with tests.
