# Admin Settings Module — Business Flows

## Overview

The Admin Settings module is the reference-data layer of the ERP. It does not perform any transactions or calculations itself; instead, it supplies the lookup tables and configuration values that every other module draws on. Administrators maintain this data through a dedicated section of the back-office dashboard.

All names in this module are stored in both Arabic and English (bilingual JSON columns), so the correct translation is served automatically depending on the active locale.

---

## Entities and Their Purpose

### Countries

A master list of countries used throughout the system wherever a geographic dimension is needed — on employee records (nationality of origin, passport country), on customer and supplier addresses, on real-estate units, and so on.

Each country has:
- A bilingual name (Arabic / English)
- A flag or representative image
- An international phone dial code (e.g. 966 for Saudi Arabia)

Countries are soft-deleted, meaning a deletion hides the record from normal use without permanently erasing it from the database.

### Cities

Cities belong to Countries. Every city record carries a `country_id` foreign key that links it back to its parent country. The same image conventions apply. Cities are also soft-deleted.

Other modules (HR employee addresses, real-estate unit locations) reference city records. The `MainController::getDistricts` AJAX helper allows filtering districts by city, which is used in forms that cascade Country → City → District.

### Districts

Districts belong to Cities via `city_id`. They carry a bilingual name and an optional status flag. Districts are the most granular geographic level in the system.

Districts are managed through a classic server-rendered form rather than the AJAX panel used by Countries and Cities. An AJAX endpoint (`POST admin/getDistricts`) is available for other modules that need to dynamically load districts when a city is selected.

### Nationalities

A standalone lookup table of nationalities (e.g. "Saudi", "Egyptian", "Filipino"). It has no parent entity. The HR module uses nationality when recording employee personal information and on employment contracts.

### Categories

Categories support a two-level self-referential hierarchy: a root category can have child categories (via `parent_id`). This is used in the inventory/product domain to classify items (e.g. "Electronics" with a child "Laptops").

Each category can have an image. The `type` and `external_id` columns allow categories to be tagged or mapped to codes from external systems.

### Type Settings (TypeSetting)

`TypeSetting` is the schema definition layer for the `MainSetting` lookup system. Each TypeSetting record represents a named classification bucket (e.g. "Employee Status Types", "Contract Types for HR", "Unit Types for Real Estate"). It is identified by a bilingual `title` and a unique `code` string that other modules use to look up the relevant MainSettings without knowing their IDs.

Deleting a TypeSetting cascades: its associated MainSetting child records are deleted first.

### Main Settings (MainSetting)

