149 lines
5.9 KiB
Python
149 lines
5.9 KiB
Python
from django.core.exceptions import ValidationError
|
|
from django.db import models
|
|
from django.contrib.auth.models import User
|
|
|
|
|
|
class StoreRoom(models.Model):
|
|
class Meta:
|
|
permissions = [
|
|
("ViewStore", "ViewStore"),
|
|
("AddStore", "AddStore"),
|
|
("EditStore", "EditStore"),
|
|
("DelStore", "DelStore")
|
|
]
|
|
store_name = models.CharField(max_length=200)
|
|
user = models.ManyToManyField(User,
|
|
blank=True,
|
|
verbose_name="دسترسی مشاهده انبار احتمالا",
|
|
help_text="از این برای دسترسی مشاهده آن اتبار استفاده می شود"
|
|
)
|
|
owners = models.ManyToManyField(
|
|
User,
|
|
through='StoreRoomOwner', # کلید حل مشکل بدون شکستن کدهای قبلی
|
|
blank=True,
|
|
related_name="owned_storerooms",
|
|
verbose_name="مالکین انبار در فاکتور و موجودی و...",
|
|
help_text="این کاربران اگر فاکتور بزنند از این انبار کسر میگردد"
|
|
)
|
|
|
|
def __str__(self):
|
|
return self.store_name
|
|
|
|
|
|
class StoreRoomOwner(models.Model):
|
|
"""
|
|
این جدول واسط، پشت صحنه قرار میگیرد تا فقط قانون مالکیت را مدیریت کند.
|
|
کدهای دیگر پروژه اصلاً نیازی ندارند مستقیم با این جدول درگیر شوند.
|
|
"""
|
|
storeroom = models.ForeignKey(StoreRoom, on_delete=models.CASCADE)
|
|
|
|
# OneToOneField تضمین میکند یک کاربر نمیتواند در این جدول (و هیچ انبار دیگری) تکرار شود
|
|
user = models.OneToOneField(
|
|
User,
|
|
on_delete=models.CASCADE,
|
|
error_messages={
|
|
'unique': 'این کاربر در حال حاضر مالک انبار دیگری است.'
|
|
}
|
|
)
|
|
|
|
|
|
class ProductStockHistory(models.Model):
|
|
store_room = models.ForeignKey(StoreRoom, on_delete=models.CASCADE, related_name='stock_histories')
|
|
product = models.ForeignKey('Product', on_delete=models.CASCADE, related_name='stock_histories')
|
|
quantity_changed = models.IntegerField(help_text="مقدار تغییر یافته (مثبت برای ورود، منفی برای خروج)")
|
|
current_stock = models.IntegerField(help_text="موجودی کل انبار در این لحظه")
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
desc = models.CharField(max_length=255, blank=True, null=True)
|
|
|
|
class Meta:
|
|
ordering = ['-created_at']
|
|
|
|
def __str__(self):
|
|
return f"{self.product.product_name} in {self.store_room.store_name} - Stock: {self.current_stock}"
|
|
|
|
|
|
class StoreTransferRequest(models.Model):
|
|
class Status(models.TextChoices):
|
|
PENDING = 'PENDING', 'در انتظار تایید انبار مقصد'
|
|
APPROVED = 'APPROVED', 'تایید شده و منتقل شده'
|
|
REJECTED = 'REJECTED', 'رد شده توسط انبار مقصد'
|
|
CANCELED = 'CANCELED', 'انصراف توسط مبدا'
|
|
|
|
source_store = models.ForeignKey(
|
|
'StoreRoom',
|
|
on_delete=models.PROTECT,
|
|
related_name='outgoing_transfers',
|
|
verbose_name="انبار مبدا"
|
|
)
|
|
target_store = models.ForeignKey(
|
|
'StoreRoom',
|
|
on_delete=models.PROTECT,
|
|
related_name='incoming_transfers',
|
|
verbose_name="انبار مقصد"
|
|
)
|
|
product = models.ForeignKey(
|
|
'Product',
|
|
on_delete=models.PROTECT,
|
|
verbose_name="کالا"
|
|
)
|
|
quantity = models.PositiveIntegerField(verbose_name="تعداد انتقال")
|
|
|
|
status = models.CharField(
|
|
max_length=15,
|
|
choices=Status.choices,
|
|
default=Status.PENDING,
|
|
db_index=True,
|
|
verbose_name="وضعیت"
|
|
)
|
|
desc = models.CharField(max_length=500, blank=True, null=True, verbose_name="توضیحات درخواست")
|
|
rejection_reason = models.CharField(max_length=500, blank=True, null=True, verbose_name="دلیل رد درخواست")
|
|
|
|
requester = models.ForeignKey(
|
|
User,
|
|
on_delete=models.PROTECT,
|
|
related_name='requested_transfers',
|
|
verbose_name="کاربر درخواستدهنده"
|
|
)
|
|
approver = models.ForeignKey(
|
|
User,
|
|
on_delete=models.PROTECT,
|
|
null=True,
|
|
blank=True,
|
|
related_name='processed_transfers',
|
|
verbose_name="کاربر تعیینتکلیفکننده"
|
|
)
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name="زمان ثبت درخواست")
|
|
updated_at = models.DateTimeField(auto_now=True, verbose_name="زمان آخرین بهروزرسانی")
|
|
action_date = models.DateTimeField(null=True, blank=True, db_index=True, verbose_name="زمان تعیین تکلیف")
|
|
|
|
out_transaction = models.ForeignKey(
|
|
'ProductInStore',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='transfer_out_ref',
|
|
verbose_name="سند خروج انبار مبدا"
|
|
)
|
|
in_transaction = models.ForeignKey(
|
|
'ProductInStore',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='transfer_in_ref',
|
|
verbose_name="سند ورود انبار مقصد"
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ['-created_at']
|
|
|
|
def __str__(self):
|
|
return f"[{self.get_status_display()}] {self.quantity} عدد {self.product.product_name} از {self.source_store.store_name} به {self.target_store.store_name}"
|
|
|
|
def clean(self):
|
|
super().clean()
|
|
if self.source_store_id == self.target_store_id:
|
|
raise ValidationError("انبار مبدا و مقصد نمیتوانند یکسان باشند.")
|
|
if self.quantity and self.quantity <= 0:
|
|
raise ValidationError("تعداد انتقال باید بیشتر از صفر باشد.")
|