add printing labels

This commit is contained in:
Flatlogic Bot 2026-01-26 07:53:04 +00:00
parent 1e9f216ade
commit 4020492307
10 changed files with 251 additions and 15 deletions

View File

@ -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 %}
<!DOCTYPE html>
@ -256,9 +256,16 @@
<i class="bi bi-robot me-2 fs-5"></i>
<h6 class="mb-0 fw-bold">MasarX AI</h6>
</div>
<button id="masar-chat-close" class="btn btn-sm btn-link text-white p-0">
<i class="bi bi-x-lg"></i>
</button>
<div class="d-flex align-items-center">
{% if platform_profile.phone_number %}
<a href="https://wa.me/{{ platform_profile.phone_number|whatsapp_url }}" target="_blank" class="btn btn-sm btn-success me-2 rounded-circle d-flex align-items-center justify-content-center" style="width: 32px; height: 32px;" title="{% trans 'Chat on WhatsApp' %}" data-bs-toggle="tooltip">
<i class="bi bi-whatsapp"></i>
</a>
{% endif %}
<button id="masar-chat-close" class="btn btn-sm btn-link text-white p-0">
<i class="bi bi-x-lg"></i>
</button>
</div>
</div>
<div id="masar-chat-messages" class="flex-grow-1 p-3 bg-light overflow-auto">
<div class="d-flex mb-3 justify-content-start">
@ -277,6 +284,13 @@
</div>
</div>
<script src="{% static "js/chat.js" %}?v={{ deployment_timestamp }}"></script>
<script>
// Initialize tooltips
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
</script>
</body>
</html>

View File

