CineDev commited on
Commit
75ecc49
·
1 Parent(s): 7f092e1

feat: update finance models

Browse files
Files changed (1) hide show
  1. finance/models.py +147 -1
finance/models.py CHANGED
@@ -1,3 +1,149 @@
1
  from django.db import models
 
2
 
3
- # Create your models here.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from django.db import models
2
+ from django.contrib.auth import get_user_model
3
 
4
+ User = get_user_model()
5
+
6
+ class Transaction(models.Model):
7
+ user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='transactions')
8
+ date = models.DateField()
9
+ merchant = models.CharField(max_length=255)
10
+ amount = models.DecimalField(max_digits=12, decimal_places=2)
11
+ category = models.CharField(max_length=100)
12
+ category_key = models.CharField(max_length=50)
13
+ time = models.TimeField(null=True, blank=True)
14
+ payment_method = models.CharField(max_length=100, null=True, blank=True)
15
+ note = models.TextField(null=True, blank=True)
16
+ created_at = models.DateTimeField(auto_now_add=True)
17
+ updated_at = models.DateTimeField(auto_now=True)
18
+
19
+ class Meta:
20
+ ordering = ['-date', '-time', '-created_at']
21
+
22
+ def __str__(self):
23
+ return f"{self.merchant} - {self.amount}"
24
+
25
+ class Budget(models.Model):
26
+ user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='budgets')
27
+ name = models.CharField(max_length=100)
28
+ category_key = models.CharField(max_length=50)
29
+ budget_amount = models.DecimalField(max_digits=12, decimal_places=2)
30
+ spent_amount = models.DecimalField(max_digits=12, decimal_places=2, default=0)
31
+ remaining_amount = models.DecimalField(max_digits=12, decimal_places=2, default=0)
32
+ status = models.CharField(max_length=50, default='good') # good, warning, danger
33
+ warning_message = models.TextField(null=True, blank=True)
34
+ created_at = models.DateTimeField(auto_now_add=True)
35
+ updated_at = models.DateTimeField(auto_now=True)
36
+
37
+ def save(self, *args, **kwargs):
38
+ self.remaining_amount = self.budget_amount - self.spent_amount
39
+ percentage = (self.spent_amount / self.budget_amount) * 100 if self.budget_amount > 0 else 0
40
+
41
+ if percentage >= 90:
42
+ self.status = 'danger'
43
+ elif percentage >= 70:
44
+ self.status = 'warning'
45
+ else:
46
+ self.status = 'good'
47
+
48
+ super().save(*args, **kwargs)
49
+
50
+ def __str__(self):
51
+ return f"{self.name} Budget - {self.user.email}"
52
+
53
+ class Goal(models.Model):
54
+ user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='goals')
55
+ name = models.CharField(max_length=255)
56
+ current_amount = models.DecimalField(max_digits=12, decimal_places=2, default=0)
57
+ target_amount = models.DecimalField(max_digits=12, decimal_places=2)
58
+ deadline = models.DateField(null=True, blank=True)
59
+ monthly_target = models.DecimalField(max_digits=12, decimal_places=2, default=0)
60
+ priority = models.CharField(max_length=50, default='medium') # low, medium, high
61
+ category = models.CharField(max_length=100, null=True, blank=True)
62
+ auto_save = models.BooleanField(default=False)
63
+ created_at = models.DateTimeField(auto_now_add=True)
64
+ updated_at = models.DateTimeField(auto_now=True)
65
+
66
+ def __str__(self):
67
+ return f"{self.name} - {self.user.email}"
68
+
69
+ class Subscription(models.Model):
70
+ user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='subscriptions')
71
+ name = models.CharField(max_length=255)
72
+ amount = models.DecimalField(max_digits=10, decimal_places=2)
73
+ renewal_date = models.DateField(null=True, blank=True)
74
+ usage_level = models.CharField(max_length=50, default='Medium')
75
+ value_score = models.IntegerField(default=3) # 1 to 5
76
+ status = models.CharField(max_length=50, default='active') # active, unused
77
+ last_used_date = models.DateField(null=True, blank=True)
78
+ warning = models.TextField(null=True, blank=True)
79
+ potential_saving = models.DecimalField(max_digits=10, decimal_places=2, default=0)
80
+ created_at = models.DateTimeField(auto_now_add=True)
81
+ updated_at = models.DateTimeField(auto_now=True)
82
+
83
+ def __str__(self):
84
+ return f"{self.name} - {self.user.email}"
85
+
86
+ from django.db.models.signals import pre_save, post_save, post_delete
87
+ from django.dispatch import receiver
88
+ from django.db.models import Sum
89
+ from users.models import FinancialProfile
90
+
91
+ @receiver(pre_save, sender=Transaction)
92
+ def store_original_amount(sender, instance, **kwargs):
93
+ if instance.pk:
94
+ try:
95
+ original = Transaction.objects.get(pk=instance.pk)
96
+ instance._original_amount = original.amount
97
+ except Transaction.DoesNotExist:
98
+ instance._original_amount = 0
99
+ else:
100
+ instance._original_amount = 0
101
+
102
+ @receiver(post_save, sender=Transaction)
103
+ def update_profile_balances(sender, instance, created, **kwargs):
104
+ try:
105
+ profile, _ = FinancialProfile.objects.get_or_create(user=instance.user)
106
+ if created:
107
+ profile.cash_available += instance.amount
108
+ profile.net_worth += instance.amount
109
+ else:
110
+ original_amount = getattr(instance, '_original_amount', 0)
111
+ diff = instance.amount - original_amount
112
+ profile.cash_available += diff
113
+ profile.net_worth += diff
114
+ profile.save()
115
+ except Exception as e:
116
+ print("Error in update_profile_balances signal:", e)
117
+
118
+ @receiver(post_delete, sender=Transaction)
119
+ def update_profile_balances_on_delete(sender, instance, **kwargs):
120
+ try:
121
+ profile, _ = FinancialProfile.objects.get_or_create(user=instance.user)
122
+ profile.cash_available -= instance.amount
123
+ profile.net_worth -= instance.amount
124
+ profile.save()
125
+ except Exception as e:
126
+ print("Error in update_profile_balances_on_delete signal:", e)
127
+
128
+ @receiver(post_save, sender=Transaction)
129
+ @receiver(post_delete, sender=Transaction)
130
+ def update_budget_spent(sender, instance, **kwargs):
131
+ try:
132
+ cat_key = instance.category_key
133
+ if cat_key == 'dining' or cat_key == 'food':
134
+ budgets = Budget.objects.filter(user=instance.user, category_key__in=['dining', 'food'])
135
+ else:
136
+ budgets = Budget.objects.filter(user=instance.user, category_key=cat_key)
137
+
138
+ for budget in budgets:
139
+ filter_keys = ['dining', 'food'] if cat_key in ['dining', 'food'] else [cat_key]
140
+ expenses_sum = Transaction.objects.filter(
141
+ user=instance.user,
142
+ category_key__in=filter_keys,
143
+ amount__lt=0
144
+ ).aggregate(total=Sum('amount'))['total'] or 0
145
+
146
+ budget.spent_amount = abs(expenses_sum)
147
+ budget.save()
148
+ except Exception as e:
149
+ print("Error in update_budget_spent signal:", e)