changes19

This commit is contained in:
Flatlogic Bot 2026-01-23 15:13:31 +00:00
parent 459f38ada6
commit 3da5d477df
12 changed files with 132 additions and 31 deletions

View File

@ -4,7 +4,7 @@ from django.shortcuts import render
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.contrib import messages from django.contrib import messages
from django.utils.translation import gettext_lazy as _ 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 from .whatsapp import send_whatsapp_message
@admin.register(Country) @admin.register(Country)
@ -12,6 +12,17 @@ class CountryAdmin(admin.ModelAdmin):
list_display = ('name', 'code', 'is_default') list_display = ('name', 'code', 'is_default')
list_editable = ('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) @admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin): class ProfileAdmin(admin.ModelAdmin):
list_display = ('user', 'role', 'country_code', 'phone_number') list_display = ('user', 'role', 'country_code', 'phone_number')
@ -20,20 +31,21 @@ class ProfileAdmin(admin.ModelAdmin):
@admin.register(Truck) @admin.register(Truck)
class TruckAdmin(admin.ModelAdmin): class TruckAdmin(admin.ModelAdmin):
list_display = ('truck_type', 'model', 'plate_no', 'owner', 'load_capacity') list_display = ('display_truck_type', 'model', 'plate_no', 'owner', 'load_capacity')
search_fields = ('plate_no', 'owner__username', 'truck_type') search_fields = ('plate_no', 'owner__username', 'model')
list_filter = ('truck_type_link', 'is_approved')
@admin.register(Shipment) @admin.register(Shipment)
class ShipmentAdmin(admin.ModelAdmin): class ShipmentAdmin(admin.ModelAdmin):
list_display = ('origin', 'destination', 'shipper', 'status', 'delivery_date') list_display = ('display_origin', 'display_destination', 'shipper', 'status', 'delivery_date')
list_filter = ('status', 'delivery_date') list_filter = ('status', 'delivery_date', 'required_truck_type_link')
search_fields = ('origin', 'destination', 'shipper__username') search_fields = ('origin', 'destination', 'shipper__username', 'description')
@admin.register(Bid) @admin.register(Bid)
class BidAdmin(admin.ModelAdmin): class BidAdmin(admin.ModelAdmin):
list_display = ('shipment', 'truck_owner', 'amount', 'status') list_display = ('shipment', 'truck_owner', 'amount', 'status')
list_filter = ('status',) list_filter = ('status',)
search_fields = ('shipment__origin', 'shipment__destination', 'truck_owner__username') search_fields = ('shipment__description', 'truck_owner__username')
@admin.register(Message) @admin.register(Message)
class MessageAdmin(admin.ModelAdmin): class MessageAdmin(admin.ModelAdmin):

View File

@ -1,5 +1,5 @@
from django import forms 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.utils.translation import gettext_lazy as _
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -64,13 +64,12 @@ class TruckForm(forms.ModelForm):
class Meta: class Meta:
model = Truck model = Truck
fields = [ 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', 'load_capacity', 'load_capacity_ar', 'color', 'color_ar', 'registration_expiry_date',
'truck_picture', 'registration_front', 'registration_back', 'driver_license' 'truck_picture', 'registration_front', 'registration_back', 'driver_license'
] ]
widgets = { widgets = {
'truck_type': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('e.g. Flatbed')}), 'truck_type_link': forms.Select(attrs={'class': 'form-select'}),
'truck_type_ar': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('مثلا سطحة')}),
'model': forms.TextInput(attrs={'class': 'form-control'}), 'model': forms.TextInput(attrs={'class': 'form-control'}),
'model_ar': forms.TextInput(attrs={'class': 'form-control'}), 'model_ar': forms.TextInput(attrs={'class': 'form-control'}),
'year': forms.NumberInput(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'}), 'registration_back': forms.FileInput(attrs={'class': 'form-control'}),
'driver_license': 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 ShipmentForm(forms.ModelForm):
class Meta: class Meta:
model = Shipment model = Shipment
fields = [ fields = [
'description', 'weight', 'description', 'weight', 'required_truck_type_link',
'origin_country', 'origin_city', 'origin_country', 'origin_city',
'destination_country', 'destination_city', 'destination_country', 'destination_city',
'delivery_date' 'delivery_date'
@ -98,12 +101,17 @@ class ShipmentForm(forms.ModelForm):
widgets = { widgets = {
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'weight': forms.TextInput(attrs={'class': 'form-control'}), '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_country': forms.Select(attrs={'class': 'form-select location-selector', 'data-type': 'origin'}),
'origin_city': forms.Select(attrs={'class': 'form-select'}), 'origin_city': forms.Select(attrs={'class': 'form-select'}),
'destination_country': forms.Select(attrs={'class': 'form-select location-selector', 'data-type': 'destination'}), 'destination_country': forms.Select(attrs={'class': 'form-select location-selector', 'data-type': 'destination'}),
'destination_city': forms.Select(attrs={'class': 'form-select'}), 'destination_city': forms.Select(attrs={'class': 'form-select'}),
'delivery_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), '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 BidForm(forms.ModelForm):
class Meta: class Meta:
@ -127,6 +135,7 @@ class BidForm(forms.ModelForm):
class ShipperOfferForm(forms.Form): class ShipperOfferForm(forms.Form):
description = forms.CharField(label=_('Goods Description'), widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 3})) 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'})) 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_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'})) origin_city = forms.ModelChoiceField(queryset=City.objects.all(), widget=forms.Select(attrs={'class': 'form-select'}))

