37794-vm/core/views.py
2026-01-26 17:46:01 +00:00

891 lines
34 KiB
Python

from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from .models import Parcel, Profile, Country, Governate, City, OTPVerification, PlatformProfile, Testimonial, DriverRating
from .forms import UserRegistrationForm, ParcelForm, ContactForm, UserProfileForm, DriverRatingForm
from django.utils.translation import gettext_lazy as _
from django.utils.translation import get_language
from django.contrib import messages
from django.http import JsonResponse, HttpResponse
from django.urls import reverse
from .payment_utils import ThawaniPay
from django.conf import settings
from django.core.mail import send_mail
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.views.decorators.http import require_POST
from django.db.models import Avg, Count
from django.template.loader import render_to_string
import random
import string
from .whatsapp_utils import (
notify_shipment_created,
notify_payment_received,
notify_driver_assigned,
notify_status_change,
send_whatsapp_message
)
from .mail import send_contact_message, send_html_email
import json
from ai.local_ai_api import LocalAIApi
import weasyprint
import qrcode
from io import BytesIO
import base64
def index(request):
# If tracking_id is present, redirect to the new track view
tracking_id = request.GET.get('tracking_id')
if tracking_id:
return redirect(f"{reverse('track')}?tracking_number={tracking_id}")
testimonials = Testimonial.objects.filter(is_active=True)
# Top 5 Drivers (by Average Rating)
top_drivers = Profile.objects.filter(role='car_owner').annotate(
avg_rating=Avg('user__received_ratings__rating'),
rating_count=Count('user__received_ratings')
).filter(rating_count__gt=0).order_by('-avg_rating')[:5]
# Top 5 Shippers (by Shipment Count)
top_shippers = Profile.objects.filter(role='shipper').annotate(
shipment_count=Count('user__sent_parcels')
).order_by('-shipment_count')[:5]
return render(request, 'core/index.html', {
'testimonials': testimonials,
'top_drivers': top_drivers,
'top_shippers': top_shippers
})
def track_parcel(request):
tracking_number = request.GET.get('tracking_number')
parcel = None
error = None
if tracking_number:
try:
parcel = Parcel.objects.get(tracking_number__iexact=tracking_number.strip())
except Parcel.DoesNotExist:
error = _("Parcel not found with this Tracking ID.")
return render(request, 'core/track.html', {
'parcel': parcel,
'error': error,
'tracking_number': tracking_number
})
def register(request):
if request.method == 'POST':
form = UserRegistrationForm(request.POST)
if form.is_valid():
# Save user but inactive
user = form.save(commit=True)
user.is_active = False
user.save()
# Generate OTP
code = ''.join(random.choices(string.digits, k=6))
OTPVerification.objects.create(user=user, code=code, purpose='registration')
# Send OTP
method = form.cleaned_data.get('verification_method', 'email')
otp_msg = _("Your Masar Verification Code is %(code)s") % {'code': code}
if method == 'whatsapp':
phone = user.profile.phone_number
send_whatsapp_message(phone, otp_msg)
messages.info(request, _("Verification code sent to WhatsApp."))
else:
send_html_email(
subject=_('Verification Code'),
message=otp_msg,
recipient_list=[user.email],
title=_('Welcome to Masar!'),
request=request
)
messages.info(request, _("Verification code sent to email."))
request.session['registration_user_id'] = user.id
return redirect('verify_registration')
else:
form = UserRegistrationForm()
return render(request, 'core/register.html', {'form': form})
def verify_registration(request):
if 'registration_user_id' not in request.session:
messages.error(request, _("Session expired or invalid."))
return redirect('register')
if request.method == 'POST':
code = request.POST.get('code')
user_id = request.session['registration_user_id']
try:
user = User.objects.get(id=user_id)
otp = OTPVerification.objects.filter(
user=user,
code=code,
purpose='registration',
is_verified=False
).latest('created_at')
if otp.is_valid():
# Activate User
user.is_active = True
user.save()
# Cleanup
otp.is_verified = True
otp.save()
del request.session['registration_user_id']
# Login
login(request, user)
messages.success(request, _("Account verified successfully!"))
return redirect('dashboard')
else:
messages.error(request, _("Invalid or expired code."))
except (User.DoesNotExist, OTPVerification.DoesNotExist):
messages.error(request, _("Invalid code."))
return render(request, 'core/verify_registration.html')
@login_required
def dashboard(request):
# Ensure profile exists
profile, created = Profile.objects.get_or_create(user=request.user)
if profile.role == 'shipper':
all_parcels = Parcel.objects.filter(shipper=request.user).order_by('-created_at')
active_parcels_list = all_parcels.exclude(status__in=['delivered', 'cancelled'])
# Split history into delivered and cancelled
delivered_parcels = all_parcels.filter(status='delivered')
cancelled_parcels = all_parcels.filter(status='cancelled')
# Pagination for Active Shipments
page = request.GET.get('page', 1)
paginator = Paginator(active_parcels_list, 9) # Show 9 parcels per page
try:
active_parcels = paginator.page(page)
except PageNotAnInteger:
active_parcels = paginator.page(1)
except EmptyPage:
active_parcels = paginator.page(paginator.num_pages)
platform_profile = PlatformProfile.objects.first()
payments_enabled = platform_profile.enable_payment if platform_profile else True
return render(request, 'core/shipper_dashboard.html', {
'active_parcels': active_parcels,
'delivered_parcels': delivered_parcels,
'cancelled_parcels': cancelled_parcels,
'payments_enabled': payments_enabled,
'platform_profile': platform_profile # Pass full profile just in case
})
else:
# Car Owner view
platform_profile = PlatformProfile.objects.first()
payments_enabled = platform_profile.enable_payment if platform_profile else True
if payments_enabled:
available_parcels_list = Parcel.objects.filter(status='pending', payment_status='paid').order_by('-created_at')
else:
available_parcels_list = Parcel.objects.filter(status='pending').order_by('-created_at')
# Pagination for Available Shipments
page = request.GET.get('page', 1)
paginator = Paginator(available_parcels_list, 9) # Show 9 parcels per page
try:
available_parcels = paginator.page(page)
except PageNotAnInteger:
available_parcels = paginator.page(1)
except EmptyPage:
available_parcels = paginator.page(paginator.num_pages)
# Active: Picked up or In Transit
my_parcels = Parcel.objects.filter(carrier=request.user).exclude(status__in=['delivered', 'cancelled']).order_by('-created_at')
# History: Delivered
completed_parcels = Parcel.objects.filter(carrier=request.user, status='delivered').order_by('-created_at')
# Cancelled
cancelled_parcels = Parcel.objects.filter(carrier=request.user, status='cancelled').order_by('-created_at')
return render(request, 'core/driver_dashboard.html', {
'available_parcels': available_parcels,
'my_parcels': my_parcels,
'completed_parcels': completed_parcels,
'cancelled_parcels': cancelled_parcels
})
@login_required
def shipment_request(request):
profile, created = Profile.objects.get_or_create(user=request.user)
if profile.role != 'shipper':
messages.error(request, _("Only shippers can request shipments."))
return redirect('dashboard')
if request.method == 'POST':
form = ParcelForm(request.POST)
if form.is_valid():
parcel = form.save(commit=False)
parcel.shipper = request.user
parcel.save()
# WhatsApp Notification
notify_shipment_created(parcel)
messages.success(request, _("Shipment requested successfully! Tracking ID: ") + parcel.tracking_number)
return redirect('dashboard')
else:
form = ParcelForm()
return render(request, 'core/shipment_request.html', {'form': form})
@login_required
def accept_parcel(request, parcel_id):
profile, created = Profile.objects.get_or_create(user=request.user)
if profile.role != 'car_owner':
messages.error(request, _("Only car owners can accept shipments."))
return redirect('dashboard')
platform_profile = PlatformProfile.objects.first()
payments_enabled = platform_profile.enable_payment if platform_profile else True
if payments_enabled:
parcel = get_object_or_404(Parcel, id=parcel_id, status='pending', payment_status='paid')
else:
parcel = get_object_or_404(Parcel, id=parcel_id, status='pending')
parcel.carrier = request.user
parcel.status = 'picked_up'
parcel.save()
# WhatsApp Notification
notify_driver_assigned(parcel)
messages.success(request, _("You have accepted the shipment!"))
return redirect('dashboard')
@login_required
def update_status(request, parcel_id):
parcel = get_object_or_404(Parcel, id=parcel_id, carrier=request.user)
if request.method == 'POST':
new_status = request.POST.get('status')
if new_status in dict(Parcel.STATUS_CHOICES):
parcel.status = new_status
parcel.save()
# WhatsApp Notification
notify_status_change(parcel)
messages.success(request, _("Status updated successfully!"))
return redirect('dashboard')
@login_required
def initiate_payment(request, parcel_id):
# Check if payments are enabled
platform_profile = PlatformProfile.objects.first()
if platform_profile and not platform_profile.enable_payment:
messages.error(request, _("Payments are currently disabled by the administrator."))
return redirect('dashboard')
parcel = get_object_or_404(Parcel, id=parcel_id, shipper=request.user, payment_status='pending')
thawani = ThawaniPay()
success_url = request.build_absolute_uri(reverse('payment_success')) + f"?session_id={{CHECKOUT_SESSION_ID}}&parcel_id={parcel.id}"
cancel_url = request.build_absolute_uri(reverse('payment_cancel')) + f"?parcel_id={parcel.id}"
session_id = thawani.create_checkout_session(parcel, success_url, cancel_url)
if session_id:
parcel.thawani_session_id = session_id
parcel.save()
checkout_url = f"{settings.THAWANI_API_URL.replace('/api/v1', '')}/pay/{session_id}?key={settings.THAWANI_PUBLISHABLE_KEY}"
return redirect(checkout_url)
else:
messages.error(request, _("Could not initiate payment. Please try again later."))
return redirect('dashboard')
@login_required
def payment_success(request):
session_id = request.GET.get('session_id')
parcel_id = request.GET.get('parcel_id')
parcel = get_object_or_404(Parcel, id=parcel_id, shipper=request.user)
thawani = ThawaniPay()
session_data = thawani.get_checkout_session(session_id)
if session_data and session_data.get('payment_status') == 'paid':
parcel.payment_status = 'paid'
parcel.save()
# WhatsApp Notification
notify_payment_received(parcel)
messages.success(request, _("Payment successful! Your shipment is now active."))
else:
messages.warning(request, _("Payment status is pending or failed. Please check your dashboard."))
return redirect('dashboard')
@login_required
def payment_cancel(request):
messages.info(request, _("Payment was cancelled."))
return redirect('dashboard')
def article_detail(request):
return render(request, 'core/article_detail.html')
def get_governates(request):
country_id = request.GET.get('country_id')
lang = get_language()
field_name = 'name_ar' if lang == 'ar' else 'name_en'
governates = Governate.objects.filter(country_id=country_id).order_by(field_name)
data = [{'id': g.id, 'name': getattr(g, field_name)} for g in governates]
return JsonResponse(data, safe=False)
def get_cities(request):
governate_id = request.GET.get('governate_id')
lang = get_language()
field_name = 'name_ar' if lang == 'ar' else 'name_en'
cities = City.objects.filter(governate_id=governate_id).order_by(field_name)
data = [{'id': c.id, 'name': getattr(c, field_name)} for c in cities]
return JsonResponse(data, safe=False)
def privacy_policy(request):
return render(request, 'core/privacy_policy.html')
def terms_conditions(request):
return render(request, 'core/terms_conditions.html')
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# Send email
sent = send_contact_message(
name=form.cleaned_data['name'],
email=form.cleaned_data['email'],
message=form.cleaned_data['message']
)
if sent:
messages.success(request, _("Your message has been sent successfully!"))
else:
messages.error(request, _("There was an error sending your message. Please try again later."))
return redirect('contact')
else:
form = ContactForm()
return render(request, 'core/contact.html', {'form': form})
@login_required
def profile_view(request):
profile = request.user.profile
reviews = []
if profile.role == 'car_owner':
reviews = request.user.received_ratings.all().order_by('-created_at')
return render(request, 'core/profile.html', {
'profile': profile,
'reviews': reviews
})
@login_required
def edit_profile(request):
if request.method == 'POST':
form = UserProfileForm(request.POST, request.FILES, instance=request.user.profile)
if form.is_valid():
# 1. Handle Image immediately (easier than session storage)
if 'profile_picture' in request.FILES:
request.user.profile.profile_picture = request.FILES['profile_picture']
request.user.profile.save()
# 2. Store other data in session for verification
data = form.cleaned_data
# Remove objects that can't be serialized or we've already handled
safe_data = {
'first_name': data['first_name'],
'last_name': data['last_name'],
'email': data['email'],
'phone_number': data['phone_number'],
'address': data['address'],
'country_id': data['country'].id if data['country'] else None,
'governate_id': data['governate'].id if data['governate'] else None,
'city_id': data['city'].id if data['city'] else None,
}
request.session['pending_profile_update'] = safe_data
# 3. Generate OTP
code = ''.join(random.choices(string.digits, k=6))
OTPVerification.objects.create(user=request.user, code=code, purpose='profile_update')
# 4. Send OTP
method = data.get('otp_method', 'email')
otp_msg = _("Your Masar Update Code is %(code)s") % {'code': code}
if method == 'whatsapp':
# Use current phone if available, else new phone
phone = request.user.profile.phone_number or data['phone_number']
send_whatsapp_message(phone, otp_msg)
messages.info(request, _("Verification code sent to WhatsApp."))
else:
# Default to email
# Send to the NEW email address (from the form), not the old one
target_email = data['email']
send_html_email(
subject=_('Verification Code'),
message=otp_msg,
recipient_list=[target_email],
title=_('Profile Update Verification'),
request=request
)
messages.info(request, _("Verification code sent to email."))
return redirect('verify_otp')
else:
form = UserProfileForm(instance=request.user.profile)
return render(request, 'core/edit_profile.html', {'form': form})
@login_required
def verify_otp_view(request):
if request.method == 'POST':
code = request.POST.get('code')
try:
otp = OTPVerification.objects.filter(
user=request.user,
code=code,
purpose='profile_update',
is_verified=False
).latest('created_at')
if otp.is_valid():
# Apply changes
data = request.session.get('pending_profile_update')
if data:
# Update User
request.user.first_name = data['first_name']
request.user.last_name = data['last_name']
request.user.email = data['email']
request.user.save()
# Update Profile
profile = request.user.profile
profile.phone_number = data['phone_number']
profile.address = data['address']
if data.get('country_id'):
profile.country_id = data['country_id']
if data.get('governate_id'):
profile.governate_id = data['governate_id']
if data.get('city_id'):
profile.city_id = data['city_id']
profile.save()
# Cleanup
otp.is_verified = True
otp.save()
del request.session['pending_profile_update']
messages.success(request, _("Profile updated successfully!"))
return redirect('profile')
else:
messages.error(request, _("Session expired. Please try again."))
return redirect('edit_profile')
else:
messages.error(request, _("Invalid or expired code."))
except OTPVerification.DoesNotExist:
messages.error(request, _("Invalid code."))
return render(request, 'core/verify_otp.html')
@login_required
def rate_driver(request, parcel_id):
parcel = get_object_or_404(Parcel, id=parcel_id)
# Validation
if parcel.shipper != request.user:
messages.error(request, _("You are not authorized to rate this shipment."))
return redirect('dashboard')
if parcel.status != 'delivered':
messages.error(request, _("You can only rate delivered shipments."))
return redirect('dashboard')
if not parcel.carrier:
messages.error(request, _("No driver was assigned to this shipment."))
return redirect('dashboard')
if hasattr(parcel, 'rating'):
messages.info(request, _("You have already rated this shipment."))
return redirect('dashboard')
if request.method == 'POST':
form = DriverRatingForm(request.POST)
if form.is_valid():
rating = form.save(commit=False)
rating.parcel = parcel
rating.driver = parcel.carrier
rating.shipper = request.user
rating.save()
messages.success(request, _("Thank you for your feedback!"))
return redirect('dashboard')
else:
form = DriverRatingForm()
return render(request, 'core/rate_driver.html', {
'form': form,
'parcel': parcel
})
@require_POST
def request_login_otp(request):
identifier = request.POST.get('identifier')
if not identifier:
return JsonResponse({'success': False, 'message': _('Please enter an email or phone number.')})
# Clean identifier
identifier = identifier.strip()
user = None
method = 'email'
# Try to find user by email
user = User.objects.filter(email__iexact=identifier).first()
# If not found, try by phone number
if not user:
profile = Profile.objects.filter(phone_number=identifier).first()
if profile:
user = profile.user
method = 'whatsapp'
else:
# Fallback: maybe they entered a phone without country code or with?
# For now, simplistic search
pass
if not user:
# Don't reveal if user exists or not for security, but for UX on this project we can be a bit more helpful
return JsonResponse({'success': False, 'message': _('User not found with this email or phone number.')})
if not user.is_active:
return JsonResponse({'success': False, 'message': _('Account is inactive. Please verify registration first.')})
# Generate OTP
code = ''.join(random.choices(string.digits, k=6))
OTPVerification.objects.create(user=user, code=code, purpose='login')
# Send OTP
otp_msg = _("Your Masar Login Code is %(code)s. Do not share this code.") % {'code': code}
try:
if method == 'whatsapp':
phone = user.profile.phone_number
send_whatsapp_message(phone, otp_msg)
message_sent = _("OTP sent to your WhatsApp.")
else:
send_html_email(
subject=_('Login OTP'),
message=otp_msg,
recipient_list=[user.email],
title=_('Login Verification'),
request=request
)
message_sent = _("OTP sent to your email.")
return JsonResponse({'success': True, 'message': message_sent, 'user_id': user.id})
except Exception as e:
return JsonResponse({'success': False, 'message': _('Failed to send OTP. Please try again.')})
@require_POST
def verify_login_otp(request):
user_id = request.POST.get('user_id')
code = request.POST.get('code')
if not user_id or not code:
return JsonResponse({'success': False, 'message': _('Invalid request.')})
try:
user = User.objects.get(id=user_id)
otp = OTPVerification.objects.filter(
user=user,
code=code,
purpose='login',
is_verified=False
).latest('created_at')
if otp.is_valid():
# Cleanup
otp.is_verified = True
otp.save()
# Login
login(request, user)
return JsonResponse({'success': True, 'redirect_url': reverse('dashboard')})
else:
return JsonResponse({'success': False, 'message': _('Invalid or expired OTP.')})
except (User.DoesNotExist, OTPVerification.DoesNotExist):
return JsonResponse({'success': False, 'message': _('Invalid OTP.')})
@require_POST
def chatbot(request):
try:
data = json.loads(request.body)
user_message = data.get("message", "")
language = data.get("language", "en")
if not user_message:
return JsonResponse({"success": False, "error": "Empty message"})
system_prompt = (
"You are MasarX AI, a helpful and professional assistant for the Masar logistics platform. "
"The platform connects shippers with drivers for small parcel deliveries. "
"Answer the user's questions about shipping, tracking, becoming a driver, or general support. "
"If the user speaks Arabic, reply in Arabic. If English, reply in English. "
"Keep responses concise and helpful."
)
if language == "ar":
system_prompt += " The user is currently browsing in Arabic."
else:
system_prompt += " The user is currently browsing in English."
response = LocalAIApi.create_response({
"input": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_message},
]
})
if response.get("success"):
text = LocalAIApi.extract_text(response)
return JsonResponse({"success": True, "response": text})
else:
return JsonResponse({"success": False, "error": response.get("error", "AI Error")})
except json.JSONDecodeError:
return JsonResponse({"success": False, "error": "Invalid JSON"})
except Exception as e:
return JsonResponse({"success": False, "error": str(e)})
@login_required
def generate_parcel_label(request, parcel_id):
parcel = get_object_or_404(Parcel, id=parcel_id)
# Security check: only shipper or carrier can print label
if parcel.shipper != request.user and parcel.carrier != request.user:
messages.error(request, _("You are not authorized to print this label."))
return redirect('dashboard')
# Generate QR Code
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data(parcel.tracking_number)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
buffer = BytesIO()
img.save(buffer, format="PNG")
qr_image_base64 = base64.b64encode(buffer.getvalue()).decode()
# Get Logo Base64
logo_base64 = None
platform_profile = PlatformProfile.objects.first()
if platform_profile and platform_profile.logo:
try:
with open(platform_profile.logo.path, "rb") as image_file:
logo_base64 = base64.b64encode(image_file.read()).decode()
except Exception:
pass
# Render Template
html_string = render_to_string('core/parcel_label.html', {
'parcel': parcel,
'qr_code': qr_image_base64,
'logo_base64': logo_base64,
'platform_profile': platform_profile,
})
# Generate PDF
html = weasyprint.HTML(string=html_string, base_url=request.build_absolute_uri())
pdf_file = html.write_pdf()
response = HttpResponse(pdf_file, content_type='application/pdf')
response['Content-Disposition'] = f'inline; filename="label_{parcel.tracking_number}.pdf"'
return response
@login_required
def generate_invoice(request, parcel_id):
parcel = get_object_or_404(Parcel, id=parcel_id)
# Security check: only shipper can view invoice (or admin)
if parcel.shipper != request.user and not request.user.is_staff:
messages.error(request, _("You are not authorized to view this invoice."))
return redirect('dashboard')
# Get Logo Base64
logo_base64 = None
platform_profile = PlatformProfile.objects.first()
if platform_profile and platform_profile.logo:
try:
with open(platform_profile.logo.path, "rb") as image_file:
logo_base64 = base64.b64encode(image_file.read()).decode()
except Exception:
pass
# Render Template
html_string = render_to_string('core/invoice.html', {
'parcel': parcel,
'logo_base64': logo_base64,
'platform_profile': platform_profile,
'request': request,
})
# Generate PDF
html = weasyprint.HTML(string=html_string, base_url=request.build_absolute_uri())
pdf_file = html.write_pdf()
response = HttpResponse(pdf_file, content_type='application/pdf')
response['Content-Disposition'] = f'inline; filename="invoice_{parcel.tracking_number}.pdf"'
return response
@login_required
def scan_qr_view(request):
"""Renders the QR Scanner page for drivers."""
# Optional: Restrict to drivers only
# if request.user.profile.role != 'car_owner':
# messages.error(request, _("Only drivers can access the scanner."))
# return redirect('dashboard')
return render(request, 'core/scan_qr.html')
@login_required
def get_parcel_details(request):
"""API to fetch parcel details by tracking number."""
tracking_number = request.GET.get('tracking_number')
if not tracking_number:
return JsonResponse({'success': False, 'error': _('Tracking number required')})
try:
parcel = Parcel.objects.get(tracking_number__iexact=tracking_number.strip())
# Check permissions: Is user the assigned driver? Or is it pending (for acceptance)?
is_driver = request.user.profile.role == 'car_owner'
is_assigned = parcel.carrier == request.user
can_update = False
if is_driver:
if is_assigned:
can_update = True
elif parcel.status == 'pending':
# Check if payments are enabled and paid
platform_profile = PlatformProfile.objects.first()
payments_enabled = platform_profile.enable_payment if platform_profile else True
if not payments_enabled or parcel.payment_status == 'paid':
can_update = True
data = {
'success': True,
'parcel': {
'id': parcel.id,
'tracking_number': parcel.tracking_number,
'status': parcel.status,
'status_display': parcel.get_status_display(),
'price': float(parcel.price),
'from': f"{parcel.pickup_governate.name} / {parcel.pickup_city.name}",
'to': f"{parcel.delivery_governate.name} / {parcel.delivery_city.name}",
'description': parcel.description,
'can_update': can_update
}
}
return JsonResponse(data)
except Parcel.DoesNotExist:
return JsonResponse({'success': False, 'error': _('Parcel not found')})
@login_required
@require_POST
def update_parcel_status_ajax(request):
"""API to update parcel status from scanner."""
try:
data = json.loads(request.body)
parcel_id = data.get('parcel_id')
action = data.get('action')
parcel = get_object_or_404(Parcel, id=parcel_id)
# Logic for actions
if action == 'accept':
# Similar to accept_parcel view
if request.user.profile.role != 'car_owner':
return JsonResponse({'success': False, 'error': _('Only drivers can accept shipments')})
if parcel.status != 'pending':
return JsonResponse({'success': False, 'error': _('Parcel is not available')})
# Check payment status if enabled
platform_profile = PlatformProfile.objects.first()
payments_enabled = platform_profile.enable_payment if platform_profile else True
if payments_enabled and parcel.payment_status != 'paid':
return JsonResponse({'success': False, 'error': _('Payment pending')})
parcel.carrier = request.user
parcel.status = 'picked_up' # Or 'assigned'? Logic says 'picked_up' in accept_parcel
parcel.save()
notify_driver_assigned(parcel)
elif action == 'delivered':
if parcel.carrier != request.user:
return JsonResponse({'success': False, 'error': _('Not authorized')})
parcel.status = 'delivered'
parcel.save()
notify_status_change(parcel)
else:
return JsonResponse({'success': False, 'error': _('Invalid action')})
return JsonResponse({'success': True})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})
@login_required
def edit_parcel(request, parcel_id):
parcel = get_object_or_404(Parcel, id=parcel_id, shipper=request.user)
if parcel.status != 'pending':
messages.error(request, _("You can only edit pending shipments."))
return redirect('dashboard')
if request.method == 'POST':
form = ParcelForm(request.POST, instance=parcel)
if form.is_valid():
form.save()
messages.success(request, _("Shipment updated successfully."))
return redirect('dashboard')
else:
form = ParcelForm(instance=parcel)
return render(request, 'core/edit_parcel.html', {'form': form, 'parcel': parcel})
@login_required
def cancel_parcel(request, parcel_id):
parcel = get_object_or_404(Parcel, id=parcel_id, shipper=request.user)
if parcel.status != 'pending':
messages.error(request, _("You can only cancel pending shipments."))
else:
parcel.status = 'cancelled'
parcel.save()
messages.success(request, _("Shipment cancelled successfully."))
return redirect('dashboard')