# Laravel Audit — ERP Gudang Reference system: `C:\Project\Website\ERPGUDANG` (Laravel 12) Audit date: 2026-08-10 Scope: full backend/business-logic audit for Go migration --- ## 1. Environment snapshot | Item | Value | |---|---| | Framework | Laravel 12 (`laravel/framework ^12.0`) | | PHP | `^8.2` (runtime 8.2, fpm) | | Database | MySQL / MariaDB (InnoDB, utf8mb4, `_unicode_ci`), db name `erp-gudang` | | Schema source | `erp-gudang.sql` (mysqldump, 2,162 lines, 43 tables) + `database/migrations/` | | Auth | `laravel/sanctum ^4.3` — session for web, opaque personal-access tokens for API | | Queue / Cache / Session | all `database` driver. Redis configured (`phpredis`) but not used | | UI framework | Blade + Bootstrap (`laravel/ui ^4.6`), Vite frontend, PWA manifest/service worker | | Extra packages | `orangehill/iseed ^3.8` (seed from DB), `tinker` | | Tests | Boilerplate only (`tests/Feature/ExampleTest`, `tests/Unit/ExampleTest`) | | Deployment | `Dockerfile` (php:8.2-fpm + nginx), `deployment/nginx.conf` | ## 2. Application structure ``` app/ Http/ Controllers/ # ALL business logic lives here (12 controllers) Auth/AuthController.php DashboardController.php HomeController.php ProfileController.php MasterDataController.php InventoryController.php PurchasingController.php SalesController.php SalesTrackingController.php FakturisasiController.php FinanceController.php HRController.php SystemController.php VoucherController.php Controllers/Controller.php Http/Requests/ # 29 FormRequest validation classes Middleware/ # RoleMiddleware, ActiveUserMiddleware, ActivityLogMiddleware Models/ # 29 Eloquent models Services/ # EMPTY directory — no service layer bootstrap/app.php # middleware registration config/ # erp.php = custom role/permission/tax/payroll config database/migrations/ # schema (see database-analysis.md) database/seeders/ # DatabaseSeeder, MasterDataSeeder, StockSeeder, UserSeeder routes/ # web.php, api.php, auth.php, console.php resources/views/ # Blade views (frontend being replaced by Vue) ``` ## 3. Key architectural characteristics 1. **Synchronous monolith.** No `Jobs/`, `Events/`, `Listeners/`, `Notifications/`, `Mail/`, `Observers/`, `Policies/`, `Commands/`, `Repositories/`, or scheduler entries. 2. **Business logic is in controllers**, not a service layer. Controllers call Eloquent directly and wrap multi-step operations in `DB::transaction()`. 3. **Authorization = role string on `users.role` + custom middleware.** No policies/gates. `config/erp.php` maps roles → permission globs; `User::can()` consults role globs + per-user `permissions` JSON. 4. **API surface is minimal** (`routes/api.php`): login + `/me`, `/logout`, dashboard KPIs, HR mobile endpoints, sales-tracking mobile endpoints, voucher endpoints. Everything else is web-only today → must be exposed as `/api/v1` in Go. 5. **Document numbers are generated at model `creating` hooks** using `count()+1` patterns — non-atomic (parity risk, see migration-risks.md). 6. **Money = `decimal(15,2)`** in MySQL, cast to strings by Eloquent. Quantities `decimal(12,4)`. No float anywhere in schema. 7. **Inventory model:** `stocks(product_id, warehouse_id, quantity, reserved_quantity)` + immutable `stock_movements` ledger with before/after quantities. Not all paths write movements (GR path does not). ## 4. Module → controller → responsibilities | Module | Controller | Core responsibilities | |---|---|---| | Auth | Auth/AuthController | Web login/logout, session regenerate, last_login_at, profile + password update | | Auth API | (closures in api.php) | Sanctum token login/logout, /me | | Dashboard | DashboardController | KPIs, monthly chart, low stock, recent activities, role module map | | Master Data | MasterDataController | Products, categories, units, suppliers, customers CRUD + search | | Inventory | InventoryController | Stock list, stock-in/out (manual), opname, transfer, movements | | Purchasing | PurchasingController | PO CRUD, approve/cancel, goods receipt, supplier debt, pay debt (voucher), voucher CRUD | | Sales | SalesController | SO CRUD, confirm/cancel/delete, POS processing, receivables | | Fakturisasi | FakturisasiController | Invoice create/pay/print, receivables | | Finance | FinanceController | Cashflow list/create, summary, export | | Sales Tracking | SalesTrackingController | Visits, check-in/out, activities, routes, GPS location pings | | HR | HRController | Employees, attendance clock-in/out, leaves, payroll, performance | | System | SystemController | Users CRUD, roles CRUD, activity logs | | Voucher | VoucherController | Voucher CRUD (see also PurchasingController::storeVoucher/payDebt) | ## 5. External integrations - **None.** No payment gateway, email service, SMS, or third-party API calls. - Storage: `FILESYSTEM_DISK=local` (product images via `Storage`, `image` upload ≤2MB). - No scheduled tasks (`routes/console.php` only has the default `inspire` command). ## 6. Known code-quality / dead-code findings - `app/Models/Role.php` + `roles` table exist but authorization does not use them (role is a string column on `users`). - `app/Http/Requests/Inventory/StockRequest.php` declares a duplicate `StockInRequest` class (dead file; `InventoryController` imports from the real `StockInRequest.php`). - `ProductsController`-style `searchProducts` route comment says "PERBAIKI INI" (needs review in Go). - `VoucherController` is largely redundant with `PurchasingController::storeVoucher`. - `ProfileController` is shadowed by `AuthController::profile` in route registration. - `SalesTrackingController` redirects reference `sales-tracking.*` names while routes are registered as `sales_tracking.*` — broken redirects (web only). - `current_stock` column on `products` is never written by any code path. - `showPayment` loads a non-existent `invoice` relation (Payment uses morph `payable`); would throw at runtime. Not reachable via routes. ## 7. Migration status The Laravel app remains the reference system. **Do not modify it.** All Go work happens in this repository root (Go module), with documentation under `docs/`.