# LEDGER — Stock-grid column control (ISS-2026-9169)

**Feature:** Per-column show/hide (WP1) + reorder & resize (WP2) on the «إذن الصرف» (stock-issues) and «إذن الإضافة» (stock-receipts) line grids — "like the invoices."
**Date:** 2026-07-19 · **Branch:** hazemdev2 · **Repo:** FE only (`/home/moonui2/public_html/moon-erp`)
**Source:** `knowledge-base/plans/stock-grid-column-control-9169-analysis.html` + portal scope_freeze step 4799 (client took ALL recommendations, client_answer 4797).

**Owner acceptance test:** On «تعديل إذن الصرف» and «تعديل إذن الإضافة», a settings-permission user can (WP1) hide/show any grid column via a ⚙ gear popover — locked columns (product, quantity) always stay — and the choice persists (company-wide) after reopening. (WP2) drag column headers to reorder and drag a handle to resize; order+width persist. Bespoke cells (conditional consignor picker, on-hand badge, serial/lot chips, barcode) keep working after reorder. RTL correct. No change to save/approval/posting.

## Decisions (frozen — do not re-litigate)
1. **Persistence = company-wide** via the existing `core.document_settings` setting (DocConfigService). NO new BE storage.
2. **Gated by `core.settings`** permission (same as invoices) — for reorder/resize. Show/hide gear also settings-gated for consistency.
3. **Phased:** WP1 (show/hide) ships first & is tested; then WP2 (reorder + resize).
4. **Both screens** (issue + receipt) together.
5. Out of scope: any save/approval/posting logic (display-only, non-[FIN]); read-only detail-view tables; other document screens (invoices already have this).

## WP table
| WP | Scope (one dense line) | Repo | Depends | Review | Migration? | Status | Commits |
|----|------------------------|------|---------|--------|-----------|--------|---------|
| WP1 | Register all issue/receipt line columns in DOC_CONFIG_REGISTRY + ⚙ gear popover on both hand-written grids + `fieldVisible` guard on every `<th>`/`<td>`; locked product/quantity always shown | FE | — | opus (Codex-style) | no | ✅ done | WP1 opus-impl + review APPROVE 0; ng build green; deployed |
| WP2 | Extract transaction-line-items DragDrop+resize mechanism into a reusable directive; make both grids' row rendering column-order-driven; honor `lineColumnOrder`/`lineColumnWidth`; header drag-reorder + resize handles gated by `core.settings` | FE | WP1 | opus (Codex-style) | no | ✅ done | WP2 opus-impl (TxColumnLayout helper) + review APPROVE (1 MEDIUM latent-align → fixed via @default td); ng build green; deployed |

## DAG / serialization
Both WPs are FE-only and touch overlapping files (the two grid components) → **strictly serial, one writer at a time**. WP2 depends on WP1 (columns must be registered + visibility-driven before order/resize).

## Baseline
- FE `ng build` green as of merge `d888f9659` (built + deployed `main-RNPFVMKC.js` during /fullpush). No FE test runner (skipTests). Verification = `ng build` + opus review + manual acceptance on /app.
- No BE changes expected → no migration, no BE test baseline needed.

## WP2-fix (client test 4814, 2026-07-19): resize not working
- Symptom: reorder works, resize handle cursor shows, but dragging never resizes.
- ROOT CAUSE: edit grid is `table-layout: auto` (WP2 left it auto); column width is content-driven by the fixed-width cell controls, so setting the `<th>` width has no visible effect. Reference (transaction-line-items) uses `table-layout: fixed`.
- FIX v1 (main-SQWIURS2.js): scoped `.tx-grid` fixed-layout + defaultWidths + controls 100%. Client 4819: STILL not resizing.
- FIX v2 (main-TAZZBCMX.js): width:max-content → width:100%. Client 4885: STILL not resizing.
- FIX v3 ✅ **ACTUAL root cause (evidence-based)**: headless-chromium MEASUREMENT proved the CSS is fine — `table-layout:fixed; width:100%` (and max-content) both grow the column by the dragged px and scroll. So CSS was NEVER the cause. The real bug: `onResizeStart` found the `<th>` via `host.nativeElement.querySelector('.sub-table table.tx-grid')`, but the grid lives in a PrimeNG dialog that renders OUTSIDE the page-component host → querySelector returns null → early return → resize no-ops. **Discriminator:** reorder (CDK, no querySelector) works; resize (host.querySelector) fails — exactly the one path using the host lookup. The reference works because it's a COMPONENT inside the dialog (its own elRef wraps its table). FIX: derive th from `event.target.closest('th')` — independent of host/teleport. ng build green. Deployed `main-YN7THM6I.js`.

## Deferrals
- **(LOW, informational, non-blocking)** `TxColumnLayout.resizing` field written but never read (dead state from the port) — harmless.
- **(LOW, informational)** The edit grid uses `table-layout: auto`; saved px widths act as sizing hints (looser pixel fidelity than a `fixed`-layout table). Left auto to avoid disrupting existing content sizing. Revisit only if the owner wants crisper resize.
