# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

EvaTrack is a Laravel 6 monolith for an import/cargo-forwarding business that moves
goods from China to Indonesia (sea/air freight). It serves a web admin/back-office UI
(Blade) plus several mobile/external JSON APIs (customer app, driver app, warehouse
app, tracking). The domain language is Indonesian — expect terms like *gudang*
(warehouse), *resi/receipt* (waybill), *coload* (consolidated shipment), *ekspedisi*
(courier/expedition), *pengeluaran* (expense), *invoice/penagihan* (billing),
*marking* (customer shipment mark), *stuffing/loading plan* (container loading).

## Stack & Versions

- PHP `^7.2`, Laravel `^6.2` (note: Models live in `App\Model`, **not** `App\Models`).
- MySQL (default connection), DBAL 2.12 for schema changes.
- Auth: session guard (`web`) for the back-office, **Laravel Passport** (`api` guard,
  driver `passport`) for mobile/API clients. `User` uses `HasApiTokens` and
  `$guarded` (not `$fillable`).
- Frontend: Blade + jQuery + Laravel Mix (Webpack 4), Bootstrap-style SCSS.
- Notable packages: `maatwebsite/excel` (exports/imports), `barryvdh/laravel-dompdf`
  + `dompdf` (PDFs — invoices, barcodes), `milon/barcode` + `simplesoftwareio/simple-qrcode`,
  `yajra/laravel-datatables-oracle` (server-side DataTables), `laravel/telescope`,
  `darkaonline/l5-swagger` (API docs), `edamov/pushok` + Firebase (push notifications).

## Common Commands

```bash
# Install
composer install
npm install

# Frontend build (Laravel Mix)
npm run dev          # one-off development build
npm run watch        # rebuild on change
npm run prod         # minified production build

# App lifecycle
php artisan serve
php artisan migrate
php artisan key:generate

# Tests (PHPUnit 8 — runs against in-memory sqlite, see phpunit.xml)
vendor/bin/phpunit
vendor/bin/phpunit tests/Unit/InvoiceRedirectTest.php          # single file
vendor/bin/phpunit --filter it_releases_deleted_invoice         # single test
```

There is no PHP linter/formatter configured beyond `.styleci.yml` (StyleCI, runs in CI,
not locally). Local environment is Laragon on Windows.

## Architecture

This is a **fat-controller** codebase: ~57 controllers in `app/Http/Controllers/` and
~89 Eloquent models in `app/Model/`. There is little service-layer abstraction — most
business logic lives directly in controller methods and in the global helpers file.

### Two route surfaces
- `routes/web.php` — back-office UI. Almost everything sits inside
  `Route::group(['middleware' => ['auth']], ...)` and is further gated by **role**
  middleware. Each feature area pairs a page route with a `*-datatable` route that
  feeds Yajra DataTables via AJAX, plus `*-excel`/`download*` routes for exports.
- `routes/api.php` — mobile + external integrations. Public endpoints, then a
  `cors:api` group, then Passport-protected (`auth:api`) endpoints. API controllers
  live in `app/Http/Controllers/Api/` (separate controllers per client:
  `MobileApiController` customer, `MobileDriverApiController` driver,
  `MobileGudangApiController` warehouse, `TrackingApiController`, `WebAppApiController`).

### Role-based authorization
Authorization is done with the custom `role` middleware (`App\Http\Middleware\CekRole`,
aliased as `role` in `app/Http/Kernel.php`), used as `role:1,2,8,9` — numeric role IDs
stored on `users.role`. There is **no enum**; role numbers are scattered across
`routes/web.php` group declarations (e.g. 1=admin, plus many department/branch roles
like 41/42/43, 51/52/53, 99). When adding a route, copy the role list from the
neighboring group for the same feature. `CekRole` also contains hardcoded per-user-ID
exceptions (`$limitedInvoiceUserIds`, `$adminDataUserIds`) for narrow access grants —
follow that pattern only if a real special case demands it.

### Cross-cutting helpers
`app/Http/Helpers/Helpers.php` is autoloaded globally (composer `files` autoload) and
holds shared domain logic: number-to-Indonesian-words (`penyebut`), date formatting,
and bonus/commission/invoice/pricing calculations that pull in many models. Prefer
extending existing helpers over duplicating calculation logic in controllers.

### Excel & PDF generation
- Excel: `app/Exports/` (~27 classes) and `app/Imports/` (~5) using maatwebsite/excel.
- PDF: dompdf via `barryvdh/laravel-dompdf`. Custom fonts `simhei.ttf` /
  `BabelStoneHan.ttf` (CJK) live at repo root with `load_font.php` for Chinese text in
  invoices/labels.

### Views & navigation
Blade views in `resources/views/` are organized one folder per feature
(`invoice/`, `coload/`, `receipt/`, `delivery/`, `loadingplan/`, etc.) extending
`layout.blade.php`. The sidebar/navbar is populated by
`app/Http/ViewComposers/NavbarComposer.php` (bound globally) — update it when adding a
top-level menu.

### External integrations (configured via .env)
SearRates container tracking (`URL_API_KAPAL`), WooNotif WhatsApp gateway (separate
API keys per brand: *antara* / *east*), Firebase + APNs (pushok) for mobile push,
IMAP mailbox polling, Qiscus chat, Google Maps API. Email-trigger routes
(`/penerimaanmte`, `/invoiceeast`, etc. → `Email` controller) send templated
transactional mail per brand (mte / east / antara / revisi).

## Conventions & Gotchas

- **Models namespace is `App\Model`** (singular). Don't generate `App\Models`.
- Invoices use an **idempotency key** (`invoices.idempotency_key`, unique) to prevent
  duplicate creation; soft-deletes are in play and the key is released on delete. See
  `InvoiceController::redirectToExistingInvoice` / `releaseDeletedInvoiceIdempotencyKey`
  and `tests/Unit/InvoiceRedirectTest.php` for the expected behavior.
- Tests run on **sqlite `:memory:`** and often build schema inline via `Schema::create`
  rather than running real migrations — follow that style for fast unit tests of
  controller methods (instantiated directly / via `ReflectionMethod`).
- Many "actions" are plain `GET` routes at the top of `web.php` (e.g.
  `/update-container-status`, `/sinkroninvoice`, `crontier`) used as cron/manual
  triggers rather than scheduled commands — `app/Console/Kernel.php` schedule is empty.
- Branch model: default branch is `dev`, current production branch is `prod_live`.
  PRs usually target `dev`.
