diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index 5fb3aff..af6442b 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc index f7f591a..f1e8d98 100644 Binary files a/core/__pycache__/forms.cpython-311.pyc and b/core/__pycache__/forms.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index c9053e4..911f0d2 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/admin.py b/core/admin.py index 833de05..40daf30 100644 --- a/core/admin.py +++ b/core/admin.py @@ -4,7 +4,7 @@ from django.shortcuts import render from django.http import HttpResponseRedirect from django.contrib import messages from django.utils.translation import gettext_lazy as _ -from .models import Profile, Truck, Shipment, Bid, Message, WhatsAppConfig, Country +from .models import Profile, Truck, Shipment, Bid, Message, WhatsAppConfig, Country, City, TruckType from .whatsapp import send_whatsapp_message @admin.register(Country) @@ -12,6 +12,17 @@ class CountryAdmin(admin.ModelAdmin): list_display = ('name', 'code', 'is_default') list_editable = ('is_default',) +@admin.register(City) +class CityAdmin(admin.ModelAdmin): + list_display = ('name', 'country') + list_filter = ('country',) + search_fields = ('name',) + +@admin.register(TruckType) +class TruckTypeAdmin(admin.ModelAdmin): + list_display = ('name', 'name_ar') + search_fields = ('name', 'name_ar') + @admin.register(Profile) class ProfileAdmin(admin.ModelAdmin): list_display = ('user', 'role', 'country_code', 'phone_number') @@ -20,20 +31,21 @@ class ProfileAdmin(admin.ModelAdmin): @admin.register(Truck) class TruckAdmin(admin.ModelAdmin): - list_display = ('truck_type', 'model', 'plate_no', 'owner', 'load_capacity') - search_fields = ('plate_no', 'owner__username', 'truck_type') + list_display = ('display_truck_type', 'model', 'plate_no', 'owner', 'load_capacity') + search_fields = ('plate_no', 'owner__username', 'model') + list_filter = ('truck_type_link', 'is_approved') @admin.register(Shipment) class ShipmentAdmin(admin.ModelAdmin): - list_display = ('origin', 'destination', 'shipper', 'status', 'delivery_date') - list_filter = ('status', 'delivery_date') - search_fields = ('origin', 'destination', 'shipper__username') + list_display = ('display_origin', 'display_destination', 'shipper', 'status', 'delivery_date') + list_filter = ('status', 'delivery_date', 'required_truck_type_link') + search_fields = ('origin', 'destination', 'shipper__username', 'description') @admin.register(Bid) class BidAdmin(admin.ModelAdmin): list_display = ('shipment', 'truck_owner', 'amount', 'status') list_filter = ('status',) - search_fields = ('shipment__origin', 'shipment__destination', 'truck_owner__username') + search_fields = ('shipment__description', 'truck_owner__username') @admin.register(Message) class MessageAdmin(admin.ModelAdmin): diff --git a/core/forms.py b/core/forms.py index c9de2b1..b48d4ec 100644 --- a/core/forms.py +++ b/core/forms.py @@ -1,5 +1,5 @@ from django import forms -from .models import Truck, Shipment, Bid, Profile, Country, OTPCode, City +from .models import Truck, Shipment, Bid, Profile, Country, OTPCode, City, TruckType from django.utils.translation import gettext_lazy as _ from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User @@ -64,13 +64,12 @@ class TruckForm(forms.ModelForm): class Meta: model = Truck fields = [ - 'truck_type', 'truck_type_ar', 'model', 'model_ar', 'year', 'plate_no', + 'truck_type_link', 'model', 'model_ar', 'year', 'plate_no', 'load_capacity', 'load_capacity_ar', 'color', 'color_ar', 'registration_expiry_date', 'truck_picture', 'registration_front', 'registration_back', 'driver_license' ] widgets = { - 'truck_type': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('e.g. Flatbed')}), - 'truck_type_ar': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('مثلا سطحة')}), + 'truck_type_link': forms.Select(attrs={'class': 'form-select'}), 'model': forms.TextInput(attrs={'class': 'form-control'}), 'model_ar': forms.TextInput(attrs={'class': 'form-control'}), 'year': forms.NumberInput(attrs={'class': 'form-control'}), @@ -85,12 +84,16 @@ class TruckForm(forms.ModelForm): 'registration_back': forms.FileInput(attrs={'class': 'form-control'}), 'driver_license': forms.FileInput(attrs={'class': 'form-control'}), } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['truck_type_link'].label = _("Truck Type") class ShipmentForm(forms.ModelForm): class Meta: model = Shipment fields = [ - 'description', 'weight', + 'description', 'weight', 'required_truck_type_link', 'origin_country', 'origin_city', 'destination_country', 'destination_city', 'delivery_date' @@ -98,12 +101,17 @@ class ShipmentForm(forms.ModelForm): widgets = { 'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'weight': forms.TextInput(attrs={'class': 'form-control'}), + 'required_truck_type_link': forms.Select(attrs={'class': 'form-select'}), 'origin_country': forms.Select(attrs={'class': 'form-select location-selector', 'data-type': 'origin'}), 'origin_city': forms.Select(attrs={'class': 'form-select'}), 'destination_country': forms.Select(attrs={'class': 'form-select location-selector', 'data-type': 'destination'}), 'destination_city': forms.Select(attrs={'class': 'form-select'}), 'delivery_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['required_truck_type_link'].label = _("Truck Type") class BidForm(forms.ModelForm): class Meta: @@ -127,6 +135,7 @@ class BidForm(forms.ModelForm): class ShipperOfferForm(forms.Form): description = forms.CharField(label=_('Goods Description'), widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 3})) weight = forms.CharField(label=_('Weight/Volume'), widget=forms.TextInput(attrs={'class': 'form-control'})) + required_truck_type_link = forms.ModelChoiceField(label=_('Required Truck Type'), queryset=TruckType.objects.all(), widget=forms.Select(attrs={'class': 'form-select'})) origin_country = forms.ModelChoiceField(queryset=Country.objects.all(), widget=forms.Select(attrs={'class': 'form-select location-selector', 'data-type': 'origin'})) origin_city = forms.ModelChoiceField(queryset=City.objects.all(), widget=forms.Select(attrs={'class': 'form-select'})) diff --git a/core/migrations/0011_trucktype_alter_truck_truck_type_and_more.py b/core/migrations/0011_trucktype_alter_truck_truck_type_and_more.py new file mode 100644 index 0000000..b09a5a6 --- /dev/null +++ b/core/migrations/0011_trucktype_alter_truck_truck_type_and_more.py @@ -0,0 +1,41 @@ +# Generated by Django 5.2.7 on 2026-01-23 15:12 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0010_shipment_destination_country_shipment_origin_country_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='TruckType', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, verbose_name='Name (EN)')), + ('name_ar', models.CharField(blank=True, max_length=100, verbose_name='Name (AR)')), + ], + options={ + 'verbose_name': 'Truck Type', + 'verbose_name_plural': 'Truck Types', + }, + ), + migrations.AlterField( + model_name='truck', + name='truck_type', + field=models.CharField(blank=True, max_length=100, verbose_name='Truck Type (EN)'), + ), + migrations.AddField( + model_name='shipment', + name='required_truck_type_link', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.trucktype', verbose_name='Required Truck Type'), + ), + migrations.AddField( + model_name='truck', + name='truck_type_link', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.trucktype', verbose_name='Truck Type (New)'), + ), + ] diff --git a/core/migrations/__pycache__/0011_trucktype_alter_truck_truck_type_and_more.cpython-311.pyc b/core/migrations/__pycache__/0011_trucktype_alter_truck_truck_type_and_more.cpython-311.pyc new file mode 100644 index 0000000..4aff7d5 Binary files /dev/null and b/core/migrations/__pycache__/0011_trucktype_alter_truck_truck_type_and_more.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0011_trucktype_remove_truck_truck_type_ar_and_more.cpython-311.pyc b/core/migrations/__pycache__/0011_trucktype_remove_truck_truck_type_ar_and_more.cpython-311.pyc new file mode 100644 index 0000000..0ec66c0 Binary files /dev/null and b/core/migrations/__pycache__/0011_trucktype_remove_truck_truck_type_ar_and_more.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index db726e5..7a45d31 100644 --- a/core/models.py +++ b/core/models.py @@ -32,6 +32,19 @@ class City(models.Model): def __str__(self): return f"{self.name} ({self.country.name})" +class TruckType(models.Model): + name = models.CharField(_('Name (EN)'), max_length=100) + name_ar = models.CharField(_('Name (AR)'), max_length=100, blank=True) + + class Meta: + verbose_name = _('Truck Type') + verbose_name_plural = _('Truck Types') + + def __str__(self): + if get_language() == 'ar' and self.name_ar: + return self.name_ar + return self.name + class Profile(models.Model): ROLE_CHOICES = ( ('SHIPPER', _('Shipper (Need Goods Moved)')), @@ -73,8 +86,11 @@ class OTPCode(models.Model): class Truck(models.Model): owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='trucks') + # Using a fresh name to avoid conflict with partially created fields + truck_type_link = models.ForeignKey(TruckType, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_('Truck Type (New)')) + # English fields - truck_type = models.CharField(_('Truck Type (EN)'), max_length=100) + truck_type = models.CharField(_('Truck Type (EN)'), max_length=100, blank=True) 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) @@ -99,10 +115,14 @@ class Truck(models.Model): created_at = models.DateTimeField(auto_now_add=True) def __str__(self): - return f"{self.display_truck_type} - {self.plate_no}" + if self.truck_type_link: + return f"{self.truck_type_link} - {self.plate_no}" + return f"{self.truck_type} - {self.plate_no}" @property def display_truck_type(self): + if self.truck_type_link: + return str(self.truck_type_link) if get_language() == 'ar' and self.truck_type_ar: return self.truck_type_ar return self.truck_type @@ -136,6 +156,9 @@ class Shipment(models.Model): description = models.TextField(_('Goods Description')) weight = models.CharField(_('Weight/Volume'), max_length=100) + # Using a fresh name + required_truck_type_link = models.ForeignKey(TruckType, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_('Required Truck Type')) + origin_country = models.ForeignKey(Country, on_delete=models.SET_NULL, null=True, related_name='shipments_origin') origin_city = models.ForeignKey(City, on_delete=models.SET_NULL, null=True, related_name='shipments_origin') destination_country = models.ForeignKey(Country, on_delete=models.SET_NULL, null=True, related_name='shipments_destination') diff --git a/core/templates/core/post_shipment.html b/core/templates/core/post_shipment.html index 2a11611..62f7932 100644 --- a/core/templates/core/post_shipment.html +++ b/core/templates/core/post_shipment.html @@ -20,13 +20,18 @@
{{ shipment.description }}