# Developer Guide

## Project Structure (beta/)

```
app/
  Models/             ← Eloquent models (all extend BaseModel with ULID)
    Concerns/         ← HasTenant trait
  Providers/          ← AppServiceProvider (Gate::before), TenancyServiceProvider
  Http/
    Controllers/      ← Web and API controllers
    Requests/         ← Form validation (one per resource action)
    Middleware/       ← Auth and tenancy middleware

database/
  migrations/         ← All schema migrations
  seeders/            ← RolesAndPermissionsSeeder, DemoTenantSeeder

docs/
  en/                 ← English documentation
  ur/                 ← Urdu documentation (RTL)

public/               ← Web root (Vite-built assets)

resources/
  views/              ← Blade templates (AdminLTE 3 layout)
    layouts/
    auth/
    backend/
    components/
    pdf/              ← A4 bill dompdf template
```

---

## Key Patterns

### All Models Extend BaseModel

```php
class Package extends BaseModel
{
    use HasTenant;
    // ...
}
```

`BaseModel` provides: `HasUlids`, `LogsActivity`, `$keyType = 'string'`, `$incrementing = false`.

### Tenant Scoping

Every query on a tenant model is automatically filtered:
```php
// This query only returns packages for the current tenant:
Package::all();

// Super-admin cross-tenant query:
Package::withoutTenantScope()->where('tenant_id', $tid)->get();
```

### Permission Team Context

Before any permission check in a tenant request, set the team:
```php
app(PermissionRegistrar::class)->setPermissionsTeamId(tenant('id'));
```
This is done automatically in the `TenancyInitialized` listener.

### ULID Route Keys

```php
Route::get('/customers/{customer}', [CustomerController::class, 'show']);
// URL: /customers/01HWZP2J3KXXXXXXXXXXXXXX
// Never: /customers/5
```

---

## Running Migrations

```bash
# Full fresh migration with seeding
php artisan migrate:fresh --seed

# Normal migration
php artisan migrate
```

---

## Artisan Commands (Planned)

| Command | Description |
|---|---|
| `bills:generate {month} {year}` | Auto-generate bills for all active profiles |
| `subscriptions:check` | Suspend tenants with expired subscriptions |
| `bills:apply-late-fees` | Add late fee BillItems to overdue bills |

---

## Coding Conventions

- Controller methods follow `index`, `create`, `store`, `show`, `edit`, `update`, `destroy`
- All write operations use a dedicated `FormRequest` class
- No raw SQL — use Eloquent or Query Builder
- No integer IDs in routes — use ULID route model binding
- Blade templates use `@can('create.customer')` before showing action buttons
