---
name: setup-page-frontend
description: The Angular first-run "Default Accounts" onboarding component (/setup wizard, Step 5) — every account field shown, the form structure, the LOAD/SAVE API endpoints, and the exact setting keys each field maps to. This is the component the owner wants to EXTEND to also cover LAB (LIS), Manufacturing (Production) and HR default accounts.
updated: 2026-06-18
---

# /setup Default-Accounts page — Frontend (Angular)

## TL;DR

- The `/setup` route loads a **9-step Setup Wizard** (`SetupWizardComponent`). The "Default Accounts" screen is **Step 5 in the UI** but is coded as **`currentStep() === 4`** (zero-indexed; index 4 in the `steps` array, `key: 'SETUP.STEP_DEFAULTS'`).
- It currently exposes **7** default-account fields, all of which map to **`accounting.*` setting keys**. There is **no LAB / Manufacturing / HR** default account on this page today — that is exactly what the owner wants added.
- **LOAD:** it does **NOT** read the stored setting values. It fetches all GL accounts (`GET /accounting/accounts`), filters to `account_type === 'detail'`, and **auto-selects defaults by hard-coded account `code` patterns** (`autoSelectDefaults`).
- **SAVE:** for each non-null field it calls `SettingService.update({ setting_key, value })` → **`PUT /core/settings`** with body `{ setting_key, value }`, one request per setting, chained sequentially. Value is the GL account **id as a string**.

---

## Files

| Role | Path |
|------|------|
| Route definition | `src/app/app.routes.ts:52-58` (path `'setup'`, guard `core.settings`) |
| Component (TS) | `src/app/features/setup/setup-wizard.component.ts` |
| Template (HTML) | `src/app/features/setup/setup-wizard.component.html` |
| Styles (SCSS) | `src/app/features/setup/setup-wizard.component.scss` |
| Save service | `src/app/core/services/setting.service.ts` (`update()` → `PUT /core/settings`) |
| Update payload model | `src/app/core/models/setting.model.ts:21-24` (`UpdateSetting { setting_key; value }`) |
| EN labels | `src/assets/i18n/en.json` → `SETUP.*` |
| AR labels | `src/assets/i18n/ar.json` → `SETUP.*` |

### Route

```ts
// src/app/app.routes.ts:52
{ path: 'setup',
  canActivate: [permissionGuard],
  data: { permissions: ['core.settings'] },
  loadComponent: () => import('./features/setup/setup-wizard.component').then((m) => m.SetupWizardComponent) }
```

---

## Wizard structure (context)

`steps[]` array — `setup-wizard.component.ts:81-91`. The wizard is rendered with PrimeNG `StepperModule`; only one panel shows at a time, gated by `currentStep()`:

| UI step | `steps[]` index = `currentStep()` | key | Template guard |
|---------|-----------------------------------|-----|----------------|
| 1 Company | 0 | `SETUP.STEP_COMPANY` | `currentStep() === 0` |
| 2 Currency | 1 | `SETUP.STEP_CURRENCY` | `=== 1` |
| 3 Fiscal Year | 2 | `SETUP.STEP_FISCAL_YEAR` | `=== 2` |
| 4 Chart of Accounts | 3 | `SETUP.STEP_COA` | `=== 3` |
| **5 Default Accounts** | **4** | **`SETUP.STEP_DEFAULTS`** | **`currentStep() === 4`** (HTML `setup-wizard.component.html:259-367`) |
| 6 Warehouse | 5 | `SETUP.STEP_WAREHOUSE` | `=== 5` |
| 7 Treasury (petty cash) | 6 | `SETUP.STEP_TREASURY` | `=== 6` |
| 8 Bank Account | 7 | `SETUP.STEP_BANK` | `=== 7` |
| 9 Finish | 8 | `SETUP.STEP_FINISH` | `=== 8` |

> NOTE the off-by-one: the HTML comment over the block reads `STEP 4: Default Accounts` and `STEP 5: Warehouse`, but the runtime `currentStep()` values are 4 and 5 respectively. When extending, the new fields go inside the existing `@if (currentStep() === 4)` block (no new wizard step needed) OR a new dedicated step — owner decision.

