# LMI ERP — Claude Code Instructions

## Project Overview

LMI ERP is an ERP system for a **truck distributor company** that handles both
vehicle distribution and aftersales service.

**Stack:** Laravel 13 (PHP 8.4) + Inertia.js v3 + Vue 3 + shadcn-vue (reka-ui,
"new-york-v4" style) + Tailwind v4. Modules are managed with
`nwidart/laravel-modules`. Auth/permissions via `laravel/fortify` +
`spatie/laravel-permission`. Frontend routes/actions are generated by
`laravel/wayfinder` (do not hand-write files under `resources/js/routes/` or
`resources/js/actions/` — they're generated from PHP routes/controllers, see
below).

The system is built as a set of modules, internally called **"apps"**:

- **CRM** — Customer Relationship Management (also covers the full vehicle
  sales pipeline: lead → quotation → sales contract → purchase order →
  shipment → receiving → BAST → STNK submission)
- **SMS** — Service Management System (aftersales/repair service tracking —
  **not** SMS text messaging. Never assume "SMS" means text messages in this
  codebase.)
- **IMS** — Inventory Management System

More apps/modules may be added over time. When adding a new one, follow the
same structural pattern as CRM (the most fleshed-out module).

## Repo Structure

**Backend — split between core app and modules:**

- `app/` — shared/core code used across every module: `Models/` (e.g.
  `User`, `SalesGroup`, cross-cutting `Approval*` models), `Services/`
  (e.g. `ApprovalEngine`), `Contracts/` (e.g. `ApprovalHandler`), `Traits/`
  (e.g. `HasApprovals`, `HasEncodedIds`, `HasUserstamps`), `Enums/`.
- `Modules/{CRM,SMS,IMS}/app/` — module-specific code, same shape as `app/`:
  `Http/Controllers`, `Http/Requests`, `Models/`, `Services/`, `Enums/`,
  `Providers/`.
- `Modules/{App}/database/{migrations,factories,seeders}/` — module-owned
  schema.
- `Modules/{App}/routes/{web,api}.php` — module routes, grouped under a
  lowercase prefix (`crm`, `ims`, `sms`) and a matching named-route prefix
  (`crm.*`) and route middleware (e.g. `->middleware('crm')`). Permission
  checks are applied per-route with `->middleware('can:leads.view')` etc.
  (spatie/laravel-permission).
- Note: `Modules/{App}/resources/assets/js/app.js` exists from the
  laravel-modules scaffold but is **not** where the real frontend lives — see
  below.

**Frontend — Vue pages live at the app root, not inside `Modules/`:**

- `resources/js/pages/{CRM,SMS,IMS}/{feature}/` — Inertia page components,
  e.g. `resources/js/pages/CRM/quotation/Quotation.vue` (list) and
  `Form.vue` (create/edit), with feature-local subfolders like
  `item/components/`, `related-sales-contract/`.
- `resources/js/pages/Admin/` — admin-only pages (users, roles, permissions,
  applications, approval policies, the shared approval inbox).
- `resources/js/components/ui/` — shadcn-vue primitives (button, dialog,
  table, form, select, etc.). Always check here before adding a new UI
  primitive — shadcn-vue components are added via its CLI, not written by
  hand.
- `resources/js/components/` (non-`ui/`) — shared app-level components:
  `DataTable.vue` / `DataTableServer.vue` (see below), `FormFloatingActions.vue`,
  `ApprovalTimeline.vue`, `FileUploader.vue`, `MetaData.vue`, `Section.vue`.
- `resources/js/layouts/{app,auth,main,settings}/` — Inertia layouts.
- `resources/js/composables/` — e.g. `usePermission`.
- `resources/js/routes/` and `resources/js/actions/` — **generated by
  laravel/wayfinder** from PHP routes/controllers. Never edit these by hand;
  regenerate via the normal Wayfinder/vite flow after changing PHP routes.
- Import alias `@/` → `resources/js/` (see `components.json`).

**Path/namespace quirk to know:** module PHP namespaces use a `Modules\{App}\app\...`
segment for `Models`/`Enums`/`Services` (e.g.
`Modules\CRM\app\Models\Quotation`, `Modules\CRM\app\Enums\QuotationStatusEnum`)
but _not_ for `Http\Controllers`/`Http\Requests` (e.g.
`Modules\CRM\Http\Controllers\QuotationController`). Match whichever sibling
file you're editing — don't "fix" the inconsistency.

## Code Consistency — Read Before Writing Code

This is the #1 priority for this project. The codebase currently has
inconsistent patterns from having grown without standardization, which makes
it harder to maintain. To avoid making this worse:

- **Before writing new code, look at how similar features are already
  implemented elsewhere in the app** (same module first, then other apps) and
  follow that existing pattern — naming, file structure, component style,
  service/repository/controller conventions, etc. For CRM specifically, the
  Quotation feature (`Modules/CRM/app/.../Quotation*`,
  `resources/js/pages/CRM/quotation/`) is the most complete reference
  implementation (list + form + items + approval + PDF export).
- **Do not introduce a new pattern or "better" approach on your own** just
  because it seems cleaner. Consistency with existing code takes priority over
  small individual improvements.
- If existing code has multiple conflicting patterns already, or you think a
  pattern really should change, **stop and ask** rather than picking one
  silently or refactoring unrelated code along the way.
- Don't rename, restructure, or "clean up" existing files as a side effect of
  an unrelated task unless asked.
