Development Guidelines

Best Practices & Checklists

Home | SoftLixx Creates
Development

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.

1

Create Model

With relationships, $fillable, $casts, and fireXxxEvent() methods

app/Models/{Module}/{ModelName}.php
2

Create Migration

Database table with proper indexes and foreign keys

database/migrations/xxxx_create_{table}_table.php
3

Create Repository

Data access layer with paginate(), find(), create(), update() methods

app/Repositories/{Module}/{ModelName}Repository.php
4

Create Service

Business logic with approval integration and transaction management

app/Services/{Module}/{ModelName}Service.php
5

Create Events

Domain events: Created, Updated, Deleted

app/Events/{Module}/{ModelName}Created.php
6

Create Listeners

Side effects: GL posting, stock updates, notifications

app/Listeners/{Module}/ManageXxxFrom{ModelName}.php
7

Create Livewire Components

Index, Create, Edit, Show pages

app/Livewire/{Module}/Form/{Feature}/Index.php
8

Create Routes

With Casbin middleware protection

routes/{module}/{feature}.php
9

Update PermissionSeeder

CRITICAL: Add UPPERCASE permission code

database/seeders/Core/PermissionSeeder.php
10

Update UserSeeder

CRITICAL: Map role-permission policies

database/seeders/Core/UserSeeder.php
11

Update Sidebar Menu

Add menu items with @canAccess directives

resources/views/livewire/components/sidebarmenu/{module}items.blade.php
12

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
Example:
php artisan app:bulk Inventory/StockRequest --model --migration --repository --service

This creates:

  • app/Models/Inventory/StockRequest.php
  • database/migrations/xxxx_create_stock_requests_table.php
  • app/Repositories/Inventory/StockRequestRepository.php
  • app/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