billing admin account 1

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-05 08:01:55 +00:00
parent f91037b729
commit 1e718105f2
12 changed files with 378 additions and 85 deletions

View File

@@ -1,6 +1,7 @@
"""
Billing Models for Credit System
"""
from decimal import Decimal
from django.db import models
from django.core.validators import MinValueValidator
from django.conf import settings
@@ -22,6 +23,11 @@ class CreditTransaction(AccountBaseModel):
balance_after = models.IntegerField(help_text="Credit balance after this transaction")
description = models.CharField(max_length=255)
metadata = models.JSONField(default=dict, help_text="Additional context (AI call details, etc.)")
reference_id = models.CharField(
max_length=255,
blank=True,
help_text="Optional reference (e.g., payment id, invoice id)"
)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
@@ -183,9 +189,10 @@ class Invoice(AccountBaseModel):
)
# Amounts
subtotal = models.DecimalField(max_digits=10, decimal_places=2)
subtotal = models.DecimalField(max_digits=10, decimal_places=2, default=0)
tax = models.DecimalField(max_digits=10, decimal_places=2, default=0)
total = models.DecimalField(max_digits=10, decimal_places=2)
total = models.DecimalField(max_digits=10, decimal_places=2, default=0)
currency = models.CharField(max_length=3, default='USD')
# Status
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending', db_index=True)
@@ -201,6 +208,9 @@ class Invoice(AccountBaseModel):
# Payment integration
stripe_invoice_id = models.CharField(max_length=255, null=True, blank=True)
payment_method = models.CharField(max_length=50, null=True, blank=True)
billing_email = models.EmailField(null=True, blank=True)
billing_period_start = models.DateTimeField(null=True, blank=True)
billing_period_end = models.DateTimeField(null=True, blank=True)
# Metadata
notes = models.TextField(blank=True)
@@ -222,6 +232,45 @@ class Invoice(AccountBaseModel):
def __str__(self):
return f"Invoice {self.invoice_number} - {self.account.name if self.account else 'No Account'}"
# ------------------------------------------------------------------
# Helpers to keep service code working with legacy field names
# ------------------------------------------------------------------
@property
def subtotal_amount(self):
return self.subtotal
@property
def tax_amount(self):
return self.tax
@property
def total_amount(self):
return self.total
def add_line_item(self, description: str, quantity: int, unit_price: Decimal, amount: Decimal = None):
"""Append a line item and keep JSON shape consistent."""
items = list(self.line_items or [])
qty = quantity or 1
amt = Decimal(amount) if amount is not None else Decimal(unit_price) * qty
items.append({
'description': description,
'quantity': qty,
'unit_price': str(unit_price),
'amount': str(amt),
})
self.line_items = items
def calculate_totals(self):
"""Recompute subtotal, tax, and total from line_items."""
subtotal = Decimal('0')
for item in self.line_items or []:
try:
subtotal += Decimal(str(item.get('amount') or 0))
except Exception:
pass
self.subtotal = subtotal
self.total = subtotal + (self.tax or Decimal('0'))
class Payment(AccountBaseModel):
"""
@@ -230,8 +279,10 @@ class Payment(AccountBaseModel):
"""
STATUS_CHOICES = [
('pending', 'Pending'),
('pending_approval', 'Pending Approval'),
('processing', 'Processing'),
('succeeded', 'Succeeded'),
('completed', 'Completed'), # Legacy alias for succeeded
('failed', 'Failed'),
('refunded', 'Refunded'),
('cancelled', 'Cancelled'),
@@ -272,6 +323,8 @@ class Payment(AccountBaseModel):
help_text="Bank transfer reference, wallet transaction ID, etc."
)
manual_notes = models.TextField(blank=True, help_text="Admin notes for manual payments")
transaction_reference = models.CharField(max_length=255, blank=True)
admin_notes = models.TextField(blank=True, help_text="Internal notes on approval/rejection")
approved_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
null=True,