Skip to content

Commit

Permalink
feat: models
Browse files Browse the repository at this point in the history
  • Loading branch information
Shavkatjon-O committed Sep 12, 2024
1 parent f9fadb4 commit 778d644
Show file tree
Hide file tree
Showing 6 changed files with 451 additions and 3 deletions.
60 changes: 60 additions & 0 deletions apps/applications/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Generated by Django 5.0.7 on 2024-09-12 07:27

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
('clients', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Application',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('loan_type', models.CharField(choices=[('PERSONAL_LOAN', 'Personal Loan'), ('BUSINESS_LOAN', 'Business Loan'), ('MORTGAGE', 'Mortgage'), ('STUDENT_LOAN', 'Student Loan'), ('CAR_LOAN', 'Car Loan')], max_length=32)),
('amount_requested', models.DecimalField(decimal_places=2, max_digits=12)),
('currency', models.CharField(choices=[('USD', 'US Dollar'), ('UZS', 'Uzbekistani Som'), ('EUR', 'Euro')], default='UZS', max_length=3)),
('application_date', models.DateField(auto_now_add=True)),
('status', models.CharField(choices=[('PENDING', 'Pending'), ('APPROVED', 'Approved'), ('REJECTED', 'Rejected'), ('UNDER_REVIEW', 'Under Review'), ('DISBURSED', 'Disbursed')], default='PENDING', max_length=20)),
('loan_purpose', models.TextField(blank=True, null=True)),
('interest_rate', models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True)),
('repayment_period_months', models.IntegerField(blank=True, null=True)),
('reference_number', models.CharField(max_length=128, unique=True)),
('approved_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='approved_applications', to=settings.AUTH_USER_MODEL)),
('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='applications', to='clients.client')),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_applications', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Loan Application',
'verbose_name_plural': 'Loan Applications',
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='ApplicationSchedule',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('due_date', models.DateField()),
('installment_amount', models.DecimalField(decimal_places=2, max_digits=12)),
('is_paid', models.BooleanField(default=False)),
('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='schedules', to='applications.application')),
],
options={
'verbose_name': 'Application Schedule',
'verbose_name_plural': 'Application Schedules',
'ordering': ['due_date'],
},
),
]
86 changes: 85 additions & 1 deletion apps/applications/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,87 @@
from django.db import models
from apps.common.models import BaseModel
from apps.clients.models import Client
from apps.users.models import User

# Create your models here.

class ApplicationStatusChoices(models.TextChoices):
PENDING = "PENDING", "Pending"
APPROVED = "APPROVED", "Approved"
REJECTED = "REJECTED", "Rejected"
UNDER_REVIEW = "UNDER_REVIEW", "Under Review"
DISBURSED = "DISBURSED", "Disbursed"


class LoanTypeChoices(models.TextChoices):
PERSONAL_LOAN = "PERSONAL_LOAN", "Personal Loan"
BUSINESS_LOAN = "BUSINESS_LOAN", "Business Loan"
MORTGAGE = "MORTGAGE", "Mortgage"
STUDENT_LOAN = "STUDENT_LOAN", "Student Loan"
CAR_LOAN = "CAR_LOAN", "Car Loan"


class CurrencyChoices(models.TextChoices):
USD = "USD", "US Dollar"
UZS = "UZS", "Uzbekistani Som"
EUR = "EUR", "Euro"


class Application(BaseModel):
client = models.ForeignKey(
Client, on_delete=models.CASCADE, related_name="applications"
)
loan_type = models.CharField(max_length=32, choices=LoanTypeChoices.choices)
amount_requested = models.DecimalField(max_digits=12, decimal_places=2)
currency = models.CharField(
max_length=3, choices=CurrencyChoices.choices, default=CurrencyChoices.UZS
)

application_date = models.DateField(auto_now_add=True)
status = models.CharField(
max_length=20,
choices=ApplicationStatusChoices.choices,
default=ApplicationStatusChoices.PENDING,
)

created_by = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="created_applications"
)
approved_by = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name="approved_applications",
blank=True,
null=True,
)

