---
name: Purchases Default Accounts
description: Every GL default-account setting that the Purchases module (and the supplier-side Accounting actions it triggers) uses when auto-posting journal entries — keys, AR/EN labels, definition file:line, consumption file:line, and /setup-page coverage. Reference for extending the first-run "Default Accounts" (/setup) onboarding page.
updated: 2026-06-18
---

# Purchases — Default Accounts

This topic enumerates every **default account** in the Purchases area: a setting whose
stored value is a GL `Account.id` that the system uses automatically when posting journal
entries. It also covers the two supplier-side **Accounting** settings (WHT payable, checks
issued/received) that fire on purchase-related vouchers/checks.

Backend repo: `/home/moonui/moon-erp-be`.
All setting definitions live in
`Modules/Core/database/seeders/SettingDefinitionSeeder.php`.
None of these are stored as `decimal`; they are `integer` GL account ids resolved at
post time via `SettingsService::get($key, $companyId)`.

---

## 1. Core Purchases default-account settings

All five are defined consecutively in the seeder, `display_group => 'purchases'`,
`value_type => 'integer'`, `scope => 'company'`, `default_value => null`.

| Key | Label EN | Label AR | Purpose | Definition (file:line) |
|-----|----------|----------|---------|------------------------|
| `purchases.inventory_account_id` | Inventory GL Account | حساب المخزون | DR asset for stock-tracked items on a posted bill (CR on a return). Also the inventory leg of a landed-cost voucher. | `SettingDefinitionSeeder.php:1084` |
| `purchases.expense_account_id` | Expense GL Account | حساب المصروفات | DR expense for non-inventory / service lines on a bill (CR on a return). Also the consumed-portion leg of a landed-cost voucher. | `SettingDefinitionSeeder.php:1100` |
| `purchases.payable_account_id` | Accounts Payable GL Account | حساب الدائنين | CR Accounts Payable (الدائنون) on a posted bill; DR AP on a payment and on a return. The global AP unless the per-supplier sub-account is enabled (see `use_supplier_ap_account`). | `SettingDefinitionSeeder.php:1116` |
| `purchases.tax_receivable_account_id` | Input Tax GL Account | حساب ضريبة المدخلات | DR input VAT (ضريبة القيمة المضافة المستردة) on a bill; CR (reversal) on a return. | `SettingDefinitionSeeder.php:1132` |
| `purchases.discount_account_id` | Purchase Discount GL Account | حساب خصم المشتريات | CR purchase discount on a bill; DR (reversal) on a return. | `SettingDefinitionSeeder.php:1148` |

### Related (non-account) toggle that changes which account is used

| Key | Type | Label EN / AR | Effect | Definition |
|-----|------|---------------|--------|------------|
| `purchases.use_supplier_ap_account` | boolean | Use Supplier AP Sub-Account / استخدام حساب الدائنين الفرعي للمورد | When `true`, bill/payment/return JEs use the supplier's own AP sub-account (`supplier.accExt.ap_account_id`) instead of the global `purchases.payable_account_id`. Falls back to the global account if the supplier has no sub-account. | `SettingDefinitionSeeder.php:1164` |

> This is **not** itself a default-account setting (it is a boolean), but it decides whether
> `purchases.payable_account_id` or the per-supplier AP account is used — relevant when
> deciding what to surface on /setup.

---

## 2. Landed-cost account (auto-created, NOT in the seeder)

| Key | Label | Purpose | Where defined | Definition? |
|-----|-------|---------|---------------|-------------|
| `purchases.landed_cost_clearing_account_id` | (no seeded label) | CR liability ("Landed Cost Clearing" / تسوية تكلفة الوصول) for the full landed-cost voucher total. | Const `SETTING_CLEARING` in `Modules/Purchases/app/Actions/PostLandedCostVoucher.php:54` | **No seeded definition.** Resolved-or-created at post time and written back via `SettingsService::set`. |

This key is **not** in `SettingDefinitionSeeder.php` — it is created on first landed-cost
post (`AutoAccountService::createChildAccount` under parent `'21'` Current Liabilities) and
the new id is persisted into the setting (`PostLandedCostVoucher.php:361-387`). The inventory
and expense legs of a landed-cost voucher reuse `purchases.inventory_account_id` /
`purchases.expense_account_id`, falling back to seeded code `1105` / auto-created accounts
(`PostLandedCostVoucher.php:393-455`).

---

