Settings ​
The Settings system provides a definition-driven, scoped configuration store for Moon ERP. Every configurable setting is backed by a SettingDefinition that specifies its type, scope, default value, and allowed values. Setting values are resolved through a fallback chain: user+branch -> branch -> company -> definition default.
Purpose ​
- Define all available settings with metadata (type, scope, allowed values, localized labels)
- Store company/branch/user-specific overrides in a scoped
settingstable - Resolve values through a hierarchical fallback chain
- Automatically cast values based on the definition's
value_type - Cache settings per company for performance (5-minute TTL)
Setting Definitions ​
Setting definitions describe what settings exist. They are seeded during installation and provide the schema for all configurable options.
Definition Attributes ​
| Field | Type | Description |
|---|---|---|
id | integer | Primary key |
setting_key | string(100) | Unique dot-notation key (e.g., accounting.ar_parent_account) |
module | string(50) | Module name (e.g., accounting, sales) |
value_type | enum | string, integer, decimal, boolean, json, enum |
default_value | text? | Default value when no override exists |
allowed_values | json? | Restricted list of valid values (for enum types) |
scope | enum | global, company, branch, user |
label_ar | string | Arabic display label |
label_en | string | English display label |
description_ar | text? | Arabic description |
description_en | text? | English description |
display_group | string(100) | UI grouping (e.g., journal_entries, partners) |
display_order | integer | Sort order within group |
is_visible | boolean | Whether to show in settings UI |
requires_restart | boolean | Whether changes require app restart |
Seeded Definitions ​
30 settings are seeded across 6 modules:
| Module | Key | Type | Default | Scope |
|---|---|---|---|---|
| accounting | accounting.fiscal_year_start | string | 01-01 | company |
| accounting | accounting.auto_post_entries | boolean | false | company |
| accounting | accounting.auto_create_partner_account | boolean | true | company |
| accounting | accounting.ar_parent_account | string | 1103 | company |
| accounting | accounting.ap_parent_account | string | 2101 | company |
| accounting | accounting.cost_center_enabled | boolean | false | company |
| accounting | accounting.cost_center_required | boolean | false | company |
| accounting | accounting.default_journal_type | string | standard | company |
| sales | sales.default_price_type | enum | wholesale | company |
| sales | sales.allow_negative_stock | boolean | false | branch |
| sales | sales.auto_approve_limit | decimal | 5000 | branch |
| sales | sales.vat_rate | decimal | 0.00 | company |
| sales | sales.vat_inclusive | boolean | false | company |
| sales | sales.revenue_account | string | 3101 | company |
| sales | sales.returns_account | string | 3102 | company |
| sales | sales.discount_account | string | 3103 | company |
| inventory | inventory.valuation_method | enum | weighted_avg | company |
| inventory | inventory.allow_negative | boolean | false | branch |
| inventory | inventory.stock_account | string | 1301 | company |
| inventory | inventory.cogs_account | string | 4101 | company |
| purchases | purchases.vat_inclusive | boolean | false | company |
| purchases | purchases.expense_account | string | 4201 | company |
| purchases | purchases.returns_account | string | 4202 | company |
| core | core.company_name_display | enum | both | company |
| core | core.default_language | enum | ar | user |
| core | core.date_format | enum | Y-m-d | company |
| core | core.timezone | string | Asia/Kuwait | company |
| core | core.default_currency | string | KWD | company |
| core | core.pagination_per_page | integer | 25 | user |
| core | core.barcode_auto_generate | boolean | true | company |
| ui | ui.show_module_nav | boolean | true | user |
Setting Values ​
Setting values are stored as overrides in the settings table. When no override exists, the definition's default_value is used.
Setting Attributes ​
| Field | Type | Description |
|---|---|---|
id | integer | Primary key |
company_id | integer | FK to companies |
setting_key | string(100) | Dot-notation key (e.g., accounting.ar_parent_account) |
value | text? | The stored value |
branch_id | integer? | FK to branches (for branch-level overrides) |
user_id | integer? | FK to users (for user-level overrides) |
updated_by | integer? | FK to users (who last updated) |
created_at | datetime | Creation timestamp |
updated_at | datetime | Last update timestamp |
deleted_at | datetime? | Soft-delete timestamp |
Fallback Chain ​
When resolving a setting value, the system checks for overrides in this order:
- User + Branch -- setting for this specific user at this specific branch
- Branch -- branch-level override (no user)
- Company -- company-level override (no branch, no user)
- Definition Default -- the
default_valuefrom the setting definition
The first match wins. If no match is found at any level, null is returned.
Type Casting ​
Values are automatically cast based on the definition's value_type:
| Type | Cast |
|---|---|
string | (string) |
integer | (int) |
decimal | (float) |
boolean | filter_var(..., FILTER_VALIDATE_BOOLEAN) |
json | json_decode() |
enum | (string) |
Relationships ​
API Endpoints ​
| Method | Path | Description |
|---|---|---|
GET | /api/core/settings/definitions | List setting definitions (filterable by module) |
GET | /api/core/settings | List settings with current values (filterable by module) |
GET | /api/core/settings/{key} | Get a single setting by dot-notation key |
PUT | /api/core/settings | Update a setting value |
Request/Response Examples ​
GET /api/core/settings/definitions ​
List all setting definitions, optionally filtered by module.
curl -X GET "https://moon-erp.test/api/core/settings/definitions?module=accounting" \
-H "Accept: application/json" \
-H "Authorization: Bearer {token}"final response = await http.get(
Uri.parse('https://moon-erp.test/api/core/settings/definitions?module=accounting'),
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer $token',
},
);Response 200 OK
{
"data": [
{
"id": 1,
"setting_key": "accounting.fiscal_year_start",
"module": "accounting",
"value_type": "string",
"default_value": "01-01",
"allowed_values": null,
"scope": "company",
"label": "Fiscal Year Start",
"label_ar": "بداية السنة المالية",
"label_en": "Fiscal Year Start",
"description": null,
"display_group": "general",
"display_order": 1,
"is_visible": true,
"requires_restart": false
}
]
}GET /api/core/settings ​
List settings with their current resolved values. Optionally filter by module.
curl -X GET "https://moon-erp.test/api/core/settings?module=accounting" \
-H "Accept: application/json" \
-H "Authorization: Bearer {token}"final response = await http.get(
Uri.parse('https://moon-erp.test/api/core/settings?module=accounting'),
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer $token',
},
);Response 200 OK
{
"data": [
{
"definition": {
"setting_key": "accounting.ar_parent_account",
"module": "accounting",
"value_type": "string",
"scope": "company",
"label": "AR Parent Account"
},
"current_value": "1103"
},
{
"definition": {
"setting_key": "accounting.auto_post_entries",
"module": "accounting",
"value_type": "boolean",
"scope": "company",
"label": "Auto Post Entries"
},
"current_value": false
}
]
}GET /api/core/settings/{key} ​
Get a single setting value by its dot-notation key.
curl -X GET "https://moon-erp.test/api/core/settings/accounting.ar_parent_account" \
-H "Accept: application/json" \
-H "Authorization: Bearer {token}"final response = await http.get(
Uri.parse('https://moon-erp.test/api/core/settings/accounting.ar_parent_account'),
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer $token',
},
);Response 200 OK
{
"key": "accounting.ar_parent_account",
"value": "1103"
}PUT /api/core/settings ​
Update a setting value.
curl -X PUT https://moon-erp.test/api/core/settings \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Authorization: Bearer {token}" \
-d '{
"setting_key": "accounting.ar_parent_account",
"value": "1201"
}'final response = await http.put(
Uri.parse('https://moon-erp.test/api/core/settings'),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token',
},
body: jsonEncode({
'setting_key': 'accounting.ar_parent_account',
'value': '1201',
}),
);Request Body
| Field | Type | Required | Description |
|---|---|---|---|
setting_key | string | Yes | Dot-notation key |
value | mixed | Yes | New value |
branch_id | integer? | No | Branch scope (for branch-level override) |
user_id | integer? | No | User scope (for user-level override) |
Response 200 OK
{
"message": "Setting updated"
}Business Rules ​
- Company scoping -- settings are automatically scoped to the authenticated user's company.
- Fallback chain -- values resolve through user+branch -> branch -> company -> definition default.
- Type casting -- values are automatically cast based on the definition's
value_typewhen retrieved. - Validation -- when a definition has
allowed_values, only those values are accepted. - Cache -- settings are cached per company for 5 minutes; the cache is flushed when any setting is updated.
- No delete -- settings are updated or reset to defaults, not deleted individually. Use
PUTto change values. - Enum validation -- for definitions with
value_type: enum, the submitted value must be in theallowed_valueslist.