loan_purpose = models.TextField(blank=True, null=True)
interest_rate = models.DecimalField(
max_digits=5, decimal_places=2, blank=True, null=True
)
repayment_period_months = models.IntegerField(blank=True, null=True)
reference_number = models.CharField(max_length=128, unique=True)

def __str__(self):
return f"Application {self.reference_number} - {self.client} - {self.amount_requested} {self.currency}"

class Meta:
verbose_name = "Loan Application"
verbose_name_plural = "Loan Applications"
ordering = ["-created_at"]


class ApplicationSchedule(BaseModel):
application = models.ForeignKey(
Application, on_delete=models.CASCADE, related_name="schedules"
)
due_date = models.DateField()
installment_amount = models.DecimalField(max_digits=12, decimal_places=2)
is_paid = models.BooleanField(default=False)

def __str__(self):
return f"Installment {self.id} for Application {self.application.reference_number} due on {self.due_date}"

class Meta:
verbose_name = "Application Schedule"
verbose_name_plural = "Application Schedules"
ordering = ["due_date"]
76 changes: 76 additions & 0 deletions apps/borrowers/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Generated by Django 5.0.7 on 2024-09-12 07:27

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Borrower',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('borrower_type', models.CharField(choices=[('INDIVIDUAL', 'Individual'), ('COMPANY', 'Company')], max_length=32)),
('full_name', models.CharField(max_length=255)),
('date_of_birth', models.DateField(blank=True, null=True)),
('identification_number', models.CharField(max_length=64, unique=True)),
('contact_email', models.EmailField(blank=True, max_length=254, null=True)),
('contact_phone', models.CharField(blank=True, max_length=20, null=True)),
('address', models.TextField(blank=True, null=True)),
('status', models.CharField(choices=[('ACTIVE', 'Active'), ('INACTIVE', 'Inactive'), ('BLACKLISTED', 'Blacklisted')], default='ACTIVE', max_length=32)),
('approved_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='approved_borrowers', to=settings.AUTH_USER_MODEL)),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_borrowers', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Borrower',
'verbose_name_plural': 'Borrowers',
'ordering': ['full_name'],
},
),
migrations.CreateModel(
name='BorrowerDocument',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('document_type', models.CharField(max_length=64)),
('document_number', models.CharField(max_length=128)),
('document_file', models.FileField(upload_to='borrower_documents/')),
('expiration_date', models.DateField(blank=True, null=True)),
('issued_date', models.DateField(blank=True, null=True)),
('borrower', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='borrowers.borrower')),
],
options={
'verbose_name': 'Borrower Document',
'verbose_name_plural': 'Borrower Documents',
'ordering': ['document_type'],
},
),
migrations.CreateModel(
name='BorrowerFinancialInfo',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('annual_income', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)),
('credit_score', models.IntegerField(blank=True, null=True)),
('total_outstanding_debt', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)),
('total_loans', models.IntegerField(default=0)),
('borrower', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='financial_info', to='borrowers.borrower')),
],
options={
'verbose_name': 'Borrower Financial Info',
'verbose_name_plural': 'Borrower Financial Infos',
},
),
]
90 changes: 89 additions & 1 deletion apps/borrowers/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,91 @@
from django.db import models
from apps.common.models import BaseModel
from apps.users.models import User

# Create your models here.

class BorrowerTypeChoices(models.TextChoices):
INDIVIDUAL = "INDIVIDUAL", "Individual"
COMPANY = "COMPANY", "Company"


class BorrowerStatusChoices(models.TextChoices):
ACTIVE = "ACTIVE", "Active"
INACTIVE = "INACTIVE", "Inactive"
BLACKLISTED = "BLACKLISTED", "Blacklisted"


class Borrower(BaseModel):
borrower_type = models.CharField(max_length=32, choices=BorrowerTypeChoices.choices)
full_name = models.CharField(max_length=255) # For both individuals and companies
date_of_birth = models.DateField(blank=True, null=True) # Only for individuals
identification_number = models.CharField(
max_length=64, unique=True
) # National ID, tax number, etc.
contact_email = models.EmailField(blank=True, null=True)
contact_phone = models.CharField(max_length=20, blank=True, null=True)
address = models.TextField(blank=True, null=True)
status = models.CharField(
max_length=32,
choices=BorrowerStatusChoices.choices,
default=BorrowerStatusChoices.ACTIVE,
)
created_by = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="created_borrowers"
)
approved_by = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name="approved_borrowers",
blank=True,
null=True,
)

