diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 6704599..767dfd6 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 50fb54d..4631679 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/templates/base.html b/core/templates/base.html index 1eff527..904b19b 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,4 +1,4 @@ -{% load i18n static i18n_urls %} +{% load i18n static i18n_urls core_tags %} {% get_current_language as LANGUAGE_CODE %} {% get_language_info for LANGUAGE_CODE as lang %} @@ -256,9 +256,16 @@
MasarX AI
- +
+ {% if platform_profile.phone_number %} + + + + {% endif %} + +
@@ -277,6 +284,13 @@
+ \ No newline at end of file diff --git a/core/templates/core/parcel_label.html b/core/templates/core/parcel_label.html new file mode 100644 index 0000000..f48534e --- /dev/null +++ b/core/templates/core/parcel_label.html @@ -0,0 +1,144 @@ +{% load i18n %} + + + + + Label {{ parcel.tracking_number }} + + + +
+
+ {% if logo_base64 %} + MasarX + {% else %} +

{{ platform_profile.name|default:"MASARX" }}

+ {% endif %} +
+ +
+ {{ parcel.delivery_city.name|upper }} +
+ +
+

{% trans "TO (Receiver)" %}

+

{{ parcel.receiver_name }}

+

{{ parcel.delivery_address }}

+

{{ parcel.delivery_city.name }}, {{ parcel.delivery_governate.name }}

+

{{ parcel.delivery_country.name }}

+

{% trans "Tel" %}: {{ parcel.receiver_phone }}

+
+ +
+

{% trans "FROM (Sender)" %}

+

{{ parcel.shipper.get_full_name|default:parcel.shipper.username }}

+

{{ parcel.pickup_address }}

+

{{ parcel.pickup_city.name }}, {{ parcel.pickup_governate.name }}

+

{% trans "Tel" %}: {{ parcel.shipper.profile.phone_number }}

+
+ +
+
+

{% trans "Date" %}

+

{{ parcel.created_at|date:"Y-m-d" }}

+
+
+

{% trans "Weight" %}

+

{{ parcel.weight }} KG

+
+
+

{% trans "Price" %}

+

{{ parcel.price }} OMR

+
+
+ +
+ QR Code +
{{ parcel.tracking_number }}
+
+ + +
+ + diff --git a/core/templates/core/shipper_dashboard.html b/core/templates/core/shipper_dashboard.html index 6d7df9a..b196631 100644 --- a/core/templates/core/shipper_dashboard.html +++ b/core/templates/core/shipper_dashboard.html @@ -63,11 +63,15 @@ {% if parcel.payment_status == 'pending' %} {% if payments_enabled %} - + {% trans "Pay Now" %} {% endif %} {% endif %} + + + {% trans "Print Label" %} +

{% trans "Receiver" %}: {{ parcel.receiver_name }}

@@ -114,15 +118,21 @@ - {% if parcel.payment_status == 'pending' and payments_enabled %} - - {% trans "Pay Now" %} +
+ + - {% else %} - - {{ parcel.get_payment_status_display }} - - {% endif %} + + {% if parcel.payment_status == 'pending' and payments_enabled %} + + {% trans "Pay Now" %} + + {% else %} + + {{ parcel.get_payment_status_display }} + + {% endif %} +
@@ -218,6 +228,9 @@ + + + {% if parcel.status == 'delivered' and parcel.carrier %} {% if not rating %} diff --git a/core/templatetags/__pycache__/core_tags.cpython-311.pyc b/core/templatetags/__pycache__/core_tags.cpython-311.pyc index 1b5ffe1..5041c44 100644 Binary files a/core/templatetags/__pycache__/core_tags.cpython-311.pyc and b/core/templatetags/__pycache__/core_tags.cpython-311.pyc differ diff --git a/core/templatetags/core_tags.py b/core/templatetags/core_tags.py index 423fc0d..c4025b8 100644 --- a/core/templatetags/core_tags.py +++ b/core/templatetags/core_tags.py @@ -12,4 +12,11 @@ def get_rating(parcel): try: return parcel.rating except: - return None \ No newline at end of file + return None + +@register.filter +def whatsapp_url(value): + """Removes non-numeric characters for WhatsApp URL.""" + if not value: + return "" + return "".join(filter(str.isdigit, str(value))) diff --git a/core/urls.py b/core/urls.py index 4f4230f..9d33bd6 100644 --- a/core/urls.py +++ b/core/urls.py @@ -36,6 +36,7 @@ urlpatterns = [ path('accept-parcel//', views.accept_parcel, name='accept_parcel'), path('update-status//', views.update_status, name='update_status'), path('rate-driver//', views.rate_driver, name='rate_driver'), + path('parcel//label/', views.generate_parcel_label, name='generate_parcel_label'), path('initiate-payment//', views.initiate_payment, name='initiate_payment'), path('payment-success/', views.payment_success, name='payment_success'), path('payment-cancel/', views.payment_cancel, name='payment_cancel'), diff --git a/core/views.py b/core/views.py index f67acf2..64e6b1a 100644 --- a/core/views.py +++ b/core/views.py @@ -8,7 +8,7 @@ from .forms import UserRegistrationForm, ParcelForm, ContactForm, UserProfileFor 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 +from django.http import JsonResponse, HttpResponse from django.urls import reverse from .payment_utils import ThawaniPay from django.conf import settings @@ -16,6 +16,7 @@ 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 ( @@ -28,6 +29,10 @@ from .whatsapp_utils import ( 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): tracking_id = request.GET.get('tracking_id') @@ -650,3 +655,53 @@ def chatbot(request): 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 diff --git a/requirements.txt b/requirements.txt index 65b2871..fe88664 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,5 @@ Django==5.2.7 mysqlclient==2.2.7 python-dotenv==1.1.1 Pillow +weasyprint +qrcode \ No newline at end of file