STripe Paymen and PK payemtns and many othe rbacekd and froentened issues

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-07 05:51:36 +00:00
parent 87d1662a18
commit 0386d4bf33
24 changed files with 1079 additions and 174 deletions

View File

@@ -153,12 +153,144 @@ class Account(SoftDeletableModel):
# System accounts bypass all filtering restrictions
return self.slug in ['aws-admin', 'default-account', 'default']
def soft_delete(self, user=None, reason=None, retention_days=None):
def soft_delete(self, user=None, reason=None, retention_days=None, cascade=True):
"""
Soft delete the account and optionally cascade to all related objects.
Args:
user: User performing the deletion
reason: Reason for deletion
retention_days: Days before permanent deletion
cascade: If True, also soft-delete related objects that support soft delete,
and hard-delete objects that don't support soft delete
"""
if self.is_system_account():
from django.core.exceptions import PermissionDenied
raise PermissionDenied("System account cannot be deleted.")
if cascade:
self._cascade_delete_related(user=user, reason=reason, retention_days=retention_days, hard_delete=False)
return super().soft_delete(user=user, reason=reason, retention_days=retention_days)
def _cascade_delete_related(self, user=None, reason=None, retention_days=None, hard_delete=False):
"""
Delete all related objects when account is deleted.
For soft delete: soft-deletes objects with SoftDeletableModel, hard-deletes others
For hard delete: hard-deletes everything
"""
from igny8_core.common.soft_delete import SoftDeletableModel
# List of related objects to delete (in order to avoid FK constraint issues)
# Related names from Account reverse relations
related_names = [
# Content & Planning related (delete first due to dependencies)
'contentclustermap_set',
'contentattribute_set',
'contenttaxonomy_set',
'content_set',
'images_set',
'contentideas_set',
'tasks_set',
'keywords_set',
'clusters_set',
'strategy_set',
# Automation
'automation_runs',
'automation_configs',
# Publishing & Integration
'syncevent_set',
'publishingsettings_set',
'publishingrecord_set',
'deploymentrecord_set',
'siteintegration_set',
# Notifications & Optimization
'notification_set',
'optimizationtask_set',
# AI & Settings
'aitasklog_set',
'aiprompt_set',
'aisettings_set',
'authorprofile_set',
# Billing (preserve invoices/payments for audit, delete others)
'planlimitusage_set',
'creditusagelog_set',
'credittransaction_set',
'accountpaymentmethod_set',
'payment_set',
'invoice_set',
# Settings
'modulesettings_set',
'moduleenablesettings_set',
'integrationsettings_set',
'user_settings',
'accountsettings_set',
# Core (last due to dependencies)
'sector_set',
'site_set',
# Subscription (OneToOne)
'subscription',
]
for related_name in related_names:
try:
related = getattr(self, related_name, None)
if related is None:
continue
# Handle OneToOne fields (subscription)
if hasattr(related, 'pk'):
# It's a single object (OneToOneField)
if hard_delete:
related.hard_delete() if hasattr(related, 'hard_delete') else related.delete()
elif isinstance(related, SoftDeletableModel):
related.soft_delete(user=user, reason=reason, retention_days=retention_days)
else:
# Non-soft-deletable single object - hard delete
related.delete()
else:
# It's a RelatedManager (ForeignKey)
queryset = related.all()
if queryset.exists():
if hard_delete:
# Hard delete all
if hasattr(queryset, 'hard_delete'):
queryset.hard_delete()
else:
for obj in queryset:
if hasattr(obj, 'hard_delete'):
obj.hard_delete()
else:
obj.delete()
else:
# Soft delete if supported, otherwise hard delete
model = queryset.model
if issubclass(model, SoftDeletableModel):
for obj in queryset:
obj.soft_delete(user=user, reason=reason, retention_days=retention_days)
else:
queryset.delete()
except Exception as e:
# Log but don't fail - some relations may not exist
import logging
logger = logging.getLogger(__name__)
logger.warning(f"Failed to delete related {related_name} for account {self.pk}: {e}")
def hard_delete_with_cascade(self, using=None, keep_parents=False):
"""
Permanently delete the account and ALL related objects.
This bypasses soft-delete and removes everything from the database.
USE WITH CAUTION - this cannot be undone!
"""
if self.is_system_account():
from django.core.exceptions import PermissionDenied
raise PermissionDenied("System account cannot be deleted.")
# Cascade hard-delete all related objects first
self._cascade_delete_related(hard_delete=True)
# Finally hard-delete the account itself
return super().hard_delete(using=using, keep_parents=keep_parents)
def delete(self, using=None, keep_parents=False):
return self.soft_delete()