@ -0,0 +1,144 @@
{% load i18n %}
<!DOCTYPE html>
<html lang="{{ LANGUAGE_CODE }}" dir="{{ LANGUAGE_BIDI|yesno:'rtl,ltr' }}">
<head>
<meta charset="utf-8">
<title>Label {{ parcel.tracking_number }}</title>
<style>
@page {
size: 100mm 150mm;
margin: 0;
}
body {
font-family: sans-serif;
margin: 0;
padding: 5mm;
font-size: 12px;
}
.container {
border: 2px solid #000;
height: 138mm; /* Adjusted slightly */
display: flex;
flex-direction: column;
}
.header {
border-bottom: 2px solid #000;
padding: 10px;
text-align: center;
height: 20mm;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.header img {
max-height: 15mm;
max-width: 80mm;
}
.big-code {
font-size: 24px;
font-weight: bold;
text-align: center;
padding: 10px;
border-bottom: 2px solid #000;
background-color: #f0f0f0;
}
.address-box {
border-bottom: 1px solid #000;
padding: 10px;
flex-grow: 1;
}
.main-info {
display: flex;
border-bottom: 2px solid #000;
}
.section {
padding: 10px;
flex: 1;
}
/* RTL support for border */
html[dir="ltr"] .section-right { border-left: 2px solid #000; }
html[dir="rtl"] .section-right { border-right: 2px solid #000; }
.qr-code {
text-align: center;
padding: 10px;
border-bottom: 2px solid #000;
}
.qr-code img {
width: 30mm;
height: 30mm;
}
.tracking-number {
font-size: 16px;
font-weight: bold;
margin-top: 5px;
font-family: monospace;
}
.footer {
padding: 5px;
text-align: center;
font-size: 10px;
}
h3 { margin: 0 0 5px 0; font-size: 10px; text-transform: uppercase; color: #555; }
p { margin: 0 0 2px 0; }
.bold { font-weight: bold; font-size: 14px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
{% if logo_base64 %}
<img src="data:image/jpeg;base64,{{ logo_base64 }}" alt="MasarX">
{% else %}
<h1>{{ platform_profile.name|default:"MASARX" }}</h1>
{% endif %}
</div>
<div class="big-code">
{{ parcel.delivery_city.name|upper }}
</div>
<div class="address-box">
<h3>{% trans "TO (Receiver)" %}</h3>
<p class="bold">{{ parcel.receiver_name }}</p>
<p>{{ parcel.delivery_address }}</p>
<p>{{ parcel.delivery_city.name }}, {{ parcel.delivery_governate.name }}</p>
<p>{{ parcel.delivery_country.name }}</p>
<p>{% trans "Tel" %}: {{ parcel.receiver_phone }}</p>
</div>
<div class="address-box">
<h3>{% trans "FROM (Sender)" %}</h3>
<p class="bold">{{ parcel.shipper.get_full_name|default:parcel.shipper.username }}</p>
<p>{{ parcel.pickup_address }}</p>
<p>{{ parcel.pickup_city.name }}, {{ parcel.pickup_governate.name }}</p>
<p>{% trans "Tel" %}: {{ parcel.shipper.profile.phone_number }}</p>
</div>
<div class="main-info">
<div class="section">
<h3>{% trans "Date" %}</h3>
<p class="bold">{{ parcel.created_at|date:"Y-m-d" }}</p>
</div>
<div class="section section-right">
<h3>{% trans "Weight" %}</h3>
<p class="bold">{{ parcel.weight }} KG</p>
</div>
<div class="section section-right">
<h3>{% trans "Price" %}</h3>
<p class="bold">{{ parcel.price }} OMR</p>
</div>
</div>
<div class="qr-code">
<img src="data:image/png;base64,{{ qr_code }}" alt="QR Code">
<div class="tracking-number">{{ parcel.tracking_number }}</div>
</div>
<div class="footer">
{% trans "Thank you for shipping with us." %}
</div>
</div>
</body>
</html>

View File

@ -63,11 +63,15 @@
{% if parcel.payment_status == 'pending' %}
{% if payments_enabled %}
<a href="{% url 'initiate_payment' parcel.id %}" class="btn btn-sm btn-outline-primary w-100 mb-3">
<a href="{% url 'initiate_payment' parcel.id %}" class="btn btn-sm btn-outline-primary w-100 mb-2">
<i class="fas fa-credit-card me-1"></i> {% trans "Pay Now" %}
</a>
{% endif %}
{% endif %}
<a href="{% url 'generate_parcel_label' parcel.id %}" class="btn btn-sm btn-outline-dark w-100 mb-3" target="_blank">
<i class="fas fa-print me-1"></i> {% trans "Print Label" %}
</a>
<hr>
<p class="card-text small mb-0"><strong>{% trans "Receiver" %}:</strong> {{ parcel.receiver_name }}</p>
@ -114,15 +118,21 @@
</span>
</div>
{% if parcel.payment_status == 'pending' and payments_enabled %}
<a href="{% url 'initiate_payment' parcel.id %}" class="btn btn-sm btn-outline-primary w-100 w-md-auto">
<i class="fas fa-credit-card me-1"></i> {% trans "Pay Now" %}
<div class="d-flex gap-2 w-100 justify-content-md-end">
<a href="{% url 'generate_parcel_label' parcel.id %}" class="btn btn-sm btn-outline-dark" target="_blank" title="{% trans 'Print Label' %}">
<i class="fas fa-print"></i>
</a>
{% else %}
<span class="badge {% if parcel.payment_status == 'paid' %}bg-success{% else %}bg-secondary{% endif %}">
{{ parcel.get_payment_status_display }}
</span>
{% endif %}
{% if parcel.payment_status == 'pending' and payments_enabled %}
<a href="{% url 'initiate_payment' parcel.id %}" class="btn btn-sm btn-outline-primary flex-grow-1 flex-md-grow-0">
<i class="fas fa-credit-card me-1"></i> {% trans "Pay Now" %}
</a>
{% else %}
<span class="badge {% if parcel.payment_status == 'paid' %}bg-success{% else %}bg-secondary{% endif %} p-2">
{{ parcel.get_payment_status_display }}
</span>
{% endif %}
</div>
</div>
</div>
</div>
@ -218,6 +228,9 @@
</span>
</td>
<td>
<a href="{% url 'generate_parcel_label' parcel.id %}" class="btn btn-sm btn-outline-dark me-2" target="_blank" title="{% trans 'Print Label' %}">
<i class="fas fa-print"></i>
</a>
{% if parcel.status == 'delivered' and parcel.carrier %}
{% if not rating %}
<a href="{% url 'rate_driver' parcel.id %}" class="btn btn-sm btn-outline-warning">

View File

@ -12,4 +12,11 @@ def get_rating(parcel):
try:
return parcel.rating
except:
return None
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)))

View File

@ -36,6 +36,7 @@ urlpatterns = [
path('accept-parcel/<int:parcel_id>/', views.accept_parcel, name='accept_parcel'),
path('update-status/<int:parcel_id>/', views.update_status, name='update_status'),
path('rate-driver/<int:parcel_id>/', views.rate_driver, name='rate_driver'),
path('parcel/<int:parcel_id>/label/', views.generate_parcel_label, name='generate_parcel_label'),
path('initiate-payment/<int:parcel_id>/', views.initiate_payment, name='initiate_payment'),
path('payment-success/', views.payment_success, name='payment_success'),
path('payment-cancel/', views.payment_cancel, name='payment_cancel'),

View File

@ -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

View File

@ -2,3 +2,5 @@ Django==5.2.7
mysqlclient==2.2.7
python-dotenv==1.1.1
Pillow
weasyprint
qrcode