From 4020492307a21f5acab20b70fe05ea3176fba67c Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Mon, 26 Jan 2026 07:53:04 +0000 Subject: [PATCH] add printing labels --- core/__pycache__/urls.cpython-311.pyc | Bin 4185 -> 4305 bytes core/__pycache__/views.cpython-311.pyc | Bin 34918 -> 37882 bytes core/templates/base.html | 22 ++- core/templates/core/parcel_label.html | 144 ++++++++++++++++++ core/templates/core/shipper_dashboard.html | 31 ++-- .../__pycache__/core_tags.cpython-311.pyc | Bin 894 -> 1307 bytes core/templatetags/core_tags.py | 9 +- core/urls.py | 1 + core/views.py | 57 ++++++- requirements.txt | 2 + 10 files changed, 251 insertions(+), 15 deletions(-) create mode 100644 core/templates/core/parcel_label.html diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 6704599325747bfbfa94410ae3a7ec2c30c62c46..767dfd6355e9a8668fa46099e8c5b46921753927 100644 GIT binary patch delta 256 zcmcbqa8XfxIWI340}yn`lxNEFFfcp@abQ3R%J`hHQQe$%;sI7ozf|Td6_|92{~ESs z%nS^xffxeP<)Tzm0)iPd12<=|E@Nhto!rYIE}N5>l$xV|OEf(-FSRJKBsIPuu_!q; zCmt-mnV0h=Bcs~p?_6Sx21V*Xb8hhz6y>L7=A_1#Wu}%FX@Z3K%TkLn(<H<LrVYv delta 162 zcmcbpcvC@rIWI340}!-{m1l0}W?*;>;=q6+l=0bSqq;dOSGsJJN{U}FgQoxH9@b^d zn=?3HFfyuc4&)YN)V{@2P?VpRnUfk{mYG^!qyf}@BB})xEz+Jm nohN|H2FPRt;^O_AKk@`JZI0$oV-(0_H05V%;0D1WE1)C*hz2PB diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 50fb54db3bfc3320747cab226ec43409c9ab864e..4631679ec4bcb86a15e9ce060ccd314b04c50359 100644 GIT binary patch delta 7726 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 F`@hMYYnuQ7 delta 5449 zcma)AdvH|c72lg|b|IUDgail>U;_cNgghVt!aE6h5QOkbKo@nn>|Vl!dv}-b?uH~H zi;9odpx|dKK5EByMkiCP#nn##sN*=D(N;%`k2>~_$msMjj*ereEuGPJ+VeYilLbO; zGnrq`cfRvI&htArUq6%cr!zT4&lDEsyXfb{^ke$@@Y$mBoIm88t$r<~xFW7dp|QwZ z99&GaCsJfon}J|}`BRJ~W=*h$>0)E4SsScny2Pk6>x1=XL$JYI7F@=>sm5}%G1$m- zsnKMv2(B8y0M`R{pXJi&(m*$JiKIM9v)cXl^7||yx3XM{{DfyQtq|4ni~PB> zmV;~ov;x`yn*i;y%Ga5z={d&8;_x+VfS;xVjw zP~Nj(-wyU#VZW9L#YD()o@0TPkSPQ}6S0-R_G*J+BN@>|2Ux820#IiF3jtMfS=Dk6 z`z9&%s(Qa1o3|&&)qXvyCSpQ71m^bu56fRvRrnqU`2^qx@={g#6jtmmi~%EQ&%$Pp z=YEXW%l4Wj1v~}IcZywdxMoKRaS$jKMG#>ZJPC?T!Fv2x_sxJD`;cA4-huH%0B|p$ z5s*s|$ap3R#*YCUel0Fnwd)$M*u~Z%J#K2zL`Y~ylL&!PTJUE2=+b$La(nu0Z7j!8 zXod7N)C8bVulPRpe1ss7>x3jcCmsbOC&Q4zhE<$t;{VXF6w99i{0P7q!X9P+^X9B@ z4Q8QhCavHq5!MXET~=AgUnEt-WyA6k$#Iy8M7vO>P>v@;&SLQ#&3~F+yKDw2EYH@? zOdHFGmEu9-I`PL=KPN9Y&QC%8z?3U4$ez18zif9z4=3!R-My{7eY-=OHgE6V)UjJ+ zwOERs+0pE_@jMN2Qi8MrPxYs^m^F^|gkn{}mEZdl#Z*aeBK<*yfhS#Pkie)-Dka?d=h8+b~p_@jy1OCtPp;##x&XicSSv0_tjG+umcUi0aXPu8skZ zWA*Wqoww$f^5ut zkGrtF={HzE!H03752)dxP&{Vn;UV#7652`2#hZ{G^Zye0O?O52bvG!cp1`z1;aD_6 z*NjCi@foe`#SKs}Pr5GpO3p04FniTIzUKFR%@^Fw;`a3ZEtix^u2}Ekm|Fo60ni1QW|2(Dh`kjp+Vbr>c9SEr8CS!BDLTqk$%yik-;T3e3CGBN)-b^z?fr7se3 z()FRY=+udG)8F;VBir618*c2mlv)9~5rW~8a7<|Rncl1JP{K5*5F}~F_6lu4*N$0K z`ne8)Dez|6#X<{f`T+HCLQPOwTXwE$7*0InI;1)mCSLI+F|N8KHFzaXs~5ul;x8m{ zSpIaopb)Oyq2wt$<$}Iu4;ykos(pcZe9XH z3OQ9<+$6v3Yg&cQNrV9V0V=`p>>bW!{C!;C{d+W_AJoI77`2rLAl5H$+EeW~Z;s=< znP9_tc3x-qR(fl)1~M@9H-HIXm!fJ2ZWg1UVt_c{C_n(F0=P6-ptynJ;x>R`t;^Mw zi3~1#+yrNXVsbVpIJ4}@s6S4F%_?)m4P&ReM?R00&l-elB^&r2NV9?2nAe=n_D({CZFWU~fQNfH-SssI#%64FZKLVy zcG}`QSjhzon_t_`O`$Ta9~=r%|Lufx9_jUb`qciN9wj9|IN0dHqa{wuDgAeQp&lC3 z$NSfEJn_pHn07^bvmU~%EMKz~LyiN!<~a->Be1=PR7*`Hgy48!+s(ox8<0)J=4gSe zCqi|RzIN_~0Ou#QtE(}17SIAfK@ncM`nCG`D8(t1oE`E>;M4R2hkk`lb=Tpy-POEj z1dEZH0mUxVOnN^^y3l^|l`lbj`C--KeM1t6yl5&B=F{E6R(#)e5gj>E7fCbuV @@ -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 1b5ffe14b5b7c2de70d469b02476052e24e6ba8c..5041c444f0d0c5d96f2ed768574f5e83532aa8fd 100644 GIT binary patch delta 492 zcmeyzHk*rYIWI340}z-am_`>`ea6!Fp%Z|VrC%zTn8kkGt@8?Go>&FGo&ys0P-qXbr|ke8pQn^&5fT9lcrkercN zl$czST2!o%mS3b0o{?Bm>{w8s5E|s8cZ)%jDl9Mq)uhd}&cmkt9$?5QvLSfkXqt2Q~&C z!4C|KtU@>V1v`pvaP#(?cbQK$o2xuWduj0s(}h(_sxAr}T@g0A$ZdRu+xP;DaS=CA zr=KRz/', 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