# Knowledge Base — INDEX

> The map of every topic. Read [`README.md`](README.md) first (how the KB works). كل موضوع وسطر وصف ولينك.
> ✅ = ملف مكتوب · 🟡 = موجود جزئيًا/يحتاج تكميل · ⬜ = مخطّط (نكتبه أول ما نشتغل عليه)

## 🏭 Production & Manufacturing
- ✅ [Production module — **FULL REFERENCE**](topics/production.md) — **the canonical, code-verified map of the whole module (read this, don't re-scan).** 47 BE models + 49 FE screens; cost-accounting megaproject **phases 0–7 + A + C shipped, B reverted (~88%)**; 3-leg standard cost + 7 variances + GL; 6 Cost-AI features ("AI computes nothing"); state machines; **unwired Core ApprovalWorkflow**; MTO net-new (intake `mfg_order_cases` + trial-as-PO Development BOM + 3 approval gates) + toll gaps. 🟢 Visual full map: [`mfg-module-overview.html`](plans/mfg-module-overview.html).
- 🆕📊 [Manufacturing — competitive gap analysis vs leading MFG software (BOARD-FACING)](plans/mfg-competitive-gap-analysis.html) — **(2026-07-07) first EXTERNAL benchmark** of the Production module vs 6 systems (SAP S/4HANA PP-PI · D365 SCM · Sage X3 · BatchMaster · Odoo · MRPeasy), built for a board go/no-go on continuing with Claude. Produced by an **18-agent Opus workflow** (code-verified baseline → 13 functional-area analysts → 3 adversarial auditors → synthesis), **directed by Fable 5**, **independently reviewed by Codex (non-Claude)** whose fixes were applied (change-log in §10). **Verdict: overall maturity 2.7/5** (weighted pharma-toll-CMO; equal-weight 2.6) — "upper-SMB with lower-mid-market spikes." Strengths (hardest-to-buy): A8 costing 7-variance+GL (3.4), A10 line-level toll ownership (3.0), A7 batch traceability (2.7), A12 deterministic-first AI (3.4), A13 Arabic-RTL+MoonStack (3.1). Troughs: **A3 finite scheduling 1.1**, A11 maintenance 1.5, A6 QMS 2.3. **5 P0 gaps:** e-signature+audit-trail layer, ApprovalWorkflow→ECO wiring, FEFO hard-block+GS1 labels, incoming-QC→GRN quarantine wiring, mandatory cleanout/cross-contamination op. **Recommendation: continue with Claude under conditions** (ship P0, keep independent-review gate, fund GxP CSV, second design partner). 🔑 **Audit correction that ran AGAINST Moon** (QMS is a real closed-loop NCR→CAPA engine in fat controllers, not an empty shell → 1.9→2.3) = the cleanest answer to "Claude assessing Claude." ⚠️ Codex flagged the **competitor benchmark column needs an edition-qualification re-audit** before any external/marketing use. Prior KB reports (490KB audit etc.) are internal build-plans — this is the only competitor benchmark. Baseline + per-area scorecards + audits at `scratchpad/mfg-gap/` (journal: `wf_5e4da26a-e73`).
- 🆕🔀 [Manufacturing — step-by-step operational flow (do-what-then-what)](plans/mfg-process-flow.html) — **(2026-07-07)** a visual "how the flow moves" guide of the Production module for operators/onboarding: the 5 phases (Setup → Plan → Execute → Quality → Close) as a numbered timeline, each step showing the **screen**, the **state transition** (order: planned→released→in_process→completed→closed with guards; operations: waiting→ready→in_progress→completed), the **GL posting** (issue Dr WIP/Cr Inventory · confirm Dr WIP/Cr labor+OH applied · receipt Dr FG/Cr WIP std · close Dr COGS/Cr WIP + 7 variances · scrap · utility clearing), and the **gate** (BOM active, CoA+BMR release, canRelease/canIssue/canClose). Includes the **toll variant** (customer-owned material = no GL on issue, settled via Toll Clearing at receipt) + a GL-summary table + screen map. Built directly from `production.md` (code-verified state machines + GL events) — the operational companion to the competitive gap analysis.
- 📦🆕 [Per-customer consignment stock ("أمانة بضاعة") in the warehouse — analysis + plan](plans/inventory-consignment-stock-analysis.html) — **(2026-07-08, code-verified + Fable 5).** Toll customer's ask: each of HIS customers has a per-customer consignment balance of the same product, segregated in the warehouse, issuable to production, with borrow → buy-from-customer OR return; must be an on/off **option** with zero disruption when off. **Finding: ~80% already ships as "Phase C" in the Production module (live, always-on):** `ConsignmentMaterialLedger` (per customer+product+warehouse balance, off-GL, declared cost) · `CreateConsignmentReceipt` (ledger + physical qty into StockBalance at declared cost, no GL) · **receipt/borrow REQUIRE an `is_consignment` warehouse** (segregation already enforced) · `RecordBorrow` (DR WIP / CR Materials-Due-to-Customer) · `SettleBorrow` buy (PurchaseBill, customer-as-supplier → DR Inventory/CR AP + clear liability) or return/replenish · FE `consignment` screen · perms `production.consignment.*`. 🔴 **LIVE BUG found:** `ConsignmentService::recordIssue` has ZERO callers → toll issues consume consigned stock but never decrement the customer ledger → **every toll issue since Phase C has overstated customer consignment balances**; also no balance guard (negative consignment possible). **The ask completes via 5 targeted moves, NO StockBalance schema surgery:** (1) wire recordIssue + balance guard [Phase 0 bug-fix], (2) exclude `is_consignment` warehouses from valuation/costing reports (sub-ledger>GL leak) + guard the perimeter (block GRN/transfer/adjustment on consignment warehouses), (3) `production.enable_consignment` toggle (default OFF, **backfill ON for tenants with existing consignment data**), (4) Inventory-side UX (receive/per-customer جرد/issue), (5) `borrow_target: stock` for warehouse-level borrow. **Segregation decision: warehouse-per-customer (incumbent, recommended, zero code, FEFO can't cross-pick) — REJECT `owner_partner_id` on StockBalance for v1** (changes the unique key + every lookup across 15 modules; NULL-in-unique trap). Accounting = SAP-style customer special stock (physical visible, value off-company-books). Fast-follows: VAT on buy-settle (currently forced tax=0, non-compliant), FIFO replenish GL/stock drift → settlement gain/loss account. Phases 0(fixes)→1(toggle+UX)→2(borrow completeness)→3(Arabic customer statement + reconciliation).

