Skip to content

Instantly share code, notes, and snippets.

@siumhossain
Last active November 29, 2024 09:24
Show Gist options
  • Save siumhossain/209a189bd8a78a957d5c54dca6611935 to your computer and use it in GitHub Desktop.
Save siumhossain/209a189bd8a78a957d5c54dca6611935 to your computer and use it in GitHub Desktop.
from django.db import models
from django.core.validators import MinValueValidator
from decimal import Decimal
from django.utils import timezone
from django.db.models import Sum
class PaymentMethod(models.Model):
name = models.CharField(max_length=50)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.name
class Order(models.Model):
order_number = models.CharField(max_length=50, unique=True)
date = models.DateTimeField(default=timezone.now)
total_amount = models.DecimalField(max_digits=10, decimal_places=2)
notes = models.TextField(blank=True)
def __str__(self):
return f"Order {self.order_number}"
class Purchase(models.Model):
STATUS_CHOICES = [
('pending', 'Pending'),
('partial', 'Partially Paid'),
('completed', 'Completed'),
]
order = models.ForeignKey(Order, on_delete=models.PROTECT)
date = models.DateTimeField(default=timezone.now)
total_amount = models.DecimalField(max_digits=10, decimal_places=2)
paid_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
notes = models.TextField(blank=True)
def get_remaining_balance(self):
return self.total_amount - self.paid_amount
def get_payment_history(self):
return self.payments.all().order_by('-date')
def get_payment_summary(self):
summary = self.payments.values('payment_method__name').annotate(
total=Sum('amount')
).order_by('payment_method__name')
return summary
def save(self, *args, **kwargs):
if self.paid_amount >= self.total_amount:
self.status = 'completed'
elif self.paid_amount > 0:
self.status = 'partial'
else:
self.status = 'pending'
super().save(*args, **kwargs)
def __str__(self):
return f"Purchase for Order {self.order.order_number} - {self.status} (Balance: {self.get_remaining_balance()})"
class PurchasePayment(models.Model):
purchase = models.ForeignKey(Purchase, on_delete=models.PROTECT, related_name='payments')
payment_method = models.ForeignKey(PaymentMethod, on_delete=models.PROTECT)
amount = models.DecimalField(max_digits=10, decimal_places=2)
date = models.DateTimeField(default=timezone.now)
reference_number = models.CharField(max_length=50, blank=True)
notes = models.TextField(blank=True)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
# Update purchase paid amount
total_paid = self.purchase.payments.aggregate(
total=Sum('amount'))['total'] or 0
self.purchase.paid_amount = total_paid
self.purchase.save()
def __str__(self):
return f"Payment of {self.amount} for Purchase {self.purchase.order.order_number}"
class Sale(models.Model):
STATUS_CHOICES = [
('pending', 'Pending'),
('partial', 'Partially Paid'),
('completed', 'Completed'),
]
order = models.ForeignKey(Order, on_delete=models.PROTECT)
date = models.DateTimeField(default=timezone.now)
total_amount = models.DecimalField(max_digits=10, decimal_places=2)
received_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
notes = models.TextField(blank=True)
def get_remaining_balance(self):
return self.total_amount - self.received_amount
def get_payment_history(self):
return self.payments.all().order_by('-date')
def get_payment_summary(self):
summary = self.payments.values('payment_method__name').annotate(
total=Sum('amount')
).order_by('payment_method__name')
return summary
def save(self, *args, **kwargs):
if self.received_amount >= self.total_amount:
self.status = 'completed'
elif self.received_amount > 0:
self.status = 'partial'
else:
self.status = 'pending'
super().save(*args, **kwargs)
def __str__(self):
return f"Sale for Order {self.order.order_number} - {self.status} (Balance: {self.get_remaining_balance()})"
class SalePayment(models.Model):
sale = models.ForeignKey(Sale, on_delete=models.PROTECT, related_name='payments')
payment_method = models.ForeignKey(PaymentMethod, on_delete=models.PROTECT)
amount = models.DecimalField(max_digits=10, decimal_places=2)
date = models.DateTimeField(default=timezone.now)
reference_number = models.CharField(max_length=50, blank=True)
notes = models.TextField(blank=True)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
# Update sale received amount
total_received = self.sale.payments.aggregate(
total=Sum('amount'))['total'] or 0
self.sale.received_amount = total_received
self.sale.save()
def __str__(self):
return f"Payment of {self.amount} for Sale {self.sale.order.order_number}"
class Expense(models.Model):
CATEGORY_CHOICES = [
('utilities', 'Utilities'),
('rent', 'Rent'),
('salary', 'Salary'),
('supplies', 'Supplies'),
('other', 'Other'),
]
date = models.DateTimeField(default=timezone.now)
category = models.CharField(max_length=20, choices=CATEGORY_CHOICES)
amount = models.DecimalField(max_digits=10, decimal_places=2)
payment_method = models.ForeignKey(PaymentMethod, on_delete=models.PROTECT)
description = models.TextField()
reference_number = models.CharField(max_length=50, blank=True)
def __str__(self):
return f"{self.category} - {self.amount}"
class DailyBalance(models.Model):
date = models.DateField(unique=True)
opening_balance = models.DecimalField(max_digits=12, decimal_places=2)
closing_balance = models.DecimalField(max_digits=12, decimal_places=2)
total_sales = models.DecimalField(max_digits=12, decimal_places=2, default=0)
total_purchases = models.DecimalField(max_digits=12, decimal_places=2, default=0)
total_expenses = models.DecimalField(max_digits=12, decimal_places=2, default=0)
class Meta:
ordering = ['-date']
def calculate_closing_balance(self):
self.closing_balance = (
self.opening_balance +
self.total_sales -
self.total_purchases -
self.total_expenses
)
return self.closing_balance
def save(self, *args, **kwargs):
# Calculate totals for the day
today = self.date
self.total_sales = SalePayment.objects.filter(
date__date=today
).aggregate(total=Sum('amount'))['total'] or 0
self.total_purchases = PurchasePayment.objects.filter(
date__date=today
).aggregate(total=Sum('amount'))['total'] or 0
self.total_expenses = Expense.objects.filter(
date__date=today
).aggregate(total=Sum('amount'))['total'] or 0
self.calculate_closing_balance()
super().save(*args, **kwargs)
def __str__(self):
return f"Balance for {self.date}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment