payemnt billing and credits refactoring
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils import timezone
|
||||
|
||||
from igny8_core.business.billing.models import Invoice, Payment, CreditTransaction, CreditPackage
|
||||
from igny8_core.auth.models import Account
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Audit invoice/payment/credits for a purchase"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("--invoice-number", dest="invoice_number", help="Invoice number (e.g., INV-26010008)")
|
||||
parser.add_argument("--invoice-id", dest="invoice_id", type=int, help="Invoice ID")
|
||||
parser.add_argument("--payment-id", dest="payment_id", type=int, help="Payment ID")
|
||||
parser.add_argument("--account-id", dest="account_id", type=int, help="Account ID (optional)")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
invoice_number = options.get("invoice_number")
|
||||
invoice_id = options.get("invoice_id")
|
||||
payment_id = options.get("payment_id")
|
||||
account_id = options.get("account_id")
|
||||
|
||||
if not any([invoice_number, invoice_id, payment_id, account_id]):
|
||||
self.stderr.write("Provide at least one filter: --invoice-number, --invoice-id, --payment-id, --account-id")
|
||||
return
|
||||
|
||||
invoice_qs = Invoice.objects.all().select_related("account", "subscription", "subscription__plan")
|
||||
payment_qs = Payment.objects.all().select_related("account", "invoice")
|
||||
|
||||
invoice = None
|
||||
if invoice_number:
|
||||
invoice = invoice_qs.filter(invoice_number=invoice_number).first()
|
||||
elif invoice_id:
|
||||
invoice = invoice_qs.filter(id=invoice_id).first()
|
||||
elif payment_id:
|
||||
payment = payment_qs.filter(id=payment_id).first()
|
||||
invoice = payment.invoice if payment else None
|
||||
elif account_id:
|
||||
invoice = invoice_qs.filter(account_id=account_id).order_by("-created_at").first()
|
||||
|
||||
if not invoice:
|
||||
self.stderr.write("No invoice found for the provided filter.")
|
||||
return
|
||||
|
||||
account = invoice.account
|
||||
invoice_type = invoice.invoice_type
|
||||
credit_package_id = (invoice.metadata or {}).get("credit_package_id")
|
||||
credit_package = None
|
||||
if credit_package_id:
|
||||
credit_package = CreditPackage.objects.filter(id=credit_package_id).first()
|
||||
|
||||
self.stdout.write("=== INVOICE ===")
|
||||
self.stdout.write(f"Invoice: {invoice.invoice_number} (ID={invoice.id})")
|
||||
self.stdout.write(f"Type: {invoice_type}")
|
||||
self.stdout.write(f"Status: {invoice.status}")
|
||||
self.stdout.write(f"Total: {invoice.total} {invoice.currency}")
|
||||
self.stdout.write(f"Paid at: {invoice.paid_at}")
|
||||
self.stdout.write(f"Expires at: {invoice.expires_at}")
|
||||
self.stdout.write(f"Void reason: {invoice.void_reason}")
|
||||
self.stdout.write(f"Account: {account.id} - {account.name}")
|
||||
self.stdout.write(f"Account credits: {account.credits}")
|
||||
if invoice.subscription:
|
||||
plan = invoice.subscription.plan
|
||||
self.stdout.write(f"Subscription: {invoice.subscription.id} (status={invoice.subscription.status})")
|
||||
self.stdout.write(f"Plan: {plan.id if plan else None} - {plan.name if plan else None}")
|
||||
if credit_package:
|
||||
self.stdout.write(f"Credit Package: {credit_package.id} - {credit_package.name} ({credit_package.credits} credits)")
|
||||
|
||||
payments = payment_qs.filter(invoice_id=invoice.id).order_by("created_at")
|
||||
self.stdout.write("\n=== PAYMENTS ===")
|
||||
if not payments.exists():
|
||||
self.stdout.write("No payments found for this invoice.")
|
||||
else:
|
||||
for pay in payments:
|
||||
self.stdout.write(
|
||||
f"Payment {pay.id}: status={pay.status}, method={pay.payment_method}, amount={pay.amount} {pay.currency}, processed_at={pay.processed_at}"
|
||||
)
|
||||
|
||||
credit_transactions = CreditTransaction.objects.filter(account=account).order_by("-created_at")[:50]
|
||||
related_transactions = CreditTransaction.objects.filter(
|
||||
account=account
|
||||
).filter(
|
||||
metadata__invoice_id=invoice.id
|
||||
).order_by("created_at")
|
||||
|
||||
self.stdout.write("\n=== CREDIT TRANSACTIONS (RELATED) ===")
|
||||
if not related_transactions.exists():
|
||||
self.stdout.write("No credit transactions linked to this invoice.")
|
||||
else:
|
||||
for tx in related_transactions:
|
||||
self.stdout.write(
|
||||
f"{tx.created_at}: {tx.transaction_type} amount={tx.amount} balance_after={tx.balance_after} desc={tx.description}"
|
||||
)
|
||||
|
||||
if not related_transactions.exists() and invoice_type == "credit_package" and invoice.status == "paid":
|
||||
self.stdout.write("\n!!! WARNING: Paid credit invoice with no linked credit transaction.")
|
||||
self.stdout.write("This indicates credits were not applied.")
|
||||
|
||||
self.stdout.write("\n=== RECENT CREDIT TRANSACTIONS (LAST 50) ===")
|
||||
for tx in credit_transactions:
|
||||
self.stdout.write(
|
||||
f"{tx.created_at}: {tx.transaction_type} amount={tx.amount} balance_after={tx.balance_after} desc={tx.description}"
|
||||
)
|
||||
|
||||
self.stdout.write("\nAudit completed at: " + timezone.now().isoformat())
|
||||
Reference in New Issue
Block a user