from django.db import models from django.contrib.auth.models import User from django.db.models.signals import post_save from django.dispatch import receiver from django.utils.translation import gettext_lazy as _ from django.utils.translation import get_language from django.utils import timezone import random import string class Country(models.Model): name = models.CharField(_('Country Name'), max_length=100) code = models.CharField(_('Country Code'), max_length=10) is_default = models.BooleanField(_('Is Default'), default=False) class Meta: verbose_name = _('Country') verbose_name_plural = _('Countries') def __str__(self): return f"{self.name} (+{self.code})" class Profile(models.Model): ROLE_CHOICES = ( ('SHIPPER', _('Shipper (Need Goods Moved)')), ('TRUCK_OWNER', _('Truck Owner (Service Provider)')), ('ADMIN', _('Administrator')), ) user = models.OneToOneField(User, on_delete=models.CASCADE) role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='SHIPPER') country_code = models.CharField(max_length=5, blank=True, default="966") phone_number = models.CharField(max_length=20, unique=True, null=True) # Changed to unique and nullable for migration safety @property def full_phone_number(self): if not self.phone_number: return "" # Remove any existing leading + from country code and phone number cc = str(self.country_code).replace("+", "").strip() pn = str(self.phone_number).replace("+", "").strip() return f"{cc}{pn}" def __str__(self): return f"{self.user.username} - {self.role}" class OTPCode(models.Model): phone_number = models.CharField(max_length=20) code = models.CharField(max_length=6) created_at = models.DateTimeField(auto_now_add=True) is_used = models.BooleanField(default=False) def is_valid(self): # Valid for 10 minutes return not self.is_used and (timezone.now() - self.created_at).total_seconds() < 600 @staticmethod def generate_code(phone_number): code = ''.join(random.choices(string.digits, k=6)) return OTPCode.objects.create(phone_number=phone_number, code=code) class Truck(models.Model): owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='trucks') # English fields truck_type = models.CharField(_('Truck Type (EN)'), max_length=100) model = models.CharField(_('Model (EN)'), max_length=100) load_capacity = models.CharField(_('Load Capacity (EN)'), max_length=100) color = models.CharField(_('Color (EN)'), max_length=50) # Arabic fields truck_type_ar = models.CharField(_('Truck Type (AR)'), max_length=100, blank=True) model_ar = models.CharField(_('Model (AR)'), max_length=100, blank=True) load_capacity_ar = models.CharField(_('Load Capacity (AR)'), max_length=100, blank=True) color_ar = models.CharField(_('Color (AR)'), max_length=50, blank=True) year = models.PositiveIntegerField(_('Year')) plate_no = models.CharField(_('Plate No'), max_length=50) registration_expiry_date = models.DateField(_('Registration Expiry Date'), null=True, blank=True) # Pictures truck_picture = models.ImageField(_('Truck Picture'), upload_to='trucks/', blank=True, null=True) registration_front = models.ImageField(_('Registration Front'), upload_to='docs/', blank=True, null=True) registration_back = models.ImageField(_('Registration Back'), upload_to='docs/', blank=True, null=True) driver_license = models.ImageField(_('Driver License'), upload_to='docs/', blank=True, null=True) is_approved = models.BooleanField(_('Is Approved'), default=False) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"{self.display_truck_type} - {self.plate_no}" @property def display_truck_type(self): if get_language() == 'ar' and self.truck_type_ar: return self.truck_type_ar return self.truck_type @property def display_model(self): if get_language() == 'ar' and self.model_ar: return self.model_ar return self.model @property def display_load_capacity(self): if get_language() == 'ar' and self.load_capacity_ar: return self.load_capacity_ar return self.load_capacity @property def display_color(self): if get_language() == 'ar' and self.color_ar: return self.color_ar return self.color class Shipment(models.Model): STATUS_CHOICES = ( ('OPEN', _('Open for Bids')), ('IN_PROGRESS', _('In Progress')), ('COMPLETED', _('Completed')), ('CANCELLED', _('Cancelled')), ) shipper = models.ForeignKey(User, on_delete=models.CASCADE, related_name='shipments') description = models.TextField(_('Goods Description')) weight = models.CharField(_('Weight/Volume'), max_length=100) origin = models.CharField(_('Origin'), max_length=255) destination = models.CharField(_('Destination'), max_length=255) delivery_date = models.DateField(_('Requested Delivery Date')) status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='OPEN') assigned_truck = models.ForeignKey(Truck, on_delete=models.SET_NULL, null=True, blank=True, related_name='assigned_shipments') created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"{self.origin} to {self.destination} - {self.status}" class Bid(models.Model): STATUS_CHOICES = ( ('PENDING', _('Pending')), ('ACCEPTED', _('Accepted')), ('REJECTED', _('Rejected')), ) shipment = models.ForeignKey(Shipment, on_delete=models.CASCADE, related_name='bids') truck_owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='bids') truck = models.ForeignKey(Truck, on_delete=models.CASCADE) amount = models.DecimalField(_('Offer Amount'), max_digits=10, decimal_places=2) comments = models.TextField(_('Comments'), blank=True) status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING') created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"Bid by {self.truck_owner.username} for {self.shipment}" class Message(models.Model): shipment = models.ForeignKey(Shipment, on_delete=models.CASCADE, related_name='messages') sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sent_messages') content = models.TextField() timestamp = models.DateTimeField(auto_now_add=True) def __str__(self): return f"From {self.sender.username} at {self.timestamp}" class WhatsAppConfig(models.Model): api_token = models.CharField(_('Wablas API Token'), max_length=255) secret_key = models.CharField(_('Wablas Secret Key'), max_length=255, blank=True, null=True) is_active = models.BooleanField(_('Is Active'), default=True) class Meta: verbose_name = _('WhatsApp Configuration') verbose_name_plural = _('WhatsApp Configuration') def __str__(self): return str(_("WhatsApp Configuration")) @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): if created: Profile.objects.create(user=instance) @receiver(post_save, sender=User) def save_user_profile(sender, instance, **kwargs): if hasattr(instance, 'profile'): instance.profile.save() else: Profile.objects.create(user=instance)