40376-vm/core/models.py
Flatlogic Bot 4f209aac8e g
2026-07-03 19:38:20 +00:00

152 lines
6.1 KiB
Python

from decimal import Decimal
from django.db import models
from django.urls import reverse
class PilotCall(models.Model):
SOURCE_YTEL = 'ytel'
SOURCE_RINGCENTRAL = 'ringcentral'
SOURCE_MANUAL = 'manual'
CALL_SOURCE_CHOICES = [
(SOURCE_YTEL, 'Ytel'),
(SOURCE_RINGCENTRAL, 'RingCentral fallback'),
(SOURCE_MANUAL, 'Manual/recording test'),
]
STATUS_UNCONFIRMED = 'unconfirmed'
STATUS_AVAILABLE = 'available'
STATUS_BLOCKED = 'blocked'
STATUS_PARTNER = 'partner'
INTEGRATION_STATUS_CHOICES = [
(STATUS_UNCONFIRMED, 'Not confirmed'),
(STATUS_AVAILABLE, 'Available'),
(STATUS_BLOCKED, 'Blocked / not exposed'),
(STATUS_PARTNER, 'Needs enterprise/partner access'),
]
client_name = models.CharField(max_length=140)
phone = models.CharField(max_length=40)
email = models.EmailField(blank=True)
call_source = models.CharField(max_length=24, choices=CALL_SOURCE_CHOICES, default=SOURCE_YTEL)
ytel_audio_status = models.CharField(max_length=24, choices=INTEGRATION_STATUS_CHOICES, default=STATUS_UNCONFIRMED)
logics_api_status = models.CharField(max_length=24, choices=INTEGRATION_STATUS_CHOICES, default=STATUS_UNCONFIRMED)
transcript = models.TextField(help_text='Paste a live transcript segment or recorded-call transcript for the pilot.')
irs_debt = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
state_debt = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
tax_years = models.CharField(max_length=120, blank=True)
filing_status = models.CharField(max_length=80, blank=True)
employment_type = models.CharField(max_length=120, blank=True)
occupation = models.CharField(max_length=120, blank=True)
monthly_gross_income = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
spouse_income = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
mortgage = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
rent = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
utilities = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
car_payment = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
insurance = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
student_loans = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
credit_cards = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
assets = models.TextField(blank=True)
business_expenses = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
irs_payment_plan = models.BooleanField(default=False)
levy = models.BooleanField(default=False)
garnishment = models.BooleanField(default=False)
best_callback_time = models.CharField(max_length=120, blank=True)
agent_notes = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
verbose_name = 'pilot call'
verbose_name_plural = 'pilot calls'
def __str__(self):
return f'{self.client_name} · {self.get_call_source_display()}'
def get_absolute_url(self):
return reverse('call_detail', args=[self.pk])
@property
def required_fields(self):
return [
('IRS debt', self.irs_debt),
('Tax years', self.tax_years),
('Employment', self.employment_type),
('Monthly income', self.monthly_gross_income),
('Best callback', self.best_callback_time),
]
@property
def completion_score(self):
if not self.required_fields:
return 0
completed = sum(1 for _label, value in self.required_fields if self._has_value(value))
return round((completed / len(self.required_fields)) * 100)
@property
def missing_required_labels(self):
return [label for label, value in self.required_fields if not self._has_value(value)]
@property
def urgent_flags(self):
flags = []
if self.levy:
flags.append('Levy mentioned')
if self.garnishment:
flags.append('Wage garnishment mentioned')
if self.irs_debt and self.irs_debt > Decimal('50000'):
flags.append('Debt over $50,000')
if self.monthly_gross_income is not None and self.monthly_gross_income <= 0:
flags.append('No monthly income captured')
return flags
@staticmethod
def _has_value(value):
if value is None:
return False
if isinstance(value, str):
return bool(value.strip())
return True
class WebhookEvent(models.Model):
PROVIDER_GENERIC = 'generic'
provider = models.CharField(max_length=60, default=PROVIDER_GENERIC)
event_type = models.CharField(max_length=120, blank=True)
external_id = models.CharField(max_length=160, blank=True)
delivery_id = models.CharField(max_length=160, blank=True)
source_ip = models.GenericIPAddressField(null=True, blank=True)
content_type = models.CharField(max_length=180, blank=True)
headers = models.JSONField(default=dict, blank=True)
query_params = models.JSONField(default=dict, blank=True)
payload = models.JSONField(default=dict, blank=True)
raw_body = models.TextField(blank=True)
processed = models.BooleanField(default=False)
notes = models.TextField(blank=True)
received_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-received_at']
indexes = [
models.Index(fields=['provider', 'event_type']),
models.Index(fields=['received_at']),
]
verbose_name = 'webhook event'
verbose_name_plural = 'webhook events'
def __str__(self):
label = self.event_type or self.external_id or f'event #{self.pk}'
return f'{self.provider}: {label}'
@property
def display_event_type(self):
return self.event_type or 'unclassified'