# MyIndependent Worker Management — Design Spec

**Date:** 2026-07-23
**Company:** Independent Sdn Bhd (1387076-M) — Malaysian HR outsourcing / foreign-worker supply.

## Goal
A fast, multi-role web application to manage foreign workers: profiles, site assignment,
passport / permit / FOMEMA expiry & renewal tracking, document storage, an expiry-focused
dashboard, and Malaysian-format payroll with downloadable payslip PDFs.

## Stack
- **Laravel 13** (PHP 8.3), **PostgreSQL 14**
- **Argon Dashboard 2** look (Bootstrap 5, vendored CSS) — no starter kit; hand-rolled auth for full theme control
- Packages: `spatie/laravel-permission`, `barryvdh/laravel-dompdf`, `maatwebsite/excel`
- Brand: primary coral/orange-red `#f5533d` with the orange→red→pink→blue logo gradient for accents
- Logos: square `logo-i.png`, landscape `logo-main.png`, favicon `logo-j.png` (from myindependent.com.my)

## Roles (single login page → role-based redirect)
| Role | Access |
|---|---|
| **admin** | Everything incl. user management |
| **finance** | View all staff + documents; full payroll (create/finalize/generate payslips) |
| **supervisor** | Manage staff: CRUD, site assignment, documents, passport/permit/FOMEMA renewals. No payroll |
| **staff** | Own profile + own documents (read); download own payslips |

## Data Model
### users
Login accounts. `role` via spatie. Nullable `staff_id` links a staff login to its worker record.

### staff (main)
Fields (from masterlist + payslip needs):
`running_no` (col A, indexed), `worker_code` (col B), `site_ref` (col C),
`status` (active|resign|inactive), `name` (indexed + trigram), `passport_no` (unique, indexed + trigram),
`has_passport` (bool from `/`), `has_epass` (bool from `/`),
`passport_expiry` (date, idx), `permit_expiry` (date, idx), `fomema_expiry` (date, idx),
`site_id` (FK sites), `nationality_id` (FK), `basic_rate` (decimal), `pay_basis` (monthly|daily|hourly),
`gender` (male|female), `bank_id` (FK), `account_number`, `account_last4` (col Q),
`designation`, `department` (payslip fields, nullable),
`date_of_join` (date), `resign_date` (date, nullable), soft deletes, timestamps.

Indexes: `running_no`, `passport_no` (unique), `site_id`, `status`, `passport_expiry`,
`permit_expiry`, `fomema_expiry`, composite `(status, permit_expiry)`;
GIN trigram on `name` and `passport_no` for fast partial search.

### sites (from Branch), nationalities, banks — lookup tables
`sites`: name, code, client, address, active. `nationalities`: name (normalized). `banks`: name.

### renewals
One table for all three types. `staff_id`, `type` (passport|permit|fomema),
`previous_expiry`, `new_expiry`, `renewed_on`, `cost`, `reference_no`, `remarks`,
`document_path` (scan), `created_by`. Saving updates the matching current expiry on `staff`.

### documents
Per-staff uploaded files: `staff_id`, `type`, `title`, `path`, `expiry_date` (nullable), `uploaded_by`.
Stored in private storage; auth-gated download route.

### payslips (matches the real PDF layout)
Header: `staff_id`, `period_month`, `period_year`, snapshot of name/passport/designation/
department/branch/bank_ac/date_joined.
Earnings: `basic_salary`, `working_days`, `days_divisor` (default 26), `working_units`,
`al_amount`, `ul_amount`, `ot_hours`, `ot_days`, `ot_amount`,
`ph_hours`, `ph_days`, `ph_amount`, `total_earning`.
Deductions (fixed): `epf_employee`, `socso_employee`, `eis_employee`, `income_tax_pcb`,
`income_tax_cp38`, `unpaid_leave`, `advance`, `loan`.
`total_deductions`, `allowance_total`, `nett_pay`, `status` (draft|finalized),
`generated_by`, `pdf_path`, timestamps. Unique `(staff_id, period_month, period_year)`.

### payslip_lines
Free-form items: `payslip_id`, `category` (deduction|allowance), `label`, `amount`.
(e.g. Cleaning, Worker Doc Process, Uniform, Safety Shoe, Rehiring.)

## Modules
1. **Staff / Profile** — CRUD + unified profile page (all docs, expiries, site, payslips).
2. **Sites / Projects** — CRUD; assign staff; per-site roster.
3. **Passport** — current + renewal history.
4. **Permit** — current + renewal history (levy).
5. **FOMEMA** — record + expiry + renewal.
6. **Documents** — upload manager per staff.
7. **Dashboard** — count cards (active/resign/other); passports/permits/FOMEMA expiring in
   30/60/90 days (color-flagged); charts by nationality, site, status.
8. **Payroll** — finance selects staff → keys payslip → finalize → PDF (1–2/page, matches sample);
   staff downloads own.

## Import (`php artisan import:masterlist {file}`)
Idempotent (upsert by running_no/passport). Normalizes nationality (INDIAN→INDIA,
MALAYSIAN→MALAYSIA), parses dates, auto-creates Sites from Branch + Banks from BANK TYPE,
`/`→booleans, blank status→inactive. Prints report of skipped/ambiguous rows.

## Payslip PDF
Replicates the LANDMARK JUN 2026 format: square logo + `INDEPENDENT SDN BHD (1387076-M)`,
"Payslip For {MONTH YEAR}", two-column GROSS EARNING / DEDUCTIONS, ALLOWANCE box,
large NETT PAY, bottom summary strip, signature line. Zero statutory values print blank.

## Security & Performance
- Uploaded scans + payslip PDFs in private storage; download only via auth-gated routes
  (staff limited to their own).
- Trigram + B-tree indexes; pagination + eager loading throughout.
- Seed one admin account; staff logins created on demand.