## 3. Supplier-side Accounting settings (fire on purchase-related vouchers/checks)

These live under the `accounting` group, not `purchases`, but they post on supplier-payment
and check flows that originate from the Purchases side. Included here because they are the
WHT-payable / checks-issued / checks-received accounts the prompt asked about.

| Key | Label EN | Label AR | Purpose | Definition (file:line) |
|-----|----------|----------|---------|------------------------|
| `accounting.wht_payable_account_id` | Withholding Tax Payable Account | حساب ضريبة الاستقطاع المستحقة | CR when a payment voucher to a supplier carries withholding tax (KSA: WHT payable to ZATCA). | `SettingDefinitionSeeder.php:373` |
| `accounting.checks_issued_account_id` | Checks Issued Intermediate Account | حساب الشيكات المُصدَرة الوسيط | Liability (≈2103) credited on check issue, debited when the issued check clears the bank. | `SettingDefinitionSeeder.php:309` |
| `accounting.checks_received_account_id` | Checks Received Intermediate Account | حساب الشيكات المُستلَمة الوسيط | Asset (≈1103) debited on check receipt, credited when the bank collects the check. | `SettingDefinitionSeeder.php:325` |

---

## 4. Where these accounts are CONSUMED (posting code)

### Purchase Bill — `Modules/Purchases/app/Actions/PostPurchaseBill.php`
JE on post (`createJournalEntry`, lines 63-167):
- `purchases.inventory_account_id` → **DR Inventory** for stock-tracked products (`:65`, used `:97-105`)
- `purchases.expense_account_id` → **DR Expense** for non-inventory lines; falls back to inventory acct if expense not set (`:66`, used `:108-117`)
- `purchases.tax_receivable_account_id` → **DR Input Tax** when `tax_amount > 0` (`:68`, used `:119-129`)
- `purchases.discount_account_id` → **CR Purchase Discount** when `discount_amount > 0` (`:69`, used `:131-141`)
- AP account (via `resolvePayableAccount`) → **CR Accounts Payable** for the bill total (`:143-151`)
- `resolvePayableAccount` (`:169-183`): if `purchases.use_supplier_ap_account` true and supplier has `accExt.ap_account_id`, uses it; else `purchases.payable_account_id`.
- Guards: throws `missing_accounting_settings` if no payable account, or if neither inventory nor expense is set (`:71-77`).

### Purchase Payment — `Modules/Purchases/app/Actions/PostPurchasePayment.php`
JE on post (`execute`, lines 19-88):
- AP account (via `resolvePayableAccount` `:90-104`) → **DR Accounts Payable** (reduces what we owe) (`:39-47`)
- **CR side = `$payment->paying_account_id`** — the cash/bank account chosen *per payment*, NOT a default-account setting (`:49-56`). So the "which cash/bank we paid from" is operator-selected, not a default.
- Same `use_supplier_ap_account` → supplier-AP-or-global resolution as the bill.

### Purchase Return — `Modules/Purchases/app/Actions/PostPurchaseReturn.php`
Reverse-AP JE on post (`createReverseApJournalEntry`, lines 54-158) — mirrors the bill:
- AP account → **DR Accounts Payable** (reverse of bill CR) for return total (`:56`, `:87-95`)
- `purchases.inventory_account_id` → **CR Inventory** (`:57`, `:97-106`)
- `purchases.expense_account_id` → **CR Expense** (fallback to inventory acct) (`:58`, `:108-118`)
- `purchases.tax_receivable_account_id` → **CR Tax** reversal (`:59`, `:120-130`)
- `purchases.discount_account_id` → **DR Discount** reversal (`:60`, `:132-142`)
- `resolvePayableAccount` (`:160-174`): same supplier-AP-or-global logic.

### Landed Cost Voucher — `Modules/Purchases/app/Actions/PostLandedCostVoucher.php`
JE: DR Inventory (capitalised on-hand) / DR Expense (consumed portion) / CR Landed Cost Clearing (full total).
- `purchases.inventory_account_id` resolved by `resolveInventoryAccount` (`:393-425`): setting → seeded `1105` → auto-create under `'11'`.
- `purchases.expense_account_id` resolved by `resolveExpenseAccount` (`:431-455`): setting → auto-create under `'52'`.
- `purchases.landed_cost_clearing_account_id` resolved by `resolveClearingAccount` (`:361-387`): setting → auto-create under `'21'`, then persisted back.

