# Solidmark HRIS Frontend and Backend Documentation Last updated: 2026-05-04 This document describes the current Solidmark HRIS project structure, frontend and backend flows, brand filtering rules, account/password rules, loan behavior, testing checklist, and risks that still need review. Documentation scope: - This is documentation only. - No source code behavior was changed for this document. - The active application database is `solidmark_db`. - The old `solidmarkinc_db` and `solidmarkwest` database names should not be used by live backend queries. - Password values, password hashes, mail credentials, and other secrets are intentionally not copied here. ## 1. Project Overview Solidmark HRIS is a React/Vite frontend with a PHP backend and a MySQL/MariaDB database. Main pieces: - Frontend: `frontend/` - Backend: `backend/` - Database: `solidmark_db` - Backend entry URL for local development: configured in `backend/server/config.js` - Frontend Vite alias: `@backend` points to `../backend` The project has been migrated to one centralized database: - Active database: `solidmark_db` - Brand separation is done inside this one database using `company_id` and `brand_id`. - `companies` stores company records. - `brands` stores brand records. - `user_brand_access` controls which non-employee account can access which brand. - Frontend sends `X-Company-Code` and `X-Brand-Code`. - Backend resolves those headers through `backend/config/brand_context.php`. Current DB state provided during the project: - `brand_id = 1` currently maps to `SOLIDMARK_WEST` - `brand_id = 2` currently maps to `SOLIDMARK_INC` Important: this mapping is current database data only. Business logic must not assume fixed brand ID numbers. Code should resolve by `brand_code`, `company_code`, and `brand_context`. Main account examples: - `admin`: main admin account, can access both brands. - `admin_inc`: admin account for one brand only. - `admin_west`: admin account for one brand only. Admin/system accounts are stored in `users`. They may not exist in `employees`. Do not assume every `users.username` has a matching `employees.employee_id`. Employee login accounts use: - `users.username` as the login username. - `employees.employee_id` as the employee profile identifier. - For employee accounts, `users.username = employees.employee_id`. ## 2. Frontend Architecture ### Frontend Stack - React 18 - Vite - React Router - Axios - Tailwind CSS - Material UI is available in the project - Lucide icons - PWA plugin through `vite-plugin-pwa` ### Frontend Entry Files | File | Purpose | |---|---| | `frontend/src/main.jsx` | React app entry. Imports the global Axios setup, wraps the app in `SessionProvider`, registers PWA service worker, and handles app build version/cache refresh logic. | | `frontend/src/App.jsx` | Main route registry. Defines public, admin, and employee route trees. | | `frontend/vite.config.js` | Vite configuration. Defines React/Tailwind/PWA plugins and `@backend` alias. | | `backend/config.js` | Shim that re-exports `backend/server/config.js`. Used by frontend imports through `@backend/config`. | | `backend/server/config.js` | Frontend-consumed `BASE_URL` config. Currently points local development to `http://localhost/solidmark/backend`. | ### Router Setup Main router file: - `frontend/src/App.jsx` Router structure: - Public routes: - `/login` - `/reset-password` - `/` - `/LandingPage` - `/pricing` - Admin protected layout: - Uses `ProtectedRoute` - Uses `Layout` - Contains dashboard, employees, attendance, payroll, loans, utilities, users, admin account settings, and related admin modules. - Employee protected layout: - Base path: `/employee` - Uses `ProtectedRoute` - Uses `Layout` - Contains mobile/employee routes such as dashboard, DTR, time in/out, leave request, and reset password. Important route: | Route | Component | |---|---| | `/AdminAccountSettings` | `frontend/src/components/utilities/admin_settings/AdminAccountSettings.jsx` | ### Layout and Navigation | File | Purpose | |---|---| | `frontend/src/components/navigation/Layou.jsx` | Main app layout. Renders sidebar/header behavior, account/brand display, and brand switcher. | | `frontend/src/components/sidebar.jsx` | Sidebar menu. Reads menu access and handles logout. | | `frontend/src/components/navigation/Logoutt.jsx` | Logout helper UI. Clears local session and brand state. | | `frontend/src/mobile/EmployeeNav.jsx` | Employee mobile navigation. Clears local session and brand state on logout. | ### Session and Auth Files | File | Purpose | |---|---| | `frontend/src/context/SessionContext.jsx` | Holds current frontend user session. Reads/writes `localStorage.user`; clears brand-related storage on logout/session reset. | | `frontend/src/authentication/login.jsx` | Login form and login request. Sends brand headers and stores backend-selected brand response. | | `frontend/src/authentication/ProtectedRoute.jsx` | Protects routes based on logged-in user and allowed roles. | | `frontend/src/authentication/RoleBaseRedirect.jsx` | Redirects logged-in users to employee or admin dashboard based on role. | | `frontend/src/authentication/useRoles.jsx` | Loads role list from backend. | ### Axios and Global Fetch Behavior Primary file: - `frontend/src/components/utils/axiosInstance.js` Responsibilities: - Uses `BASE_URL` from `@backend/config`. - Sends `X-Company-Code: SOLIDMARK`. - Sends `X-Brand-Code` based on detected/selected brand. - Detects forced production domains: - Hostnames containing `solidmarkinc` request `SOLIDMARK_INC`. - Hostnames containing `solidmarkwest` request `SOLIDMARK_WEST`. - For localhost, uses `localStorage.selectedBrandCode` when valid, otherwise defaults to `SOLIDMARK_INC`. - Reads `allowed_brands`, `current_brand`, and `selectedBrandCode` from localStorage. - Adds Axios interceptors so every backend request gets brand headers. - Installs a global `fetch` wrapper for backend requests so direct fetch calls can receive brand headers too. Needs verification: - The frontend currently has a small valid brand code list in `axiosInstance.js`. This is practical for the current deployment, but it should be kept synced with the `brands` table or eventually loaded dynamically. ### Brand Switcher File: - `frontend/src/components/utils/BrandSwitcher.jsx` Behavior: - Only shows when the logged-in user has more than one allowed brand. - Hidden on forced brand domains. - Changing the select updates `localStorage.selectedBrandCode`. - Dispatches a `solidmark:brand-change` browser event. - Reloads/refetches app data by refreshing the page. ### Account and Brand Badge File: - `frontend/src/components/utils/AccountBrandBadge.jsx` Behavior: - Displays the logged-in username and role. - Displays the current brand name and brand code. - Reads from session/localStorage/context. - Uses `current_brand` first. - Falls back to finding the brand in `allowed_brands` by `selectedBrandCode`. - Shows `No brand selected` when no brand can be resolved. - Handles missing or invalid JSON safely. ### Logout Flow Logout-related files: - `frontend/src/components/sidebar.jsx` - `frontend/src/components/navigation/Logoutt.jsx` - `frontend/src/mobile/EmployeeNav.jsx` - `frontend/src/context/SessionContext.jsx` Expected local cleanup: - `user` - `allowed_brands` - `current_brand` - `selectedBrandCode` - Session storage where used ### Important Frontend Pages and Modules #### Login | Item | Value | |---|---| | File | `frontend/src/authentication/login.jsx` | | Route | `/login` | | Purpose | Authenticates a user and stores session/brand data returned by backend. | | Backend endpoint | `backend/login.php` | | Permissions | Public route. Backend validates credentials and brand access. | | Brand behavior | Sends `X-Company-Code` and `X-Brand-Code`. Backend has final decision and returns `allowed_brands`, `current_brand`, and `selectedBrandCode`. | #### Employees | Item | Value | |---|---| | File | `frontend/src/components/employees/employees.jsx` | | Route | `/employees` | | Purpose | Employee list, add, update, status, bulk update, schedule-related employee UI. | | Main endpoints | `/employeesSide/employees.php`, `/employeesSide/add_employee.php`, `/employeesSide/update_employee.php`, `/employeesSide/delete_employee.php`, `/employeesSide/update_employee_status.php`, `/employeesSide/bulk_update_employees.php`, `/work_time/read_work_time.php` | | Permissions | Sidebar/menu and role access are controlled through frontend permission data. Exact page-level permission needs verification. | | Brand behavior | Main employee endpoints include backend brand filtering through `brand_context.php`. | #### Attendance | Item | Value | |---|---| | Files | `frontend/src/components/attendance/*` | | Routes | `/attendance`, `/attendanceSummary`, `/AttendanceMAdmin`, `/AttendanceLogsM`, `/att-dashboard` | | Purpose | Attendance records, summaries, logs, and dashboards. | | Main endpoints | `/attendance/attendance.php`, `/attendance/get_attendance.php`, plus create/update/delete/calculate attendance endpoints. | | Permissions | Related permissions include attendance/DTR permissions from `user_access` and `user_menu_access`. Needs verification per component. | | Brand behavior | Risk: scanned attendance backend files do not consistently include `brand_context.php`. Needs review before relying on brand isolation. | #### DTR | Item | Value | |---|---| | Files | `frontend/src/components/DTRattenance/*`, `frontend/src/mobile/dtr/*` | | Routes | `/attendanceRecord`, `/employee/employeeDTR`, `/employee/DTRForEmployee` | | Purpose | Daily time record views and employee DTR display. | | Main endpoints | Employee and attendance endpoints, schedule-manager endpoints. | | Permissions | DTR/attendance-related access. Needs verification per screen. | | Brand behavior | Employee list calls are brand-scoped. Attendance endpoints need review. | #### Payroll | Item | Value | |---|---| | File | `frontend/src/payrollPage/payrollpage/PayrollPage.jsx` | | Route | `/payroll-page` | | Purpose | Payroll generation, live payroll records, finalized payroll records, loan deductions, overrides, and payroll cycle operations. | | API file | `frontend/src/payrollPage/payrollApi/payrollapi.jsx` | | Main endpoints | `/payroll/check_existing_dates.php`, `/payroll/payroll_cycle_start.php`, `/payroll/payroll_cycle_finalize.php`, `/payroll/payroll.php`, `/payroll/delete_payroll.php`, `/payroll/save_loan_deduction.php`, `/payroll/update_payroll.php`, `/payroll/update_loan_deduction_applied.php`, `/payroll/sync_loan_deductions.php`, `/payroll/update_contribution_override.php`, `/payroll/payroll_summary.php` | | Permissions | Payroll permissions such as `payroll_records`, `can_edit_payroll_date`, `can_print_payroll`, and `can_payroll_logs` appear in access tables. Needs verification per component. | | Brand behavior | Core payroll cycle/live/finalized endpoints are brand-scoped. Some helper endpoints still need review. | #### Payslip and Payroll Prints | Item | Value | |---|---| | Files | `frontend/src/payrollPage/payrollComponents/*` | | Routes | `/Attendance-summary` and print/payslip flows from payroll UI | | Purpose | Payroll summaries, payslip display, attendance summary print. | | Backend endpoints | `/payroll/payroll_print_details.php`, `/payroll/payroll_summary.php`, and payroll records endpoints. | | Permissions | Print payroll permissions where used. Needs verification per component. | | Brand behavior | Print/summary endpoints include brand context, but finalized print scope should be rechecked. | #### Loans | Item | Value | |---|---| | File | `frontend/src/components/loan/LoanPage.jsx` | | Route | `/LoanPage` | | Purpose | Employee loans and loan balances. | | API file | `frontend/src/components/loan/loan_hooks/useLoanAPI.jsx` | | Main endpoints | `/loan_api/read_loans.php`, `/loan_api/create_loan.php`, `/loan_api/update_loan.php`, `/loan_api/delete_loan.php` | | Permissions | Loan permission appears as `loan` in `user_access`. Needs verification in page visibility code. | | Brand behavior | Loan endpoints include `brand_context.php` and use `company_id`/`brand_id`. | #### Loan Journal | Item | Value | |---|---| | Files | `frontend/src/components/loan/LoanJournalEntries/*`, `frontend/src/components/loan/loan_hooks/useLoanJournalAPI.jsx` | | Route | Opened through loan module UI | | Purpose | Records loan debits/credits and journal history. | | Main endpoints | `/loan_journal_entry_api/read_journal_entries.php`, `/loan_journal_entry_api/create_journal_entry.php`, `/loan_journal_entry_api/update_journal_entry.php`, `/loan_journal_entry_api/delete_journal_entry.php` | | Permissions | Loan permission likely applies. Needs verification. | | Brand behavior | Journal endpoints include `brand_context.php` and filter by `company_id`/`brand_id`. | #### Loan Payment History | Item | Value | |---|---| | API file | `frontend/src/components/loan/loan_hooks/useLoanPaymentHistoryAPI.jsx` | | Purpose | Loan payment history UI/API integration. | | Backend endpoints | Backend has `/loan_payment_history_api/read_loan_payment.php`, `/create_loan_payment.php`, `/update_loan_payment.php`, `/delete_loan_payment.php`. | | Risk | Frontend hook appears to reference shorter names such as `read_payment_history.php` and `create_payment.php`. Needs verification/fix if this feature is live. | #### Loan Skip Requests | Item | Value | |---|---| | API file | `frontend/src/components/loan/loan_hooks/useLoanSkipAPI.jsx` | | Purpose | Loan skip request create/read/update/delete. | | Backend endpoints | `/loan_skip_request_api/read_skip_request.php`, `/create_skip_request.php`, `/update_skip_request.php`, `/delete_skip_request.php` | | Brand behavior | Backend endpoints include `brand_context.php`. | #### Utilities | Item | Value | |---|---| | File | `frontend/src/components/utilities/utilities.jsx` | | Dashboard file | `frontend/src/components/utilities/utilities_DashboardDesign/util_dashboard_v2.jsx` | | Route | `/utilitiesdashboard` | | Purpose | Utility/settings module launcher. | | Modules | Leave Type, Overtime, Holidays, Leave Balances, Work Time Settings, Admin Account Settings. | | Permissions | Uses permissions from `usePermissions`. Admin Account Settings is tied to manage-user/user-role permissions in the utility dashboard. Needs verification for exact condition. | #### User Management | Item | Value | |---|---| | Files | `frontend/src/users/*` | | Routes | `/users`, `/usersDashboard`, `/menu-access`, `/role-manager` | | Purpose | User accounts, access settings, menu access, role management. | | Main endpoints | `/users/users.php`, `/users/update_users.php`, `/users/get_user_access.php`, `/users/update_user_access.php`, `/users/update_access.php`, `/users/permissions.php`, `/users/menu/getMenuAccess.php`, `/users/menu/getAllUsers.php`, `/users/menu/update_menu_access.php`, `/user_role_lists/*` | | Permissions | `manage_users_access`, `user_role_management`, and menu access fields. | | Brand behavior | User/admin permission tables are mostly global. Needs review for multi-brand admin visibility behavior. | #### Admin Account Settings | Item | Value | |---|---| | File | `frontend/src/components/utilities/admin_settings/AdminAccountSettings.jsx` | | Route | `/AdminAccountSettings` | | Purpose | Lists admin/user accounts and allows password changes for admin accounts according to backend security rules. | | Endpoints | `/admin_account_settings/read_admin_accounts.php`, `/admin_account_settings/update_admin_password.php` | | Permissions | UI entry is under utilities and should be visible only through admin/manage-user permissions. Backend also enforces role rules. | | Brand behavior | Listing and update use `user_brand_access` and `brands` to determine manageable accounts. Admin/system accounts are listed from `users`, not from `employees`. | ## 3. Backend Architecture ### Backend Core Files | File | Purpose | |---|---| | `backend/server/connection.php` | Database connection. Points the application at `solidmark_db`. Credentials are intentionally not documented here. | | `backend/server/cors.php` | Central CORS handler. Allows brand/company headers and handles preflight. | | `backend/config/brand_context.php` | Resolves requested brand from headers/domains and returns `$brand_context`. | | `backend/login.php` | Authenticates users and returns session/access/brand data. | ### CORS File: - `backend/server/cors.php` Behavior: - Allows `GET`, `POST`, `PUT`, `DELETE`, and `OPTIONS`. - Allows `Content-Type`, `Authorization`, `X-Requested-With`, `X-Company-Code`, and `X-Brand-Code`. - If `HTTP_ACCESS_CONTROL_REQUEST_HEADERS` is present, it echoes the requested headers to make preflight more future-proof. - Returns HTTP 204 and exits for `OPTIONS`. ### Brand Context File: - `backend/config/brand_context.php` Brand code resolution priority: 1. `X-Brand-Code` request header. 2. `HTTP_ORIGIN` or `HTTP_REFERER` host: - `solidmarkinc.centraljuan.com` maps to `SOLIDMARK_INC`. - `solidmarkwest.centraljuan.com` maps to `SOLIDMARK_WEST`. 3. `HTTP_HOST`: - `solidmarkinc.centraljuan.com` maps to `SOLIDMARK_INC`. - `solidmarkwest.centraljuan.com` maps to `SOLIDMARK_WEST`. 4. Local fallback to `SOLIDMARK_INC`. Database lookup: - Uses `brands.brand_code`. - Joins `companies`. - Produces `$brand_context` with: - `company_id` - `brand_id` - `company_code` - `brand_code` - `brand_name` Important: endpoint code should use `$brand_context['company_id']` and `$brand_context['brand_id']` for filtering and inserts. ### Login Endpoint File: - `backend/login.php` Purpose: - Authenticates username/password from the `users` table. - Resolves brand access after password validation. - Returns user info, role, permissions/access, allowed brands, and current brand. Password behavior: - Uses `hash('sha256', $password)` / SHA-256 style validation against `users.password`. - Password values and hashes are not documented here. Employee login behavior: - If `LOWER(users.role) = 'employee'`, backend does not require `user_brand_access`. - Loads employee profile by `employees.employee_id = users.username`. - Joins `brands` and `companies`. - Requires employee profile to exist and be active. - Requires employee company/brand to match `$brand_context`. - Returns one allowed brand: the employee's own brand. Admin/staff login behavior: - Uses `users.user_id -> user_brand_access.user_id -> brands`. - Does not depend on `employees` for admin/staff brand access. - Single-brand users are automatically selected into their only allowed brand. - Multi-brand users use the requested brand if allowed, otherwise default brand, otherwise first allowed brand. Response includes: - `success` - `user` - `allowed_brands` - `current_brand` - `selectedBrandCode` - access/menu-related data used by frontend ### User Access and Role Endpoints Main folders: - `backend/users/` - `backend/users/menu/` - `backend/user_role_lists/` Purpose: - Manage users and permissions. - Return menu access. - Return role list. - Update `user_access` and `user_menu_access`. Tables touched: - `users` - `user_access` - `user_menu_access` - `user_role_list` - Optional `employees` with `LEFT JOIN` for display details Brand behavior: - Most user access endpoints are global and do not include `brand_context.php`. - This may be intentional for global admin/user management, but multi-brand visibility still needs review. Security notes: - `users.role` should be considered canonical for backend security. - Role list table is only a list of role names. It should not be treated as brand access. - Any endpoint updating `users.password` must enforce backend role rules. ### Employee Endpoints Main folder: - `backend/employeesSide/` Important files: | Endpoint | Purpose | Brand Context | Tables | |---|---|---:|---| | `employees.php` | List/count/search employees | Yes | `employees`, related employee tables | | `add_employee.php` | Create employee | Yes | `employees`, email/reset-related fields | | `update_employee.php` | Update employee and create/sync user account when needed | Yes | `employees`, `users` | | `delete_employee.php` | Delete employee | Yes | `employees` | | `bulk_update_employees.php` | Bulk update employees | Yes | `employees` | | `update_employee_status.php` | Update status | Yes | `employees` | | `generate_employee_id.php` | Generate employee ID | Yes | `employees` | | `get_employee.php` | Get one employee | Yes | `employees` | | `request_reset.php` | Request employee reset | Yes | `employees` | | `verify_code.php` | Verify reset code | Yes | `employees` | | `reset_password.php` | Reset employee password field | Yes | `employees` | | `get_payroll_types.php` | Payroll type helper | No | Needs verification | | `send_employee_credentials.php` | Credential sending helper | No | Needs verification | | `update_user_id.php` | User ID helper | No | Needs verification | Security notes: - Core employee CRUD is brand-scoped. - `add_employee.php` appears to include mail configuration in source. Needs security review; do not expose secrets. - `update_employee.php` can create corresponding `users` rows for employee accounts. - `reset_password.php` updates `employees.password`; login currently validates `users.password`. Password sync behavior needs verification. ### Loan Endpoints Main folders: - `backend/loan_api/` - `backend/loan_journal_entry_api/` - `backend/loan_payment_history_api/` - `backend/loan_skip_request_api/` Core loan files: | Endpoint | Purpose | Method | Brand Context | Tables | |---|---|---|---:|---| | `loan_api/read_loans.php` | List/read loans by brand, loan ID, loan IDs, or employee | GET | Yes | `loans`, `loan_journal_entry` | | `loan_api/create_loan.php` | Create loan | POST | Yes | `loans`, `employees` | | `loan_api/update_loan.php` | Update loan | POST | Yes | `loans` | | `loan_api/delete_loan.php` | Delete loan | POST | Yes | `loans` | | `loan_api/get_loan_summary.php` | Payroll/editor loan summary | GET | Yes | `loans`, `loan_journal_entry` | | `loan_api/update_loan_summary.php` | Update loan summary/editor data | POST | Yes | `loans`, `loan_journal_entry` | | `loan_journal_entry_api/read_journal_entries.php` | Read journal entries | GET | Yes | `loan_journal_entry`, `loans` | | `loan_journal_entry_api/create_journal_entry.php` | Create journal entry | POST | Yes | `loan_journal_entry`, `loans` | | `loan_journal_entry_api/update_journal_entry.php` | Update journal entry | POST | Yes | `loan_journal_entry`, `loans` | | `loan_journal_entry_api/delete_journal_entry.php` | Delete journal entry | POST | Yes | `loan_journal_entry`, `loans` | | `loan_payment_history_api/*` | Loan payment history | Mixed | Yes | `loan_payment_history`, `loans` | | `loan_skip_request_api/*` | Loan skip requests | Mixed | Yes | `loan_skip_request`, `loans` | Loan security/brand rules: - `SELECT` from `loans` should filter by `company_id` and `brand_id`. - `INSERT` into `loans` should set `company_id` and `brand_id` from `$brand_context`. - `UPDATE` and `DELETE` on `loans` should scope by `loan_id`, `company_id`, and `brand_id`. - `INSERT` into `loan_journal_entry` should set company/brand from the matching loan when `loan_id` exists. - `SELECT` from `loan_journal_entry` should filter by company/brand. ### Payroll Endpoints Main folder: - `backend/payroll/` Important active endpoints: | Endpoint | Purpose | Brand Context | Notes | |---|---|---:|---| | `payroll.php` | Read live/finalized payroll data | Yes | Core payroll endpoint. | | `payroll_cycle_start.php` | Start payroll cycle and seed/recalculate live payroll | Yes | Scopes cycle/live cache by company/brand. | | `payroll_cycle_finalize.php` | Finalize payroll cycle | Yes | Scopes cycle/finalization by company/brand. | | `update_payroll.php` | Update payroll row | Yes | Brand-scoped. | | `save_loan_deduction.php` | Save loan deduction | Yes | Brand-scoped. | | `sync_loan_deductions.php` | Sync loan deductions from journal entries | Yes | Brand-scoped. | | `update_loan_deduction_applied.php` | Mark loan deduction applied | Yes | Brand-scoped. | | `update_contribution_override.php` | Update payroll contribution override | Yes | Brand-scoped. | | `payroll_summary.php` | Payroll summary | Yes | Needs verification for all query branches. | | `payroll_print_details.php` | Payslip/print details | Yes | Needs verification for finalized print scope. | | `delete_payroll.php` | Delete payroll | No | Needs brand-scope review. | | `check_existing_dates.php` | Check payroll dates | No | Needs brand-scope review. | | `get_allowances_for_payrolls.php` | Allowance helper | No | Needs brand-scope review. | | `get_retro_for_employee.php` | Retro helper | No | Needs brand-scope review. | | `apply_reward_journal.php` | Reward journal helper | No | Needs brand-scope review. | | `cancel_retro.php` | Cancel retro helper | No | Needs brand-scope review. | Loan deduction behavior: - Payroll loan deduction logic depends on `loan_journal_entry` credits. - `payroll.php` and related payroll loan endpoints use journal entries within payroll date ranges/cutoffs. - Brand scoping is required because payroll and loan records now live in one database. ### Attendance Endpoints Main folder: - `backend/attendance/` Purpose: - Attendance reads/writes. - Attendance summaries. - Attendance logs. - Late/credit calculations. Tables touched: - Attendance-related tables. - Employee/schedule data. Brand behavior: - Scanned attendance endpoints did not consistently include `brand_context.php`. - This is a critical review area if attendance data is brand-separated in the merged database. ### Utility and Settings Endpoints Examples: - `backend/overtime_settings/` - `backend/holiday/` - `backend/leave/` - `backend/leave_balance/` - `backend/work_time/` - `backend/schedule-manager/` - `backend/admin_account_settings/` Brand behavior: - Needs endpoint-by-endpoint verification. - Admin Account Settings uses users/user_brand_access/brands and should not require `employees` for admin/system accounts. ### Admin Account Settings Endpoints Folder: - `backend/admin_account_settings/` #### `read_admin_accounts.php` Purpose: - Lists admin/system accounts from `users`. - Uses `user_brand_access` and `brands` to show allowed brands. - Uses `LEFT JOIN employees` only for optional employee name/details. - Does not return password hashes. Expected response: - `success` - `accounts[]` - Each account: - `user_id` - `username` - `role` - `status` - `allowed_brands` - `employee_name` if available Security: - Actor must be an active admin user. - Main admin with both brands can see accounts from both brands. - Single-brand admin can see accounts sharing its allowed brand. #### `update_admin_password.php` Purpose: - Changes passwords in `users.password`. - Does not touch `employees`. Expected request: ```json { "current_username": "actor_username", "target_username": "target_username", "new_password": "new_password" } ``` Security behavior: - Loads actor and target from `users`. - Uses `users.role` as canonical role. - Only `ADMIN` can change `ADMIN` account passwords. - `HR` can only change employee passwords. - Non-ADMIN/non-HR users are blocked. - Non-employee target accounts require a common brand between actor and target through `user_brand_access`. - Employee target accounts must match the request brand from `brand_context`. Needs verification: - The endpoint falls back to `current_username` from request if no server session username exists. This follows the current app pattern, but a stronger server-side session or token identity should be added later. ## 4. Authentication and Session Flow ### Login Flow 1. Frontend login form collects username and password. 2. Frontend determines a requested brand: - Forced production domain if applicable. - Local selected brand if valid. - Local fallback to `SOLIDMARK_INC`. 3. Frontend sends: - `X-Company-Code` - `X-Brand-Code` 4. Backend `login.php` authenticates username/password against `users`. 5. After password is valid, backend resolves allowed/current brand. 6. Backend returns: - User details - Role - Access/permissions - `allowed_brands` - `current_brand` - `selectedBrandCode` 7. Frontend stores: - `localStorage.user` - `localStorage.allowed_brands` - `localStorage.current_brand` - `localStorage.selectedBrandCode` ### Role Handling - `users.role` is the canonical backend role. - Frontend normalizes roles to uppercase for routing. - `user_role_list` is a role name list only. It is not brand access. ### Admin Account Behavior Expected: - `admin` can log in and access both assigned brands. - `admin_inc` can log in to its assigned brand only. - `admin_west` can log in to its assigned brand only. - Single-brand admin accounts should be logged into their one allowed brand even if stale localStorage requests another brand. - Multi-brand admin accounts can switch brand through `BrandSwitcher`. ### Employee Login Behavior Employee accounts differ from admin/staff accounts: - Employees do not require `user_brand_access`. - Employee brand comes from `employees.company_id` and `employees.brand_id`. - Employee profile is looked up by `employees.employee_id = users.username`. - Employee login is allowed only when the requested backend brand context matches the employee profile brand. - Employee response should include one allowed brand and that same current brand. ## 5. Brand Filtering Flow Step-by-step: 1. Frontend determines requested brand: - Forced by domain for production domains. - Selected in `BrandSwitcher` for multi-brand users. - Local fallback during development. 2. Frontend sends headers: - `X-Company-Code: SOLIDMARK` - `X-Brand-Code: selected brand code` 3. Backend includes `backend/config/brand_context.php`. 4. `brand_context.php` resolves brand code and company code using headers/domains. 5. `brand_context.php` queries `brands` and `companies`. 6. Backend receives `$brand_context`. 7. Endpoint SQL uses: - `$brand_context['company_id']` - `$brand_context['brand_id']` 8. Endpoint `SELECT`/list/count/search queries filter by `company_id` and `brand_id` when data is brand-specific. 9. Endpoint `INSERT` writes `company_id` and `brand_id` from `$brand_context`, not frontend payload. 10. Endpoint `UPDATE`/`DELETE` scopes by record ID plus `company_id` and `brand_id`. Multi-brand behavior: - Main admin can select between allowed brands. - Single-brand users are forced to their assigned brand by backend response. - Backend brand filtering is the source of truth. Frontend filtering is not enough for security. ## 6. Password and Account Management Rules Expected security rules: - `users.role` is canonical for security checks. - Only `ADMIN` can change `ADMIN` account passwords. - `HR` must not change `ADMIN`, `HR`, `IT ADMIN`, `MANAGER`, or `SUPERVISOR` passwords unless explicitly allowed later. - `HR` may change employee passwords only when backend policy allows it and brand checks pass. - Admin/system accounts may not exist in `employees`. - Admin/system account listing must use `users` as the main table. - Use `LEFT JOIN employees` only when optional employee details are needed. - Do not use `INNER JOIN employees` when listing admin/system users. - Employees can use `employee_id` as username. - Password hashes must not be returned by list endpoints. - Password update endpoints must identify actor and target from `users`. Observed password-related endpoints: | Endpoint | Password Field | Notes | |---|---|---| | `backend/login.php` | Reads `users.password` | Login source of truth. | | `backend/admin_account_settings/update_admin_password.php` | Updates `users.password` | Has role checks and brand checks. | | `backend/employeesSide/update_employee.php` | Can create/update employee user account | May create `users` row for employee account. | | `backend/employeesSide/reset_password.php` | Updates `employees.password` | Needs verification because login reads `users.password`. | Needs verification: - Employee password reset should update the same password source used by login, or the project should document why `employees.password` and `users.password` are intentionally separate. ## 7. Loan Module Documentation ### Loan ID Rule `loans.loan_id` is a `VARCHAR(100)`. Reason: - Legacy MDB loan IDs were imported. - Journal entries need to sync with legacy loan IDs. - `loan_journal_entry.loan_id` must match `loans.loan_id`. Do not: - Use `intval()` for `loan_id`. - Bind `loan_id` as integer in PHP. - `parseInt()` loan IDs in frontend. - Assume new loan IDs are auto-increment integers. Do: - Treat loan IDs as strings. - Bind loan IDs with `s` in mysqli. - Generate safe string loan IDs when creating new loans without a supplied ID. ### Loan Creation Endpoint: - `backend/loan_api/create_loan.php` Expected behavior: - Accept loan payload. - Verify employee belongs to current brand. - Use provided string `loan_id` when valid, or generate a safe string loan ID. - Insert `company_id` and `brand_id` from `$brand_context`. - Do not trust frontend company/brand IDs. ### Loan Journal Entries Endpoint group: - `backend/loan_journal_entry_api/` Expected behavior: - Journal entries should point to existing loans. - If `loan_id` exists, journal company/brand should come from the matching loan. - If no matching loan behavior is intentionally allowed, use `$brand_context`. - Reads should filter by `company_id` and `brand_id`. - Creates/updates/deletes should ensure the related loan belongs to current brand. ### Payroll Loan Deductions Payroll loan deductions depend on `loan_journal_entry` credits. Expected behavior: - Payroll reads unpaid or applied loan credits within payroll date cutoffs. - Payroll updates loan deduction status through brand-scoped endpoints. - Loan and journal records must have matching company/brand fields. Needs verification: - Some loan summary/editor files have older branches and comments. Confirm all active query branches filter by company/brand. - Frontend loan payment history endpoint names may not match backend filenames. ## 8. Current Known Critical Rules Follow these rules when continuing development: - Do not use `solidmarkinc_db` or `solidmarkwest` in live backend queries. - Use only `solidmark_db` as the active app database. - Do not assume admin users exist in `employees`. - Do not `INNER JOIN employees` when listing admin/system users. - Use `LEFT JOIN employees` only when optional employee display fields are needed. - Do not hardcode Inc/West brand IDs. - Resolve brand by `brand_code`, `company_code`, and `$brand_context`. - Do not let HR update ADMIN passwords. - Keep brand filtering in backend, not only frontend. - Do not trust frontend payload for `company_id` or `brand_id`. - For brand-specific data, scope reads/writes by `company_id` and `brand_id`. - Treat `loans.loan_id` as a string. - Keep RBAC/user access logic separate from brand access logic. ## 9. Testing Checklist ### Login Tests - `admin` can log in. - `admin_inc` can log in. - `admin_west` can log in. - Employee can log in using `employee_id`. - Single-brand users can log in even when localStorage has a stale `selectedBrandCode`. - Login response includes `allowed_brands`. - Login response includes `current_brand`. - Login response includes `selectedBrandCode`. - Login response does not include password hashes. ### Brand Tests - `admin` can switch and view both assigned brands. - `admin_inc` only sees its assigned brand data. - `admin_west` only sees its assigned brand data. - Employee only sees their own/brand-related data where applicable. - Switching brand updates `selectedBrandCode`. - Switching brand updates `AccountBrandBadge`. - Forced production domains request the correct brand code. - Localhost allows selected brand, but backend has final decision. ### Module Tests - Employees page: - List employees by brand. - Add employee and verify company/brand IDs. - Update employee only in current brand. - Delete/status update only in current brand. - Loans page: - List loans by brand. - Create loan with string loan ID. - Update/delete loan scoped to current brand. - Loan Journal page: - Create debit/credit entry. - Confirm journal entry brand matches loan brand. - Confirm entries do not cross brands. - Payroll list: - Start cycle by brand. - Confirm live payroll rows are brand-scoped. - Finalize by brand. - Confirm loan deductions come from matching brand journal entries. - Payslip: - Print/read payslip for current brand. - Confirm finalized mode is brand-safe. Needs verification. - Admin Account Settings: - Main admin sees `admin`, `admin_inc`, and `admin_west`. - Single-brand admin only sees accounts sharing its brand. - HR cannot change admin passwords. - ADMIN can change admin passwords. - Password hashes are not returned in list responses. - Change password: - Backend rejects new passwords shorter than 6 characters. - Backend returns 403 for blocked actor/target role combinations. ### Network Tests In browser DevTools, verify backend requests include: - `X-Company-Code` - `X-Brand-Code` - `Content-Type` where needed - `Authorization` if authentication token is introduced later Preflight tests: - `OPTIONS` requests return 204. - `Access-Control-Allow-Headers` includes requested headers. - `x-company-code` is accepted. - `x-brand-code` is accepted. Brand context test: - `backend/test_brand_context.php` was not found in this workspace during this documentation pass. - If recreated, it should include `backend/config/brand_context.php`, return `SELECT DATABASE()`, request headers, host/origin/referer, and `$brand_context`. ### Database Verification Queries Do not copy password values or password hashes from query results into tickets/docs. Brands: ```sql SELECT brand_id, company_id, brand_code, brand_name FROM brands ORDER BY company_id, brand_code; ``` Companies: ```sql SELECT company_id, company_code, company_name FROM companies ORDER BY company_id; ``` Admin accounts and brand access: ```sql SELECT u.user_id, u.username, u.role, u.status, b.brand_code, b.brand_name, uba.is_default FROM users u LEFT JOIN user_brand_access uba ON uba.user_id = u.user_id LEFT JOIN brands b ON b.brand_id = uba.brand_id WHERE u.username IN ('admin', 'admin_inc', 'admin_west') ORDER BY u.username, b.brand_code; ``` All user brand access: ```sql SELECT u.username, u.role, u.status, c.company_code, b.brand_code, b.brand_name, uba.is_default FROM user_brand_access uba INNER JOIN users u ON u.user_id = uba.user_id INNER JOIN brands b ON b.brand_id = uba.brand_id INNER JOIN companies c ON c.company_id = b.company_id ORDER BY u.username, b.brand_code; ``` Employees by brand: ```sql SELECT e.employee_id, e.last_name, e.first_name, e.status, c.company_code, b.brand_code, b.brand_name FROM employees e INNER JOIN brands b ON b.brand_id = e.brand_id INNER JOIN companies c ON c.company_id = e.company_id ORDER BY b.brand_code, e.employee_id; ``` Loans by brand: ```sql SELECT l.loan_id, l.employee_id, l.status, l.balance, c.company_code, b.brand_code FROM loans l INNER JOIN brands b ON b.brand_id = l.brand_id INNER JOIN companies c ON c.company_id = l.company_id ORDER BY b.brand_code, l.loan_id; ``` Loan journal entries by brand: ```sql SELECT lje.journal_id, lje.loan_id, lje.employee_id, lje.entry_type, lje.amount, lje.entry_date, c.company_code, b.brand_code FROM loan_journal_entry lje INNER JOIN brands b ON b.brand_id = lje.brand_id INNER JOIN companies c ON c.company_id = lje.company_id ORDER BY b.brand_code, lje.entry_date DESC, lje.journal_id DESC; ``` Loan journal totals by brand: ```sql SELECT b.brand_code, lje.entry_type, COUNT(*) AS entry_count, SUM(lje.amount) AS total_amount FROM loan_journal_entry lje INNER JOIN brands b ON b.brand_id = lje.brand_id GROUP BY b.brand_code, lje.entry_type ORDER BY b.brand_code, lje.entry_type; ``` Password hash presence check without exposing hashes: ```sql SELECT username, role, status, CHAR_LENGTH(password) AS password_hash_length FROM users WHERE username IN ('admin', 'admin_inc', 'admin_west') ORDER BY username; ``` Employee account password field length check: ```sql SELECT e.employee_id, e.status, CHAR_LENGTH(e.password) AS employee_password_length, CHAR_LENGTH(u.password) AS user_password_hash_length FROM employees e LEFT JOIN users u ON u.username = e.employee_id ORDER BY e.employee_id; ``` Find users without employee rows: ```sql SELECT u.username, u.role, u.status FROM users u LEFT JOIN employees e ON e.employee_id = u.username WHERE e.employee_id IS NULL ORDER BY u.username; ``` This query should include admin/system accounts. That is expected and not an error. ## 10. Endpoint Audit Tables ### Frontend Page or Route to Backend Endpoints | Frontend Route/Page | Frontend File | Backend Endpoint(s) | Brand Headers | |---|---|---|---| | `/login` | `frontend/src/authentication/login.jsx` | `backend/login.php` | Yes | | `/employees` | `frontend/src/components/employees/employees.jsx` | `employeesSide/employees.php`, `add_employee.php`, `update_employee.php`, `delete_employee.php`, `update_employee_status.php`, `bulk_update_employees.php` | Yes through Axios/global fetch | | `/attendance` | `frontend/src/components/attendance/*` | `attendance/*` | Headers yes, backend scope needs review | | `/attendanceRecord` | `frontend/src/components/DTRattenance/*` | `attendance/*`, `employeesSide/*`, `schedule-manager/*` | Mixed | | `/payroll-page` | `frontend/src/payrollPage/payrollpage/PayrollPage.jsx` | `payroll/payroll.php`, `payroll_cycle_start.php`, `payroll_cycle_finalize.php`, `save_loan_deduction.php`, `sync_loan_deductions.php`, etc. | Yes, some backend endpoints need review | | Payroll print/payslip | `frontend/src/payrollPage/payrollComponents/*` | `payroll/payroll_print_details.php`, `payroll/payroll_summary.php` | Yes, finalized query scope needs verification | | `/LoanPage` | `frontend/src/components/loan/LoanPage.jsx` | `loan_api/*`, `loan_journal_entry_api/*`, `loan_skip_request_api/*` | Yes | | Loan payment history | `frontend/src/components/loan/loan_hooks/useLoanPaymentHistoryAPI.jsx` | `loan_payment_history_api/*` | Yes, filename mismatch needs verification | | `/utilitiesdashboard` | `frontend/src/components/utilities/utilities.jsx` | Various settings endpoints | Mixed | | `/users`, `/usersDashboard` | `frontend/src/users/*` | `users/*`, `users/menu/*`, `user_role_lists/*` | Headers yes, backend global behavior needs review | | `/AdminAccountSettings` | `frontend/src/components/utilities/admin_settings/AdminAccountSettings.jsx` | `admin_account_settings/read_admin_accounts.php`, `update_admin_password.php` | Yes | ### Backend Endpoint Group to DB Tables | Backend Group | Main Tables | Brand Scoped | Security Notes | |---|---|---:|---| | `login.php` | `users`, `employees`, `brands`, `companies`, `user_brand_access`, `user_access`, menu/access tables | Yes | Password first, then brand access. Employee and admin flows differ. | | `employeesSide/*` | `employees`, `users`, departments/positions/rate/history tables | Mostly yes | Core CRUD brand-scoped. Password sync needs verification. | | `loan_api/*` | `loans`, `loan_journal_entry`, `employees` | Yes | `loan_id` is string. Scope by company/brand. | | `loan_journal_entry_api/*` | `loan_journal_entry`, `loans` | Yes | Journal brand should match loan brand. | | `loan_payment_history_api/*` | `loan_payment_history`, `loans` | Yes | Frontend/backend filename matching needs verification. | | `loan_skip_request_api/*` | `loan_skip_request`, `loans` | Yes | Scope through current brand and related loan. | | `payroll/*` | Payroll tables, employees, loans, loan journal entries, contribution tables | Mixed | Core payroll cycle endpoints scoped. Helper endpoints need review. | | `attendance/*` | Attendance tables, employees, schedules | Needs review | Brand context not consistently included. | | `users/*` | `users`, `user_access`, optional `employees` | Mostly global | Should not expose hashes. Global behavior needs brand policy review. | | `users/menu/*` | `user_menu_access`, `users` | Mostly global | Menu permissions are RBAC, not brand access. | | `user_role_lists/*` | `user_role_list` | Global | Role list is not brand access. | | `admin_account_settings/*` | `users`, `user_brand_access`, `brands`, optional `employees` | Yes for account visibility/management | Uses users as main table. Password update has role checks. | ### Backend Brand Scope Notes | Endpoint/File | Brand Scoped | Notes | |---|---:|---| | `backend/config/brand_context.php` | Core resolver | Resolves company/brand context. | | `backend/login.php` | Yes | Employee/admin flows both use brand context. | | `backend/employeesSide/employees.php` | Yes | List/count/search by brand. | | `backend/employeesSide/add_employee.php` | Yes | Inserts current company/brand. | | `backend/employeesSide/update_employee.php` | Yes | Updates scoped employee. | | `backend/loan_api/read_loans.php` | Yes | Filters loans by company/brand. | | `backend/loan_api/create_loan.php` | Yes | Inserts company/brand from context. | | `backend/loan_api/update_loan.php` | Yes | Scopes by loan ID and company/brand. | | `backend/loan_api/delete_loan.php` | Yes | Scopes by loan ID and company/brand. | | `backend/loan_journal_entry_api/*` | Yes | Scopes journal entries by company/brand. | | `backend/payroll/payroll.php` | Yes | Core payroll read endpoint. | | `backend/payroll/payroll_cycle_start.php` | Yes | Cycle creation/recalculation scoped. | | `backend/payroll/payroll_cycle_finalize.php` | Yes | Finalization scoped. | | `backend/payroll/delete_payroll.php` | No | Needs review. | | `backend/payroll/check_existing_dates.php` | No | Needs review. | | `backend/attendance/*` | No or mixed | Needs review. | | `backend/users/*` | Mostly no | May be global by design, but needs brand policy review. | ## 11. Risks and Items To Review ### High Priority 1. Attendance endpoints need brand scoping review. - Scanned files in `backend/attendance/` do not consistently include `brand_context.php`. - In a merged database, attendance reads/writes can leak across brands if not scoped. 2. Payroll helper endpoints need brand scoping review. - Examples: `delete_payroll.php`, `check_existing_dates.php`, `get_allowances_for_payrolls.php`, `get_retro_for_employee.php`, `apply_reward_journal.php`, `cancel_retro.php`. - Core payroll cycle endpoints are better scoped, but helper endpoints can still create cross-brand issues. 3. Employee password reset source needs verification. - Login checks `users.password`. - `employeesSide/reset_password.php` updates `employees.password`. - Confirm whether a trigger or other backend logic syncs these, or update reset flow later. 4. Password actor identity should be hardened. - `update_admin_password.php` prefers session username, then falls back to request `current_username`. - This matches current app constraints, but a real server session/JWT identity should eventually be required. 5. Secret-like configuration in source needs security review. - Some email sending code appears to contain mail configuration. - Do not copy secrets into docs, tickets, or logs. ### Medium Priority 6. Loan payment history frontend/backend filename mismatch needs verification. - Frontend hook appears to call shorter names. - Backend files use names like `read_loan_payment.php`. 7. Payroll print/finalized scope needs verification. - `payroll_print_details.php` includes brand context. - Confirm all finalized payroll branches are scoped to current company/brand. 8. Payroll summary main query needs verification. - `payroll_summary.php` includes brand context. - Confirm every query branch includes company/brand filters. 9. User management multi-brand policy needs review. - User/access endpoints are mostly global. - This may be intended for super-admin workflows, but should be documented as a policy. 10. Dynamic SQL in menu access update should be reviewed. - `users/menu/update_menu_access.php` should be checked for prepared statement safety and field allow-listing. 11. Frontend hardcoded valid brand code list should be reviewed. - Current brand code list is practical. - Longer term, consider loading valid brand codes from backend or deriving from login response. 12. Old/copy PHP files in backend should be archived or removed after review. - Some folders contain copy/backup variants. - They can confuse audits if accidentally deployed or called. ### Lower Priority 13. `backend/server/config.js` contains old commented URLs. - Not live SQL risk. - Clean later to avoid confusion. 14. `backend/test_brand_context.php` is not present in this workspace. - Recreate if needed for manual brand context testing. 15. Some employee helper endpoints do not include brand context. - Examples: `get_payroll_types.php`, `send_employee_credentials.php`, `update_user_id.php`. - Confirm whether they are global helpers or should be scoped. ## 12. Final Summary ### Already Safer - Active backend connection uses centralized `solidmark_db`. - Central CORS handler allows brand/company headers and handles preflight. - `brand_context.php` resolves company/brand from headers/domains and database records. - Login now separates employee brand logic from admin/staff brand access logic. - Admin/staff brand access uses `users.user_id -> user_brand_access -> brands`. - Employee login uses employee profile brand and does not require `user_brand_access`. - Core employee endpoints are brand-scoped. - Core loan and loan journal endpoints are brand-scoped. - `loans.loan_id` is treated as a string in the patched backend loan flow. - Admin Account Settings uses `users` as the main table and does not require admin/system accounts to exist in `employees`. - Admin password updates use backend role checks and block HR from changing ADMIN passwords. - Frontend Axios and global fetch wrapper send `X-Company-Code` and `X-Brand-Code`. - Frontend stores backend-selected brand after login. - Logout clears stale brand/session data. ### Still Needs Review - Attendance endpoint brand scoping. - Payroll helper endpoint brand scoping. - Employee reset password synchronization with `users.password`. - Stronger authenticated actor identity for password updates. - Loan payment history frontend/backend endpoint filename alignment. - Payroll summary and finalized payslip brand scope. - User/access management brand visibility policy. - Secret-like mail configuration in source. - Old/copy backend files that could confuse deployment or audits. ### Recommended Next Fixes in Priority Order 1. Patch and test attendance endpoints with `brand_context.php` and company/brand filters. 2. Patch payroll helper endpoints that currently lack brand context. 3. Fix employee password reset so it updates the login password source, or clearly document the intended split. 4. Harden password update identity using a real server session or signed token. 5. Verify loan payment history endpoint filenames and update frontend/backend to match. 6. Audit payroll summary and finalized payslip queries for company/brand scope. 7. Define a policy for whether user management is global or brand-limited, then enforce it backend-side. 8. Move any mail credentials/secrets out of source and into environment configuration. 9. Archive or remove old/copy backend files after confirming they are not routed or deployed. 10. Add a small brand context test endpoint for local and production header/domain verification.