## 🔬 LIS (Laboratory)
- 🔐 [**LIS Permissions Overhaul — MASTER**](topics/lis-permissions-overhaul.md) — **اقرأه أول حاجة لأي شغل صلاحيات معمل.** يربط 3 مسارات: (1) **Authority Limits** (حدّ خصم رقمي per-role/user — SHIPPED main، topic [[authority-limits]]) · (2) **محرّر «حسب الشاشة» + `LisScreenCatalog`** (٤٦ شاشة/٨ مجموعات، اعتماد خفي مُشتق من الكود ومُتحقَّق بـ**Pest gate** — الطبقة الحتمية «المنتج»؛ hazemdev+/app) · (3) **مساعد AI** (دراسة، اختياري فوق الحتمي). المشكلة الجذرية (٩٨٧ صلاحية بلا وصف + أعطال حيّة: board→sections.view, invoices→invoices.post, critical-alerts guard-mismatch) + حكم Fable «AI الواجهة، الكتالوج المُتحقَّق المنتج» + الروادماب. دراسات: `authority-limits-analysis` · `menu-driven-permissions-study` · `ai-permission-builder-study` · `lab-roles-editor-prototype`.
- ✅ [Analyzer middleware](topics/middleware.md) — on-prem Python middleware (Maglumi/VITROS/Dymind/Udichem), source-of-truth/deploy loop, drivers, ASTM/HL7, SSH access, VITROS onboarding.
- ✅ [Client PC access — reverse SSH tunnel](topics/client-pc-tunnel.md) — **read when the tunnel drops.** How we reach the analyzer's Windows box (`ssh -4 -p 2222 hp@127.0.0.1`, reverse tunnel the client dials out), why it drops, and the fix (free port 2222 on our server: `kill -9` the zombie sshd → user re-runs the tunnel). scp/PowerShell/python toolkit + paths.
- ✅ [Sample generation & the barcode gate](topics/lis-sample-generation.md) — **✅ BARCODE UNIFIED (2026-06-27, elmadina LR-2026-00006):** all barcode printing now flows through ONE source (`LisBarcodeBuilderService`) — panel prints as a single label (members hidden), specimen names English-only (no Arabic mojibake), no extra/unrequested tubes; + BE data-heal migrations (align panel-member specimen/section to its panel · merge duplicate `SPT-*` shadow specimen types, both history-guarded/transactional) + aliquot safeguard + `store()` panelMembers load. Shipped hazemdev+main+`/app`; ⏳ elmadina via MoonStack update (migrations A+C together). Owner report `lab-barcode-bug-analysis.html`. — **🔴 sample tubes are NOT created by the BE at request creation**; the FE generates them in a separate fire-and-forget step (`onOrderSuccess` → `POST /lis/samples auto_split` → `aliquot`) after save → if it doesn't complete the request stays `pending` with 0 samples and barcode print warns "No collected samples". [Investigation report](plans/lab-barcode-no-samples-investigation.html). Fix = move generation to the BE (transactional). **✅ FIXED (2026-06-25):** calculated (`formula`) tests (e.g. eAG in panel HBA1C602) used to spawn a **spurious extra tube/barcode** (and silently broke the formula's own value) — now a formula rides its inputs' section/tube (validation + aliquot safeguard + catalog align); full writeup at top of the topic.
- ✅ [Reference ranges — model, data-loss fix, override feature](topics/lis-reference-ranges.md) — ranges show from a **per-result SNAPSHOT** (taken at first entry; stale if added later) not live; **🔴→✅ DATA-LOSS FIXED (2026-06-25):** saving a test/panel with an empty array used to **wipe all ranges/panel-members** (no soft-delete) — now empty=no-change, wipe needs an explicit clear flag; + **new feature:** edit a result's range inline in Worklist/Validation with who/when audit (`POST /lis/results/{id}/range`). Study: [`lis-reference-range-study.html`](https://moonui.elbaset.com/lis-reference-range-study.html).
- ✅ [8-point batch — portals, report comments, culture catalog](topics/lis-portal-and-culture-fixes.md) — **2026-06-26** (BE `1b85a9278`/FE `0258c1a`): P1 password eye · P2 B2B prints request invoice (buyer=client lab, IDOR-scoped) · P3 internal/process comments (retest/Auto-calc/Auto-verify) stop printing — writer-level + lossless cleanup migration · P4 portals honor report settings via shared `BuildsPortalReportConfig` trait · P5 sample-reasons seeder (reject+result) · P6 portal A4 invoice Arabic via `printHtmlA4` (jsPDF can't shape Arabic) · P7 barcode panel = already fixed · **P8 culture: 32 C&S tests retyped→`culture` in catalog + idempotent corrective migration (history-guarded) + `LabAntibioticSeeder`**. [Plan](https://moonui.elbaset.com/lab-8-fixes-plan.html). 🔴 2 data migrations run on the client via update, not moonui_dev_be.
- ✅ [On-screen report preview (Validation)](topics/lis-report-preview.md) — `/lab/validation` got a **Preview** button rendering the EXACT report (PDF/HTML) in an in-app iframe dialog, reusing the canonical print engine so **rich types (culture/histopath/narrative/file) render right** — fixing the orphaned, divergent old `LisResultPreviewComponent`. Pure FE (reaches all labs via update). New `LisPrintReportService.previewRequest()` + `LisHtmlReportService.buildReportHtml()`; `includeDraft=true` (DRAFT watermark) so you preview before release. FE `1339b3b`.
- ✅ [Patient-portal report-access tracking](topics/lis-patient-portal-tracking.md) — Validation/result-entry worklist cards show whether the patient **viewed**/**downloaded** their report on the public portal (badges + date tooltip), so staff know before editing a released result. New `lab_patient_portal_access_logs` table + `POST /lis/portal/requests/{id}/track` (ownership-checked, token hashed once, company-scoped, best-effort). Shipped hazemdev+main (BE `f8061545b`+`3fa81e9fa`/FE `ddb24c5`); **migration RUN on moonui** (caught + fixed a MySQL 64-char index-name overflow — name composite indexes explicitly). Caveat: raw browser Ctrl+P can't be tracked.
- ✅ [Formula-test dependencies (portability) + result decimals](topics/lis-formula-dependencies.md) — **2026-07-01:** onboarded installs carried each formula test's input links as SOURCE-install IDs (not portable) → editing ANY formula test (MCH/MCV/LDL) 422'd («مش بعرف أعدّله»). Fix = **derive deps from the formula's codes** (matched-only, in `prepareForValidation`) + repair migration + import 3rd pass + catalog LDL `TC`→`CHOL`. Codex(read-only, WORKS now)+native reviewed. **⚠️ elmadina LDL alias needs its formula edited `TC`→`CHOL`.** + per-test **decimal_places** now consistent (dp>0=exact, dp=0=as-entered, cap 4→6, display-only) across worklist/validation/report.
- ✅ [LIS first-run Setup wizard](topics/lis-setup-screen.md) — the **Lab Setup** screen (`/lab-setup`, 7 steps: identity · locale & main branch · reference data · **investigations import** [current curated catalog / NAFIS 1,666 / CSV] · pricing · accounting + lab accounts), shipped on hazemdev. Locale writes the company source (single truth, no `lis.currency` dup); installer seeder-gap fix; **full architecture map + how-to-extend + bugs/fixes** ([plan](plans/lis-setup-screen-plan.html)).
- ⬜ LIS flow & rebuild — worklist-centric flow, kanban/validation, external labs, the rebuild plan. (content currently in MEMORY.md `lis-*` notes → consolidate here)
- ✅ [LIS discount system](topics/lis-discount.md) — price-list / insurance / package / **manual whole-request discount**. **✅ FIXED 2026-06-27:** wizard discount was silently dropped; now whole request+invoice with %/fixed toggle, VAT after, blocked w/ insurance. **+ 2026-06-28:** B2B claim **split by price list** (per-lab `claim_split_mode`, shipped 4.0.26) · discount now shows in the **B2B client portal** (request detail + statement; +100%-discount no-invoice fallback — 4.0.28+[Unreleased]). Reports `lab-discount-report.html` · `lab-invoice-discount-display-study.html` · `b2b-claim-split-plan.html`.
- 📋 [LIS permission granularity (cost/pricing + external-lab features)](topics/lis-permissions-granularity.md) — **PLAN (2026-06-28, analysis only):** split Investigations cost/pricing into their own perms (now merged in view/update → margin leak) + give each External-Lab feature its own perm (🔴 found `price-lists sync` ungated WRITE). [HTML](https://moonui.elbaset.com/lis-permissions-granularity-plan.html).
- 📋 [Smart Report (التقرير الذكي)](topics/lis-smart-report.md) — **PLANNED + owner-approved direction (2026-06-30):** rich visual lab report (Dr-Sulaiman-Al-Habib style: cards + severity gradient bar + trend charts) + **AI severity assessment via DeepSeek** (reusing the existing AI infra). 🔑 visual layer = NO AI; AI only **words** a rule-computed severity. 5 caveats (rules-decide / anonymize PHI / best-effort no-hang / disclaimer / cache). **Owner chose 🆎 PHASED → start Phase 1 = the AI assessment box.** Analysis: `smart-report-analysis.html`. Sample ref: `1777288186381.pdf`.
- ⬜ Lab accounting & invoicing — LIS GL settings, VAT, customer invoice printing, payments/cashier.
- ✅ [Reference-range display (worklist/validation/print)](topics/lis-reference-range-display.md) — the range render path across the 3 surfaces; worklist + print ignored **text ranges** (`text_normal`) and print read the **wrong field names** → blank ranges (e.g. Vitamin B12). Fixed: BE emits one canonical `reference_range_text`, all surfaces consume it (validation = the gold-standard pattern). Data model: `lab_investigation_normal_ranges`.

## 🏥 HIS (Hospital Information System)
- 🔴🔒 [**خطة تنفيذ الكلينك — المراحل الملزمة + سجل التقدم الحي**](topics/clinic-execution-phases.md) — **THE EXECUTION PLAN (2026-07-02، في انتظار اعتماد المالك):** 12 مرحلة م0-م11 بتتابع صارم (لا انتقال قبل إتمام + اعتماد المالك بتسته)، كل مرحلة: الهدف + «اللي هيطلع منها» + شيك ليست تست المالك (م1 مثال المالك: الخدمة CRUD كاملة). ~28-36 يوم. **سجل تقدم حي أول الملف يتحدث مع كل خطوة** (session-proof). Opus ينفذ/Fable أدفايزور. [HTML](https://moonui.elbaset.com/his-analysis/clinic-execution-phases.html).
- ✅ [خطة تظبيط الكلينك — رحلة المريض (v2)](topics/clinic-tightening-plan.md) — **NEWEST (2026-07-02), الأولوية الحاكمة، ⛔ تخطيط فقط (لا تنفيذ قبل اعتماد المالك).** 11 محطة على فلو المريض (أرضية→تسجيل→طابور→كشف→تنفيذ→كاشير→معمل/أشعة→تأمين→خروج→نهاية اليوم)، كل محطة: فلو مستهدف + «من الجينا» + الفورمات بالاسم + «اللي هتشوفه». مبنية على 3 تدقيقات opus — **4 CRITICAL فلوس مثبتة على dev data** + 10 routes بلا guard + history per-encounter. ملاحق: تدقيق الفلوس الكامل + جرد الشاشات. [HTML](https://moonui.elbaset.com/his-analysis/clinic-tightening-plan.html). عند التنفيذ: Opus ينفذ، Fable الأدفايزور.
- ✅ [سيناريوهات تست المالك + الاعتمادات وموجات التسليم](topics/clinic-test-scenarios.md) — 10 سيناريوهات (م0-م9) / 81 خطوة عملية يمشيها المالك بنفسه، كل واحد بعلامات نجاح + «جرّب تكسرها» (متتبعة لمعرفات التدقيق C/H/M) + جدول اعتمادات + قائمة التوازي + **8 موجات تسليم** + DoD. ⚠️ قرار مفتوح: مصدر مطابقة م9 («الدفاتر» = GL/Z-report/تصدير؟).
- ✅ [مبادئ المالك الحاكمة للكلينك](topics/clinic-principles.md) — **ملزمة لكل تصميم:** نماذج البيع (±LIS standalone) · الأدوية (±مشتريات، الصرف منفصل دائمًا) · تصنيف الخدمات 3 أنواع (كشف بـ3 احتمالات إسناد تترتب عليها الحسابات / تحاليل / أشعة) · سرعة الروشتة «كله اختيارات» · عقيدة «الدكتور يختار مش يكتب» (تحاليل/أشعة/شكوى/تشخيص) · الكور واحد (عيادة→مركز→مستشفى) · حقول إكلينيكية ديناميكية · أطباء بأقسام (فلترة 1000 طبيب) · الخدمة=العمود الفقري + الإعدادات بترتّب الدنيا.
- ✅ [الخدمة والإعدادات — ربط الدنيا ببعض](topics/clinic-service-settings.md) — تشريح `clinic_services` من الكود (service_type=4 قيم؛ **جسر LIS/الأشعة غير مستهلك**؛ تصنيف المالك: احتمال (أ) دكتور-بعينه-بساعاته **ناقص**=F9، (ب)+(ج) موجودان) + شبكة الترابط (تسعير→عقود→حجز→بند→تغطية→مطالبة→تقسيم→تقارير) + «اللوجيك المستهدف» لإضافة خدمة تشغّل كل حاجة تلقائيًا + **مخطط الإعدادات الشامل** (4 معرفة + 2 بلا تعريف مقابل ~15 مخططة في 6 مجموعات؛ الشاشة definition-driven) + بنية خدمات الجينا (الإعادة=صف أرخص → `parent_service_id`).
- 📐 [ملف المريض وملف الدكتور — حقول الجينا + إظهار/إخفاء لكل عميل](topics/clinic-profiles-fields.md) — **DESIGN (م1+م9):** جرد الجينا (~41 حقل مريض بحزمة الزوج/الزوجة الـ10 + NID→ميلاد + مسودة + فحص تكرار 5 محاور) مقابل Moon (~16 على الفورم) + جدول فجوات + التوصية الهجينة: سجل حقول core بإعداد ظهور/إلزام per-company (`clinic.patient/doctor_form_fields` — hidden≠required بالـ validation) + الحقول الإضافية على محرك الحقول (scope=patient/category=profile) + presets تخصصية. ≈4-6 أيام؛ إتمام شاشة الأطباء دين مسبق.
- 📐 [محرك الحقول الإكلينيكية الديناميكية — تصميم](topics/clinic-dynamic-fields.md) — **DESIGN (م3):** التوصية = **تمديد محرك history_questions الموجود** (stack كامل: CRUD+validation+HistoryInputType+renderer+seeder-presets) بأعمدة unit/scope/category/danger-ranges/is_computed + توحيد التخزين (تعميم patient_history_answers) + fold الـ vitals كتعريفات (النطاقات من VITAL_FIELDS الثابتة حاليًا FE) + جدول القيم الموحد + compat shim مؤقت. BMI يظل server-computed. ≈5-7 أيام، مخاطرة منخفضة.
- ✅ [مرجع الجينا القديم (OBGY)](topics/gyna-reference.md) — «أساسي في التقييم»: مقارنة 8 أبعاد + حكم الـ history (شكوى المالك مثبتة 100%) + **ميكانيكا الروشتة السريعة** (~نقرتين/دواء، عقد 8 بنود) + **ميكانيكا «يختار مش يكتب»** (شبكة 5 أعمدة ~270 تحليل مرئي + حدد-الفئة + favorites + inline-add يترسّخ للأبد؛ الأشعة فئة بنفس الشبكة؛ التشخيص select2 بخيار Add السحري — **عقد 10 بنود: يُبنى مكوّن مشترك واحد**) + top-10 اقتباسات بمحطاتها. [HTML](https://moonui.elbaset.com/his-analysis/gyna-reference.html).
- ✅ [HIS / Clinic module — analysis · approved design · implementation plan](topics/his.md) — **ACTIVE (2026-06-22)** building a Clinic/HIS module on Moon ERP. **Analysis v2** (29-section report + adversarial-review corrections) ✅ · **UI design APPROVED by owner** (`clinic-his-form-design.html` — clinical workstation, IBM Plex+teal, 5 screens, dense single-page) ✅ · **full build-ready implementation plan** in `his-analysis/clinic-impl/` (`00-master-plan.md` + 8 phase files P0–P7, ~217KB, WPs with file:line+migrations+API+tests) ✅. Binding decisions: Clinic owns money / `source=clinic` / canonical enums / reuse=refactors-with-migrations / patient=shim. **▶️ NEXT: build P0 on `hazemdev` with the per-part review gate (Codex + Claude reviewer + KB log).** Read the topic's 🟢 RESUME block. (LIS = the proven clinical template; Phase-0 HIS-readiness already in LIS.)

## 🚀 Distribution & Updates (MoonStack)
- ✅ [Staging / Mirror environment](topics/staging-mirror.md) — **NEW (2026-06-28):** test a MoonStack update on a data-identical mirror BEFORE live. **elmadina staging LIVE** = `s-elmadina.elbaset.com` (cPanel `selmadina`, DB `selmadina_stg`) + a token'd **Refresh-from-live** PHP link (DATA only, no shell) + red STAGING banner. ⚠️ refresh ≠ code update. Generic-feature plan inside.
- ✅ [Dev & release branch workflow](topics/dev-workflow.md) — **read before cutting a release.** Branches (`hazemdev` work branch → `main` release source), the release page + CLI, **prerequisites + ALL env vars + gotchas so ANY dev env/user can release correctly.**
- ✅ [Parallel development (2nd Claude / fatamadev)](topics/parallel-dev.md) — running a second Claude instance in parallel on its own branch (`fatamadev`), each on a different module, merging to `main`. **Must be separate per instance: DB + URL/deploy + working dir** (🔴 never let the 2nd touch `moonui_dev_be` — not binlogged). Partition by module. [Visual guide](https://moonui.elbaset.com/parallel-claude-fatamadev.html).

## 🛠️ Tooling & Workflow Automation (custom skills/commands — project-local)
- ✅ [Workflow automation — /fullpush + implement-research + implement-plan (+ problem-investigation)](topics/workflow-automation.md) — **الأدوات المخصّصة للـinstall ده (نطوّر عليها):** 🔬 **implement-research** (Fable) = Phase-1 «ابحث أولًا» (KB-first → فحص Opus → HTML gap-analysis 8 أقسام + **معاينة UI متوقّعة** → قف للاعتماد) · 🕵️ **problem-investigation** (Fable) = تحقيق RCA لمشكلة (أسباب جذرية + ليه حصلت + حلول بكل أبعادها → HTML يتغذّى منه implement-plan) · 🏗️ **implement-plan** (Fable) = Phase-2 تنفيذ الخطة المعتمدة WP-by-WP بـledger على القرص (pause/resume) + مراجعة/تست/CHANGELOG لكل إمكانية · 🚀 **/fullpush** = توحيد الفرع↔main (BE+FE) + migrate/seed dev + بناء/نشر `/app`. **ولا واحدة تنشر لوحدها — المالك يشغّل /fullpush.** المصدر canonical = ملفات `.claude/`.
- ✅ [MoonStack update & changelog](topics/moonstack-update.md) — fast/crash-safe self-host updates, the what's-new/changelog process, the **seeder gap** (updates don't seed new definitions), release flow, per-client gotchas, **🔴 docroot-ready packaging fix** ([plan](plans/moonstack-docroot-ready-plan.html)) — the zip extracts as raw Laravel (public/ subfolder → fresh-install 404 + insecure); fix = inject a root `.htaccess` rewrite→`public/`.
- ⬜ Self-hosted distribution plan — the separate WordPress-style installer direction (do NOT touch Ahmed's Moon Central). (in MEMORY.md `self-hosted-distribution-plan`)

## 🎤 Sales & Presentations
- ✅ [Sales presentation & deck visuals](topics/presentation.md) — the EN/AR lab decks (web root, 19 slides, design system), real-screenshot capture recipe, **AI image-gen playbook** (Gemini "Nano Banana" / OpenAI / WaveSpeed) + **🔐 secure key handling (keys never in repo/chat)**.

## 💼 Sales / Accounting
- ✅ [Partial goods-issue → GL vs stock divergence](plans/sales-partial-issue-accounting.html) — **ANALYSIS (2026-06-22, code-verified) → FIX IMPLEMENTED.** Sales invoice posts COGS `Dr COGS / Cr Inventory` immediately for the **FULL** invoice qty (100), but the auto-created GDN can be approved for only **part** (50) — and the GDN posts **no GL entry**. → GL Inventory ≠ physical stock (off by the unissued qty), COGS overstated, profit inflated, no invoice↔issue qty link / no `delivered_qty` / no back-order. Root causes: COGS tied to invoice not delivery; `ApproveIssue` never calls `CreateJournalEntry`; single `quantity` column; no GL-vs-stock tie-out check. Recommended fix: move COGS posting into `ApproveIssue` at the **actual issued qty** (skip invoice-time COGS when GDN active) + add tie-out integrity check. Refs in report. Related config: `sales.stock_deduction_point`, `sales.auto_create_stock_issue_on_invoice`, `auto_approve_stock_issue_on_invoice`. → **Permanent-fix implementation plan (chosen, long-term — rev2):** [`sales-partial-issue-fix-plan.html`](plans/sales-partial-issue-fix-plan.html) — invariant "COGS posts with the stock movement, for the issued qty". rev2 adds: explicit **partial-issue model** (`issued_quantity` vs `quantity` on issue line + new `PartiallyIssued` status), **role visibility** (accountant: invoice delivered/remaining cols + "invoiced-but-undelivered" report; warehouse: requested/issued/remaining on the issue + deliveries worklist), **over-delivery guard** + **auto back-order** so the remaining never gets lost/double-issued, and **new settings in Sales + Inventory** to pick the flow (`cogs_recognition_point`, `allow_partial_delivery`, `auto_create_backorder`, `block_over_delivery`, `inventory.allow_partial_issue`, …) with 3 ready presets (immediate / delivery-driven / auto-deliver). 10 TDD phases via `InventoryIssueApproved` event + Sales listener (revenue stays at invoice — policy A); cols `cogs_journal_entry_id`/`source_item_id`/`delivered_quantity`/`fulfillment_status`; cancel-reversal; **short-close credit note** (financial-only, no inventory leg, reuses SalesReturn with `affects_inventory=false`) to bill the customer only for what shipped when the rest will never be delivered → AR/revenue/VAT reversed for the undelivered qty, invoice `short_closed`; GL⇄stock tie-out check. **No backfill** (all data is test). → ✅ **IMPLEMENTED on `hazemdev2`** (11 phases, subagent-driven, per-phase code review + opus review of the core; gated on `cogs_recognition_point=delivery` so default/legacy behaviour is byte-for-byte unchanged): BE `b3287b5b3`→`2eb880ef6`, FE `310390383`+`27a2449d5` (built + deployed to `/app`); MoonStack changelog bullet added. ✅ **MERGED TO `main`** (2026-06-22): opus final whole-branch review = READY TO MERGE (its 2 Important edge cases — closed-period back-order JE date → now(), and overlapping draft back-orders — fixed in `f01830a31` before merge); integrated the parallel instance's Clinic-module work (BE main `ee8ecb038`, FE main `fb157b4b8` — only CHANGELOG conflicted, both bullets kept); 206-test feature suite green on the merged tree. Default (invoice) mode is byte-for-byte unchanged — feature is opt-in via `cogs_recognition_point=delivery`.
- 🛒🆕 [Purchases + Inventory cycle — review & settings-based fix (the PURCHASES mirror of the sales COGS bug)](plans/purchases-inventory-cycle-analysis.html) — **(2026-07-08) code-verified, cross-confirmed by Codex (deep audit) + Fable 5.** Owner sensed the purchasing→inventory→accounting cycle is "not right." Intended flow: PR → PO → preliminary GRN on the PO → partial/full receiving → stock-add note (إذن إضافة) → invoice on actual received (3-way match + GR/IR). **Finding: the documents/states of a correct cycle already exist; the ACCOUNTING is that of a simple one.** `purchases.grn_mode = direct|grn|grn_quality` (default **direct**): in `direct` the **bill drives inventory** (auto-creates+approves an InventoryReceipt from bill lines — `PostPurchaseBill.php:185-190,233-261`); in `grn`/`grn_quality` the GRN approve adds stock (creates+approves InventoryReceipt = إذن إضافة, quality gate, accepted qty) but posts **NO GL at receipt**, and the bill **still** debits Inventory/CR AP directly (mode-agnostic JE `PostPurchaseBill.php:63-166`). 🔴 **No GR/IR (goods-received-not-invoiced) account exists anywhere** (exact search = 0) → received-not-invoiced liability invisible, GL≠stock timing — **exact mirror of the fixed sales `cogs_recognition_point` bug**. 🔴 **No 3-way match**: can over-bill, over-receive, bill unreceived goods, standalone bill (`PurchaseBillController.php:427-453,509-528`; `PurchaseGrnController.php:508-525`); bill-from-PO uses ordered-minus-billed, not received. 🐛 Two latent bugs: fallback mismatch (`getGrnMode()`→'grn' at :494 vs `PostPurchaseBill`→'direct' at :187 → **double stock** if no setting row) + loose `!=='direct'` enum check. **Fix = settings + targeted code, non-disruptive, `direct` stays default:** two orthogonal axes — `grn_mode` (physical) + **new `purchases.inventory_recognition_point` = bill|receipt** (mirror `sales.cogs_recognition_point`) + new `grni_account_id`/`price_variance_account_id` + PO-line 3-way guard + `billing_tolerance_percent`/`require_po_for_bill`. **Steps 1-5 reachable by CONFIG today** (`grn_mode=grn_quality` + enable_purchase_requests + approval + auto_approve=false); only the GR/IR accounting + guards need code. Migration is clean (direct mode ⇒ GR/IR opens at zero, no opening JE). Phases: 0 config (steps 1-5 live + monthly manual accrual) → 1 guards → 2 GR/IR accounting core → 3 GRNI-aging report. Recommended config table in §7. Codex report + Fable advisory at `scratchpad/purchasing/`. **✅ IMPLEMENTED — all phases 0-3 on `hazemdev2` (2026-07-09), Fable-designed + Codex-tested, ~69 new tests green, full Purchases suite 292 pass / 9 pre-existing fails (0 introduced), all opt-in (default byte-for-byte unchanged), migrated+seeded on moonui2, NOT pushed/merged.** P0 latent bugs: `GrnMode` enum resolves grn_mode consistently (closes double-stock/lost-stock). P1 guards: 5 settings (`enforce_three_way_match`+`billing_tolerance_percent`, `require_po_for_bill`, `enforce_receiving_limit`+`receiving_tolerance_percent`) + 3-way/2-way bill guard + GRN over-receive + quality accepted+rejected cap + cross-company fix. P2 GR/IR: `inventory_recognition_point`=bill|receipt + `grni_account_id`/`price_variance_account_id`; GRN approve DR Inventory/CR GR-IR, bill clears GR-IR + PPV (pro-rata + sweep, nets to zero), value accumulators on PO items + bill-line stamp, cancel reverses, config-state guards + flip-back guard, `GrnReceipt` JournalEntryType. P3: `GET /purchases/reports/grni-aging` (outstanding per PO line, supplier-grouped, aged). Progress/resume at `scratchpad/purchasing/PROGRESS.md`.
- 📘🆕 [Purchases + Inventory — USER MANUAL (with direct screen links)](plans/purchases-inventory-user-manual.html) — **(2026-07-09)** end-user guide to the purchasing + inventory cycle as it works in the app after the phase 0-3 changes: login + navigation (Purchases module · Inventory under `/core/`), the full PR→PO→GRN→stock-receipt→bill cycle step-by-step with states + the new controls at each step, the warehouse standalone operations (receipts/issues/transfers/counts/adjustments/balances/reorder), ALL settings + how to configure them (grn_mode, the new 3-way-match toggles, GR/IR recognition + accounts) with a recommended config, the 4 new features + how to enable each, a statuses reference, and a quick-links grid. **43 direct `moonui2.elbaset.com/app/...` links** (path-based routing: inventory at `/app/core/*`, purchases at `/app/purchases/*`, settings at `/app/core/settings`). Companion to the technical analysis; audience = users/operators.
- 👥🆕 [Purchases + Inventory — ROLES & USERS (who receives / buys / inspects / accounts)](plans/purchases-inventory-roles-and-users.html) — **(2026-07-09)** answers the owner's org question: "the one who receives + adds to stock is the WAREHOUSE MANAGER, not purchasing — how, when receiving is done against a PO?" **Viewpoint: the GRN screen (`/purchases/grns`) IS the warehouse-receiving screen** (select PO → quality gate → quantity-capped → approve adds stock); it's only labeled under Purchases, but *who* uses it is decided by the permission `purchases.grns.*`. So the fix is **organizational (permissions), not code**: give the warehouse-manager role `purchases.grns.*` + `inventory.*`. The owner's imagined flow (warehouse makes إذن إضافة → picks PO → forced quality + fixed qty) = exactly the GRN + `grn_mode=grn_quality` + `enforce_receiving_limit`. The generic Inventory إذن إضافة (`/core/stock-receipts`) is NOT PO-aware (don't duplicate). Contains: a swimlane (Purchasing → Warehouse → Quality → Accounting), the 4 role permission checklists (exact `purchases.*`/`inventory.*` grants + what each is denied), a who-can-do-what matrix, and **step-by-step user creation** at [`/core/roles`](https://moonui2.elbaset.com/app/core/roles) + [`/core/users`](https://moonui2.elbaset.com/app/core/users). ⚠️ Documents that **GRN quality-check + approve share one permission** (`purchases.grns.approve`) — a strict separate-QC role needs a small code split (`purchases.grns.quality`), offered as option (d).
- 🔄🆕 [Purchases — CONTROLLED FLOW redesign (desired-vs-current + one-button preset)](plans/purchases-controlled-flow-redesign.html) — **(2026-07-09) code-read (direct + 2 independent agents, file/line-cited) + Fable 5 design. (Codex verify pass could not run — host bwrap sandbox nesting limit; verification done by direct code reads instead.)** Owner described their REAL desired procurement flow (PR→PO→approve→preliminary GRN→**one active GRN per PO**→quality sets accepted-qty + **expiry + batch**→approve **locks qty**→auto-drafts إذن إضافة needing **warehouse-keeper approval**→bill by **actually-received** qty with variance settlement→**one bill per cycle, fully traceable**) and demanded ONE switch (not 20 settings). Maps desired-vs-current and pins **7 problems by root cause**: **(A)** empty إذن إضافة — `PurchaseGrnStatus::canApprove()` allows `Draft` in quality mode → `accepted_quantity` NULL → `if(stockQty<=0)continue` skips every line → empty receipt, no stock; **(B)** `qualityCheck()` doesn't capture batch/expiry (only qty); **(C)** bill prefills `ordered−billed` not received (`remainingBillQuantity`), no `createFromGrn`; **(D)** batch/expiry are a DEAD END — `StockService::increaseStock` drops them, no lot/batch stock model (only serial via ProductSerial); **(E)** bill links only to PO, no `grn_id`/status-history; **(F)** NO duplicate-doc guard (2nd GRN/2nd bill unblocked; caps default OFF) + standalone InventoryReceipt bypasses governance; **(G)** `approve()` auto-approves the receipt inline — no separate أمين-مخازن gate. **Solution (Fable):** one virtual setting `purchases.procurement_mode = simple|controlled` (default simple = byte-for-byte today) resolved by a new **`ProcurementPolicy`** service (option a, NOT a macro — drift-proof + MoonStack fleet auto-updates); `controlled` forces grn_quality + receipt-recognition + the 3 guards + one-active-cycle/one-bill-per-GRN/standalone-restriction as BEHAVIOR (no new setting rows); block the switch if GR/IR+PPV accounts missing; grandfather in-flight docs. **BUG A fix = both layers** (`canApprove(bool $qualityRequired)` → quality?QualityApproved only; + empty-receipt circuit-breaker abort). **Guards = one OPEN receiving cycle per PO** (not one-GRN-ever — partial deliveries are sequential cycles) enforced 3 rings (FormRequest / `lockForUpdate(PO)` in-tx = authoritative / generated-column unique backstop); `purchase_bills.purchase_grn_id` UNIQUE. **Perms = 2 new only**: `purchases.grns.quality_check` (QC, split from approve) + `inventory.receipts.create_manual` (inventory mgr); receipt-approve→أمين المخازن via existing `inventory.receipts.approve`. **Accounting:** qty variance needs no entry (GR/IR at accepted + capped bill + PO closes short), price variance→existing PPV (IAS 2), over-accept→3-way blocks→debit-note; + pre-post settlement panel. **Batch/expiry line drawn:** capture+trace now (P0+P3 movement-level), true FEFO/expiry-block = separate P4 initiative. **Phases P0** (BUG A + perm split + quality batch/expiry — quick wins) **→ P1** the switch **→ P2** guards+billing+traceability **→ P3** expiry-on-movements **→ P4** lot/FEFO (separate KB topic). 8-section HTML with 12-step flow, 7 root-cause cards, one-button visual, phase roadmap, permission matrix, accounting table, expiry-reality matrix. Design notes at `scratchpad/purchasing/FLOW-REDESIGN.md`. **Status: DESIGN — not yet implemented.** → Implementation plan written (below).
- 🧩🆕 [Purchases Controlled Flow — IMPLEMENTATION PLAN (TDD, task-by-task)](plans/purchases-controlled-flow-implementation-plan.md) — **(2026-07-09)** the executable plan for the controlled-flow redesign above (use `superpowers:subagent-driven-development` to run it). Markdown, writing-plans format. 5 phases / 18 tasks. **Phase 0 is code-complete + executable now** (4 tasks, 21 bite-sized TDD steps): 0.1 BUG A fix (`canApprove(bool $qualityRequired)` mode-aware + empty-receipt circuit-breaker `throw ValidationException` inside the approve tx — exact code given, call sites `PurchaseGrnStatus:29`/`PurchaseGrn:92`/`PurchaseGrnController:363,447-476`); 0.2 quality captures batch/expiry (validation + coalesced update); 0.3 split `purchases.grns.quality_check` permission (controller middleware `:48` + RolePermissionSeeder, non-breaking grant); 0.4 cleanup command for legacy empty receipts. **Phases 1-3 are task-level** (files/interfaces/test-cases/sequencing, code written at each phase start — honest fidelity boundary, not speculative code): P1 `procurement_mode` setting + `ProcurementPolicy` sole-reader service + read-site migration + drift arch-test + switch validation/UI-lock; P2 one-active-cycle guard (3 rings) + `purchase_bills.purchase_grn_id` unique + `createFromGrn`/bill-from-received + settlement panel + trail + separate إذن-إضافة keeper approval + `inventory.receipts.create_manual`; P3 batch/expiry on stock movements + expiry report. P4 (lot/FEFO) explicitly out-of-plan. Global constraints: default `simple` byte-identical, hazemdev2 only, tests are the gate, bilingual, MoonStack changelog per phase. **✅ FULLY IMPLEMENTED + PUSHED + DEPLOYED (2026-07-09).** All BE phases on `moon-erp-be` hazemdev2 (P0 ..2c3b94f89 · P1 ..5175396d7 · P2 ..3d453860f · P3 ..3b2aee23d) + FE on `moon-erp-angular` hazemdev2 (..78c960229) + deployed to `/app` on moonui2 (LIVE). Executed autonomously with Fable design consults + code-reviewer/Codex reviews per phase (2 CRITICALs caught+fixed pre-push: P1 controlled↔simple GL-corruption transition guards, P2 cancel-vs-keeper-approval race). Every phase: simple mode byte-identical, 0 new test failures (baselines: Purchases 9 pre-existing double-seed/return, Inventory 1 pre-existing OpeningBalance). Migrations applied + settings/permissions seeded on moonui2. Deferred (not done): 2.4 settlement-summary panel (BE+FE), P4 lot/FEFO stock (separate initiative), pre-existing-test fast-follows. Progress ledger: `scratchpad/purchasing/EXECUTION.md` + design `P2-DESIGN.md`/`FLOW-REDESIGN.md`.
- 🧪🆕 [Purchases Controlled Flow — E2E TEST SCENARIO](plans/purchases-controlled-flow-e2e-test.html) — **(2026-07-09)** step-by-step QA script to verify the live controlled flow on moonui2/app. Setup (GR/IR accounts → enable controlled → optional role users), the full happy path (PR→PO→GRN→quality[accepted qty+batch+expiry]→GRN approve=**pending_receipt, no stock yet**→warehouse-keeper approves إذن إضافة=**stock+GR/IR post now**→bill-by-received→trail), 5 guard/negative tests (2nd GRN blocked · 2nd bill blocked · createFromOrder blocked · manual receipt 403 · can't leave controlled while pending_receipt), + a pass/fail checklist incl. the reverse "back to simple = old behaviour" check. 12 steps, 10 direct app links.
- 🔀🆕 [Purchases + Inventory — FLEXIBLE FLOW (existing vs wanted + batches/serials/expiry)](plans/purchases-inventory-flexible-flow.html) — **(2026-07-09) code-read (2 investigation agents, file/line-cited) + Fable 5 design. FOR APPROVAL before implementing.** Owner, while testing the controlled flow, wants it relaxed from **hard-blocks → "flexible + accounting decides"**: bill from PO (cumulative across GRNs) **OR** from GRN (per-batch) both available; **standalone purchase bill (no PO)** → auto draft إذن إضافة → keeper approves → stock (invoice-first GR/IR); multiple GRNs per PO; 3-way match caps over-billing (verified: `alreadyBilled+billQty ≤ received×tol`). Maps the 9 controlled guards, the desired flow, and the gaps. **Batch/serial findings:** stock balance is **purely aggregate** (no per-lot, no FEFO — both flagged unbuilt in-code); `product_serials` = per-unit serial+batch+**expiry-END only (no production/start date anywhere)**; movements carry batch/expiry for **genealogy only**; **2 real blockers** — (a) serial-tracked product bought via PO→GRN is **impossible to receive** (no GRN/quality serial capture → `serial_count_mismatch` on keeper approve + GRN receipts edit-locked `receipt_locked_by_grn`), (b) per-serial expiry collected in the stock-receipts serial dialog is **dropped** on submit (one line-level expiry stamped on all). **Capture-point decision (Fable): unify at the keeper's receipt approval (C)** — partial-unlock GRN receipts (qty/items stay locked, batch/serial section opens) + **batches-first dialog** (batch rows: lot·prod·expiry·qty, sum=line qty; serials nest under each batch, inherit dates → makes bug (b) impossible) + quality pre-fills. Add `production_date` (informational). **Expiry visibility on stock-balances = derived "nearest expiry" column + badges + drill-down to the existing expiry report** (visibility NOT FEFO). **Phases: 1** flexible billing + direct bill + fix bug (b) · **2** unify capture (fixes blocker (a)) + production_date · **3** expiry visibility · **4 deferred** per-lot balances/FEFO. **§3b — NEW smart-sourcing module (RFQ), owner-requested:** greenfield (none exists; borrow SalesQuotation lifecycle + SupplierPriceList model + the **Patient-Portal** `portal_link_token` no-login pattern) — buyer sends a Purchase Request to N suppliers by email/WhatsApp via a unique per-supplier link → sealed-bid supplier pricing portal (no login) → **product×supplier comparison matrix** (green=lowest/red=highest per row, only over quoted cells) → select best-per-product → **one draft PO per winning supplier**. 4 tables (purchase_rfq + items-snapshot + suppliers/invitation-quote + quote_items), award tracked by columns (rfq_id on PO). Requires **request→multiple POs** first (today a hard 1→1 lock: `canConvert()`=Approved→Converted + single `converted_to_order_id` → switch to hasMany via `purchase_orders.purchase_request_id` + item-level partial conversion). Buyer-entered (phone) quotes mandatory or the matrix stays empty. Messaging: WhatsApp via client-side `wa.me`+template (ready); email needs a new Mailable + real SMTP (default mailer=log, zero Mailables today). RFQ phases: A request→multiple POs · B RFQ+portal+matrix+draft-POs (MLP) · C reminders/award emails/export/WA-API. §8 has 11 approval decisions. **Status: ✅ Phases 1–3 IMPLEMENTED + SHIPPED (2026-07-10) to `hazemdev2` + live on `/app`; Phase 4 (per-lot balances/FEFO) + the RFQ module (§3b) deferred.** See the **[controlled-flow topic](topics/purchases-controlled-flow.md)** (canonical state + backlog) + the per-phase trackers below. Also documents the 2 already-shipped test-phase fixes (PO bill button `fully_received` status, quick-receipt double-approve false error).
- 🧭🆕 [Purchases controlled→flexible flow — STATE + BACKLOG (topic)](topics/purchases-controlled-flow.md) — **(2026-07-10) canonical current-state record.** The `procurement_mode = simple|controlled` flow relaxed into "flexible + accounting decides". **Shipped Phases 1–3:** flexible billing + direct purchase bill (BE `f8d77d181`/FE `17371cb4d`); lot/batch/serial capture at receipt approval fixing the 2 blockers (BE `91cf7c634`/FE `7005989e9`); expiry visibility on stock-balances (BE `20e06ee5b`/FE `b8625169e`). **Deferred backlog:** (1) Phase 4 per-lot on-hand balances + FEFO; (2) RFQ/supplier-sourcing module + request→multiple-POs; (3) small review fast-follows (pendingReceipt N+1, dup-serial 500→422, ReceiptLotService tracking_type defense, expiry drill-down empty-list); (4) settlement/variance panel; (5) pre-existing baseline test failures (OpeningBalance + PurchasesSettingApiTest double-seed). Trackers: `plans/phase{1,2,3}-*.md`.
- 🧩🆕 Phase trackers (executable, task-by-task, with commit hashes): [Phase 1 — flexible billing + direct bill](plans/phase1-flexible-billing-plan.md) · [Phase 2 — lot capture at approval](plans/phase2-lot-capture-plan.md) · [Phase 3 — expiry visibility](plans/phase3-expiry-visibility-plan.md). All ☑ DONE + shipped 2026-07-10.
- ✅ [Sales bug/feature batch](topics/sales-bugfix-batch.md) — RESOLVED (2026-06-22). Fixed: commission-rules 404, auto stock-issue items, order→invoice warehouse, "draft only" error. Shipped BE 3afcbc03c / FE 57e2370a6. #1 (product price tiers) + #2 (default warehouse) already built — config-only. #7 commission = tracking-only (no GL) by choice.
- 🟡 [Sales cash quick-sale + line-entry UX](topics/sales-cash-quicksale-and-line-entry.md) — **built + deployed to `/app`, awaiting user test, NOT yet on `main` (2026-06-23).** Three FE features: **(A)** product-search shows **on-hand stock per warehouse** in the dropdown (opt-in `showStock`/`warehouseId`, cached per-warehouse via `WarehouseStockCacheService` + `shareReplay`; chosen over per-keystroke `search` to avoid false-0 + unbounded fetch); **(B)** **auto-append row + focus next product** on **last-row** pick across all 12 docs (`TxLineDescriptor.appendRow`; middle-row picks keep normal advance); **(C)** **cash quick-sale** — settings `sales.enable_cash_customer` + `sales.default_cash_customer_id` (BE `be629e391`: validation + `SalesSettingDefinitionSeeder`, partners=`business_partners`), new invoice pre-selects the cash customer + full **cash** payment to the user's **branch cashbox** (`PettyCash`, first or localStorage-preferred per branch; `receiving_account_id = cashbox.account_id`, GL-only — PettyCash balance not auto-synced), **Save&New** / **Print&Save&New** buttons. 🔴 review fix: cashbox destinations build even without GL defaults + `onSave` **aborts** (no silent payment skip) via `SALES.NO_CASHBOX`. Design note: cash mode posts immediately (bypasses approval) — cashier needs post/approve perm. FE `d79f0e3bf…154c19b4a`.
- ✅ [Inventory count: counted product "disappears" from Stock Balances](topics/inventory-count-zero-balance-fix.md) — **FIXED + MERGED to `main` (2026-06-27, BE `d247a63fa`).** Portal ticket **ISS-2026-0004** (مون). Counting a never-stocked product to **zero** → `difference=0` → `FinalizeCount` created **no adjustment and no `StockBalance` row** → product absent from "أرصدة المخزون" (nothing deletes/filters rows — it just never had one; client's clue: ADJ auto-made for the product that stayed, not the vanished one). Fix: `FinalizeCount::execute` now **`firstOrCreate`s** the balance row for every counted item (canonical `getOrCreateBalance` pattern) → counted product always shows (0-qty if never stocked); adjustment logic unchanged. +Pest test (18 green), changelog bullet. ⚠️ client `smart` needs the next MoonStack update to get it.
- 📋 [B2B partner packages at direct price — analysis + design (v2)](plans/lis-b2b-packages-analysis.html) — **ANALYSIS (2026-07-03, code-verified 3 investigations + Fable 5 advisory). Not built.** Feature: a **package (bundle of tests) with ONE direct flat price**, assignable to **multiple B2B partner labs** (inbound). 🔄 **v2 pivots from v1 after owner clarification: NO allocation/splitting** — the package bills as **ONE invoice line at the direct price** (member tests still execute in the lab at zero price; billing + revenue are package-level). Current state (unchanged facts): existing retail packages ALLOCATE `package_price` across members via `LabRequestService::recalculateTotals` (per-component invoice); B2B pricing/invoicing/JE is all **per-item**, no `package_id` anywhere, portal takes no packages. **v2 design:** add `billing_mode` (allocated=retail | **direct**=B2B) to `lab_packages` + new pivot **`lab_external_lab_packages`** (external_lab_id, package_id, optional per-partner price override, is_active) = "assign one package to many partners"; **explicit opt-in** (orderable only if assigned); new **single package-line** invoice path. **Accounting = ONE JE for the whole package at completion** (last member result released): Dr partner AR / Cr revenue (net) / Cr VAT — no allocation, **no money-invariant risk (that risk is gone with splitting)**. Net revenue (Fable 5/IFRS 15: direct price = transaction price; savings visible via report = Σ member list − direct, not GL). 🔴 biggest risk (changed) = **adapting the per-item invoice/JE machinery to a single package line + reliable idempotent completion trigger**. Owner decisions (§5): per-partner price override? revenue timing (completion vs invoice)? partial-result (cancelled member) policy? VAT-exclusive? Phases: BE core (billing_mode + pivot + package-line) → package JE at completion → FE assign tab → portal + claim/report.

## 🧩 Core / Platform
- ✅ [Authority Limits (الصلاحيات ذات القيمة)](topics/authority-limits.md) — **NEW (2026-07-04, SHIPPED hazemdev+/app):** a general layer for **numeric per-role/per-user ceilings** over boolean Spatie perms (no new table — reuses the settings store like nav_config). First case: **max manual discount % on a lab request** (enforced on the effective %, both modes, remove-test bypass closed, grandfathering, default=unlimited so upgrades are no-ops). Resolution = «user wins; else most-generous role; else default»; owner/admin unlimited. **Add a new limit = 1 registry entry + 1 `assertWithin` call.** Fable-ruled + native review (2 HIGH+3 MED fixed); 25 Pest green. Analysis `authority-limits-analysis.html`.
- ✅ [Audit / Activity Log (سجل التحركات)](topics/audit-activity-log.md) — **who changed what (before→after) when.** spatie/activitylog auto-logs every BaseModel create/update/delete; the endpoint excluded LIS (fixed → +16 LIS/+5 core subjects), machine-heartbeat noise muted (~57%), and a **new `/core/activity-log` screen** (3-agent design: filter table + git-style diff). BE `4f3e0c6e1` / FE `0f936f3`. Reports: `system-audit-log-report.html` · `activity-log-feature-plan.html`.
- 🔬 [Transaction-document-form + product-search unification](topics/document-form-unification.md) — **DEEP ANALYSIS (2026-06-20, manager-grade)** — one common product-search widget everywhere + a unified `<transaction-document-form>` (header+lines+totals, config-driven) across ALL 11+ line-item editors (sales/purchases/inventory) + **backend-driven** field-visibility (generalize `sales.invoice_visible_fields`) & branch-routed treasury. [HTML plan](https://moonui.elbaset.com/document-form-unification-plan.html), Codex-reviewed. 5-agent code sweep.
- 🔬 [Multi-Branch system (الفروع)](topics/branches.md) — **ANALYSIS PHASE (2026-06-20)** — generalize branches across Core/Accounting/Purchases/Inventory/Sales (+ fix HR/Manufacturing): each branch has warehouses + treasuries + employees (employee can be multi-branch), a main branch/warehouse/treasury, and **transactions default the branch's warehouse + treasury from the logged-in user's branch** instead of picking per-invoice. Big problem: **purchase bill has no warehouse at all.** Reuse the existing **LIS cash-routing-by-`primaryBranch()`** pattern. 3-agent code sweep → HTML study + plan; Codex verifies.
- ✅ [Default Accounts — master inventory & /setup plan](topics/default-accounts/INDEX.md) — **every GL-default-account setting across ALL modules** (de-duped ~52 keys), the 28 missing from the first-run `/setup` page (Lab 10 / Manufacturing 8 / HR 10), the **setup page writes 7 INERT legacy keys nothing reads**, the setting-definition schema, and the extension plan. Per-area topics in [`topics/default-accounts/`](topics/default-accounts/). ⚠️ AR-header trap.
- 🟡 [Setup wizard — module operational settings](topics/setup-wizard-settings.md) — **study (plan-first, 2026-06-19)** of Sales/Purchases/Inventory/Accounting operational settings for the `/setup` wizard: which are **LIVE vs SCAFFOLDED**, each one's effect (file:line) + recommended default, the **2 default-mismatches** (`purchases.grn_mode` seeded `grn` vs code-assumed `direct` → the moontest no-stock bug; `sales.auto_approve_stock_issue_on_invoice` `true`-but-inert), and **4 proposed wizard steps** (definition-driven, after Default-Accounts). [HTML study](https://moonui.elbaset.com/setup-wizard-settings-study.html) · [purchase-bill report](https://moonui.elbaset.com/purchase-bill-no-stock-report.html). ⏳ owner decision: grn_mode default + show/hide scaffolded.
- 🔄 [Owner / Super-Admin account](topics/super-admin-owner.md) — **IN PROGRESS (2026-06-20)** — a sealed "program owner" account baked into every install that does **precise** module on/off (BE 404 + strip perms + nav hide) and caps **users/companies/branches** via a tamper-resistant **signed `entitlement.json`** (reuses `PackageSigner::verify`). Owner ABOVE the tenant `owner` role; isolated from Ahmed's Moon Central. **7-phase roadmap + progress in the topic.** ✅ Phase 1 (EntitlementService 3-state fail-safe + `moonstack:entitlement`) + owner account (seeded every install, in-app password change sticks) DONE+tested+LIVE (login `owner@moonerp.app`). 🔄 Phase 2 FE owner-only gating. ⏳ Phase 3-7 (BE module/limit enforcement, owner screen, mgmt flow). [HTML plan](https://moonui.elbaset.com/super-admin-owner-plan.html).
- ⬜ API conventions — auth (`X-Authorization`), pagination (25 cap / `listAll`), dates/money/soft-deletes, envelope.
- ⬜ Deploy & environments — build/deploy to moonui `/app`, the dev-vs-prod BE targets, `.htaccess`, suexec/chown.

---

### How to extend this index
Add your topic under the right section as `- ✅ [Title](topics/<slug>.md) — hook`. Keep it to one line. See [`README.md`](README.md) §3–4.
- 🛡️ [Regression-safety / test strategy — "be sure the old stuff still works before I change anything"](plans/regression-safety-plan.html) — **STRATEGY (2026-07-04, code-measured + Fable 5 advisory). Not yet implemented.** Owner's pain: editing/adding a feature silently breaks another module (shared Core services: JE/GL engine, stock, VAT). **Measured reality:** BE already has **478 test files / 4,672 tests** (LIS 83, Accounting 65, Production 59…), phpunit CI-ready (SQLite `:memory:`, BCRYPT=4) — but **~50-60 min serial, no ParaTest, and NO CI anywhere** → nothing runs them, nothing gates merge/ship. FE = **0 unit tests** but 2 seeded patterns (`scripts/report-contract.mjs` esbuild contract+snapshot harness for LIS report templates; 2 orphaned Playwright specs, no config/runner). **Core insight: the gap is a GATE, not tests.** Solo dev pushes direct to `hazemdev2` → a PR-merge gate never fires; the load-bearing pair is **`on:push` CI** + **`moonstack:ship` refusing to package unless the release SHA has a green check-run**. **Fast gate:** 4-6 balanced matrix shards + ParaTest → <10 min (SQLite `:memory:` is per-connection = ideal for parallel; real flake risk = DomPDF/`storage/` writes, use `Storage::fake()`). **SQLite≠MySQL → nightly full run on real MySQL (ships migrations to clients — non-negotiable).** **Cross-module:** suite is small — always run all (selection degenerates to full anyway) + add 10-20 golden money-path characterization tests early (JE debit/credit rows, stock balances, VAT, invoice totals). **FE priority d→b→a→c:** prod build+typecheck gate → Playwright 5-8 smokes (nightly first, promote only when non-flaky, cap ~10) → extend report-contract → NO broad unit. **Release gate:** #1 = **upgrade rehearsal** (prev version's post-migrate DB → migrate→seeders→health-check→smoke; catches the seeder gap + migration-on-real-data bugs the suite can't see; keep fixture fresh via saved per-release dump artifact) + fresh-install rehearsal + `settings:verify` (code-referenced setting keys vs seeded, in CI AND client health-check) + ship verifies green SHA/signature/monotonic version. **Anti-list (explicitly overrides the global 80%/TDD rule for this brownfield):** no coverage %, no full-page snapshots, no broad E2E, no mutation/TIA, no self-hosted runners on cPanel, no permanent quarantine (dated skip-list that must shrink). **4-week plan:** wk1 baseline triage + BE/FE CI on-push + ship-SHA gate; wk2-3 golden tests + Playwright + nightly MySQL; wk4 upgrade/install rehearsal + settings:verify. First step = run the full suite once to get the green baseline + serial-vs-parallel timing.
- ✅ [Line-items grid: unify + drag-reorder/resize columns (company-wide persist)](plans/line-items-unify-column-control-plan.html) — **SHIPPED TO `main` (2026-06-23).** 12 document screens (all sales + all purchases + 4 stock screens) migrated to ONE shared `TransactionLineItemsComponent` (descriptor-driven; reorder/resize/show-hide work everywhere at once). User with `core.settings` permission can **drag column headers to reorder** + **drag a column edge to resize**, persisted **company-wide** (not per-user) via `DocConfigService` (`core.document_settings` → `lineOrder`/`lineWidths` per doc, merged non-destructively with the existing field-visibility). All product pickers are type-to-search (incl. server-side supplier search). Built subagent-driven, per-screen + shared-component reviews + final approval; backward-compatible (no saved config → identical render). FE shipped `0f0e36f9a`; deployed to `/app`; changelog bullet added. **Deferred follow-up:** stock-issues + stock-receipts (need `dual-qty` + serial/expiry picker cell types), the linked-return read-only modes, and transfers receive dialog stay hand-written for now. Analysis that led here: [`line-items-grid-column-control.html`](plans/line-items-grid-column-control.html). Every document's product/qty/price table → one shared `TransactionLineItemsComponent` (today only Sales Orders uses it; `app-product-search` IS already shared in 19 screens — that's the shared bit, NOT the grid). Then drag-reorder + show/hide columns, persisted **company-wide** (owner's choice, not per-user) via `DocConfigService` (`core.document_settings` JSON, new `lineColumns` key). Analysis: [`line-items-grid-column-control.html`](plans/line-items-grid-column-control.html). Order: prep shared component → unify screens (invoice first) → column control → persist + settings UI.
- ✅ [Global Command Bar & deep links](plans/global-command-bar-plan.html) — ⌘K command bar that finds any sales invoice/order/customer from anywhere and opens it via a deep-link URL. **Phase 1 SHIPPED to `main` (2026-06-22)** — BE `GET /api/core/search` (company-scoped + permission-filtered, 15 Pest tests) `47a948af3`; FE command bar + `?viewId=` deep-links for orders/bills + invoice copy-link `e5e918e74`. Built subagent-driven (8 tasks, per-task review + opus final whole-branch review; 2 Important FE findings caught+fixed: customer deep-link wiring, recents/flatIndex desync). Plan: [`global-command-bar-impl-plan.md`](plans/global-command-bar-impl-plan.md). Deployed to moonui2 `/app`; changelog bullet in `[Unreleased]`. **Phase 2 SHIPPED (2026-06-22):** search expanded to purchase bills + sales quotations + products; ⭐ Pinned + Recent sections in the bar; **fast standalone invoice view `/sales/invoices/:id`** (loads only the one invoice — ~3 calls vs the list screen's 14+ — fixes the slow deep-link open); shareable-link `/app` base-href bug fixed. BE `ab8bf8d19` / FE `dc6302792`. **Phase 2b SHIPPED (2026-06-22):** the fast standalone view is now generalized to **sales orders (`/sales/orders/:id`), purchase bills (`/purchases/bills/:id`), sales quotations (`/sales/quotations/:id`)** — same pattern as invoices (loads only the one record, lazy print-logo). All command-bar types now open fast (invoices/orders/bills/quotations = dedicated pages; products = own detail route; customers = partner dialog). BE `47906c2d5` / FE `b13c83b86`. **Phase 2c SHIPPED (2026-06-22) — feature complete:** filter-chip **scopes**, `@`/`#`/`>` **shorthand**, permission-filtered **quick-actions** in the bar (BE `7fbf6d7a2` / FE `c43f7a4bd`); BE search **hardened** (LIKE-wildcard escaping via `ESCAPE` + company-scoped relation sub-queries, 28 Pest). Built subagent-driven across Phases 1→2c (implementer+review per task, opus final review, all fixes verified). **Possible future:** fuzzy-filter actions by text; auto-open create dialog on quick-action (currently navigates to the screen).
