191 lines
8.9 KiB
Python
191 lines
8.9 KiB
Python
from django.db import models
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.utils import timezone
|
|
|
|
class Category(models.Model):
|
|
name_en = models.CharField(_("Name (English)"), max_length=100)
|
|
name_ar = models.CharField(_("Name (Arabic)"), max_length=100)
|
|
slug = models.SlugField(unique=True)
|
|
|
|
class Meta:
|
|
verbose_name_plural = _("Categories")
|
|
|
|
def __str__(self):
|
|
return f"{self.name_en} / {self.name_ar}"
|
|
|
|
class Unit(models.Model):
|
|
name_en = models.CharField(_("Name (English)"), max_length=50)
|
|
name_ar = models.CharField(_("Name (Arabic)"), max_length=50)
|
|
short_name = models.CharField(_("Short Name"), max_length=10)
|
|
|
|
def __str__(self):
|
|
return f"{self.name_en} / {self.name_ar}"
|
|
|
|
class Product(models.Model):
|
|
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="products")
|
|
unit = models.ForeignKey(Unit, on_delete=models.SET_NULL, null=True, blank=True, related_name="products")
|
|
supplier = models.ForeignKey('Supplier', on_delete=models.SET_NULL, null=True, blank=True, related_name="products")
|
|
name_en = models.CharField(_("Name (English)"), max_length=200)
|
|
name_ar = models.CharField(_("Name (Arabic)"), max_length=200)
|
|
sku = models.CharField(_("Barcode/SKU"), max_length=50, unique=True)
|
|
description = models.TextField(_("Description"), blank=True)
|
|
cost_price = models.DecimalField(_("Cost Price"), max_digits=12, decimal_places=3, default=0)
|
|
price = models.DecimalField(_("Sale Price"), max_digits=12, decimal_places=3)
|
|
vat = models.DecimalField(_("VAT (%)"), max_digits=5, decimal_places=2, default=0)
|
|
opening_stock = models.PositiveIntegerField(_("Opening Stock"), default=0)
|
|
stock_quantity = models.PositiveIntegerField(_("In Stock"), default=0)
|
|
image = models.ImageField(_("Product Image"), upload_to="product_images/", blank=True, null=True)
|
|
is_active = models.BooleanField(_("Active"), default=True)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
def __str__(self):
|
|
return f"{self.name_en} ({self.sku})"
|
|
|
|
class Customer(models.Model):
|
|
name = models.CharField(_("Name"), max_length=200)
|
|
phone = models.CharField(_("Phone"), max_length=20, blank=True)
|
|
email = models.EmailField(_("Email"), blank=True)
|
|
address = models.TextField(_("Address"), blank=True)
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
class Supplier(models.Model):
|
|
name = models.CharField(_("Name"), max_length=200)
|
|
contact_person = models.CharField(_("Contact Person"), max_length=200, blank=True)
|
|
phone = models.CharField(_("Phone"), max_length=20, blank=True)
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
class Sale(models.Model):
|
|
PAYMENT_TYPE_CHOICES = [
|
|
('cash', _('Cash')),
|
|
('credit', _('Credit')),
|
|
('partial', _('Partial')),
|
|
]
|
|
STATUS_CHOICES = [
|
|
('paid', _('Paid')),
|
|
('partial', _('Partial')),
|
|
('unpaid', _('Unpaid')),
|
|
]
|
|
|
|
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, null=True, blank=True, related_name="sales")
|
|
invoice_number = models.CharField(_("Invoice Number"), max_length=50, blank=True)
|
|
total_amount = models.DecimalField(_("Total Amount"), max_digits=15, decimal_places=3)
|
|
paid_amount = models.DecimalField(_("Paid Amount"), max_digits=15, decimal_places=3, default=0)
|
|
balance_due = models.DecimalField(_("Balance Due"), max_digits=15, decimal_places=3, default=0)
|
|
discount = models.DecimalField(_("Discount"), max_digits=15, decimal_places=3, default=0)
|
|
payment_type = models.CharField(_("Payment Type"), max_length=20, choices=PAYMENT_TYPE_CHOICES, default='cash')
|
|
status = models.CharField(_("Status"), max_length=20, choices=STATUS_CHOICES, default='paid')
|
|
due_date = models.DateField(_("Due Date"), null=True, blank=True)
|
|
notes = models.TextField(_("Notes"), blank=True)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
def __str__(self):
|
|
return f"Sale #{self.id} - {self.customer.name if self.customer else 'Guest'}"
|
|
|
|
def update_balance(self):
|
|
payments_total = self.payments.aggregate(total=models.Sum('amount'))['total'] or 0
|
|
self.paid_amount = payments_total
|
|
self.balance_due = self.total_amount - self.paid_amount
|
|
if self.balance_due <= 0:
|
|
self.status = 'paid'
|
|
elif self.paid_amount > 0:
|
|
self.status = 'partial'
|
|
else:
|
|
self.status = 'unpaid'
|
|
self.save()
|
|
|
|
class SaleItem(models.Model):
|
|
sale = models.ForeignKey(Sale, on_delete=models.CASCADE, related_name="items")
|
|
product = models.ForeignKey(Product, on_delete=models.CASCADE)
|
|
quantity = models.PositiveIntegerField(_("Quantity"))
|
|
unit_price = models.DecimalField(_("Unit Price"), max_digits=12, decimal_places=3)
|
|
line_total = models.DecimalField(_("Line Total"), max_digits=15, decimal_places=3)
|
|
|
|
def __str__(self):
|
|
return f"{self.product.name_en} x {self.quantity}"
|
|
|
|
class SalePayment(models.Model):
|
|
sale = models.ForeignKey(Sale, on_delete=models.CASCADE, related_name="payments")
|
|
amount = models.DecimalField(_("Amount"), max_digits=15, decimal_places=3)
|
|
payment_date = models.DateField(_("Payment Date"), default=timezone.now)
|
|
payment_method = models.CharField(_("Payment Method"), max_length=50, default="Cash")
|
|
notes = models.TextField(_("Notes"), blank=True)
|
|
|
|
def __str__(self):
|
|
return f"Payment of {self.amount} for Sale #{self.sale.id}"
|
|
|
|
class Purchase(models.Model):
|
|
PAYMENT_TYPE_CHOICES = [
|
|
('cash', _('Cash')),
|
|
('credit', _('Credit')),
|
|
('partial', _('Partial')),
|
|
]
|
|
STATUS_CHOICES = [
|
|
('paid', _('Paid')),
|
|
('partial', _('Partial')),
|
|
('unpaid', _('Unpaid')),
|
|
]
|
|
|
|
supplier = models.ForeignKey(Supplier, on_delete=models.SET_NULL, null=True, related_name="purchases")
|
|
invoice_number = models.CharField(_("Invoice Number"), max_length=50, blank=True)
|
|
total_amount = models.DecimalField(_("Total Amount"), max_digits=15, decimal_places=3)
|
|
paid_amount = models.DecimalField(_("Paid Amount"), max_digits=15, decimal_places=3, default=0)
|
|
balance_due = models.DecimalField(_("Balance Due"), max_digits=15, decimal_places=3, default=0)
|
|
payment_type = models.CharField(_("Payment Type"), max_length=20, choices=PAYMENT_TYPE_CHOICES, default='cash')
|
|
status = models.CharField(_("Status"), max_length=20, choices=STATUS_CHOICES, default='paid')
|
|
due_date = models.DateField(_("Due Date"), null=True, blank=True)
|
|
notes = models.TextField(_("Notes"), blank=True)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
def __str__(self):
|
|
return f"Purchase #{self.id} - {self.supplier.name if self.supplier else 'N/A'}"
|
|
|
|
def update_balance(self):
|
|
payments_total = self.payments.aggregate(total=models.Sum('amount'))['total'] or 0
|
|
self.paid_amount = payments_total
|
|
self.balance_due = self.total_amount - self.paid_amount
|
|
if self.balance_due <= 0:
|
|
self.status = 'paid'
|
|
elif self.paid_amount > 0:
|
|
self.status = 'partial'
|
|
else:
|
|
self.status = 'unpaid'
|
|
self.save()
|
|
|
|
class PurchaseItem(models.Model):
|
|
purchase = models.ForeignKey(Purchase, on_delete=models.CASCADE, related_name="items")
|
|
product = models.ForeignKey(Product, on_delete=models.CASCADE)
|
|
quantity = models.PositiveIntegerField(_("Quantity"))
|
|
cost_price = models.DecimalField(_("Cost Price"), max_digits=12, decimal_places=3)
|
|
line_total = models.DecimalField(_("Line Total"), max_digits=15, decimal_places=3)
|
|
|
|
def __str__(self):
|
|
return f"{self.product.name_en} x {self.quantity}"
|
|
|
|
class PurchasePayment(models.Model):
|
|
purchase = models.ForeignKey(Purchase, on_delete=models.CASCADE, related_name="payments")
|
|
amount = models.DecimalField(_("Amount"), max_digits=15, decimal_places=3)
|
|
payment_date = models.DateField(_("Payment Date"), default=timezone.now)
|
|
payment_method = models.CharField(_("Payment Method"), max_length=50, default="Cash")
|
|
notes = models.TextField(_("Notes"), blank=True)
|
|
|
|
def __str__(self):
|
|
return f"Payment of {self.amount} for Purchase #{self.purchase.id}"
|
|
|
|
class SystemSetting(models.Model):
|
|
business_name = models.CharField(_("Business Name"), max_length=200, default="Meezan Accounting")
|
|
address = models.TextField(_("Address"), blank=True)
|
|
phone = models.CharField(_("Phone"), max_length=20, blank=True)
|
|
email = models.EmailField(_("Email"), blank=True)
|
|
currency_symbol = models.CharField(_("Currency Symbol"), max_length=10, default="OMR")
|
|
tax_rate = models.DecimalField(_("Tax Rate (%)"), max_digits=5, decimal_places=2, default=0)
|
|
logo = models.ImageField(_("Logo"), upload_to="business_logos/", blank=True, null=True)
|
|
vat_number = models.CharField(_("VAT Number"), max_length=50, blank=True)
|
|
registration_number = models.CharField(_("Registration Number"), max_length=50, blank=True)
|
|
|
|
def __str__(self):
|
|
return self.business_name
|