def __str__(self):
return f"{self.full_name} ({self.borrower_type})"

class Meta:
verbose_name = "Borrower"
verbose_name_plural = "Borrowers"
ordering = ["full_name"]


class BorrowerFinancialInfo(BaseModel):
borrower = models.OneToOneField(
Borrower, on_delete=models.CASCADE, related_name="financial_info"
)
annual_income = models.DecimalField(
max_digits=15, decimal_places=2, blank=True, null=True
) # Can apply for both individuals and companies
credit_score = models.IntegerField(blank=True, null=True)
total_outstanding_debt = models.DecimalField(
max_digits=15, decimal_places=2, blank=True, null=True
)
total_loans = models.IntegerField(default=0)

def __str__(self):
return f"Financial Info for {self.borrower.full_name}"

class Meta:
verbose_name = "Borrower Financial Info"
verbose_name_plural = "Borrower Financial Infos"


class BorrowerDocument(BaseModel):
borrower = models.ForeignKey(
Borrower, on_delete=models.CASCADE, related_name="documents"
)
document_type = models.CharField(
max_length=64
) # E.g., Passport, Tax Document, Utility Bill, etc.
document_number = models.CharField(max_length=128)
document_file = models.FileField(upload_to="borrower_documents/")
expiration_date = models.DateField(blank=True, null=True)
issued_date = models.DateField(blank=True, null=True)

def __str__(self):
return f"{self.document_type} for {self.borrower.full_name}"

class Meta:
verbose_name = "Borrower Document"
verbose_name_plural = "Borrower Documents"
ordering = ["document_type"]
60 changes: 60 additions & 0 deletions apps/loans/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Generated by Django 5.0.7 on 2024-09-12 07:27

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
('clients', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Loan',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('loan_type', models.CharField(choices=[('PERSONAL_LOAN', 'Personal Loan'), ('BUSINESS_LOAN', 'Business Loan'), ('MORTGAGE', 'Mortgage'), ('STUDENT_LOAN', 'Student Loan'), ('CAR_LOAN', 'Car Loan')], max_length=32)),
('amount_disbursed', models.DecimalField(decimal_places=2, max_digits=12)),
('currency', models.CharField(choices=[('USD', 'US Dollar'), ('UZS', 'Uzbekistani Som'), ('EUR', 'Euro')], default='UZS', max_length=3)),
('disbursement_date', models.DateField(auto_now_add=True)),
('status', models.CharField(choices=[('ACTIVE', 'Active'), ('PAID_OFF', 'Paid Off'), ('DEFAULTED', 'Defaulted'), ('CLOSED', 'Closed'), ('UNDER_REVIEW', 'Under Review'), ('PENDING_DISBURSEMENT', 'Pending Disbursement')], default='PENDING_DISBURSEMENT', max_length=32)),
('interest_rate', models.DecimalField(decimal_places=2, max_digits=5)),
('repayment_period_months', models.IntegerField()),
('total_amount_due', models.DecimalField(decimal_places=2, max_digits=12)),
('reference_number', models.CharField(max_length=128, unique=True)),
('approved_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='approved_loans', to=settings.AUTH_USER_MODEL)),
('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='loans', to='clients.client')),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_loans', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Loan',
'verbose_name_plural': 'Loans',
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='LoanRepaymentSchedule',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('due_date', models.DateField()),
('installment_amount', models.DecimalField(decimal_places=2, max_digits=12)),
('is_paid', models.BooleanField(default=False)),
('loan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='repayment_schedules', to='loans.loan')),
],
options={
'verbose_name': 'Loan Repayment Schedule',
'verbose_name_plural': 'Loan Repayment Schedules',
'ordering': ['due_date'],
},
),
]
Loading

0 comments on commit 778d644

Please sign in to comment.