Complete Implemenation of tenancy
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user