### Supplier payment WHT — `Modules/Accounting/app/Actions/ApprovePaymentVoucher.php`
- `accounting.wht_payable_account_id` → **CR WHT Payable** when a payment voucher carries withholding tax (`:140-152`); throws `wht_payable_account_missing` if unset (`:141-143`).

### Checks — `Modules/Accounting/app/Actions/`
- `accounting.checks_issued_account_id` → used in `CashCheck.php:32` when an issued check clears (intermediate liability). Missing-account message at `Modules/Accounting/lang/{en,ar}/accounting.php:331`.
- `accounting.checks_received_account_id` → used in `CollectCheck.php:30` when a received check is collected (intermediate asset). Missing-account message at `…accounting.php:332`.

---

## 5. Seeded default values (first-run / setup)

`SettingDefinitionSeeder::seedDefaultAccountSettings(int $companyId)` (`:~2100`) maps a few
keys to GL **codes** and writes the resolved account id into the company setting. For
purchases only two are auto-seeded (`:2121-2122`):

| Key | Seeded GL code |
|-----|----------------|
| `purchases.inventory_account_id` | `1105` |
| `purchases.payable_account_id` | `2101` |

`purchases.expense_account_id`, `purchases.tax_receivable_account_id`,
`purchases.discount_account_id`, and all three Accounting check/WHT accounts are **NOT**
auto-seeded — they start `null` and must be set by the admin (or fall back / auto-create at
post time where the code supports it).

---

## 6. Already on the /setup "Default Accounts" page?

The FE first-run page is `src/app/features/setup/setup-wizard.component.ts` (route `/setup`,
step `SETUP.STEP_DEFAULTS`). Its "Default Accounts" step (`saveDefaults`, lines 674-712)
writes ONLY these seven **`accounting.default_*`** keys: `default_cash_account`,
`default_bank_account`, `default_client_account`, `default_supplier_account`,
`default_expense_account`, `default_revenue_account`, `default_check_account`
(auto-selected by COA code in `autoSelectDefaults` `:656-672`).

**None of the `purchases.*` account keys (nor the `accounting.wht_payable` /
`checks_issued` / `checks_received` keys) are on the /setup page today.** The page's
`default_supplier_account` is a *different* key (`accounting.default_supplier_account`, code
`2101`) from `purchases.payable_account_id` — they are not the same setting, even though both
default to code `2101`. So to extend /setup for Purchases, all five `purchases.*` account
keys (plus optionally the WHT/checks Accounting keys) need to be added.

| Key | On /setup today? |
|-----|------------------|
| `purchases.inventory_account_id` | No |
| `purchases.expense_account_id` | No |
| `purchases.payable_account_id` | No |
| `purchases.tax_receivable_account_id` | No |
| `purchases.discount_account_id` | No |
| `purchases.landed_cost_clearing_account_id` | No (and not even seeded) |
| `accounting.wht_payable_account_id` | No |
| `accounting.checks_issued_account_id` | No |
| `accounting.checks_received_account_id` | No |

---

## Key file references

| File | Role |
|------|------|
| `Modules/Core/database/seeders/SettingDefinitionSeeder.php` | Definitions: purchases accounts `:1084-1148`, `use_supplier_ap_account` `:1164`; accounting `checks_issued` `:309`, `checks_received` `:325`, `wht_payable` `:373`; default seeding `:2121-2122` |
| `Modules/Purchases/app/Actions/PostPurchaseBill.php` | Bill JE — DR Inv/Exp/Tax, CR AP/Discount; supplier-AP resolution `:169-183` |
| `Modules/Purchases/app/Actions/PostPurchasePayment.php` | Payment JE — DR AP, CR `paying_account_id` (per-payment, not a default) |
| `Modules/Purchases/app/Actions/PostPurchaseReturn.php` | Reverse-AP JE — mirrors the bill |
| `Modules/Purchases/app/Actions/PostLandedCostVoucher.php` | Landed-cost JE; inventory/expense/clearing resolution `:361-455` |
| `Modules/Accounting/app/Actions/ApprovePaymentVoucher.php` | WHT payable CR on supplier payment voucher `:140-152` |
| `Modules/Accounting/app/Actions/CashCheck.php` / `CollectCheck.php` | Checks issued/received intermediate accounts |
| `src/app/features/setup/setup-wizard.component.ts` (FE) | /setup Default-Accounts step — currently only 7 `accounting.default_*` keys (`:656-712`) |