View File

@ -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)'),
),
]

View File

@ -32,6 +32,19 @@ class City(models.Model):
def __str__(self): def __str__(self):
return f"{self.name} ({self.country.name})" 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): class Profile(models.Model):
ROLE_CHOICES = ( ROLE_CHOICES = (
('SHIPPER', _('Shipper (Need Goods Moved)')), ('SHIPPER', _('Shipper (Need Goods Moved)')),
@ -73,8 +86,11 @@ class OTPCode(models.Model):
class Truck(models.Model): class Truck(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='trucks') 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 # 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) model = models.CharField(_('Model (EN)'), max_length=100)
load_capacity = models.CharField(_('Load Capacity (EN)'), max_length=100) load_capacity = models.CharField(_('Load Capacity (EN)'), max_length=100)
color = models.CharField(_('Color (EN)'), max_length=50) color = models.CharField(_('Color (EN)'), max_length=50)
@ -99,10 +115,14 @@ class Truck(models.Model):
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
def __str__(self): 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 @property
def display_truck_type(self): 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: if get_language() == 'ar' and self.truck_type_ar:
return self.truck_type_ar return self.truck_type_ar
return self.truck_type return self.truck_type
@ -136,6 +156,9 @@ class Shipment(models.Model):
description = models.TextField(_('Goods Description')) description = models.TextField(_('Goods Description'))
weight = models.CharField(_('Weight/Volume'), max_length=100) 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_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') 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') destination_country = models.ForeignKey(Country, on_delete=models.SET_NULL, null=True, related_name='shipments_destination')

View File

@ -20,13 +20,18 @@
</div> </div>
<div class="row mb-4"> <div class="row mb-4">
<div class="col-md-6"> <div class="col-md-4">
<label class="form-label fw-bold">{% trans "Weight/Volume" %}</label> <label class="form-label fw-bold">{% trans "Weight/Volume" %}</label>
{{ form.weight }} {{ form.weight }}
{% if form.weight.errors %}<div class="text-danger small">{{ form.weight.errors }}</div>{% endif %} {% if form.weight.errors %}<div class="text-danger small">{{ form.weight.errors }}</div>{% endif %}
</div> </div>
<div class="col-md-6"> <div class="col-md-4">
<label class="form-label fw-bold">{% trans "Requested Delivery Date" %}</label> <label class="form-label fw-bold">{% trans "Truck Type" %}</label>
{{ form.required_truck_type_link }}
{% if form.required_truck_type_link.errors %}<div class="text-danger small">{{ form.required_truck_type_link.errors }}</div>{% endif %}
</div>
<div class="col-md-4">
<label class="form-label fw-bold">{% trans "Delivery Date" %}</label>
{{ form.delivery_date }} {{ form.delivery_date }}
{% if form.delivery_date.errors %}<div class="text-danger small">{{ form.delivery_date.errors }}</div>{% endif %} {% if form.delivery_date.errors %}<div class="text-danger small">{{ form.delivery_date.errors }}</div>{% endif %}
</div> </div>

View File

@ -17,11 +17,21 @@
<h5 class="text-muted small text-uppercase fw-bold">{% trans "Shipment Details" %}</h5> <h5 class="text-muted small text-uppercase fw-bold">{% trans "Shipment Details" %}</h5>
<p class="lead fw-normal">{{ shipment.description }}</p> <p class="lead fw-normal">{{ shipment.description }}</p>
<div class="row mt-4"> <div class="row mt-4">
<div class="col-6 mb-3"> <div class="col-md-4 mb-3">
<small class="text-muted d-block text-uppercase small">{% trans "Weight / Volume" %}</small> <small class="text-muted d-block text-uppercase small">{% trans "Weight / Volume" %}</small>
<strong class="fs-5">{{ shipment.weight }}</strong> <strong class="fs-5">{{ shipment.weight }}</strong>
</div> </div>
<div class="col-6 mb-3"> <div class="col-md-4 mb-3">
<small class="text-muted d-block text-uppercase small">{% trans "Required Truck Type" %}</small>
<strong class="fs-5">
{% if shipment.required_truck_type_link %}
{{ shipment.required_truck_type_link }}
{% else %}
<span class="text-muted opacity-50">{% trans "Any" %}</span>
{% endif %}
</strong>
</div>
<div class="col-md-4 mb-3">
<small class="text-muted d-block text-uppercase small">{% trans "Requested Delivery Date" %}</small> <small class="text-muted d-block text-uppercase small">{% trans "Requested Delivery Date" %}</small>
<strong class="fs-5">{{ shipment.delivery_date }}</strong> <strong class="fs-5">{{ shipment.delivery_date }}</strong>
</div> </div>
@ -141,4 +151,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -26,14 +26,20 @@
<form method="post" enctype="multipart/form-data"> <form method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<div class="row mb-4">
<div class="col-12">
<div class="card bg-light border-0 p-3">
<label class="form-label fw-bold">{% trans "Truck Type" %}</label>
{{ form.truck_type_link }}
{{ form.truck_type_link.errors }}
<small class="text-muted">{% trans "Select the type of your truck from the list." %}</small>
</div>
</div>
</div>
<div class="row {% if CURRENT_LANGUAGE == "ar" %}flex-row-reverse{% endif %}"> <div class="row {% if CURRENT_LANGUAGE == "ar" %}flex-row-reverse{% endif %}">
<div class="col-md-6 {% if CURRENT_LANGUAGE == "ar" %}border-start{% else %}border-end{% endif %}"> <div class="col-md-6 {% if CURRENT_LANGUAGE == "ar" %}border-start{% else %}border-end{% endif %}">
<h5 class="mb-3 text-primary">{% trans "English Details" %}</h5> <h5 class="mb-3 text-primary">{% trans "English Details" %}</h5>
<div class="mb-3">
<label class="form-label">{% trans "Truck Type" %}</label>
{{ form.truck_type }}
{{ form.truck_type.errors }}
</div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">{% trans "Model" %}</label> <label class="form-label">{% trans "Model" %}</label>
{{ form.model }} {{ form.model }}
@ -53,11 +59,6 @@
<div class="col-md-6"> <div class="col-md-6">
<h5 class="mb-3 text-success">{% trans "التفاصيل باللغة العربية" %}</h5> <h5 class="mb-3 text-success">{% trans "التفاصيل باللغة العربية" %}</h5>
<div class="mb-3">
<label class="form-label">{% trans "نوع الشاحنة" %}</label>
{{ form.truck_type_ar }}
{{ form.truck_type_ar.errors }}
</div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">{% trans "الموديل" %}</label> <label class="form-label">{% trans "الموديل" %}</label>
{{ form.model_ar }} {{ form.model_ar }}
@ -157,4 +158,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}