===== STRUCTURE =====
.
├── ActionRequest.php
├── Appointment.php
├── Budget.php
├── City.php
├── Client.php
├── Cycle.php
├── DelegueSalary.php
├── Deplacement.php
├── Expense.php
├── Listing.php
├── Location.php
├── Notification.php
├── Objective.php
├── output.txt
├── Permission.php
├── Product.php
├── SalarySetting.php
├── User.php
├── VisitInProgress.php
├── Visit.php
├── VisitProduct.php
├── VisitType.php
├── WeeklyProgram.php
└── WorkSession.php

1 directory, 24 files

===== CONTENU DES FICHIERS =====

----- ./Objective.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Objective extends Model
{
    use HasFactory;

    protected $fillable = [
        'cycle_id','user_id','category','target_visits','achieved'
    ];

    public function cycle()
    {
        return $this->belongsTo(Cycle::class);
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
----- ./Permission.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Permission extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id',
        'start_date',
        'end_date',
        'half_day',
        'period',
        'reason',
        'notes',
        'remarque_admin',
        'status',
    ];

    protected $casts = [
        'start_date' => 'date',
        'end_date'   => 'date',
        'half_day'   => 'boolean',
    ];

    protected $appends = [
        'date_debut_formatted',
        'date_fin_formatted',
        'isFullDay',
        'motif',
    ];

    /* ✅ Dates formatées */
    public function getDateDebutFormattedAttribute()
    {
        return $this->start_date?->format('d/m/Y');
    }

    public function getDateFinFormattedAttribute()
    {
        return $this->end_date?->format('d/m/Y');
    }

    /* ✅ Journée entière / demi */
    public function getIsFullDayAttribute()
    {
        return !$this->half_day;
    }

    /* ✅ Motif lisible */
    public function getMotifAttribute()
    {
        return match ($this->reason) {
            'trajet' => 'Trajet',
            'reunion' => 'Réunion',
            'congres' => 'Congrès',
            'formation' => 'Formation',
            'maladie' => 'Congé maladie',
            'affaire_personnel' => 'Affaire personnelle',
            default => ucfirst($this->reason),
        };
    }
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
----- ./Notification.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Notification extends Model
{
    protected $fillable = [
        'user_id',
        'title',
        'message',
        'type',
        'is_read',
        'data',
    ];

    protected $casts = [
        'data' => 'array',
        'is_read' => 'boolean',
    ];
}
----- ./VisitInProgress.php -----
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class VisitInProgress extends Model
{
    protected $table = 'visits_in_progress';

    protected $fillable = [
        'user_id',
        'listing_id',
        'start_time',
        'end_time',
        'start_lat',
        'start_lng',
        'end_lat',
        'end_lng',
        'status',
    ];
    protected $casts = [
    'start_time' => 'datetime',
    'end_time' => 'datetime',
];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function listing()
    {
        return $this->belongsTo(Listing::class);
    }
}
----- ./Visit.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Visit extends Model
{
    use HasFactory;

    protected $table = 'visits';

protected $fillable = [
    'user_id',
    'cycle_id',
    'listing_id',
    'user_accompagnant_id',
    'visite_type',
    'date_visit',
    'user_latitude',
    'user_longitude',
    'distance_meters',
    'admin_pending',
    'interlocuteur',
    'role',
    'rupture_stock',
    'marche_direct',
    'objection',
    'important',
    'elapsed_time',
    'proposed_meds',
    'requested_meds',
    'results',
    'notes',
    'status',
    'poslat',
    'poslong',
    'has_ticket',
    'is_far',
    'far_reason',
    'far_explanation',
    'image',
];

protected $casts = [
    'rupture_stock'       => 'boolean',
    'marche_direct'       => 'boolean',
    'objection'           => 'boolean',
    'important'           => 'boolean',
    'admin_pending'       => 'boolean',
    'has_ticket'          => 'boolean',
    'is_far'              => 'boolean',
    'elapsed_time'        => 'integer',
    'distance_meters'     => 'float',
    'poslat'              => 'float',
    'poslong'             => 'float',
    'proposed_meds'       => 'array', 
    'requested_meds'      => 'array',
    'results'             => 'array',
    'date_visit'          => 'datetime:d/m/Y H:i',
];

    // Relations
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function listing()
    {
        return $this->belongsTo(Listing::class);
    }

    public function visitType()
    {
        return $this->belongsTo(VisitType::class);
    }

    public function cycle()
    {
        return $this->belongsTo(Cycle::class);
    }


    public function user_accompagnant()
    {
        return $this->belongsTo(User::class, 'user_accompagnant_id');
    }

}

----- ./Appointment.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Appointment extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id','listing_id','date_appointment','notes','status'
    ];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function listing()
    {
        return $this->belongsTo(Listing::class);
    }
}
----- ./Product.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use HasFactory;

    protected $fillable = [
        'name', 'image_url', 'description'
    ];

    public function visitProducts()
    {
        return $this->hasMany(VisitProduct::class);
    }

    public function actionRequests()
    {
        return $this->hasMany(ActionRequest::class);
    }
}
----- ./Deplacement.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Deplacement extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id', 'from_city', 'to_city',
        'distance_km', 'date_depart', 'status'
    ];
    protected $casts = [
    // 'date_depart' => 'datetime',
    'date_depart' => 'datetime:d/m/Y H:i',

];


    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
