Development Guidelines
Best practices, checklists, and code generation commands for developing features in AlKareem ERP.
New Feature Checklist
When adding a new feature or entity to the ERP, follow this comprehensive checklist to ensure all components are properly created and integrated.
Create Model
With relationships, $fillable, $casts, and fireXxxEvent() methods
app/Models/{Module}/{ModelName}.php
Create Migration
Database table with proper indexes and foreign keys
database/migrations/xxxx_create_{table}_table.php
Create Repository
Data access layer with paginate(), find(), create(), update() methods
app/Repositories/{Module}/{ModelName}Repository.php
Create Service
Business logic with approval integration and transaction management
app/Services/{Module}/{ModelName}Service.php
Create Events
Domain events: Created, Updated, Deleted
app/Events/{Module}/{ModelName}Created.php
Create Listeners
Side effects: GL posting, stock updates, notifications
app/Listeners/{Module}/ManageXxxFrom{ModelName}.php
Create Livewire Components
Index, Create, Edit, Show pages
app/Livewire/{Module}/Form/{Feature}/Index.php
Create Routes
With Casbin middleware protection
routes/{module}/{feature}.php
Update PermissionSeeder
CRITICAL: Add UPPERCASE permission code
database/seeders/Core/PermissionSeeder.php
Update UserSeeder
CRITICAL: Map role-permission policies
database/seeders/Core/UserSeeder.php
Update Sidebar Menu
Add menu items with @canAccess directives
resources/views/livewire/components/sidebarmenu/{module}items.blade.php
Add Approval Config (if needed)
Notification templates for approval workflow
config/approvals.php
Code Generation Commands
Bulk Scaffolding (Recommended)
Create Model, Migration, Repository, and Service in one command:
php artisan app:bulk {Module}/{Name} --model --migration --repository --service
php artisan app:bulk Inventory/StockRequest --model --migration --repository --service
This creates:
app/Models/Inventory/StockRequest.phpdatabase/migrations/xxxx_create_stock_requests_table.phpapp/Repositories/Inventory/StockRequestRepository.phpapp/Services/Inventory/StockRequestService.php
Create Repository
php artisan app:repository Purchase/PurchaseInvoice
Create Service
php artisan app:make-service Purchase/PurchaseInvoiceService
Create Livewire Component
php artisan make:livewire Purchase/Form/Invoices/Create
Create Event
php artisan make:event Inventory/TransferCreated
Naming Conventions
| Type | Convention | Example |
|---|---|---|
| Models | PascalCase, Singular | Transfer, PurchaseInvoice |
| Tables | snake_case, Plural | transfers, purchase_invoices |
| Services | PascalCase + Service | TransferService |
| Repositories | PascalCase + Repository | TransferRepository |
| Events | PascalCase + Action | TransferCreated, TransferUpdated |
| Listeners | PascalCase, Descriptive | ManageStockTransactionFromTransfer |
| Permissions | UPPER_SNAKE_CASE | TRANSFERS, PURCHASE_INVOICES |
| Actions | UPPERCASE | VIEW, CREATE, UPDATE, DELETE |
Transaction Management
Always wrap write operations in database transactions to ensure data consistency. If any operation fails, all changes are rolled back.
Transaction Pattern
public function create(array $data): Model
{
return DB::transaction(function () use ($data) {
// All database operations go inside this closure
$record = $this->repository->create($data);
if (isset($data['items'])) {
$record->items()->createMany($data['items']);
}
// Approval check...
return $record;
// If ANY exception is thrown, ALL changes roll back
});
}
Error Handling
Use custom exceptions for domain-specific errors. This makes error handling more meaningful and testable.
Custom Exception
// app/Exceptions/InsufficientStockException.php
namespace App\Exceptions;
class InsufficientStockException extends \Exception
{
public function __construct(
string $message = 'Insufficient stock'
) {
parent::__construct($message);
}
}
Throwing Exception
// In Service
public function validateStock($items): void
{
foreach ($items as $item) {
if (!$this->hasStock($item)) {
throw new InsufficientStockException(
"Not enough stock for {$item->name}"
);
}
}
}
Common Pitfalls
Missing DB::transaction()
Data inconsistency on partial failures. Always wrap service writes in transactions.
Premature fireCreateEvent()
Events fired before approval. Only fire events after auto-approval or when approval completes.
Missing Seeder Updates
Users get 403 Forbidden. Always update PermissionSeeder and UserSeeder.
Lowercase Permission Codes
Authorization fails silently. Always use UPPERCASE: PURCHASE_INVOICES, not purchase_invoices.
Business Logic in Livewire
Hard to test, inconsistent behavior. Keep Livewire for validation only, delegate to Services.
Missing Menu Item
Feature not accessible via UI. Add to appropriate sidebarmenu/*.blade.php with @canAccess.
Development Scripts
Start Dev Server
composer dev
Runs concurrently:
- • php artisan serve (Web server)
- • php artisan queue:listen (Queue worker)
- • php artisan schedule:work (Scheduler)
- • npm run dev (Vite)
Testing
composer test
Runs Pest test suite
Static Analysis
composer phpstan
PHPStan static analysis
Code Formatting
./vendor/bin/pint
Laravel Pint code formatter