You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

193 lines
8.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

from django.db import models
from django.contrib.auth.models import AbstractUser, Permission, Group
from product_directory.models import Product
from warehouse.models import Warehouse
# todo: нужно перенести уникальные поля в новую модель
# Справочник товаров
# class Product(models.Model):
# name = models.CharField(max_length=255, verbose_name="Наименование товара")
# manufacturer_name = models.CharField(max_length=255, verbose_name="Производитель")
# manufacturer_country = models.CharField(max_length=255, verbose_name="Страна производителя")
# manufacturer_code = models.CharField(max_length=50, verbose_name="Код производителя", blank=True, null=True)
# dimensions = models.CharField(max_length=255, verbose_name="Размеры", blank=True, null=True)
# unit_of_measure = models.CharField(max_length=50, verbose_name="Единица измерения")
# shelf_life_days = models.IntegerField(verbose_name="Срок годности (дни)")
# barcode = models.CharField(max_length=50, unique=True, verbose_name="Штрихкод")
#
# def __str__(self):
# return self.name
# Справочник должностей
class Position(models.Model):
name = models.CharField(max_length=255, verbose_name="Наименование должности")
hourly_rate = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Ставка в час")
monthly_hours = models.IntegerField(verbose_name="Часы в месяц")
def __str__(self):
return self.name
# Справочник сотрудников
class Employee(AbstractUser):
position = models.ForeignKey(Position, on_delete=models.SET_NULL, null=True, verbose_name="Должность")
department = models.CharField(max_length=255, verbose_name="Подразделение")
work_phone = models.CharField(max_length=20, blank=True, null=True, verbose_name="Рабочий телефон")
personal_phone = models.CharField(max_length=20, blank=True, null=True, verbose_name="Личный телефон")
email = models.EmailField(unique=True, verbose_name="Электронная почта")
groups = models.ManyToManyField(
Group,
related_name='employee_groups',
blank=True,
verbose_name='Группы'
)
user_permissions = models.ManyToManyField(
Permission,
related_name='employee_permissions',
blank=True,
verbose_name='Разрешения'
)
def __str__(self):
return self.get_full_name()
# Справочник мест хранения
class StorageLocation(models.Model):
TYPE_CHOICES = [
('Store', 'Магазин'),
('Distribution Center', 'Распределительный центр'),
('Office', 'Офис'),
]
name = models.CharField(max_length=255, verbose_name="Наименование")
type = models.CharField(max_length=50, choices=TYPE_CHOICES, verbose_name="Тип места хранения")
def __str__(self):
return self.name
# Справочник контрагентов и договоров
class Contractor(models.Model):
name = models.CharField(max_length=255, verbose_name="Наименование контрагента")
address = models.TextField(verbose_name="Адрес")
phone = models.CharField(max_length=20, verbose_name="Телефон")
email = models.EmailField(verbose_name="Электронная почта")
def __str__(self):
return self.name
class SupplyContract(models.Model):
contractor = models.ForeignKey(Contractor, on_delete=models.CASCADE, verbose_name="Контрагент")
description = models.TextField(verbose_name="Описание")
start_date = models.DateField(verbose_name="Дата начала")
end_date = models.DateField(verbose_name="Дата окончания", blank=True, null=True)
def __str__(self):
return f"Договор с {self.contractor.name}"
# Справочник грузовиков
class Truck(models.Model):
plate_number = models.CharField(max_length=20, verbose_name="Номерной знак")
model = models.CharField(max_length=50, verbose_name="Модель")
capacity = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Грузоподъемность (тонны)")
def __str__(self):
return self.plate_number
class StockOperation(models.Model):
OPERATION_TYPE_CHOICES = [
('Incoming', 'Приход'),
('Outgoing', 'Расход'),
('Transfer', 'Перемещение'),
]
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name="Товар")
location = models.ForeignKey(StorageLocation, on_delete=models.CASCADE, verbose_name="Место хранения")
operation_type = models.CharField(max_length=50, choices=OPERATION_TYPE_CHOICES, verbose_name="Тип операции")
quantity = models.IntegerField(verbose_name="Количество")
operation_date = models.DateTimeField(auto_now_add=True, verbose_name="Дата операции")
expiration_date = models.DateField(blank=True, null=True, verbose_name="Срок годности")
def __str__(self):
return f"{self.operation_type} - {self.product.name}"
class PriceList(models.Model):
PRICE_TYPE_CHOICES = [
('Regular', 'Регулярная'),
('Discount', 'Скидочная'),
('Promotional', 'Акционная'),
]
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name="Товар")
price_type = models.CharField(max_length=50, choices=PRICE_TYPE_CHOICES, verbose_name="Тип цены")
entry_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Входная цена")
final_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Итоговая цена")
date_effective = models.DateField(verbose_name="Дата вступления в силу")
constraint_percent_limit = models.DecimalField(max_digits=5, decimal_places=2, default=1000, verbose_name="Лимит на наценку (%)")
constraint_price_change = models.DecimalField(max_digits=5, decimal_places=2, default=90, verbose_name="Лимит на изменение цены (%)")
def __str__(self):
return f"{self.price_type} - {self.product.name}"
class Inventory(models.Model):
warehouse = models.ForeignKey(
Warehouse,
on_delete=models.CASCADE,
verbose_name="Склад",
null=True,
blank=True # Разрешить null и пустое значение
)
storage_location = models.ForeignKey(
StorageLocation,
on_delete=models.CASCADE,
verbose_name="Место хранения",
null=True,
blank=True # Разрешить null и пустое значение
)
product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
verbose_name="Товар",
null=True,
blank=True # Разрешить null и пустое значение
)
actual_quantity = models.PositiveIntegerField(
null=True,
blank=True, # Разрешить null и пустое значение
verbose_name="Фактическое количество"
)
recorded_quantity = models.PositiveIntegerField(
null=True,
blank=True, # Разрешить null и пустое значение
verbose_name="Учтенное количество"
)
difference = models.IntegerField(
null=True,
blank=True, # Разрешить null и пустое значение
verbose_name="Разница",
default=0
)
inventory_date = models.DateTimeField(
auto_now_add=True,
verbose_name="Дата инвентаризации",
null=True,
blank=True # Разрешить null и пустое значение
)
class Meta:
verbose_name = "Инвентаризация"
verbose_name_plural = "Инвентаризации"
def save(self, *args, **kwargs):
if self.actual_quantity is not None and self.recorded_quantity is not None:
self.difference = self.actual_quantity - self.recorded_quantity
else:
self.difference = 0
super().save(*args, **kwargs)
def __str__(self):
return f"{self.product.name if self.product else 'No Product'} ({self.warehouse.name if self.warehouse else 'No Warehouse'}) - {self.difference}"