---
title: azab new-instance bug fixes (10 issues) — investigation + fixes
slug: azab-bugs-fixes
status: ALL 10 DONE + RELEASED. batch-1 in 3.0.18; batch-2 shipped as **3.0.19 stable** (merged hazemdev→main, pushed) and **azab updated 3.0.18→3.0.19** (delta) + verified (payment settings seeded, app HTTP 200).
owner: hazem
created: 2026-06-21
report: https://moonui.elbaset.com/azab-bugs-report.html
related:
  - production
  - document-form-unification
---

# azab bug fixes — 10 issues from fresh-instance testing

Owner tested on **azab.elbaset.com** (user `azab`) and reported 10 issues. Process: 4 read-only
investigation agents → Codex independent verification of the investigation → HTML report →
fix → native + Codex review of the fixes → deploy. **No data was lost** — all were code/config.
Full per-issue root-cause + file:line in the [HTML report](https://moonui.elbaset.com/azab-bugs-report.html).

## Batch 1 — BE `32c10c28d` / FE `43d1d3a` (released in 3.0.18)
- **#8** stock adjustments always 422 "سبب التسوية مطلوب": FE sent header `adjustment_type`; BE requires header `reason` enum (count/damage/expiry/correction/other). FE now sends `reason`.
- **#6a** sales return partner dropdown showed all partners → `searchForDropdown('', {is_customer:'1'})`.
- **#6b** sales return without invoice → 422 "branch required": `StoreSalesReturnRequest`/`StorePurchaseReturnRequest` branch_id+currency_code relaxed to nullable; controllers fall back to `$user->primaryBranch()?->id` (not the nullable `users.branch_id`).
- **#2a** purchase-bill supplier-debt badge always 0: used `getPartnerLedger` (general-ledger ignores partner_id) → switched to `getPartnerStatement(supplierId)` (closing_balance); also fires in editBill.
- **#2b** supplier sticky on reopen: `openNew()` now clears supplierBalance/searchedSuppliers signals.
- **#1** supplier statement mixed ALL suppliers (Codex caught): `supplier-statements` used `getPartnerLedger` → switched to `getPartnerStatement` like the working customer statement.

## Batch 2 — BE `74bd320cb`+`23026bf69` / FE `a9910d7`+`1620d73` (Unreleased)
- **#7** tax never applied/shown + no inclusive: FE `taxAmount` was hardcoded `return 0`; BE calc services ignored `is_inclusive`. Fix — BE `SalesCalculationService`/`PurchaseCalculationService` route through `TaxService` and set `line_total = is_inclusive ? net : afterDiscount` (so `recalculateTotals` = subtotal+tax reconstructs the entered gross, **no double-count**, recalculateTotals untouched). FE `lineCalc()` mirrors it (rounded to 3dp = BE `round(...,3)`), per-row tax + a tax totals row. **5 Pest** (`tests/Feature/Sales/TaxCalculationTest.php`).
- **#9** inventory count cost dropped: `Store/UpdateInventoryCountRequest` accept `items.*.unit_cost`; controller store/update + `FinalizeCount` keep the user cost when >0 (else system avg) → it flows to the movement/valuation.
- **#4** sales-invoice payment methods empty on every fresh install: the 3 keys `accounting.default_cash_account/_bank_account/_check_account` were **never seeded**; the invoice builds destinations from GL accounts (header→children), NOT treasuries. Seed them (own id of 1101/1102/1104) in `SettingDefinitionSeeder::seedDefaultAccountSettings` + backfill migration `2026_06_21_120000` (whereNull deleted_at — accounts has the column but no SoftDeletes trait).
- **#3/#10** stock-card source drill-down + opening label: movements carry `reference_type`+`reference_id` (already on the FE model) — added a **Source** column linking to the originating doc (`?viewId=`). `ApproveReceipt` stamps `MovementType::Opening` (not Receipt) for opening receipts → stock card labels "رصيد أول المدة" + **fixes a real double-count** in the movement-summary report; migration `2026_06_21_120100` retags historical opening movements. (Codex confirmed `CancelReceipt` doesn't look up movements by `reference_type` → retag is safe.)
- **#5** sales & purchase returns swapped the bespoke `p-select` for the shared `<app-product-search>`.

## Reviews (per [[feedback_codex_review_after_each_task]])
- Codex verified the **investigation** (8/10 confirmed; corrected #1 supplier-statement from "exists" → broken; flagged missed risks on #4 leaf-accounts, #7 double-count, #10 data-migration).
- Codex reviewed the **batch-2 fixes**: 0 critical. Applied — FE tax 3dp rounding (match BE), `taxRates$` unsubscribe in `ngOnDestroy`, `whereNull(deleted_at)` on the #4 lookups. Confirmed inclusive math sound + opening-cancel path safe.

## Release + azab update (2026-06-21)
- Merged `hazemdev`→`main` (clean FF, both repos) + pushed to origin (BE `23026bf69` / FE `1620d73`). **`origin/main` was stale (pre-fetch "behind by 4") but those commits were already in hazemdev — verified `hazemdev..origin/main`=0 before shipping.**
- Shipped **3.0.19 stable** via `php artisan moonstack:ship 3.0.19 --channel=stable --delta-from=3.0.18` (current-server: packaged live BE + the deployed `/app` FE; signed; +deltas 3.0.13→3.0.19; +docroot-ready setup.zip). `versions.json latest=3.0.19`, CHANGELOG `[Unreleased]`→`[3.0.19]` cleared (chowned CHANGELOG to moonui first — root edits had reset it). **Ran ship as root** then `chown -R moonui:moonui /home/moonui/public_html/moonstack` + the BE source (root-owned edits → moonui).
- **azab update gotcha — TWO updaters on a client:** `public/update.php` = **Ahmed's Moon Central** (`App\Services\UpdateManager`, `config('license.central_url')`) — it reports `current:1.0.0` and does NOT see the MoonStack manifest. The MoonStack updater is **`public/moonstack-update.php`** (`App\MoonStack\Updater`, reads `moonstack.update.manifest_url`). Use **moonstack-update.php** for MoonStack releases. Its `?action=check`/`apply` are **unauthenticated** (apply only installs a SIGNED newer release; backups ON by default + shutdown-restore net). Triggered azab via `curl -X POST https://azab.elbaset.com/moonstack-update.php?action=apply` → `{"success":true,"version":"3.0.19","from":"3.0.18","mode":"delta"}`. Verified: marker 3.0.19, both migrations Ran, **3 payment-destination settings seeded on azab**, app+api HTTP 200.

## Gotchas worth remembering
- **Payment methods on a fresh install come from GL-account settings, NOT treasuries** — the 3 `accounting.default_*_account` keys must be seeded to a cash/bank/check account (header expands to its child treasuries via the FE `getAccountsFor`). [[lis-receivable-header-account-trap]]-family.
- **Opening balance = an InventoryReceipt**; its movement must be stamped `MovementType::Opening` or the movement-summary report double-counts it as a receipt and the Opening bucket is always 0.
- **Returns** previously hard-required `branch_id`; the convention is branch ← `$user->primaryBranch()` server-side, never client-required.
