|
| 1 | +# ERP System — Claude Code Guide |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +Full-featured multi-tenant ERP built with **Laravel 13 + Inertia.js v2 + React 19 + TypeScript + Tailwind CSS v3**. |
| 6 | + |
| 7 | +The ERP application lives entirely under `erp/`. The repo root contains a legacy React Create App project — ignore it for ERP work. |
| 8 | + |
| 9 | +## Quick Start |
| 10 | + |
| 11 | +```bash |
| 12 | +cd erp |
| 13 | +composer install |
| 14 | +npm install |
| 15 | +cp .env.example .env |
| 16 | +php artisan key:generate |
| 17 | +php artisan migrate --seed |
| 18 | +npm run dev |
| 19 | +# In another terminal: |
| 20 | +php artisan serve |
| 21 | +``` |
| 22 | + |
| 23 | +## Architecture |
| 24 | + |
| 25 | +### Stack |
| 26 | + |
| 27 | +- **Backend**: Laravel 13, PHP 8.3, SQLite (dev/test), MySQL (prod) |
| 28 | +- **Frontend**: Inertia.js v2, React 19, TypeScript, Tailwind CSS v3 |
| 29 | +- **Auth**: Laravel Sanctum (API tokens) + Spatie Roles/Permissions |
| 30 | +- **Queue**: Database queue (`QUEUE_CONNECTION=database`) |
| 31 | +- **WebSockets**: Laravel Reverb + Laravel Echo |
| 32 | +- **PDF**: barryvdh/laravel-dompdf |
| 33 | +- **Excel**: maatwebsite/excel v3.1 |
| 34 | +- **Testing**: Pest v3 |
| 35 | + |
| 36 | +### Module Structure (35 modules) |
| 37 | + |
| 38 | +All modules live under `app/Modules/{Name}/`: |
| 39 | + |
| 40 | +``` |
| 41 | +app/Modules/ |
| 42 | +├── Core/ # Tenant model, BelongsToTenant trait |
| 43 | +├── Finance/ # Invoices, Bills, Contacts, Chart of Accounts |
| 44 | +├── Inventory/ # Products, Warehouses, Stock Movements, Transfers |
| 45 | +├── HR/ # Employees, Leave, Payroll |
| 46 | +├── CRM/ # Leads, Opportunities, Activities |
| 47 | +├── PM/ # Projects, Tasks, Milestones |
| 48 | +├── Purchase/ # Purchase Orders, RFQs, Vendors |
| 49 | +├── Accounting/ # Journal Entries, General Ledger |
| 50 | +├── Manufacturing/ # BOMs, Work Orders, Quality |
| 51 | +├── Maintenance/ # Assets, Work Orders |
| 52 | +├── Subscriptions/ # Plans, Subscriptions |
| 53 | +├── LiveChat/ # Channels, Sessions, Messages |
| 54 | +├── Discuss/ # Channels, Messages |
| 55 | +├── HelpDesk/ # Tickets, SLAs |
| 56 | +├── KnowledgeBase/ # Articles |
| 57 | +├── Survey/ # Surveys, Questions, Responses |
| 58 | +├── Timesheets/ # Entries |
| 59 | +├── Expenses/ # Claims |
| 60 | +├── Fleet/ # Vehicles, Trips |
| 61 | +├── Recruitment/ # Job Postings, Applications |
| 62 | +├── Training/ # Programs, Enrollments |
| 63 | +├── Events/ # Events, Registrations |
| 64 | +├── Subcontracting/ # Contracts |
| 65 | +├── FieldService/ # Work Orders |
| 66 | +├── Rental/ # Items, Bookings |
| 67 | +├── POS/ # Sessions, Orders |
| 68 | +└── ... |
| 69 | +``` |
| 70 | + |
| 71 | +### Multi-Tenancy Pattern |
| 72 | + |
| 73 | +Every module model uses the `BelongsToTenant` trait (`app/Traits/BelongsToTenant.php`): |
| 74 | + |
| 75 | +- Global scope auto-filters by `tenant_id` |
| 76 | +- Observer auto-sets `tenant_id` on create |
| 77 | +- Always call `app()->instance('tenant', $tenant)` in tests to set the active tenant |
| 78 | + |
| 79 | +### API Structure |
| 80 | + |
| 81 | +All REST endpoints at `/api/v1/*`. Base controller: `app/Http/Controllers/Api/V1/ApiController.php`. |
| 82 | + |
| 83 | +- `success($data)` — 200 with `{data: ...}` |
| 84 | +- `error($msg, $code)` — error response |
| 85 | +- `paginated($paginator)` — paginated response |
| 86 | + |
| 87 | +Auth: Bearer token via `withToken($token)` in tests. |
| 88 | + |
| 89 | +### Tenant Detection in Controllers |
| 90 | + |
| 91 | +```php |
| 92 | +$tenantId = app()->has('tenant') ? app('tenant')->id : $request->user()->tenant_id; |
| 93 | +``` |
| 94 | + |
| 95 | +### Broadcasting (WebSockets) |
| 96 | + |
| 97 | +Events in `app/Events/` implement `ShouldBroadcast`. Channels in `routes/channels.php`. |
| 98 | + |
| 99 | +- Live Chat: `private-chat-session.{id}` → `.NewChatMessage` |
| 100 | +- Discuss: `private-discuss-channel.{id}` → `.NewDiscussMessage` |
| 101 | +- Notifications: `private-tenant.{id}` → `.ErpNotification` |
| 102 | + |
| 103 | +Frontend hook: `useEchoPrivateChannel(channelName, event, handler)` in `resources/js/Hooks/useEchoChannel.ts`. |
| 104 | + |
| 105 | +### Key Conventions |
| 106 | + |
| 107 | +- Migrations always start with `Schema::dropIfExists('table')` before `Schema::create` |
| 108 | +- Event auto-discovery: Laravel 13 discovers listeners automatically — no manual EventServiceProvider needed |
| 109 | +- Use `broadcast(new Event())->toOthers()` to exclude the sender from WebSocket events |
| 110 | +- Rate limiting: 60 req/min on `/api/v1/*`, 10 req/min on auth endpoints |
| 111 | +- Audit logging: `LogsActivity` trait auto-logs created/updated/deleted on key models |
| 112 | +- Security headers: `SecurityHeaders` middleware appended globally |
| 113 | + |
| 114 | +## Testing |
| 115 | + |
| 116 | +```bash |
| 117 | +cd erp |
| 118 | +php artisan test # Run all tests |
| 119 | +php artisan test --filter "FinanceTest" # Filter by name |
| 120 | +php artisan test tests/Feature/Finance/ # Run a directory |
| 121 | +``` |
| 122 | + |
| 123 | +All tests use SQLite in-memory (`DB_CONNECTION=sqlite DB_DATABASE=:memory:`). `tests/Pest.php` applies `RefreshDatabase` globally. |
| 124 | + |
| 125 | +Test pattern: |
| 126 | + |
| 127 | +```php |
| 128 | +beforeEach(function () { |
| 129 | + $this->seed(RolePermissionSeeder::class); |
| 130 | + $this->tenant = Tenant::create(['name' => 'Test Co', 'slug' => 'test-co']); |
| 131 | + $this->user = User::factory()->create(['tenant_id' => $this->tenant->id]); |
| 132 | + $this->user->assignRole('super-admin'); |
| 133 | + $this->token = $this->user->createToken('test')->plainTextToken; |
| 134 | + app()->instance('tenant', $this->tenant); |
| 135 | +}); |
| 136 | +``` |
| 137 | + |
| 138 | +## Development Phases Completed |
| 139 | + |
| 140 | +| Phase | Description | Status | |
| 141 | +| ----- | ---------------------------------------------------------- | ------ | |
| 142 | +| 1–8 | Core modules, models, migrations, seeders, Inertia pages | ✅ | |
| 143 | +| 9 | REST API — 200+ endpoints across 40 modules | ✅ | |
| 144 | +| 10 | Demo data seeders for all 35 modules | ✅ | |
| 145 | +| 11 | WebSockets — Laravel Reverb + Echo | ✅ | |
| 146 | +| 12 | Queue jobs — invoice, low stock, payroll, bulk import | ✅ | |
| 147 | +| 13 | Mail notifications — invoice, low stock, payroll, approval | ✅ | |
| 148 | +| 14 | PDF generation — invoices, purchase orders, payslips | ✅ | |
| 149 | +| 15 | Import/Export — CSV/XLSX for products, contacts, invoices | ✅ | |
| 150 | +| 16 | Dashboard analytics — module stats + activity feed | ✅ | |
| 151 | +| 17 | Tenant isolation tests — 22 cross-tenant security tests | ✅ | |
| 152 | +| 18 | API rate limiting (60/min) + security headers | ✅ | |
| 153 | +| 19 | Global search — 7 modules, frontend component | ✅ | |
| 154 | +| 20 | Audit log — migration, trait, observer, API endpoint | ✅ | |
| 155 | +| 21 | GitHub Actions CI/CD — PHP tests + TS check + ESLint | ✅ | |
| 156 | +| 22 | Reports API — financial/inventory/HR + CLAUDE.md | ✅ | |
| 157 | +| 23 | In-app notifications — DB model, API, frontend bell | ✅ | |
| 158 | + |
| 159 | +## File Locations Reference |
| 160 | + |
| 161 | +| Concern | Path | |
| 162 | +| --------------------- | --------------------------------------- | |
| 163 | +| Module models | `app/Modules/{Name}/Models/` | |
| 164 | +| API controllers | `app/Http/Controllers/Api/V1/` | |
| 165 | +| Inertia pages | `resources/js/Pages/` | |
| 166 | +| Shared components | `resources/js/Components/` | |
| 167 | +| Layouts | `resources/js/Layouts/AppLayout.tsx` | |
| 168 | +| Routes (web) | `routes/web.php` | |
| 169 | +| Routes (api) | `routes/api.php` | |
| 170 | +| Broadcasting channels | `routes/channels.php` | |
| 171 | +| Migrations | `database/migrations/` | |
| 172 | +| Seeders | `database/seeders/` | |
| 173 | +| Jobs | `app/Jobs/` | |
| 174 | +| Mail | `app/Mail/` + `resources/views/emails/` | |
| 175 | +| Events | `app/Events/` | |
| 176 | +| Traits | `app/Traits/` | |
| 177 | +| Services | `app/Services/` | |
| 178 | +| Tests | `tests/Feature/` | |
0 commit comments