diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 00afa59..21e051d 100644 Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ diff --git a/config/settings.py b/config/settings.py index ca81335..aacb809 100644 --- a/config/settings.py +++ b/config/settings.py @@ -205,7 +205,7 @@ else: WHATSAPP_API_KEY = os.getenv("WHATSAPP_API_KEY", "") WHATSAPP_PHONE_ID = os.getenv("WHATSAPP_PHONE_ID", "") WHATSAPP_BUSINESS_ACCOUNT_ID = os.getenv("WHATSAPP_BUSINESS_ACCOUNT_ID", "") -WHATSAPP_ENABLED = os.getenv("WHATSAPP_ENABLED", "false").lower() == "true" +WHATSAPP_ENABLED = os.getenv("WHATSAPP_ENABLED", "true").lower() == "true" # Default primary key field type # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index 83a696d..2653ae8 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 8a0cf21..68424aa 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/whatsapp_utils.cpython-311.pyc b/core/__pycache__/whatsapp_utils.cpython-311.pyc index 3a3a5af..ecb7983 100644 Binary files a/core/__pycache__/whatsapp_utils.cpython-311.pyc and b/core/__pycache__/whatsapp_utils.cpython-311.pyc differ diff --git a/core/admin.py b/core/admin.py index c987a18..c0b25b6 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,57 +1,96 @@ from django.contrib import admin +from django.contrib.auth.admin import UserAdmin +from django.contrib.auth.models import User from .models import Profile, Parcel, Country, Governate, City, PlatformProfile +from django.utils.translation import gettext_lazy as _ +from django.urls import path, reverse +from django.shortcuts import render +from django.utils.html import format_html +from django.contrib import messages +from .whatsapp_utils import send_whatsapp_message_detailed +import logging -@admin.register(Country) -class CountryAdmin(admin.ModelAdmin): - list_display = ('name_en', 'name_ar') - search_fields = ('name_en', 'name_ar') +class ProfileInline(admin.StackedInline): + model = Profile + can_delete = False + verbose_name_plural = _('Profiles') -@admin.register(Governate) -class GovernateAdmin(admin.ModelAdmin): - list_display = ('name_en', 'name_ar', 'country') - list_filter = ('country',) - search_fields = ('name_en', 'name_ar') +class CustomUserAdmin(UserAdmin): + inlines = (ProfileInline,) -@admin.register(City) -class CityAdmin(admin.ModelAdmin): - list_display = ('name_en', 'name_ar', 'governate') - list_filter = ('governate__country', 'governate') - search_fields = ('name_en', 'name_ar') - -@admin.register(Profile) -class ProfileAdmin(admin.ModelAdmin): - list_display = ('user', 'role', 'phone_number', 'country', 'governate', 'city') - list_filter = ('role', 'country', 'governate') - search_fields = ('user__username', 'phone_number') - -@admin.register(Parcel) class ParcelAdmin(admin.ModelAdmin): - list_display = ('tracking_number', 'shipper', 'carrier', 'status', 'payment_status', 'created_at') - list_filter = ('status', 'payment_status', 'pickup_country', 'delivery_country') - search_fields = ('tracking_number', 'shipper__username', 'carrier__username', 'receiver_name') - readonly_fields = ('tracking_number', 'created_at', 'updated_at') + list_display = ('tracking_number', 'shipper', 'status', 'created_at') + list_filter = ('status', 'created_at') + search_fields = ('tracking_number', 'shipper__username', 'receiver_name') -@admin.register(PlatformProfile) class PlatformProfileAdmin(admin.ModelAdmin): - list_display = ('name', 'phone_number', 'registration_number') fieldsets = ( - (None, { - 'fields': ('name', 'logo', 'slogan') + (_('General Info'), { + 'fields': ('name', 'logo', 'slogan', 'address', 'phone_number', 'registration_number', 'vat_number') }), - ('Contact Information', { - 'fields': ('address', 'phone_number', 'registration_number', 'vat_number') - }), - ('Legal', { + (_('Policies'), { 'fields': ('privacy_policy', 'terms_conditions') }), - ('WhatsApp Configuration', { - 'fields': ('whatsapp_access_token', 'whatsapp_business_phone_number_id'), - 'description': 'Enter your Meta WhatsApp Business API credentials here. These will override the system defaults.' + (_('WhatsApp Configuration (Wablas Gateway)'), { + 'fields': ('whatsapp_access_token', 'whatsapp_app_secret', 'whatsapp_business_phone_number_id'), + 'description': _('Configure your Wablas API connection. Use "Test WhatsApp Configuration" to verify.') }), ) def has_add_permission(self, request): - # Allow adding only if no instance exists + # Allow only one instance if self.model.objects.exists(): return False - return super().has_add_permission(request) \ No newline at end of file + return super().has_add_permission(request) + + def get_urls(self): + urls = super().get_urls() + custom_urls = [ + path('test-whatsapp/', self.admin_site.admin_view(self.test_whatsapp_view), name='test-whatsapp'), + ] + return custom_urls + urls + + def test_whatsapp_view(self, request): + phone_number = '' + if request.method == 'POST': + phone_number = request.POST.get('phone_number') + if phone_number: + success, msg = send_whatsapp_message_detailed(phone_number, "This is a test message from your Platform.") + if success: + messages.success(request, f"Success: {msg}") + else: + messages.error(request, f"Error: {msg}") + else: + messages.warning(request, "Please enter a phone number.") + + context = dict( + self.admin_site.each_context(request), + phone_number=phone_number, + ) + return render(request, "admin/core/platformprofile/test_whatsapp.html", context) + + def test_connection_link(self, obj): + return format_html( + '{}', + reverse('admin:test-whatsapp'), + _('Test WhatsApp Configuration') + ) + test_connection_link.short_description = _("Actions") + test_connection_link.allow_tags = True + + readonly_fields = ('test_connection_link',) + + def get_fieldsets(self, request, obj=None): + fieldsets = super().get_fieldsets(request, obj) + # Add the test link to the first fieldset or a new one + if obj: + fieldsets += ((_('Tools'), {'fields': ('test_connection_link',)}),) + return fieldsets + +admin.site.unregister(User) +admin.site.register(User, CustomUserAdmin) +admin.site.register(Parcel, ParcelAdmin) +admin.site.register(Country) +admin.site.register(Governate) +admin.site.register(City) +admin.site.register(PlatformProfile, PlatformProfileAdmin) \ No newline at end of file diff --git a/core/migrations/0011_platformprofile_whatsapp_app_secret.py b/core/migrations/0011_platformprofile_whatsapp_app_secret.py new file mode 100644 index 0000000..55c8fd3 --- /dev/null +++ b/core/migrations/0011_platformprofile_whatsapp_app_secret.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2026-01-25 12:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0010_platformprofile_whatsapp_access_token_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='platformprofile', + name='whatsapp_app_secret', + field=models.CharField(blank=True, help_text='App Secret or Verify Token for Webhooks.', max_length=255, verbose_name='WhatsApp App Secret (Security Key)'), + ), + ] diff --git a/core/migrations/0012_alter_platformprofile_whatsapp_access_token_and_more.py b/core/migrations/0012_alter_platformprofile_whatsapp_access_token_and_more.py new file mode 100644 index 0000000..074eb60 --- /dev/null +++ b/core/migrations/0012_alter_platformprofile_whatsapp_access_token_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.7 on 2026-01-25 12:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0011_platformprofile_whatsapp_app_secret'), + ] + + operations = [ + migrations.AlterField( + model_name='platformprofile', + name='whatsapp_access_token', + field=models.TextField(blank=True, help_text='Your Wablas API Token.', verbose_name='Wablas API Token'), + ), + migrations.AlterField( + model_name='platformprofile', + name='whatsapp_app_secret', + field=models.CharField(blank=True, help_text='Your Wablas API Secret Key (if required).', max_length=255, verbose_name='Wablas Secret Key'), + ), + migrations.AlterField( + model_name='platformprofile', + name='whatsapp_business_phone_number_id', + field=models.CharField(blank=True, default='https://deu.wablas.com', help_text='The Wablas API domain (e.g., https://deu.wablas.com).', max_length=100, verbose_name='Wablas Domain'), + ), + ] diff --git a/core/migrations/__pycache__/0011_platformprofile_whatsapp_app_secret.cpython-311.pyc b/core/migrations/__pycache__/0011_platformprofile_whatsapp_app_secret.cpython-311.pyc new file mode 100644 index 0000000..5bff709 Binary files /dev/null and b/core/migrations/__pycache__/0011_platformprofile_whatsapp_app_secret.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0012_alter_platformprofile_whatsapp_access_token_and_more.cpython-311.pyc b/core/migrations/__pycache__/0012_alter_platformprofile_whatsapp_access_token_and_more.cpython-311.pyc new file mode 100644 index 0000000..e37de1b Binary files /dev/null and b/core/migrations/__pycache__/0012_alter_platformprofile_whatsapp_access_token_and_more.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index b7f2233..07c5370 100644 --- a/core/models.py +++ b/core/models.py @@ -5,6 +5,7 @@ from django.utils.translation import get_language from django.db.models.signals import post_save from django.dispatch import receiver from django.utils import timezone +from django.core.exceptions import ValidationError import uuid class Country(models.Model): @@ -160,9 +161,21 @@ class PlatformProfile(models.Model): privacy_policy = models.TextField(_('Privacy Policy'), blank=True) terms_conditions = models.TextField(_('Terms and Conditions'), blank=True) - # WhatsApp Configuration - whatsapp_access_token = models.TextField(_('WhatsApp Access Token'), blank=True, help_text=_("Permanent or temporary access token from Meta Business.")) - whatsapp_business_phone_number_id = models.CharField(_('WhatsApp Phone Number ID'), max_length=100, blank=True, help_text=_("The Phone Number ID from WhatsApp API setup.")) + # WhatsApp Configuration (Wablas Gateway) + whatsapp_access_token = models.TextField(_('Wablas API Token'), blank=True, help_text=_("Your Wablas API Token.")) + whatsapp_business_phone_number_id = models.CharField(_('Wablas Domain'), max_length=100, blank=True, default="https://deu.wablas.com", help_text=_("The Wablas API domain (e.g., https://deu.wablas.com).")) + whatsapp_app_secret = models.CharField(_('Wablas Secret Key'), max_length=255, blank=True, help_text=_("Your Wablas API Secret Key (if required).")) + + def save(self, *args, **kwargs): + # Auto-clean whitespace from credentials + if self.whatsapp_access_token: + self.whatsapp_access_token = self.whatsapp_access_token.strip() + if self.whatsapp_business_phone_number_id: + self.whatsapp_business_phone_number_id = self.whatsapp_business_phone_number_id.strip() + if self.whatsapp_app_secret: + self.whatsapp_app_secret = self.whatsapp_app_secret.strip() + + super().save(*args, **kwargs) def __str__(self): return self.name diff --git a/core/templates/admin/core/platformprofile/test_whatsapp.html b/core/templates/admin/core/platformprofile/test_whatsapp.html new file mode 100644 index 0000000..8875851 --- /dev/null +++ b/core/templates/admin/core/platformprofile/test_whatsapp.html @@ -0,0 +1,39 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_urls static admin_modify %} + +{% block extrahead %}{{ block.super }} + +{{ media }} +{% endblock %} + +{% block breadcrumbs %} +
+{% endblock %} + +{% block content %} +