----- ./output.txt -----

----- ./Cycle.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Cycle extends Model
{
    use HasFactory;

    protected $fillable = [
        'name',
        'start_date',
        'end_date',
        'status',
    ];

    protected $casts = [
        'start_date' => 'date',
        'end_date' => 'date',
    ];

    // 🧩 Relation : un cycle contient plusieurs visites
    public function visits()
    {
        return $this->hasMany(Visit::class);
    }

    /**
     * Lorsqu’un cycle est créé ou activé, les autres deviennent inactifs.
     */
    protected static function boot()
    {
        parent::boot();

        static::saving(function (Cycle $cycle) {
            // Si ce cycle est défini comme "actif"
            if ($cycle->status === 'actif') {
                // On désactive tous les autres cycles
                static::where('id', '!=', $cycle->id)
                    ->where('status', 'actif')
                    ->update(['status' => 'inactif']);
            }
        });
    }
}
----- ./Client.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Client extends Model
{
    use HasFactory, SoftDeletes;

    /**
     * The attributes that are mass assignable.
     */
    protected $fillable = [
        'code',
        'client_type',
        'name',
        'trade_name',
        'speciality',
        'email',
        'phone',
        'mobile',
        'fax',
        'website',
        'tax_id',
        'license_number',
        'rc_number',
        'cnss_number',
        'address',
        'address_2',
        'city_id',
        'neighborhood',
        'latitude',
        'longitude',
        'category',
        'potential_type',
        'loyalty_status',
        'monthly_potential',
        'payment_terms',
        'credit_limit',
        'discount_rate',
        'is_key_account',
        'visiting_days',
        'preferred_visit_time',
        'opening_time',
        'closing_time',
        'break_start_time',
        'break_end_time',
        'decision_maker_name',
        'decision_maker_phone',
        'decision_maker_email',
        'decision_maker_position',
        'notes',
        'tags',
        'preferences',
        'birthday',
        'secretary_name',
        'secretary_phone',
        'created_by',
        'assigned_to',
        'parent_client_id',
        'is_active',
        'do_not_visit',
        'do_not_visit_reason',
        'is_prospect',
        'last_visit_date',
        'next_visit_date',
    ];

    /**
     * The attributes that should be cast.
     */
    protected $casts = [
        'visiting_days' => 'array',
        'tags' => 'array',
        'preferences' => 'array',
        'latitude' => 'decimal:8',
        'longitude' => 'decimal:8',
        'credit_limit' => 'decimal:2',
        'discount_rate' => 'decimal:2',
        'monthly_potential' => 'integer',
        'payment_terms' => 'integer',
        'is_active' => 'boolean',
        'is_key_account' => 'boolean',
        'do_not_visit' => 'boolean',
        'is_prospect' => 'boolean',
        'birthday' => 'date',
        'last_visit_date' => 'date',
        'next_visit_date' => 'date',
        'opening_time' => 'datetime:H:i',
        'closing_time' => 'datetime:H:i',
        'break_start_time' => 'datetime:H:i',
        'break_end_time' => 'datetime:H:i',
    ];

    /**
     * Boot method
     */
    protected static function boot()
    {
        parent::boot();

        static::creating(function ($client) {
            if (empty($client->code)) {
                $client->code = $client->generateUniqueCode();
            }
        });

        static::updating(function ($client) {
            // Mettre à jour les coordonnées si l'adresse change
            if ($client->isDirty(['address', 'city_id'])) {
                // Ici vous pouvez appeler un service de géocodage
            }
        });
    }

    /**
     * Génération du code unique selon le type
     */
    protected function generateUniqueCode(): string
    {
        $prefix = match($this->client_type) {
            'doctor' => 'DOC',
            'pharmacy' => 'PHA',
            'clinic' => 'CLI',
            'hospital' => 'HOS',
            'wholesaler' => 'WHO',
            default => 'CLT',
        };

        do {
            $number = str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
            $code = $prefix . $number;
        } while (self::where('code', $code)->exists());

        return $code;
    }

    /**
     * Relations
     */
    
    // Ville
    public function city()
    {
        return $this->belongsTo(City::class);
    }

    // Créateur
    public function creator()
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    // Visiteur assigné
    public function assignedVisitor()
    {
        return $this->belongsTo(User::class, 'assigned_to');
    }

    // Client parent (pour groupes)
    public function parentClient()
    {
        return $this->belongsTo(self::class, 'parent_client_id');
    }

    // Clients enfants (filiales)
    public function childClients()
    {
        return $this->hasMany(self::class, 'parent_client_id');
    }

    // Visites
    public function visits()
    {
        return $this->hasMany(Visit::class);
    }

    // Commandes
    public function orders()
    {
        return $this->hasMany(Order::class);
    }

    // Contacts additionnels
    public function contacts()
    {
        return $this->hasMany(ClientContact::class);
    }

    /**
     * Scopes
     */
    
    // Clients actifs
    public function scopeActive($query)
    {
        return $query->where('is_active', true)
                     ->where('do_not_visit', false);
    }

    // Par type
    public function scopeOfType($query, $type)
    {
        return $query->where('client_type', $type);
    }

    // Médecins
    public function scopeDoctors($query)
    {
        return $query->where('client_type', 'doctor');
    }

    // Pharmacies
    public function scopePharmacies($query)
    {
        return $query->where('client_type', 'pharmacy');
    }

    // Par catégorie
    public function scopeCategory($query, $category)
    {
        return $query->where('category', $category);
    }

    // Comptes clés
    public function scopeKeyAccounts($query)
    {
        return $query->where('is_key_account', true);
    }

    // Prospects
    public function scopeProspects($query)
    {
        return $query->where('is_prospect', true);
    }

    // Clients d'un visiteur
    public function scopeAssignedTo($query, $visitorId)
    {
        return $query->where('assigned_to', $visitorId);
    }

    // Recherche
    public function scopeSearch($query, $search)
    {
        return $query->where(function ($q) use ($search) {
            $q->where('name', 'like', "%{$search}%")
              ->orWhere('code', 'like', "%{$search}%")
              ->orWhere('phone', 'like', "%{$search}%")
              ->orWhere('mobile', 'like', "%{$search}%")
              ->orWhere('email', 'like', "%{$search}%");
        });
    }

    /**
     * Accesseurs
     */
    
    // Type formaté
    public function getTypeFormattedAttribute()
    {
        return match($this->client_type) {
            'doctor' => 'Médecin',
            'pharmacy' => 'Pharmacie',
            'clinic' => 'Clinique',
            'hospital' => 'Hôpital',
            'wholesaler' => 'Grossiste',
            default => 'Autre',
        };
    }

    // Nom d'affichage
    public function getDisplayNameAttribute()
    {
        if ($this->client_type === 'doctor' && $this->speciality) {
            return "Dr. {$this->name} ({$this->speciality})";
                    }
        return $this->trade_name ?? $this->name;
    }

    // Adresse complète
    public function getFullAddressAttribute()
    {
        $parts = array_filter([
            $this->address,
            $this->address_2,
            $this->neighborhood,
            $this->city?->name
        ]);
        
        return implode(', ', $parts);
    }

    // Coordonnées GPS
    public function getCoordinatesAttribute()
    {
        if ($this->latitude && $this->longitude) {
            return [
                'lat' => (float) $this->latitude,
                'lng' => (float) $this->longitude
            ];
        }
        return null;
    }

    // Jours de visite formatés
    public function getVisitingDaysFormattedAttribute()
    {
        if (!$this->visiting_days) {
            return 'Tous les jours';
        }

        $days = [
            'monday' => 'Lundi',
            'tuesday' => 'Mardi',
            'wednesday' => 'Mercredi',
            'thursday' => 'Jeudi',
            'friday' => 'Vendredi',
            'saturday' => 'Samedi',
            'sunday' => 'Dimanche'
        ];

        $visitDays = [];
        foreach ($this->visiting_days as $day) {
            $visitDays[] = $days[$day] ?? $day;
        }

        return implode(', ', $visitDays);
    }

    // Heures d'ouverture formatées
    public function getBusinessHoursAttribute()
    {
        if (!$this->opening_time || !$this->closing_time) {
            return null;
        }

        $hours = "{$this->opening_time->format('H:i')} - {$this->closing_time->format('H:i')}";
        
        if ($this->break_start_time && $this->break_end_time) {
            $hours .= " (Pause: {$this->break_start_time->format('H:i')} - {$this->break_end_time->format('H:i')})";
        }

        return $hours;
    }

    // Statut de crédit
    public function getCreditStatusAttribute()
    {
        if ($this->payment_terms === 0) {
            return 'Comptant';
        }
        
        return "Crédit {$this->payment_terms} jours";
    }

    /**
     * Méthodes utilitaires
     */
    
    // Vérifier si le client peut être visité
    public function canBeVisited(): bool
    {
        return $this->is_active && !$this->do_not_visit;
    }

    // Vérifier si c'est un jour de visite
    public function isVisitDay($day = null): bool
    {
        if (!$this->visiting_days || count($this->visiting_days) === 0) {
            return true; // Peut être visité tous les jours
        }

        $day = $day ?? strtolower(now()->format('l'));
        
        return in_array($day, $this->visiting_days);
    }

    // Vérifier si c'est l'heure de visite
    public function isVisitTime($time = null): bool
    {
        if (!$this->opening_time || !$this->closing_time) {
            return true; // Pas d'heures définies
        }

        $time = $time ?? now();
        $currentTime = $time->format('H:i:s');
        
        // Vérifier si c'est pendant les heures d'ouverture
        if ($currentTime < $this->opening_time || $currentTime > $this->closing_time) {
            return false;
        }

        // Vérifier si c'est pendant la pause
        if ($this->break_start_time && $this->break_end_time) {
            if ($currentTime >= $this->break_start_time && $currentTime <= $this->break_end_time) {
                return false;
            }
        }

        return true;
    }

    // Calculer la distance depuis une position
    public function distanceFrom($latitude, $longitude): float
    {
        if (!$this->latitude || !$this->longitude) {
            return 0;
        }

        $earthRadius = 6371; // km

        $latDiff = deg2rad($latitude - $this->latitude);
        $lonDiff = deg2rad($longitude - $this->longitude);

        $a = sin($latDiff / 2) * sin($latDiff / 2) +
             cos(deg2rad($this->latitude)) * cos(deg2rad($latitude)) *
             sin($lonDiff / 2) * sin($lonDiff / 2);

        $c = 2 * atan2(sqrt($a), sqrt(1 - $a));

        return round($earthRadius * $c, 2);
    }

    // Obtenir les statistiques de visite
    public function getVisitStats($startDate = null, $endDate = null)
    {
        $query = $this->visits()->completed();

        if ($startDate) {
            $query->where('visit_date', '>=', $startDate);
        }

        if ($endDate) {
            $query->where('visit_date', '<=', $endDate);
        }

        return [
            'total_visits' => $query->count(),
            'last_visit' => $this->last_visit_date,
            'next_visit' => $this->next_visit_date,
            'average_duration' => $query->avg('duration_minutes'),
        ];
    }

    // Obtenir les statistiques de commandes
    public function getOrderStats($startDate = null, $endDate = null)
    {
        $query = $this->orders();

        if ($startDate) {
            $query->where('order_date', '>=', $startDate);
        }

        if ($endDate) {
            $query->where('order_date', '<=', $endDate);
        }

        return [
            'total_orders' => $query->count(),
            'total_amount' => $query->sum('total_amount'),
            'average_order' => $query->avg('total_amount'),
            'pending_amount' => $query->where('payment_status', 'pending')->sum('total_amount'),
        ];
    }

    // Mettre à jour la date de prochaine visite
    public function updateNextVisitDate(): void
    {
        // Logique basée sur la catégorie et l'historique
        $days = match($this->category) {
            'A' => 7,   // Visite hebdomadaire
            'B' => 14,  // Visite bi-mensuelle
            'C' => 30,  // Visite mensuelle
            default => 21
        };

        $this->update([
            'next_visit_date' => now()->addDays($days)
        ]);
    }

    /**
     * Méthodes pour l'API
     */
    public function toArray()
    {
        $array = parent::toArray();
        
        // Ajouter les attributs calculés
        $array['type_formatted'] = $this->type_formatted;
        $array['display_name'] = $this->display_name;
        $array['full_address'] = $this->full_address;
        $array['coordinates'] = $this->coordinates;
        $array['credit_status'] = $this->credit_status;
        $array['can_be_visited'] = $this->canBeVisited();
        
        // Ajouter les relations si chargées
        if ($this->relationLoaded('city')) {
            $array['city_name'] = $this->city->name;
        }
        
        if ($this->relationLoaded('assignedVisitor')) {
            $array['visitor_name'] = $this->assignedVisitor->name;
        }
        
        return $array;
    }
}
----- ./Listing.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Listing extends Model
{
    use HasFactory;

    protected $fillable = [
        'name', 'type', 'specialty', 'address',
        'city_id', 'lat', 'lng'
    ];

    public function city()
    {
        return $this->belongsTo(City::class);
    }

    public function visits()
    {
        return $this->hasMany(Visit::class);
    }

    public function actionRequests()
    {
        return $this->hasMany(ActionRequest::class);
    }

    public function appointments()
    {
        return $this->hasMany(Appointment::class);
    }
}
----- ./Budget.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Budget extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id','cycle_id','total_budget','used_budget','remaining_budget'
    ];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function cycle()
    {
        return $this->belongsTo(Cycle::class);
    }
}
----- ./WeeklyProgram.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class WeeklyProgram extends Model
{
    use HasFactory;

    protected $fillable = [
        'cycle_id','user_id','week_number','city_id','notes'
    ];

    public function cycle()
    {
        return $this->belongsTo(Cycle::class);
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function city()
    {
        return $this->belongsTo(City::class);
    }
}
----- ./SalarySetting.php -----
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class SalarySetting extends Model
{
    protected $fillable = ['prime_per_visit'];
}
----- ./VisitType.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class VisitType extends Model
{
    use HasFactory;

    protected $fillable = ['label'];

    public function visits()
    {
        return $this->hasMany(Visit::class);
    }
}
----- ./WorkSession.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class WorkSession extends Model
{
    protected $fillable = [
        'user_id',
        'started_at',
        'ended_at'
    ];

    protected $casts = [
        'started_at' => 'datetime',
        'ended_at' => 'datetime'
    ];
}
----- ./Location.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Location extends Model
{
    protected $fillable = [
        'user_id',
        'latitude',
        'longitude',
        'recorded_at'
    ];

    protected $casts = [
        'recorded_at' => 'datetime'
    ];

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
----- ./DelegueSalary.php -----
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class DelegueSalary extends Model
{
    protected $fillable = ['user_id','base_salary'];
    public function user() { return $this->belongsTo(User::class); }
}
----- ./VisitProduct.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class VisitProduct extends Model
{
    use HasFactory;

    protected $fillable = [
        'visit_id', 'product_id', 'type', 'etat', 'quantity'
    ];

    public function visit()
    {
        return $this->belongsTo(Visit::class);
    }

    public function product()
    {
        return $this->belongsTo(Product::class);
    }
}
----- ./Expense.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Expense extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id', 'category', 'description', 'amount', 'status'
    ];

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
----- ./User.php -----
<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Support\Facades\Hash;
use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements MustVerifyEmail, JWTSubject
{
    use HasApiTokens, HasFactory, Notifiable, SoftDeletes;

    protected $fillable = [
        'code', 'name', 'email', 'password', 'phone', 'mobile', 'address',
        'birth_date', 'national_id', 'city_id', 'manager_id', 'role',
        'hire_date', 'contract_type', 'department', 'monthly_target',
        'commission_rate', 'car_registration', 'car_model', 'fcm_token',
        'last_login_at', 'last_login_ip', 'last_location_latitude',
        'last_location_longitude', 'last_location_updated_at',
        'settings', 'is_active', 'must_change_password', 'password_changed_at',
    ];

    protected $hidden = [
        'password', 'remember_token', 'fcm_token', 'login_attempts', 'locked_until',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
        'birth_date' => 'date',
        'hire_date' => 'date',
        'last_login_at' => 'datetime',
        'locked_until' => 'datetime',
        'password_changed_at' => 'datetime',
        'last_location_updated_at' => 'datetime',
        'settings' => 'array',
        'is_active' => 'boolean',
        'must_change_password' => 'boolean',
        'monthly_target' => 'decimal:2',
        'commission_rate' => 'decimal:2',
        'last_location_latitude' => 'decimal:8',
        'last_location_longitude' => 'decimal:8',
    ];

    protected static function boot()
    {
        parent::boot();

        static::creating(function ($user) {
            if (empty($user->code)) {
                $user->code = $user->generateUniqueCode();
            }

            if (!empty($user->password) && !Hash::isHashed($user->password)) {
                $user->password = Hash::make($user->password);
            }
        });

        static::updating(function ($user) {
            if ($user->isDirty('password') && !Hash::isHashed($user->password)) {
                $user->password = Hash::make($user->password);
                $user->password_changed_at = now();
            }
        });
    }

    protected function generateUniqueCode(): string
    {
        $prefix = match($this->role) {
            'admin' => 'DEL1',
            'manager' => 'DEL2',
            'supervisor' => 'DEL3',
            'visitor' => 'DEL4',
            default => 'DEL5',
        };

        do {
            $number = str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
            $code = $prefix . $number;
        } while (self::where('code', $code)->exists());

        return $code;
    }


    // 👥 Un délégué a plusieurs visites
    public function visits()
    {
        return $this->hasMany(Visit::class);
    }

    // 🗺️ Relation N:N avec les villes autorisées
    
    public function cities()
    {
        return $this->belongsToMany(City::class, 'city_user');
    }
    // 🧾 Notes de frais
    public function expenses()
    {
        return $this->hasMany(Expense::class);
    }

    // 🕒 Déplacements
    public function deplacements()
    {
        return $this->hasMany(Deplacement::class);
    }

    // ⏰ Permissions
    public function permissions()
    {
        return $this->hasMany(Permission::class);
    }

    // 🧩 Objectifs
    public function objectives()
    {
        return $this->hasMany(Objective::class);
    }

    // 💼 Budgets promotionnels
    public function budgets()
    {
        return $this->hasMany(Budget::class);
    }

    // 🧱 Actions promotionnelles
    public function actionRequests()
    {
        return $this->hasMany(ActionRequest::class);
    }

    // 📅 Rendez-vous
    public function appointments()
    {
        return $this->hasMany(Appointment::class);
    }
    // JWT methods
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims()
    {
        return [];
    }
    public function locations()
{
    return $this->hasMany(Location::class);
}
}

----- ./City.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class City extends Model
{
    use HasFactory;

    protected $fillable = ['name', 'region'];

    // 🔁 Relation N:N avec les utilisateurs
  public function users()
{
    return $this->belongsToMany(User::class, 'city_user', 'city_id', 'user_id');
    // ou si c’est un lien direct One-to-Many :
    // return $this->hasMany(User::class);
}


    // 🏥 Une ville possède plusieurs listings
    public function listings()
    {
        return $this->hasMany(Listing::class);
    }
}
----- ./ActionRequest.php -----
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class ActionRequest extends Model
{
    protected $fillable = [
        'user_id',
        'type',
        'produit',
        'objectif',
        'budget',
        'date_demande',
        'duree_mois',
        'status',
    ];

    protected $casts = [
        'date_demande' => 'date',
    ];

    protected $appends = ['date_formatted'];

    public function getDateFormattedAttribute()
    {
        return $this->date_demande->format('d M Y');
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}