Complete Implemenation of tenancy

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-09 00:11:35 +00:00
parent c54db6c2d9
commit bfbade7624
25 changed files with 4959 additions and 35 deletions

View File

@@ -60,10 +60,9 @@ class InvoiceAdmin(AccountAdminMixin, admin.ModelAdmin):
'currency',
'invoice_date',
'due_date',
'subscription',
]
list_filter = ['status', 'currency', 'invoice_date', 'account']
search_fields = ['invoice_number', 'account__name', 'subscription__id']
search_fields = ['invoice_number', 'account__name']
readonly_fields = ['created_at', 'updated_at']
@@ -77,11 +76,106 @@ class PaymentAdmin(AccountAdminMixin, admin.ModelAdmin):
'status',
'amount',
'currency',
'manual_reference',
'approved_by',
'processed_at',
]
list_filter = ['status', 'payment_method', 'currency', 'created_at']
search_fields = ['invoice__invoice_number', 'account__name', 'stripe_payment_intent_id', 'paypal_order_id']
readonly_fields = ['created_at', 'updated_at']
list_filter = ['status', 'payment_method', 'currency', 'created_at', 'processed_at']
search_fields = [
'invoice__invoice_number',
'account__name',
'stripe_payment_intent_id',
'paypal_order_id',
'manual_reference',
'admin_notes',
'manual_notes'
]
readonly_fields = ['created_at', 'updated_at', 'approved_at', 'processed_at', 'failed_at', 'refunded_at']
actions = ['approve_payments', 'reject_payments']
def approve_payments(self, request, queryset):
"""Approve selected manual payments"""
from django.db import transaction
from django.utils import timezone
from igny8_core.business.billing.services.credit_service import CreditService
count = 0
errors = []
for payment in queryset.filter(status='pending_approval'):
try:
with transaction.atomic():
invoice = payment.invoice
subscription = invoice.subscription if hasattr(invoice, 'subscription') else None
account = payment.account
# Update Payment
payment.status = 'succeeded'
payment.approved_by = request.user
payment.approved_at = timezone.now()
payment.processed_at = timezone.now()
payment.admin_notes = f'Bulk approved by {request.user.email}'
payment.save()
# Update Invoice
invoice.status = 'paid'
invoice.paid_at = timezone.now()
invoice.save()
# Update Subscription
if subscription:
subscription.status = 'active'
subscription.external_payment_id = payment.manual_reference
subscription.save()
# Update Account
account.status = 'active'
account.save()
# Add Credits
if subscription and subscription.plan:
CreditService.add_credits(
account=account,
amount=subscription.plan.included_credits,
transaction_type='subscription',
description=f'{subscription.plan.name} - Invoice {invoice.invoice_number}',
metadata={
'subscription_id': subscription.id,
'invoice_id': invoice.id,
'payment_id': payment.id,
'approved_by': request.user.email
}
)
count += 1
except Exception as e:
errors.append(f'Payment {payment.id}: {str(e)}')
if count:
self.message_user(request, f'Successfully approved {count} payment(s)')
if errors:
for error in errors:
self.message_user(request, error, level='ERROR')
approve_payments.short_description = 'Approve selected manual payments'
def reject_payments(self, request, queryset):
"""Reject selected manual payments"""
from django.utils import timezone
count = queryset.filter(status='pending_approval').update(
status='failed',
approved_by=request.user,
approved_at=timezone.now(),
failed_at=timezone.now(),
admin_notes=f'Bulk rejected by {request.user.email}',
failure_reason='Rejected by admin'
)
self.message_user(request, f'Rejected {count} payment(s)')
reject_payments.short_description = 'Reject selected manual payments'
@admin.register(CreditPackage)

View File

@@ -4,6 +4,7 @@ Serializers for Billing Models
from rest_framework import serializers
from .models import CreditTransaction, CreditUsageLog
from igny8_core.auth.models import Account
from igny8_core.business.billing.models import PaymentMethodConfig, Payment
class CreditTransactionSerializer(serializers.ModelSerializer):
@@ -48,6 +49,48 @@ class UsageSummarySerializer(serializers.Serializer):
by_model = serializers.DictField()
class PaymentMethodConfigSerializer(serializers.ModelSerializer):
"""Serializer for payment method configuration"""
payment_method_display = serializers.CharField(source='get_payment_method_display', read_only=True)
class Meta:
model = PaymentMethodConfig
fields = [
'id', 'country_code', 'payment_method', 'payment_method_display',
'is_enabled', 'display_name', 'instructions',
'bank_name', 'account_number', 'swift_code',
'wallet_type', 'wallet_id', 'sort_order'
]
read_only_fields = ['id']
class PaymentConfirmationSerializer(serializers.Serializer):
"""Serializer for manual payment confirmation"""
invoice_id = serializers.IntegerField(required=True)
payment_method = serializers.ChoiceField(
choices=['bank_transfer', 'local_wallet'],
required=True
)
manual_reference = serializers.CharField(
required=True,
max_length=255,
help_text="Transaction reference number"
)
manual_notes = serializers.CharField(
required=False,
allow_blank=True,
help_text="Additional notes about the payment"
)
amount = serializers.DecimalField(
max_digits=10,
decimal_places=2,
required=True
)
proof_url = serializers.URLField(
required=False,
allow_blank=True,
help_text="URL to receipt/proof of payment"
)
class LimitCardSerializer(serializers.Serializer):
"""Serializer for individual limit card"""
title = serializers.CharField()