The "Default Accounts" panel itself is **NOT** a reactive `FormGroup`; it is built from **7 individual Angular `signal`s** bound to PrimeNG `p-select` dropdowns via `[ngModel]` / `(ngModelChange)`.

---

## The 7 default-account fields (current, exact)

Signals declared at `setup-wizard.component.ts:135-141`. Template dropdowns at `setup-wizard.component.html:274-356`. Each `p-select` lists `detailAccounts()` (all `detail` accounts), `optionValue="id"`, filterable by `name,name_ar,name_en,code`.

| # | Signal (TS) | Saved setting key | EN label | AR label | i18n key | Auto-select code(s) | HTML line |
|---|-------------|-------------------|----------|----------|----------|---------------------|-----------|
| 1 | `defaultCash` | `accounting.default_cash_account` | Cash Account | حساب الصندوق | `SETUP.DEFAULT_CASH` | `1101` | 276 |
| 2 | `defaultBank` | `accounting.default_bank_account` | Bank Account | حساب البنك | `SETUP.DEFAULT_BANK` | `1102` | 288 |
| 3 | `defaultReceivable` | `accounting.default_client_account` | Accounts Receivable | حساب العملاء (المدينون) | `SETUP.DEFAULT_RECEIVABLE` | `1103` | 300 |
| 4 | `defaultPayable` | `accounting.default_supplier_account` | Accounts Payable | حساب الموردين (الدائنون) | `SETUP.DEFAULT_PAYABLE` | `2101` | 312 |
| 5 | `defaultExpense` | `accounting.default_expense_account` | Expense Account | حساب المصروفات | `SETUP.DEFAULT_EXPENSE` | `5201`, `5101` | 324 |
| 6 | `defaultRevenue` | `accounting.default_revenue_account` | Revenue Account | حساب الإيرادات | `SETUP.DEFAULT_REVENUE` | `41` | 336 |
| 7 | `defaultCheckAccount` | `accounting.default_check_account` | Notes Receivable/Payable | حساب أوراق القبض/الدفع | `SETUP.DEFAULT_CHECK` | `1104` | 348 |

Panel header labels: `SETUP.DEFAULTS_TITLE` = "Default Accounts" / "الحسابات الافتراضية"; `SETUP.DEFAULTS_DESC` = "Set the default accounting accounts that the system will use automatically" / "حدد الحسابات المحاسبية الافتراضية التي سيستخدمها النظام تلقائياً". Dropdown placeholder `SETUP.SELECT_ACCOUNT` = "Select account" / "اختر حساب".

> Mapping caveat: the FE *labels* differ from the *keys*. The "Receivable"/"المدينون" field saves to `accounting.default_client_account`, and "Payable"/"الدائنون" saves to `accounting.default_supplier_account`. "Notes Receivable/Payable" (أوراق القبض/الدفع) saves to a single `accounting.default_check_account`. Keep this in mind — these are the **only 7 keys** the page writes, all under the `accounting.` prefix.

---

## How it LOADS (no stored-value read)

The page never asks the backend for the *current* setting values. Instead:

1. `loadDetailAccounts()` — `setup-wizard.component.ts:616-654`. Fetches **`GET /accounting/accounts?page=1`**, follows `meta.last_page`, parallel-fetches remaining pages via `forkJoin`, flattens, then filters `account_type === 'detail'` into the `detailAccounts` signal (the dropdown options).
2. `autoSelectDefaults(accounts)` — `setup-wizard.component.ts:656-672`. For each field it does `find([codes])` = first account whose `code` matches the listed code(s), and sets the signal to that account's `id`:

```ts
// setup-wizard.component.ts:665-671
this.defaultCash.set(find(['1101']));
this.defaultBank.set(find(['1102']));
this.defaultReceivable.set(find(['1103']));
this.defaultPayable.set(find(['2101']));
this.defaultExpense.set(find(['5201', '5101']));
this.defaultRevenue.set(find(['41']));
this.defaultCheckAccount.set(find(['1104']));
```

