37733-vm/core/views.py
Flatlogic Bot 066a111def changes20
2026-01-23 15:58:41 +00:00

380 lines
16 KiB
Python

from django.shortcuts import render, redirect, get_object_or_404
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, Country, City
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
from django.contrib.auth.models import User
from .whatsapp import send_whatsapp_message
from django.contrib.auth.forms import AuthenticationForm
def home(request):
"""Render the landing screen for MASAR CARGO."""
context = {
"deployment_timestamp": timezone.now().timestamp(),
}
return render(request, "core/index.html", context)
def register(request):
if request.method == 'POST':
form = UserRegistrationForm(request.POST)
if form.is_valid():
# Store data in session to be used after OTP verification
registration_data = {
'username': form.cleaned_data['username'],
'email': form.cleaned_data['email'],
'password': form.data['password1'], # We need raw password to create user later
'role': form.cleaned_data['role'],
'phone_number': form.cleaned_data['phone_number'],
'country_code': form.cleaned_data['country_code'],
}
request.session['registration_data'] = registration_data
# Send OTP
full_phone = f"{registration_data['country_code']}{registration_data['phone_number']}"
otp = OTPCode.generate_code(full_phone)
msg = _("Your verification code for MASAR CARGO is: %(code)s") % {"code": otp.code}
if send_whatsapp_message(full_phone, msg):
messages.info(request, _("A verification code has been sent to your WhatsApp."))
return redirect('verify_otp_registration')
else:
messages.error(request, _("Failed to send verification code. Please check your phone number."))
else:
messages.error(request, _("Please correct the errors below."))
else:
form = UserRegistrationForm()
return render(request, 'registration/register.html', {'form': form})
def verify_otp_registration(request):
registration_data = request.session.get('registration_data')
if not registration_data:
return redirect('register')
if request.method == 'POST':
form = OTPVerifyForm(request.POST)
if form.is_valid():
code = form.cleaned_data['otp_code']
full_phone = f"{registration_data['country_code']}{registration_data['phone_number']}"
otp_record = OTPCode.objects.filter(phone_number=full_phone, code=code, is_used=False).last()
if otp_record and otp_record.is_valid():
otp_record.is_used = True
otp_record.save()
# Create user
user = User.objects.create_user(
username=registration_data['username'],
email=registration_data['email'],
password=registration_data['password']
)
profile = user.profile
profile.role = registration_data['role']
profile.phone_number = registration_data['phone_number']
profile.country_code = registration_data['country_code']
profile.save()
login(request, user)
if 'registration_data' in request.session:
del request.session['registration_data']
messages.success(request, _("Registration successful. Welcome!"))
return redirect('dashboard')
else:
messages.error(request, _("Invalid or expired verification code."))
else:
form = OTPVerifyForm()
return render(request, 'registration/verify_otp.html', {'form': form, 'purpose': 'registration'})
def custom_login(request):
if request.method == 'POST':
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
user = form.get_user()
profile = user.profile
if not profile.phone_number:
messages.error(request, _("Your account does not have a phone number. Please contact admin."))
return redirect('login')
# Store user ID in session temporarily
request.session['pre_otp_user_id'] = user.id
# Send OTP
full_phone = profile.full_phone_number
otp = OTPCode.generate_code(full_phone)
msg = _("Your login verification code for MASAR CARGO is: %(code)s") % {"code": otp.code}
if send_whatsapp_message(full_phone, msg):
messages.info(request, _("A verification code has been sent to your WhatsApp."))
return redirect('verify_otp_login')
else:
# If WhatsApp fails, maybe allow login but warn? Or strictly enforce?
# For now, strictly enforce
messages.error(request, _("Failed to send verification code. Please check your connection."))
else:
messages.error(request, _("Invalid username or password."))
else:
form = AuthenticationForm()
return render(request, 'registration/login.html', {'form': form})
def verify_otp_login(request):
user_id = request.session.get('pre_otp_user_id')
if not user_id:
return redirect('login')
user = get_object_or_404(User, id=user_id)
profile = user.profile
if request.method == 'POST':
form = OTPVerifyForm(request.POST)
if form.is_valid():
code = form.cleaned_data['otp_code']
full_phone = profile.full_phone_number
otp_record = OTPCode.objects.filter(phone_number=full_phone, code=code, is_used=False).last()
if otp_record and otp_record.is_valid():
otp_record.is_used = True
otp_record.save()
login(request, user)
if 'pre_otp_user_id' in request.session:
del request.session['pre_otp_user_id']
messages.success(request, _("Logged in successfully!"))
return redirect('dashboard')
else:
messages.error(request, _("Invalid or expired verification code."))
else:
form = OTPVerifyForm()
return render(request, 'registration/verify_otp.html', {'form': form, 'purpose': 'login'})
@login_required
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')
# 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)
# 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_received_bids
})
elif profile.role == 'ADMIN' or request.user.is_superuser:
pending_trucks = Truck.objects.filter(is_approved=False).order_by('-created_at')
approved_trucks = Truck.objects.filter(is_approved=True).order_by('-created_at')
context = {
'total_users': User.objects.count(),
'total_trucks': Truck.objects.count(),
'total_shipments': Shipment.objects.count(),
'total_bids': Bid.objects.count(),
'pending_trucks': pending_trucks,
'approved_trucks': approved_trucks,
}
return render(request, 'core/admin_dashboard.html', context)
else:
return redirect('/')
@login_required
def truck_register(request):
if request.user.profile.role != 'TRUCK_OWNER':
return redirect('dashboard')
if request.method == 'POST':
form = TruckForm(request.POST, request.FILES)
if form.is_valid():
truck = form.save(commit=False)
truck.owner = request.user
truck.is_approved = False
truck.save()
messages.success(request, _("Truck registered successfully! It will be visible after admin approval."))
return redirect('dashboard')
else:
messages.error(request, _("There was an error in your registration. Please check the form."))
else:
form = TruckForm()
return render(request, 'core/truck_register.html', {'form': form})
@login_required
def edit_truck(request, truck_id):
truck = get_object_or_404(Truck, id=truck_id, owner=request.user)
if request.method == 'POST':
form = TruckForm(request.POST, request.FILES, instance=truck)
if form.is_valid():
truck = form.save(commit=False)
truck.is_approved = False
truck.save()
messages.success(request, _("Truck data updated successfully! It will be reviewed by admin again."))
return redirect('dashboard')
else:
messages.error(request, _("There was an error updating your truck. Please check the form."))
else:
form = TruckForm(instance=truck)
return render(request, 'core/truck_register.html', {'form': form, 'edit_mode': True, 'truck': truck})
@login_required
def approve_truck(request, truck_id):
if not (request.user.profile.role == 'ADMIN' or request.user.is_superuser):
return redirect('dashboard')
truck = get_object_or_404(Truck, id=truck_id)
truck.is_approved = True
truck.save()
owner_phone = getattr(truck.owner.profile, 'full_phone_number', None)
if owner_phone:
msg = _("Your truck (%(plate)s) has been approved! You can now receive offers for shipments.") % {"plate": truck.plate_no}
send_whatsapp_message(owner_phone, msg)
messages.success(request, _("Truck approved successfully!"))
return redirect('dashboard')
@login_required
def suspend_truck(request, truck_id):
if not (request.user.profile.role == 'ADMIN' or request.user.is_superuser):
return redirect('dashboard')
truck = get_object_or_404(Truck, id=truck_id)
truck.is_approved = False
truck.save()
messages.warning(request, _("Truck has been suspended."))
return redirect('dashboard')
@login_required
def post_shipment(request):
"""Note: This is used as the 'Add A Bid' / 'Add Post' action for Shippers."""
if request.user.profile.role != 'SHIPPER':
return redirect('dashboard')
if request.method == 'POST':
form = ShipmentForm(request.POST)
if form.is_valid():
shipment = form.save(commit=False)
shipment.shipper = request.user
shipment.save()
messages.success(request, _("Shipment posted successfully! It is now open for bids or you can browse trucks to send it as an offer."))
return redirect('dashboard')
else:
messages.error(request, _("Please correct the errors in the form."))
else:
form = ShipmentForm()
return render(request, 'core/post_shipment.html', {'form': form})
@login_required
def marketplace(request):
"""Shippers browse available trucks here."""
if request.user.profile.role != 'SHIPPER':
return redirect('dashboard')
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, 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 = ShipperOfferForm(request.POST)
if form.is_valid():
# Create Shipment
shipment = Shipment.objects.create(
shipper=request.user,
description=form.cleaned_data['description'],
weight=form.cleaned_data['weight'],
origin_country=form.cleaned_data['origin_country'],
origin_city=form.cleaned_data['origin_city'],
destination_country=form.cleaned_data['destination_country'],
destination_city=form.cleaned_data['destination_city'],
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 Truck Owner via WhatsApp
owner_phone = getattr(truck.owner.profile, 'full_phone_number', None)
if owner_phone:
msg = _("New offer received for your truck (%(plate)s)! Route: %(origin)s to %(dest)s. Amount: %(amount)s") % {"plate": truck.plate_no, "origin": shipment.display_origin, "dest": shipment.display_destination, "amount": bid.amount}
send_whatsapp_message(owner_phone, msg)
messages.success(request, _("Offer sent successfully!"))
return redirect('dashboard')
else:
messages.error(request, _("Error sending offer. Please check the form."))
else:
form = ShipperOfferForm()
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, 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.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()
# Update shipment
bid.shipment.status = 'IN_PROGRESS'
bid.shipment.assigned_truck = bid.truck
bid.shipment.save()
# Notify Shipper via WhatsApp
shipper_phone = getattr(bid.shipment.shipper.profile, 'full_phone_number', None)
if shipper_phone:
msg = _("Your offer for truck %(plate)s (%(origin)s to %(dest)s) has been accepted!") % {"plate": bid.truck.plate_no, "origin": bid.shipment.display_origin, "dest": bid.shipment.display_destination}
send_whatsapp_message(shipper_phone, msg)
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')