From 80485899cb714fbd8c7bd2618dd9f6ae0470490c Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 23 Jan 2026 13:50:58 +0000 Subject: [PATCH] demo16 --- core/__pycache__/forms.cpython-311.pyc | Bin 10719 -> 12434 bytes core/__pycache__/urls.cpython-311.pyc | Bin 1986 -> 2085 bytes core/__pycache__/views.cpython-311.pyc | Bin 20815 -> 22043 bytes core/forms.py | 11 +- core/templates/core/marketplace.html | 51 +++-- core/templates/core/place_bid.html | 98 ++++++--- core/templates/core/shipment_detail.html | 83 +++++--- core/templates/core/shipper_dashboard.html | 164 ++++++++++----- .../templates/core/truck_owner_dashboard.html | 196 +++++++++--------- core/urls.py | 5 +- core/views.py | 128 +++++++----- 11 files changed, 457 insertions(+), 279 deletions(-) diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc index b9f3e549c53b26a5729385cae315dc476ce2a26a..82e7cc0f947e9b3dbb67aff82a84edb54ad1a527 100644 GIT binary patch delta 1606 zcma)6O-vg{6y90$4{Q8~p)?H;6k?+`fFkNhsFn!EATrF?BLM--QP|*E#xPy;ON->>-2Y|BaCK8!*qtlKR0=8$=%t>b5boO zJA~sNz*8Q;Syt<`ynERzr*ch_+ZZ8LM~Om7tsDzedF$7(4lA)#CMG8pJBXC85WYrm0C;)Djqhy$IbmLkMOW0!z=v`? zn~)UjwNy8)#G_0ZNTnW-unjsd59!FOh5sNMSs+ z%wI=dL)b=m1K}jX353ECtiwyX8+yufc@+WXp`&N(^!7lJ6bb8pPxtr@kAHt-Kl@~Z zMKWwd{hQ+!$teFSOp*)ybB@FCq8wn)Iffh{P=NXO4}}w(bGJf;A($b(ci8A1-jnyF zUu8CTjV;C52Wo=(le#}?_>)YKbwM@+IUlT)E!-KoJMu_J3Lh_ZQ!U5YSYRQE*< zUz7jbKdZr-|yOQd(y=gLQIs|e2R^(>7#4L=o)h3e=~% zdbh^6r;9zs9`^RQ-XApjg9qwC_NmI`8_>}tKZQ+4$aF=RAv4$~251xmq_kZ|4iG57 z{G~&oYje&Jx(Z@JEL!zGpV8;rzrf}fn5Zx@Vu%W;)Bt}4)z|oC3^@|62+M?`Ck8OZ z04Y`pIY6KQq4U<0o7r1x;WEtJoyEJ0dvgc=gW;!swz>}aV|)N*A{nm;7fBFZE|RlB z^uZ`9P7Up<)slj^vY P-OfEX_g?%@hI!0?9A=R2 delta 98 zcmbP~ct4nLIWI340}#j-6lY3HOyrYb7|ft4v^j>cQIWlh#X!%%P;>JW yC1Ga9>6?G3E@x!+(-fclP51EReR`5yOMvqMc+^j`%Kmh>L-x)pt diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index fddd74705a416f62f44c89cd26a7a115b2c74a0b..aadbeb1aedc187757798f4e4a1546ff1833f289a 100644 GIT binary patch delta 264 zcmX@azf?edIWI340}yPfD9&_aWng#=;=q6?l<|4nM)h8%i4RycRa2R>#9`7YYHQe* zF*7i%24VudsOH691qq#Lv{g4T43A GKyv~25K3?W delta 165 zcmZ1~aEM=hIWI340}vd|Db8eIWng#=;=q6il<|4eM)h7Mu5^JY@f6iy22HiikC^T< z%F7pLWESM6=9TE%WagDv!FchRDR%lvnJJTRu^2J?X-Z5sWnDJ;0jn>U63{qCATB<) s*_>@2qkz^5*ERkJv@fvuU1agQ!s0jiFS{haq98w012+g3DFD?108L>l-2eap diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index be364c3471f8249f9bf73f5e50ea144fede760ed..0d02fbb5857a57865929d9b9eb03e6205d552eab 100644 GIT binary patch delta 7583 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 SC!1J+Bmg;57QwJgUH=EqY8bNs delta 6221 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 -

{% trans "Shipment Marketplace" %}

+

{% trans "Truck Marketplace" %}

+

{% trans "Browse available trucks and send your shipping offers directly to truck owners." %}

+
- {% for shipment in shipments %} -
-
+ {% for truck in trucks %} +
+
+ {% if truck.truck_picture %} + {{ truck.display_truck_type }} + {% else %} +
+ +
+ {% endif %}
- {% trans "Open for Bids" %} - {{ shipment.created_at|timesince }} {% trans "ago" %} + {% trans "Available" %} + {% trans "Plate" %}: {{ truck.plate_no }}
-
{{ shipment.origin }} {{ shipment.destination }}
-

{{ shipment.description|truncatechars:100 }}

-
-
- {% trans "Weight" %} - {{ shipment.weight }} -
-
- {% trans "Delivery Date" %} - {{ shipment.delivery_date }} -
+
{{ truck.display_truck_type }} - {{ truck.display_model }}
+
+ {% trans "Year" %}: {{ truck.year }} + {% trans "Capacity" %}: {{ truck.display_load_capacity }}
- {% trans "Place an Offer" %} +

+ {% trans "Owner" %}: {{ truck.owner.username }} +

+ + {% trans "Send Shipping Offer" %} +
{% empty %}
-

{% trans "No shipments available at the moment." %}

+
+ +

{% trans "No approved trucks are currently available." %}

+
{% endfor %}
-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/place_bid.html b/core/templates/core/place_bid.html index ea68a77..6abaa7d 100644 --- a/core/templates/core/place_bid.html +++ b/core/templates/core/place_bid.html @@ -4,54 +4,94 @@ {% block content %}
-
-
+
+
-

{% trans "Place an Offer" %}

-
- {% trans "Shipment:" %} {{ shipment.origin }} to {{ shipment.destination }}
- {% trans "Goods:" %} {{ shipment.description }} +

{% trans "Send Shipping Offer" %}

+ +
+
+
{% trans "Target Truck" %}
+
+ {% if truck.truck_picture %} + + {% else %} +
+ +
+ {% endif %} +
+
{{ truck.display_truck_type }} - {{ truck.display_model }}
+ {% trans "Owner" %}: {{ truck.owner.username }} | {% trans "Plate" %}: {{ truck.plate_no }} +
+
+
- {% if form.errors %} -
- {% trans "Please correct the errors below." %} - {{ form.non_field_errors }} -
- {% endif %} - - {% if form.fields.truck.queryset.exists %} -
+ {% csrf_token %} + +
{% trans "Shipment Details" %}
+
- - {{ form.truck }} - {{ form.truck.errors }} + + {{ form.description }} + {{ form.description.errors }}
+ +
+
+ + {{ form.origin }} + {{ form.origin.errors }} +
+
+ + {{ form.destination }} + {{ form.destination.errors }} +
+
+ +
+
+ + {{ form.weight }} + {{ form.weight.errors }} +
+
+ + {{ form.delivery_date }} + {{ form.delivery_date.errors }} +
+
+ +
{% trans "Pricing & Terms" %}
+
- +
$ {{ form.amount }}
{{ form.amount.errors }}
-
- + +
+ {{ form.comments }} {{ form.comments.errors }}
- + +
+ + {% trans "Cancel" %} +
- {% else %} -
-

{% trans "You must register a truck before placing a bid." %}

- {% trans "Register Truck Now" %} -
- {% endif %}
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/core/templates/core/shipment_detail.html b/core/templates/core/shipment_detail.html index 7238546..df5aab2 100644 --- a/core/templates/core/shipment_detail.html +++ b/core/templates/core/shipment_detail.html @@ -14,50 +14,61 @@

-
{% trans "Details" %}
-

{{ shipment.description }}

+
{% trans "Shipment Details" %}
+

{{ shipment.description }}

{% trans "Weight" %} {{ shipment.weight }}
- {% trans "Delivery Date" %} + {% trans "Requested Delivery Date" %} {{ shipment.delivery_date }}
- {% if user == shipment.shipper and shipment.status == 'OPEN' %} -
+ +
-
{% trans "Received Bids" %}
+
{% trans "Offer Status" %}
- + {% for bid in bids %} - - + + {% empty %} - + {% endfor %} @@ -65,37 +76,49 @@ - {% endif %} {% if shipment.status == 'IN_PROGRESS' %} -
- +
+
- {% trans "Shipment in progress!" %}
- {% trans "Assigned Truck:" %} {{ shipment.assigned_truck.display_truck_type }} ({{ shipment.assigned_truck.plate_no }}) +
{% trans "Shipment is IN PROGRESS" %}
+

{% trans "Assigned Truck:" %} {{ shipment.assigned_truck.display_truck_type }} ({{ shipment.assigned_truck.plate_no }})

- {% endif %}
-
+
-
{% trans "Contact Information" %}
-

- {% trans "Shipper:" %} {{ shipment.shipper.username }}
- {% if shipment.status == 'IN_PROGRESS' %} - {% trans "Phone:" %} {{ shipment.shipper.profile.phone_number }} - {% endif %} -

+
{% trans "Stakeholders" %}
+
+ {% trans "Shipper" %} + {{ shipment.shipper.username }} +
+ {% if shipment.assigned_truck %} +
+ {% trans "Truck Owner" %} + {{ shipment.assigned_truck.owner.username }} +
+ {% endif %}
+ + {% if shipment.status == 'IN_PROGRESS' %} +
+ {% if user == shipment.shipper %} + + {% trans "Message Driver" %} + + {% elif user == shipment.assigned_truck.owner %} + + {% trans "Message Shipper" %} + + {% endif %} +
+ {% endif %}
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/core/templates/core/shipper_dashboard.html b/core/templates/core/shipper_dashboard.html index 9672079..cf8ba59 100644 --- a/core/templates/core/shipper_dashboard.html +++ b/core/templates/core/shipper_dashboard.html @@ -4,58 +4,126 @@ {% block content %}
{% trans "Truck Owner" %} {% trans "Truck" %} {% trans "Amount" %}{% trans "Status" %} {% trans "Action" %}
{{ bid.truck_owner.username }}{{ bid.truck.display_truck_type }}{{ bid.truck.display_truck_type }} ({{ bid.truck.plate_no }}) ${{ bid.amount }} - {% trans "Accept" %} + + {{ bid.get_status_display }} + + + {% if bid.status == 'PENDING' and user == bid.truck_owner %} + + {% else %} + {% trans "No action available" %} + {% endif %}
{% trans "No bids received yet." %}{% trans "No offers for this shipment." %}
- - - - - - - - - - - - {% for shipment in shipments %} - - - - - - - - - {% empty %} - - - - {% endfor %} - -
{% trans "Description" %}{% trans "Route" %}{% trans "Delivery Date" %}{% trans "Status" %}{% trans "Bids" %}{% trans "Action" %}
{{ shipment.description|truncatechars:30 }}{{ shipment.origin }} {{ shipment.destination }}{{ shipment.delivery_date }} - - {{ shipment.get_status_display }} - - {{ shipment.bids.count }} - {% trans "View Details" %} -
{% trans "No shipments posted yet." %}
-
-
+ +
+
+
{% trans "My Shipping Offers" %}
+
+
+
+ + + + + + + + + + + + + {% for bid in bids %} + + + + + + + + + {% empty %} + + + + {% endfor %} + +
{% trans "Truck" %}{% trans "Route" %}{% trans "Amount" %}{% trans "Date Sent" %}{% trans "Status" %}{% trans "Action" %}
+
{{ bid.truck.display_truck_type }}
+ {{ bid.truck.plate_no }} +
+ {{ bid.shipment.origin }} + + {{ bid.shipment.destination }} + ${{ bid.amount }}{{ bid.created_at|date:"d M Y" }} + {% if bid.status == 'PENDING' %} + {% trans "Pending" %} + {% elif bid.status == 'ACCEPTED' %} + {% trans "Accepted" %} + {% else %} + {% trans "Rejected" %} + {% endif %} + + {% trans "Details" %} +
+ +

{% trans "You haven't sent any offers yet." %}

+ {% trans "Find a Truck" %} +
+
+
+
+ + +
+
+
{% trans "Active Shipments" %}
+
+
+
+ + + + + + + + + + + + + {% for shipment in shipments %} + {% if shipment.status != 'OPEN' or not shipment.bids.exists %} + + + + + + + + + {% endif %} + {% empty %} + + + + {% endfor %} + +
{% trans "Description" %}{% trans "Route" %}{% trans "Delivery Date" %}{% trans "Status" %}{% trans "Assigned Truck" %}{% trans "Action" %}
{{ shipment.description|truncatechars:30 }}{{ shipment.origin }} {{ shipment.destination }}{{ shipment.delivery_date }} + + {{ shipment.get_status_display }} + + + {% if shipment.assigned_truck %} + {{ shipment.assigned_truck.plate_no }} + {% else %} + {% trans "None" %} + {% endif %} + + {% trans "Track" %} +
{% trans "No active shipments." %}
-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/truck_owner_dashboard.html b/core/templates/core/truck_owner_dashboard.html index f51caf0..8c1013d 100644 --- a/core/templates/core/truck_owner_dashboard.html +++ b/core/templates/core/truck_owner_dashboard.html @@ -7,121 +7,119 @@

{% trans "Truck Owner Dashboard" %}

-

{% trans "Manage your fleet and active bids." %}

+

{% trans "Manage your fleet and incoming shipping offers." %}

- -

{% trans "My Approved Trucks" %}

-
- {% if trucks %} - {% for truck in trucks %} -
-
- {% if truck.truck_picture %} - {{ truck.display_truck_type }} - {% else %} -
- -
- {% endif %} -
-
{{ truck.display_truck_type }}
-

{% trans "Plate No:" %} {{ truck.plate_no }}

-

{% trans "Model:" %} {{ truck.display_model }} ({{ truck.year }})

-

{% trans "Capacity:" %} {{ truck.display_load_capacity }}

- {% if truck.registration_expiry_date %} -

{% trans "Expiry Date:" %} {{ truck.registration_expiry_date }}

- {% endif %} - - {% trans "Edit Details" %} - -
- -
+ +
+
+
{% trans "Received Shipping Offers" %}
+
+
+
+ + + + + + + + + + + + + {% for bid in bids %} + + + + + + + + + {% empty %} + + + + {% endfor %} + +
{% trans "Route" %}{% trans "Truck" %}{% trans "Shipper" %}{% trans "Offer Amount" %}{% trans "Status" %}{% trans "Action" %}
+
{{ bid.shipment.origin }} {{ bid.shipment.destination }}
+ {{ bid.shipment.delivery_date }} +
{{ bid.truck.plate_no }}{{ bid.shipment.shipper.username }}${{ bid.amount }} + {% if bid.status == 'PENDING' %} + {% trans "Pending" %} + {% elif bid.status == 'ACCEPTED' %} + {% trans "Accepted" %} + {% else %} + {% trans "Rejected" %} + {% endif %} + + {% if bid.status == 'PENDING' %} + + {% else %} + {% trans "View Details" %} + {% endif %} +
+ +

{% trans "No offers received yet." %}

+
- {% endfor %} - {% else %} -
-

{% trans "No approved trucks yet." %}

-
- {% endif %} +
- - {% if pending_trucks %} -

{% trans "Pending Approval" %}

-
- {% for truck in pending_trucks %} + +

{% trans "My Fleet" %}

+
+ {% for truck in trucks %}
-
+
+ {% if truck.truck_picture %} + {{ truck.display_truck_type }} + {% else %} +
+ +
+ {% endif %} - +
+ {% endfor %} + + {% for truck in pending_trucks %} +
+
+
+
+
{{ truck.display_truck_type }}
+ {% trans "Pending" %} +
+

{% trans "Plate No:" %} {{ truck.plate_no }}

+

{% trans "Waiting for admin approval..." %}

{% endfor %}
- {% endif %} - -
-
-
-
-
{% trans "My Active Bids" %}
-
-
-
- - - - - - - - - - - {% for bid in bids %} - - - - - - - {% empty %} - - - - {% endfor %} - -
{% trans "Shipment" %}{% trans "Amount" %}{% trans "Status" %}{% trans "Date" %}
{{ bid.shipment.origin }} - {{ bid.shipment.destination }}${{ bid.amount }} - - {{ bid.get_status_display }} - - {{ bid.created_at|date }}
{% trans "No bids placed yet." %}
-
-
-
-
-
-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/urls.py b/core/urls.py index 597f03a..1283e6c 100644 --- a/core/urls.py +++ b/core/urls.py @@ -17,6 +17,7 @@ urlpatterns = [ path("shipment/post/", views.post_shipment, name="post_shipment"), path("marketplace/", views.marketplace, name="marketplace"), path("shipment//", views.shipment_detail, name="shipment_detail"), - path("shipment//bid/", views.place_bid, name="place_bid"), + path("truck//offer/", views.place_bid, name="place_bid"), path("bid//accept/", views.accept_bid, name="accept_bid"), -] + path("bid//reject/", views.reject_bid, name="reject_bid"), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index 1bda90a..ee86512 100644 --- a/core/views.py +++ b/core/views.py @@ -3,7 +3,7 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth import login, authenticate, logout from django.utils import timezone from .models import Profile, Truck, Shipment, Bid, Message, OTPCode -from .forms import TruckForm, ShipmentForm, BidForm, UserRegistrationForm, OTPVerifyForm +from .forms import TruckForm, ShipmentForm, BidForm, UserRegistrationForm, OTPVerifyForm, ShipperOfferForm from django.contrib import messages from django.utils.translation import gettext as _ from django.db.models import Q @@ -152,15 +152,21 @@ def dashboard(request): profile, created = Profile.objects.get_or_create(user=request.user) if profile.role == 'SHIPPER': my_shipments = Shipment.objects.filter(shipper=request.user).order_by('-created_at') - return render(request, 'core/shipper_dashboard.html', {'shipments': my_shipments}) + # In the new flow, Shippers place bids. + my_bids = Bid.objects.filter(shipment__shipper=request.user).order_by('-created_at') + return render(request, 'core/shipper_dashboard.html', { + 'shipments': my_shipments, + 'bids': my_bids + }) elif profile.role == 'TRUCK_OWNER': approved_trucks = Truck.objects.filter(owner=request.user, is_approved=True) pending_trucks = Truck.objects.filter(owner=request.user, is_approved=False) - my_bids = Bid.objects.filter(truck_owner=request.user).order_by('-created_at') + # Truck owners receive bids in the new flow + my_received_bids = Bid.objects.filter(truck_owner=request.user).order_by('-created_at') return render(request, 'core/truck_owner_dashboard.html', { 'trucks': approved_trucks, 'pending_trucks': pending_trucks, - 'bids': my_bids + 'bids': my_received_bids }) elif profile.role == 'ADMIN' or request.user.is_superuser: pending_trucks = Truck.objects.filter(is_approved=False).order_by('-created_at') @@ -175,7 +181,6 @@ def dashboard(request): } return render(request, 'core/admin_dashboard.html', context) else: - # Fallback for undefined roles return redirect('/') @login_required @@ -188,7 +193,7 @@ def truck_register(request): if form.is_valid(): truck = form.save(commit=False) truck.owner = request.user - truck.is_approved = False # Ensure it stays false on new reg + truck.is_approved = False truck.save() messages.success(request, _("Truck registered successfully! It will be visible after admin approval.")) return redirect('dashboard') @@ -207,7 +212,7 @@ def edit_truck(request, truck_id): form = TruckForm(request.POST, request.FILES, instance=truck) if form.is_valid(): truck = form.save(commit=False) - truck.is_approved = False # Reset approval status on update + truck.is_approved = False truck.save() messages.success(request, _("Truck data updated successfully! It will be reviewed by admin again.")) return redirect('dashboard') @@ -227,10 +232,9 @@ def approve_truck(request, truck_id): truck.is_approved = True truck.save() - # Notify Truck Owner via WhatsApp owner_phone = getattr(truck.owner.profile, 'full_phone_number', None) if owner_phone: - msg = f"Your truck ({truck.plate_no}) has been approved! You can now place bids on shipments." + msg = f"Your truck ({truck.plate_no}) has been approved! You can now receive offers for shipments." send_whatsapp_message(owner_phone, msg) messages.success(request, _("Truck approved successfully!")) @@ -249,6 +253,7 @@ def suspend_truck(request, truck_id): @login_required def post_shipment(request): + """Note: This is now largely redundant but kept for compatibility or direct posting.""" if request.user.profile.role != 'SHIPPER': return redirect('dashboard') @@ -269,80 +274,103 @@ def post_shipment(request): @login_required def marketplace(request): - if request.user.profile.role != 'TRUCK_OWNER': + """Shippers browse available trucks here.""" + if request.user.profile.role != 'SHIPPER': return redirect('dashboard') - shipments = Shipment.objects.filter(status='OPEN').order_by('-created_at') - return render(request, 'core/marketplace.html', {'shipments': shipments}) + trucks = Truck.objects.filter(is_approved=True).order_by('-created_at') + return render(request, 'core/marketplace.html', {'trucks': trucks}) @login_required -def place_bid(request, shipment_id): - shipment = get_object_or_404(Shipment, id=shipment_id) - if request.user.profile.role != 'TRUCK_OWNER': - return redirect('dashboard') - - # Optional: Only allow bidding if user has at least one approved truck - if not Truck.objects.filter(owner=request.user, is_approved=True).exists(): - messages.warning(request, _("You must have at least one approved truck to place a bid.")) +def place_bid(request, truck_id): + """Shipper makes an offer to a specific truck.""" + truck = get_object_or_404(Truck, id=truck_id, is_approved=True) + if request.user.profile.role != 'SHIPPER': return redirect('dashboard') if request.method == 'POST': - form = BidForm(request.POST, user=request.user) + form = ShipperOfferForm(request.POST) if form.is_valid(): - bid = form.save(commit=False) - bid.truck_owner = request.user - bid.shipment = shipment - bid.save() + # Create Shipment + shipment = Shipment.objects.create( + shipper=request.user, + description=form.cleaned_data['description'], + weight=form.cleaned_data['weight'], + origin=form.cleaned_data['origin'], + destination=form.cleaned_data['destination'], + delivery_date=form.cleaned_data['delivery_date'], + status='OPEN' + ) + # Create Bid (Offer) + bid = Bid.objects.create( + shipment=shipment, + truck_owner=truck.owner, + truck=truck, + amount=form.cleaned_data['amount'], + comments=form.cleaned_data.get('comments', ''), + status='PENDING' + ) - # Notify Shipper via WhatsApp - shipper_phone = getattr(shipment.shipper.profile, 'full_phone_number', None) - if shipper_phone: - msg = f"New bid placed on your shipment from {shipment.origin} to {shipment.destination}! Amount: {bid.amount}" - send_whatsapp_message(shipper_phone, msg) + # Notify Truck Owner via WhatsApp + owner_phone = getattr(truck.owner.profile, 'full_phone_number', None) + if owner_phone: + msg = f"New offer received for your truck ({truck.plate_no})! Route: {shipment.origin} to {shipment.destination}. Amount: {bid.amount}" + send_whatsapp_message(owner_phone, msg) - messages.success(request, _("Bid placed successfully!")) - return redirect('marketplace') + messages.success(request, _("Offer sent successfully!")) + return redirect('dashboard') else: - messages.error(request, _("Error placing bid. Please check the form.")) + messages.error(request, _("Error sending offer. Please check the form.")) else: - form = BidForm(user=request.user) + form = ShipperOfferForm() - return render(request, 'core/place_bid.html', {'form': form, 'shipment': shipment}) + return render(request, 'core/place_bid.html', {'form': form, 'truck': truck}) @login_required def shipment_detail(request, shipment_id): shipment = get_object_or_404(Shipment, id=shipment_id) - # Security: check if user is shipper or a truck owner who bid - if shipment.shipper != request.user and not Bid.objects.filter(shipment=shipment, truck_owner=request.user).exists(): - if request.user.profile.role != 'ADMIN' and not request.user.is_superuser: - return redirect('dashboard') + # Security: check if user is shipper, truck owner of a bid, or admin + is_involved = Bid.objects.filter(shipment=shipment, truck_owner=request.user).exists() or shipment.shipper == request.user + if not is_involved and not (request.user.profile.role == 'ADMIN' or request.user.is_superuser): + return redirect('dashboard') bids = shipment.bids.all() return render(request, 'core/shipment_detail.html', {'shipment': shipment, 'bids': bids}) @login_required def accept_bid(request, bid_id): + """Truck owner accepts an offer from a shipper.""" bid = get_object_or_404(Bid, id=bid_id) - if bid.shipment.shipper != request.user: + if bid.truck_owner != request.user: + messages.error(request, _("You are not authorized to accept this offer.")) return redirect('dashboard') # Accept this bid bid.status = 'ACCEPTED' bid.save() - # Reject others - bid.shipment.bids.exclude(id=bid_id).update(status='REJECTED') - # Update shipment bid.shipment.status = 'IN_PROGRESS' bid.shipment.assigned_truck = bid.truck bid.shipment.save() - # Notify Truck Owner via WhatsApp - owner_phone = getattr(bid.truck_owner.profile, 'full_phone_number', None) - if owner_phone: - msg = f"Congratulations! Your bid for the shipment from {bid.shipment.origin} to {bid.shipment.destination} has been accepted." - send_whatsapp_message(owner_phone, msg) + # Notify Shipper via WhatsApp + shipper_phone = getattr(bid.shipment.shipper.profile, 'full_phone_number', None) + if shipper_phone: + msg = f"Your offer for truck {bid.truck.plate_no} ({bid.shipment.origin} to {bid.shipment.destination}) has been accepted!" + send_whatsapp_message(shipper_phone, msg) - messages.success(request, _("Bid accepted! Shipment is now in progress.")) - return redirect('shipment_detail', shipment_id=bid.shipment.id) \ No newline at end of file + messages.success(request, _("Offer accepted! Shipment is now in progress.")) + return redirect('dashboard') + +@login_required +def reject_bid(request, bid_id): + """Truck owner rejects an offer.""" + bid = get_object_or_404(Bid, id=bid_id) + if bid.truck_owner != request.user: + return redirect('dashboard') + + bid.status = 'REJECTED' + bid.save() + messages.info(request, _("Offer rejected.")) + return redirect('dashboard')