117 lines
5.6 KiB
Python
117 lines
5.6 KiB
Python
from django.conf import settings
|
|
from django.db import models
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
# ریزاپهای سادهی کسبوکار کوچک — مشتریان، دفتر درآمد/هزینه، انبار،
|
|
# نوبتدهی. دقیقا همالگو با apps.banker: id همان UUID تولیدشده در خودِ اپ
|
|
# فلاتر است (نه auto-increment)، هر رکورد به user صاحبش محدود میشود.
|
|
|
|
|
|
class Customer(models.Model):
|
|
id = models.CharField(max_length=36, primary_key=True)
|
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="customers")
|
|
name = models.CharField(max_length=150, verbose_name=_("نام"))
|
|
phone = models.CharField(max_length=20, blank=True, verbose_name=_("شماره تماس"))
|
|
email = models.CharField(max_length=150, blank=True, verbose_name=_("ایمیل"))
|
|
address = models.CharField(max_length=255, blank=True, verbose_name=_("آدرس"))
|
|
notes = models.TextField(blank=True, verbose_name=_("یادداشت"))
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
verbose_name = _("مشتری")
|
|
verbose_name_plural = _("مشتریان")
|
|
ordering = ["name"]
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class LedgerEntry(models.Model):
|
|
class EntryType(models.TextChoices):
|
|
INCOME = "income", _("درآمد")
|
|
EXPENSE = "expense", _("هزینه")
|
|
|
|
id = models.CharField(max_length=36, primary_key=True)
|
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="ledger_entries")
|
|
entry_type = models.CharField(max_length=10, choices=EntryType.choices, verbose_name=_("نوع"))
|
|
title = models.CharField(max_length=150, verbose_name=_("عنوان"))
|
|
amount = models.FloatField(verbose_name=_("مبلغ"))
|
|
category = models.CharField(max_length=100, blank=True, verbose_name=_("دسته"))
|
|
entry_date = models.DateField(verbose_name=_("تاریخ"))
|
|
note = models.TextField(blank=True, verbose_name=_("یادداشت"))
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
verbose_name = _("ردیف دفتر درآمد/هزینه")
|
|
verbose_name_plural = _("دفتر درآمد و هزینه")
|
|
ordering = ["-entry_date"]
|
|
|
|
def __str__(self):
|
|
return f"{self.title} ({self.get_entry_type_display()})"
|
|
|
|
|
|
class Product(models.Model):
|
|
id = models.CharField(max_length=36, primary_key=True)
|
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="products")
|
|
name = models.CharField(max_length=150, verbose_name=_("نام کالا"))
|
|
sku = models.CharField(max_length=100, blank=True, verbose_name=_("کد کالا"))
|
|
category = models.CharField(max_length=100, blank=True, verbose_name=_("دسته"))
|
|
quantity = models.FloatField(default=0, verbose_name=_("موجودی"))
|
|
unit_price = models.FloatField(default=0, verbose_name=_("قیمت واحد"))
|
|
low_stock_threshold = models.FloatField(default=0, verbose_name=_("آستانهی هشدار موجودی کم"))
|
|
notes = models.TextField(blank=True, verbose_name=_("یادداشت"))
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
verbose_name = _("کالا")
|
|
verbose_name_plural = _("انبار")
|
|
ordering = ["name"]
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class StockMovement(models.Model):
|
|
"""تاریخچهی تغییرات موجودی — هر تعدیل (دستیِ +/- یا ویرایش مستقیم فرم)
|
|
یک ردیف اینجا ثبت میکند تا معلوم باشد موجودی کِی و چقدر تغییر کرده."""
|
|
|
|
id = models.AutoField(primary_key=True)
|
|
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="movements")
|
|
delta = models.FloatField(verbose_name=_("تغییر"))
|
|
quantity_after = models.FloatField(verbose_name=_("موجودی بعد از تغییر"))
|
|
note = models.CharField(max_length=255, blank=True, verbose_name=_("یادداشت"))
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
verbose_name = _("تغییر موجودی")
|
|
verbose_name_plural = _("تاریخچهی موجودی")
|
|
ordering = ["-created_at"]
|
|
|
|
def __str__(self):
|
|
return f"{self.product.name} — {self.delta:+g} — {self.created_at:%Y-%m-%d %H:%M}"
|
|
|
|
|
|
class Appointment(models.Model):
|
|
class Status(models.TextChoices):
|
|
SCHEDULED = "scheduled", _("زمانبندیشده")
|
|
DONE = "done", _("انجامشده")
|
|
CANCELLED = "cancelled", _("لغوشده")
|
|
|
|
id = models.CharField(max_length=36, primary_key=True)
|
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="appointments")
|
|
customer_name = models.CharField(max_length=150, verbose_name=_("نام مشتری"))
|
|
customer_phone = models.CharField(max_length=20, blank=True, verbose_name=_("شماره مشتری"))
|
|
title = models.CharField(max_length=150, verbose_name=_("عنوان/خدمت"))
|
|
start_at = models.DateTimeField(verbose_name=_("زمان نوبت"))
|
|
status = models.CharField(max_length=10, choices=Status.choices, default=Status.SCHEDULED)
|
|
notes = models.TextField(blank=True, verbose_name=_("یادداشت"))
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
verbose_name = _("نوبت")
|
|
verbose_name_plural = _("نوبتدهی")
|
|
ordering = ["start_at"]
|
|
|
|
def __str__(self):
|
|
return f"{self.customer_name} — {self.title}"
|