`loadDetailAccounts()` is triggered from `skipCOA()` (`:609-612`) and at the end of `importCOA()` (`:536`) — i.e. when the user enters Step 5 from the Chart-of-Accounts step. It is **not** called in `ngOnInit`.

> Implication for the owner's extension: this page **pre-fills from COA codes, not from existing settings**. A LAB/MFG/HR extension that wants to show the *currently stored* account for each default would need to additionally read the values (e.g. `SettingService.list('lis')` / `GET /core/settings?module=...`, or `GET /core/settings/{key}` via `SettingService.getByKey`). Today none of that is done here.

---

## How it SAVES

`saveDefaults()` — `setup-wizard.component.ts:674-739`.

1. Builds a `settings[]` array of `{ setting_key, value }`; pushes one entry **only if the signal is non-null**, with `value = String(<accountId>)`. The 7 keys are listed verbatim at `:680-712` (same keys as the table above).
2. If `settings.length === 0` → just `nextStep()`.
3. Otherwise chains the saves **sequentially** (RxJS `switchMap` reduce), each `this.settingService.update(s)` wrapped in `catchError(() => of(null))` so one failure doesn't abort the rest. On completion → `nextStep()`.

`SettingService.update()` — `src/app/core/services/setting.service.ts:64-66`:

```ts
update(data: UpdateSetting): Observable<{ message: string }> {
  return this.http.put<{ message: string }>(this.apiUrl, data); // apiUrl = `${environment.apiUrl}/core/settings`
}
```

So each default-account save is:

```
PUT {apiUrl}/core/settings
Body: { "setting_key": "accounting.default_cash_account", "value": "<accountId>" }
```

`UpdateSetting` model — `src/app/core/models/setting.model.ts:21-24`: `{ setting_key: string; value: any }`.

Setup completion flag: `finishSetup()` (`:864-869`) writes `PUT /core/settings` with `{ setting_key: 'setup.completed', value: '1' }` when the user finishes / goes to dashboard.

---

## Extension hooks (for the owner's LAB / MFG / HR work)

When adding lab/manufacturing/HR default accounts to THIS page, the minimal-change pattern that matches the existing code is:

1. **Add a signal per new account** next to `setup-wizard.component.ts:135-141`.
2. **Add a `p-select`** for it inside the `@if (currentStep() === 4)` block (`setup-wizard.component.html:273-357`), reusing `detailAccounts()` as options + the same `name/name_ar/name_en/code` filter + the `SETUP.SELECT_ACCOUNT` placeholder.
3. **Add an EN+AR `SETUP.*` label** in both i18n files.
4. **Push the key in `saveDefaults()`** (`:677-712`) with `setting_key: '<module>.<key>'` (e.g. `lis.receivable_account_id`, etc. — see the LIS/MFG/HR KB topics for the canonical keys) and `value: String(...)`.
5. Optionally extend `autoSelectDefaults()` (`:656-672`) with code patterns OR (better, since lab/mfg/hr defaults rarely have predictable COA codes) read the current stored value via the settings API and pre-select that instead.

> The owner has not decided whether these go in the *same* Step-5 panel (grouped by module: Accounting / Lab / Manufacturing / HR sections) or as additional wizard steps. Either fits the existing structure; grouping inside Step 5 avoids changing the `steps[]`/`currentStep()` indexing.

---

## Quick reference — current account list saved by this page

```
accounting.default_cash_account      (Cash / الصندوق,           pre-fill code 1101)
accounting.default_bank_account      (Bank / البنك,             pre-fill code 1102)
accounting.default_client_account    (Receivable / المدينون,    pre-fill code 1103)
accounting.default_supplier_account  (Payable / الدائنون,       pre-fill code 2101)
accounting.default_expense_account   (Expense / المصروفات,      pre-fill code 5201|5101)
accounting.default_revenue_account   (Revenue / الإيرادات,      pre-fill code 41)
accounting.default_check_account     (Notes Rec/Pay / أوراق القبض والدفع, pre-fill code 1104)
```

All 7 are `accounting.` keys. No `lis.*`, `mfg.*`/`production.*`, or `hr.*` default-account keys are present on the /setup page yet.