- **List pages are being migrated to server-side pagination.** Newer/updated
  list pages use `DataTableServer.vue` (server-driven paging/sorting/filtering
  via Inertia requests); some older pages still use the client-side
  `DataTable.vue`. When touching a list page, check which one it currently
  uses and follow that — don't silently swap one for the other unless the
  task is specifically about migrating that page.
- **Status fields are modeled two ways at once** in this codebase: a DB-backed
  `*Status` model/table (e.g. `QuotationStatus`, referenced via `status_id`)
  _and_ a parallel PHP `*StatusEnum` (e.g. `QuotationStatusEnum`) used for the
  numeric IDs in code instead of magic numbers. Both exist for most CRM
  entities — use whichever the surrounding code already uses, don't try to
  collapse them into one.

## Commands

- Install frontend deps: `npm install` — Build frontend: `npm run build` —
  Dev frontend (watch): `npm run dev`
- Full dev environment (server + queue + vite, concurrently): `composer run dev`
- Lint PHP: `composer run lint` (Pint, auto-fix) / `composer run lint:check`
  (check only, no changes)
- Lint/format JS/TS: `npm run lint` / `npm run lint:check` (ESLint),
  `npm run format` / `npm run format:check` (Prettier)
- Type-check frontend: `npm run types:check` (vue-tsc)
- Run tests: `composer run test` (clears config, runs Pint check, then
  `php artisan test`) — or `php artisan test` directly for just Pest/PHPUnit.
  Tests live in `tests/{Unit,Feature}` (core) and
  `Modules/{App}/tests/{Unit,Feature}` (per module).
- Full CI check locally: `composer run ci:check`
- Clear caches: `php artisan optimize:clear` (or individually: `config:clear`,
  `cache:clear`, `view:clear`, `route:clear`)
- This project uses **Laravel Boost** (MCP tools). If Boost tools are
  available in-session, prefer them over raw shell equivalents:
  `database-query` for read-only DB queries instead of ad-hoc SQL/tinker,
  `database-schema` before writing migrations/models, `search-docs` before
  making framework/package changes, `browser-logs` for recent browser
  errors, `get-absolute-url` before sharing any URL with the user.

## Database Rules — Non-Negotiable

- **ALWAYS ask before running any migration**, every single time, even for
  something that seems small or safe. Never run `php artisan migrate` (or
  similar) without explicit confirmation for that specific instance.
- **NEVER clear, truncate, drop, or reset the database** or any table, in any
  environment — no `migrate:fresh`, `migrate:refresh`, `db:wipe`, manual
  truncates, or similar, under any circumstance, even if it seems like the
  fastest fix.
- If a task seems to require a migration or schema change, propose it and wait
  for explicit approval before running anything.

## The Approval System

Several CRM documents (Quotation, Sales Contract, Receiving, ...) go through a
shared multi-step approval workflow instead of each rolling its own. The
shared engine and inbox live in their own module, **`Modules/ApprovalSystem`**
(`Modules\ApprovalSystem\app\Services\ApprovalEngine`,
`Modules\ApprovalSystem\app\Contracts\ApprovalHandler`,
`Modules\ApprovalSystem\app\Traits\HasApprovals`, models `Approval`/`Approver`),
reachable at `/approval-system` — it's a real module like CRM/SMS/IMS but not a
truck-domain app, similar in spirit to Admin. The policy *configuration* side
(`App\Models\ApprovalPolicy`/`ApprovalPolicyStep`, the Admin > Approval
Policies UI) stays in core/ERP-Admin. Each business module only supplies a
small `*ApprovalHandler` class (see
`Modules/CRM/app/Services/ReceivingApprovalHandler.php`,
`SalesContractApprovalHandler.php`, `QuotationApprovalService.php`).

**When asked to wire a new menu into approvals, follow
[`docs/approval-system.md`](docs/approval-system.md) step by step** rather
than improvising — it documents the exact contract (handler methods, the
`menus.href`-based menu lookup, static vs. custom policy resolution, the
`pending_for_me` / `approval_steps` / `current_rejection_reason` Inertia props,
the `ApprovalTimeline.vue` + `FormFloatingActions` frontend wiring, and how to
verify it end-to-end). Do not invent a parallel approval mechanism for a new
menu just because it seems simpler for that one case.

## App-Specific Notes

- **CRM:** The largest and most mature module — covers the whole vehicle
  sales pipeline (lead → quotation → sales contract → purchase order →
  shipment → receiving → BAST → STNK submission), plus customer, inventory,
  truck specification, leasing, marketing request, and payment. Most existing
  patterns to copy from live here.
- **SMS (Service Management):** aftersales/repair service tracking. Smaller
  and less built-out than CRM as of now — when in doubt, still check CRM for
  the conventions to follow (route naming, controller shape, page structure),
  don't invent SMS-local conventions from scratch.
- **IMS:** Inventory Management. Also smaller/less built-out than CRM —
  same guidance as SMS.
- **Admin:** not a truck-domain "app" like CRM/SMS/IMS, but a real module of
  its own for cross-cutting system administration: users, roles/permissions,
  applications/menus, and approval policy configuration
  (`resources/js/pages/Admin/`).
- **Approval System:** likewise not a truck-domain app — a real module
  (`Modules/ApprovalSystem`) holding just the shared approval engine and the
  cross-app approval inbox (`resources/js/pages/ApprovalSystem/`), split out
  from Admin so it isn't coupled to one app. See "The Approval System" above.

## Things Claude Has Gotten Wrong Before

- clearing the database without asking first
- not reffering to old code
