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