feat(core): add auto_mark_paid setting to PlatformProfile for testing
This commit is contained in:
parent
ce26876865
commit
e42f1985b5
@ -122,11 +122,6 @@ class ParcelAdmin(admin.ModelAdmin):
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_changeform_initial_data(self, request):
|
|
||||||
initial = super().get_changeform_initial_data(request)
|
|
||||||
initial['payment_status'] = 'paid'
|
|
||||||
return initial
|
|
||||||
|
|
||||||
def export_as_csv(self, request, queryset):
|
def export_as_csv(self, request, queryset):
|
||||||
response = HttpResponse(content_type='text/csv')
|
response = HttpResponse(content_type='text/csv')
|
||||||
response['Content-Disposition'] = 'attachment; filename="parcels_report.csv"'
|
response['Content-Disposition'] = 'attachment; filename="parcels_report.csv"'
|
||||||
@ -176,6 +171,10 @@ class PlatformProfileAdmin(admin.ModelAdmin):
|
|||||||
(_('Financial Configuration'), {
|
(_('Financial Configuration'), {
|
||||||
'fields': ('platform_fee_percentage', 'enable_payment')
|
'fields': ('platform_fee_percentage', 'enable_payment')
|
||||||
}),
|
}),
|
||||||
|
(_('Testing / Development'), {
|
||||||
|
'fields': ('auto_mark_paid',),
|
||||||
|
'description': _('Enable this to automatically mark NEW parcels as "Paid" (useful for testing so drivers can see them immediately).')
|
||||||
|
}),
|
||||||
(_('Integrations'), {
|
(_('Integrations'), {
|
||||||
'fields': ('google_maps_api_key',),
|
'fields': ('google_maps_api_key',),
|
||||||
'description': _('API Keys for external services.')
|
'description': _('API Keys for external services.')
|
||||||
|
|||||||
18
core/migrations/0024_platformprofile_auto_mark_paid.py
Normal file
18
core/migrations/0024_platformprofile_auto_mark_paid.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2026-01-31 10:42
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0023_pricingrule_parcel_delivery_lat_parcel_delivery_lng_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='platformprofile',
|
||||||
|
name='auto_mark_paid',
|
||||||
|
field=models.BooleanField(default=False, help_text="If enabled, newly created parcels will automatically be marked as 'Paid' for testing.", verbose_name='Test Mode: Auto-Paid'),
|
||||||
|
),
|
||||||
|
]
|
||||||
149
core/models.py
149
core/models.py
@ -131,6 +131,75 @@ class PricingRule(models.Model):
|
|||||||
verbose_name_plural = _('Pricing Rules')
|
verbose_name_plural = _('Pricing Rules')
|
||||||
ordering = ['min_distance', 'min_weight']
|
ordering = ['min_distance', 'min_weight']
|
||||||
|
|
||||||
|
class PlatformProfile(models.Model):
|
||||||
|
name = models.CharField(_('Platform Name'), max_length=100)
|
||||||
|
logo = models.ImageField(_('Logo'), upload_to='platform_logos/', blank=True, null=True)
|
||||||
|
slogan = models.CharField(_('Slogan'), max_length=255, blank=True)
|
||||||
|
address = models.TextField(_('Address'), blank=True)
|
||||||
|
phone_number = models.CharField(_('Phone Number'), max_length=50, blank=True)
|
||||||
|
registration_number = models.CharField(_('Registration Number'), max_length=100, blank=True)
|
||||||
|
vat_number = models.CharField(_('VAT Number'), max_length=100, blank=True)
|
||||||
|
|
||||||
|
# Financial Configuration
|
||||||
|
platform_fee_percentage = models.DecimalField(_('Platform Fee (%)'), max_digits=5, decimal_places=2, default=Decimal('0.00'), help_text=_("Percentage deducted from total trip price."))
|
||||||
|
|
||||||
|
# Integrations
|
||||||
|
google_maps_api_key = models.CharField(_('Google Maps API Key'), max_length=255, blank=True, help_text=_("API Key for Google Maps (Distance Matrix, Maps JS)."))
|
||||||
|
|
||||||
|
# Bilingual Policies
|
||||||
|
privacy_policy_en = models.TextField(_('Privacy Policy (English)'), blank=True)
|
||||||
|
privacy_policy_ar = models.TextField(_('Privacy Policy (Arabic)'), blank=True)
|
||||||
|
terms_conditions_en = models.TextField(_('Terms and Conditions (English)'), blank=True)
|
||||||
|
terms_conditions_ar = models.TextField(_('Terms and Conditions (Arabic)'), blank=True)
|
||||||
|
|
||||||
|
# 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)."))
|
||||||
|
|
||||||
|
# Payment Configuration
|
||||||
|
enable_payment = models.BooleanField(_('Enable Payment'), default=True, help_text=_("Toggle to enable or disable payments on the platform."))
|
||||||
|
|
||||||
|
# Testing / Development
|
||||||
|
auto_mark_paid = models.BooleanField(_('Test Mode: Auto-Paid'), default=False, help_text=_("If enabled, newly created parcels will automatically be marked as 'Paid' for testing."))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def privacy_policy(self):
|
||||||
|
if get_language() == 'ar':
|
||||||
|
return self.privacy_policy_ar
|
||||||
|
return self.privacy_policy_en
|
||||||
|
|
||||||
|
@property
|
||||||
|
def terms_conditions(self):
|
||||||
|
if get_language() == 'ar':
|
||||||
|
return self.terms_conditions_ar
|
||||||
|
return self.terms_conditions_en
|
||||||
|
|
||||||
|
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:
|
||||||
|
val = self.whatsapp_business_phone_number_id.strip()
|
||||||
|
# Remove common path suffixes if user pasted full URL
|
||||||
|
for suffix in ['/api/send-message', '/api/v2/send-message']:
|
||||||
|
if val.endswith(suffix):
|
||||||
|
val = val[:-len(suffix)]
|
||||||
|
if val.endswith('/'):
|
||||||
|
val = val[:-1]
|
||||||
|
self.whatsapp_business_phone_number_id = val
|
||||||
|
if self.whatsapp_app_secret:
|
||||||
|
self.whatsapp_app_secret = self.whatsapp_app_secret.strip()
|
||||||
|
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Platform Profile')
|
||||||
|
verbose_name_plural = _('Platform Profile')
|
||||||
|
|
||||||
class Parcel(models.Model):
|
class Parcel(models.Model):
|
||||||
STATUS_CHOICES = (
|
STATUS_CHOICES = (
|
||||||
('pending', _('Pending Pickup')),
|
('pending', _('Pending Pickup')),
|
||||||
@ -189,12 +258,24 @@ class Parcel(models.Model):
|
|||||||
updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
|
updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
is_new = self.pk is None
|
||||||
|
|
||||||
|
# Test Mode Logic: Auto-Mark Paid
|
||||||
|
if is_new and self.payment_status == 'pending':
|
||||||
|
try:
|
||||||
|
# Use filter().first() to avoid error if table is empty
|
||||||
|
profile = PlatformProfile.objects.first()
|
||||||
|
if profile and profile.auto_mark_paid:
|
||||||
|
self.payment_status = 'paid'
|
||||||
|
except Exception:
|
||||||
|
# Fail gracefully (e.g. during migrations)
|
||||||
|
pass
|
||||||
|
|
||||||
if not self.tracking_number:
|
if not self.tracking_number:
|
||||||
self.tracking_number = str(uuid.uuid4().hex[:10]).upper()
|
self.tracking_number = str(uuid.uuid4().hex[:10]).upper()
|
||||||
|
|
||||||
# Calculate Distance and Price if Lat/Lng provided and price is 0 (or always update to ensure accuracy)
|
# Calculate Distance and Price if Lat/Lng provided and price is 0 (or always update to ensure accuracy)
|
||||||
# We only recalculate if status is pending or if it's a new object to avoid changing history for completed trips
|
# We only recalculate if status is pending or if it's a new object to avoid changing history for completed trips
|
||||||
is_new = self.pk is None
|
|
||||||
if (is_new or self.status == 'pending') and self.pickup_lat and self.pickup_lng and self.delivery_lat and self.delivery_lng:
|
if (is_new or self.status == 'pending') and self.pickup_lat and self.pickup_lng and self.delivery_lat and self.delivery_lng:
|
||||||
# Local import to avoid circular dependency
|
# Local import to avoid circular dependency
|
||||||
from .pricing import calculate_haversine_distance, get_pricing_breakdown
|
from .pricing import calculate_haversine_distance, get_pricing_breakdown
|
||||||
@ -223,72 +304,6 @@ class Parcel(models.Model):
|
|||||||
verbose_name = _('Parcel')
|
verbose_name = _('Parcel')
|
||||||
verbose_name_plural = _('Parcels')
|
verbose_name_plural = _('Parcels')
|
||||||
|
|
||||||
class PlatformProfile(models.Model):
|
|
||||||
name = models.CharField(_('Platform Name'), max_length=100)
|
|
||||||
logo = models.ImageField(_('Logo'), upload_to='platform_logos/', blank=True, null=True)
|
|
||||||
slogan = models.CharField(_('Slogan'), max_length=255, blank=True)
|
|
||||||
address = models.TextField(_('Address'), blank=True)
|
|
||||||
phone_number = models.CharField(_('Phone Number'), max_length=50, blank=True)
|
|
||||||
registration_number = models.CharField(_('Registration Number'), max_length=100, blank=True)
|
|
||||||
vat_number = models.CharField(_('VAT Number'), max_length=100, blank=True)
|
|
||||||
|
|
||||||
# Financial Configuration
|
|
||||||
platform_fee_percentage = models.DecimalField(_('Platform Fee (%)'), max_digits=5, decimal_places=2, default=Decimal('0.00'), help_text=_("Percentage deducted from total trip price."))
|
|
||||||
|
|
||||||
# Integrations
|
|
||||||
google_maps_api_key = models.CharField(_('Google Maps API Key'), max_length=255, blank=True, help_text=_("API Key for Google Maps (Distance Matrix, Maps JS)."))
|
|
||||||
|
|
||||||
# Bilingual Policies
|
|
||||||
privacy_policy_en = models.TextField(_('Privacy Policy (English)'), blank=True)
|
|
||||||
privacy_policy_ar = models.TextField(_('Privacy Policy (Arabic)'), blank=True)
|
|
||||||
terms_conditions_en = models.TextField(_('Terms and Conditions (English)'), blank=True)
|
|
||||||
terms_conditions_ar = models.TextField(_('Terms and Conditions (Arabic)'), blank=True)
|
|
||||||
|
|
||||||
# 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)."))
|
|
||||||
|
|
||||||
# Payment Configuration
|
|
||||||
enable_payment = models.BooleanField(_('Enable Payment'), default=True, help_text=_("Toggle to enable or disable payments on the platform."))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def privacy_policy(self):
|
|
||||||
if get_language() == 'ar':
|
|
||||||
return self.privacy_policy_ar
|
|
||||||
return self.privacy_policy_en
|
|
||||||
|
|
||||||
@property
|
|
||||||
def terms_conditions(self):
|
|
||||||
if get_language() == 'ar':
|
|
||||||
return self.terms_conditions_ar
|
|
||||||
return self.terms_conditions_en
|
|
||||||
|
|
||||||
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:
|
|
||||||
val = self.whatsapp_business_phone_number_id.strip()
|
|
||||||
# Remove common path suffixes if user pasted full URL
|
|
||||||
for suffix in ['/api/send-message', '/api/v2/send-message']:
|
|
||||||
if val.endswith(suffix):
|
|
||||||
val = val[:-len(suffix)]
|
|
||||||
if val.endswith('/'):
|
|
||||||
val = val[:-1]
|
|
||||||
self.whatsapp_business_phone_number_id = val
|
|
||||||
if self.whatsapp_app_secret:
|
|
||||||
self.whatsapp_app_secret = self.whatsapp_app_secret.strip()
|
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Platform Profile')
|
|
||||||
verbose_name_plural = _('Platform Profile')
|
|
||||||
|
|
||||||
class OTPVerification(models.Model):
|
class OTPVerification(models.Model):
|
||||||
PURPOSE_CHOICES = (
|
PURPOSE_CHOICES = (
|
||||||
('profile_update', _('Profile Update')),
|
('profile_update', _('Profile Update')),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user