MainSetting rows are the actual values within a TypeSetting bucket. For example, the TypeSetting "Contract Types" might contain MainSetting values of "Full Time", "Part Time", and "Freelance". Each MainSetting has:
- A bilingual `title`
- A `type_code` string (mirrors the parent TypeSetting's `code`, stored redundantly for fast querying without a join)
- A `status` flag (active / inactive)

MainSetting is heavily cached. Any time a record is saved or deleted, the `main_settings_all` cache key is cleared. Consumers call `MainSetting::getAllCached()` to get the active set without hitting the database on every request, or `MainSetting::getTitlesByIds(array $ids)` to resolve multiple IDs to titles from that cache.

This design means the admin controls what values are available in dropdowns across the system — for example, the list of permitted leave statuses or unit condition labels — without any code change.

### Services

Services are named service types with an image, a machine-readable `type` string, and a `code`. They are used in the real-estate module (service offerings on a property or complex) and potentially in other domains. Both `type` and `name` enforce uniqueness (excluding soft-deleted records).

### Files (Setting Files)

The `setting_files` table stores named file type labels — for example "National ID", "Passport", "Work Permit". This is a pure reference list. Employee document management and HR onboarding use these labels when classifying uploaded documents. The table carries only a bilingual name.

---

## CRUD Flows

All settings entities follow the same general pattern. The differences are in which fields are required and whether file upload is involved.

### Standard Ajax-Panel Pattern (Country, City, Nationality, Files, Category, Service)

1. **List page** — The browser loads the index view. On load, JavaScript fires an AJAX request to the same URL with `Accept: application/json` (DataTables). The controller's `index()` method detects `$request->ajax()` and returns a DataTables JSON response containing name, image (where applicable), and action buttons.
2. **Create** — A slide-in panel or modal on the list page contains the creation form. On submit, JavaScript POSTs to the `store` route.
3. **Validate** — The `store` method runs inline `Validator::make()` rules. Unique checks exclude the current record's ID and also exclude soft-deleted rows (`whereNull('deleted_at')`). On failure, JSON `{errors: {...}}` with HTTP 422 is returned and the front-end displays field-level messages.
4. **Upsert** — `updateOrCreate(['id' => $id], $data)` is used uniformly. If the request body contains a non-null `id`, the existing record is updated; otherwise a new record is created. This keeps create and update logic in a single method.
5. **Edit** — The edit button in the DataTables row calls the `edit($id)` route, which returns `{one_data: {...}}` JSON. JavaScript populates the slide-in panel fields from this response.
6. **Delete** — The delete button calls the `destroy($id)` route. The record is soft-deleted (or for Country and City, the associated image is also removed from disk first). Success or error is returned as JSON.

### Server-Redirect Pattern (District, TypeSetting, MainSetting)

These three entities use a traditional form-submit / redirect flow instead of the AJAX panel:

1. **List page** — `index()` loads all records and renders a Blade view with an inline form at the bottom (or a separate form at the top).
2. **Create / Update** — Form submits POST to `store` or PUT to `update`. The controller uses a typed FormRequest (e.g. `EditeDistrictRequest`, `type_settingRequest`) for validation. On success, `toastr()->addSuccess()` is called and the user is redirected back to the index. On failure, `redirect()->back()->withErrors()` is used.
3. **Delete** — A dedicated `delete($id)` method handles removal, called via both GET and DELETE HTTP verbs. It redirects back to the index with a toastr notification.

### SubSetting

`SubSettingController` exists in the codebase but its `store`, `edit`, and `update` methods are incomplete (references to an undefined `sub_setting` model class). The index and create views are wired but the entity is not fully implemented; treat it as work-in-progress.

---

## File / Image Management

Several entities (Country, City, Category, Service) allow an image to be uploaded alongside the record.

**Upload path** — Images are stored under `storage/app/images/{folder}/` where `{folder}` is an entity-specific subdirectory:
- Countries: `countries/`
- Cities: `city/`
- Categories: `Products/Category/`
- Services: `services/`

**File naming** — The `ImageProcessing` trait generates a random 8-character string concatenated with the Unix timestamp as the file name (e.g. `aXb3kZqR1716800000.jpg`). This prevents collisions and avoids revealing original file names.

**Processing** — If the Intervention Image library is available, the trait processes the uploaded file through it (which normalises format and strips metadata). If the library is absent, the file is copied raw.

**Deletion** — Before deleting a Country, City, or Service record, the controller calls `$this->deleteImage($record->image)` which removes the stored file from the `images` disk.

**Display** — Each model with an image appends an `imageUrl` computed attribute that calls the global `getDefultImage($path)` helper. This helper returns the full URL to the stored image, or a placeholder URL if the path is null.

**Validation** — On create, the image is required (`required|mimes:jpg,jpeg,png,webp`). On update (when an existing record ID is provided), the image becomes optional (`nullable|mimes:...`) so existing images are preserved if the user does not re-upload.

---

## How Settings Relate to Other Modules

| Setting entity | Consumed by |
|---|---|
| Country | Employee personal data, customer/supplier addresses, real-estate units |
| City | Same as Country, and cascades to Districts |
| District | Address selection in employee and property forms |
| Nationality | Employee HR record (personal info tab) |
| Category | Inventory items, real-estate unit classification |
| TypeSetting / MainSetting | System-wide dropdown values (contract types, employee statuses, unit conditions, etc.) |
| Service | Real-estate complex amenities/services |
| Files (setting_files) | HR employee document type labels |

The `SettingService` (`App\Services\Core\SettingService`) is a separate, lower-level service that manages system-wide key-value configuration (e.g. whether a particular finance posting gate is open). It is distinct from the Settings module described here.
