import uuid from datetime import datetime, timedelta from django.contrib.auth.models import ( AbstractBaseUser, BaseUserManager, PermissionsMixin, ) from django.core.validators import RegexValidator from django.db import models from django.utils import timezone from django.utils.translation import gettext as _ from foldbank.utils import get_bank_logo_file_path # object managers class CustomAccountManager(BaseUserManager): def create_user(self, username, password): if not username: raise ValueError("Users must have a username.") user = self.model(username=username) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, username, password): user = self.create_user(username, password) user.is_staff = True user.is_superuser = True user.save(using=self._db) return user # Create your models here. class User(AbstractBaseUser, PermissionsMixin): # TODO: make username case insensitive yet retain case sensitivity id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) username = models.CharField( max_length=32, unique=True, null=True, blank=True, validators=[ RegexValidator( r"^[a-zA-Z0-9_]*$", message="only allows letters, numbers and underscores", ) ], help_text=_("only allows letters, numbers and underscores"), ) name = models.CharField(null=True, blank=True, max_length=128) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) is_superuser = models.BooleanField(default=False) date_joined = models.DateTimeField(auto_now_add=True) last_login = models.DateTimeField(default=timezone.now) last_updated = models.DateTimeField(auto_now=True) objects = CustomAccountManager() USERNAME_FIELD = "username" def __str__(self): if not self.username: return "USERNAME-NOT-SET" return self.username class Tag(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) title = models.CharField(blank=True, null=True, max_length=64) sub_category = models.CharField(blank=True, null=True, max_length=64) icon_type = models.CharField(blank=True, null=True, max_length=64) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): ret = self.title if self.sub_category: ret += f" - {self.sub_category}" return ret class Bank(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(blank=True, null=True, max_length=64) logo = models.FileField( upload_to=get_bank_logo_file_path, null=True, blank=True, default=None, ) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.name class Account(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) created_at = models.DateTimeField(auto_now_add=True) user = models.ForeignKey("User", on_delete=models.PROTECT) bank = models.ForeignKey("Bank", on_delete=models.PROTECT) account_number = models.CharField(blank=True, null=True, max_length=64) SAVINGS_ACCOUNT = "SA" CURRENT_ACCOUNT = "CA" ACCOUNT_TYPES = [ (SAVINGS_ACCOUNT, "Savings Account"), (CURRENT_ACCOUNT, "Current Account"), ] account_type = models.CharField( max_length=2, choices=ACCOUNT_TYPES, default=SAVINGS_ACCOUNT, ) ifsc_code = models.CharField(blank=True, null=True, max_length=64) swift_bic = models.CharField(blank=True, null=True, max_length=64) holders_name = models.CharField(blank=True, null=True, max_length=64) USER_ACCOUNT = "UA" ORG_ACCOUNT = "OA" ACCOUNT_OWNER_TYPES = [ (USER_ACCOUNT, "User Account"), (ORG_ACCOUNT, "Organization Account"), ] account_owner_type = models.CharField( max_length=2, choices=ACCOUNT_OWNER_TYPES, default=USER_ACCOUNT, ) balance = models.FloatField(null=True, blank=True) def __str__(self): return f"{self.account_owner_type} - {self.bank} - {self.account_number} - {self.holders_name}" class Transaction(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) created_at = models.DateTimeField(auto_now_add=True) amount = models.FloatField(null=True, blank=True) tag = models.ForeignKey("Tag", on_delete=models.PROTECT) from_account = models.ForeignKey( "Account", on_delete=models.PROTECT, related_name="transactions_from" ) to_account = models.ForeignKey( "Account", on_delete=models.PROTECT, related_name="transactions_to" ) def __str__(self): return f"{self.from_account.holders_name} - {self.to_account.holders_name}" class RecurringPayment(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) from_account = models.ForeignKey( "Account", on_delete=models.PROTECT, related_name="recurring_payment_from" ) to_account = models.ForeignKey( "Account", on_delete=models.PROTECT, related_name="recurring_payment_to" ) frequency = models.DurationField(default=timedelta(days=30)) amount = models.FloatField(null=True, blank=True) due_on = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True) tag = models.ForeignKey("Tag", on_delete=models.PROTECT) def __str__(self): return f"{self.from_account.holders_name} - {self.to_account.holders_name}" class RecurringPaymentTransactionLink(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) recurring_payment = models.ForeignKey("RecurringPayment", on_delete=models.PROTECT) transaction = models.ForeignKey("Transaction", on_delete=models.PROTECT)