37882-vm/core/models.py
Flatlogic Bot d58a583044 Ver.06
2026-01-27 20:26:20 +00:00

218 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import qrcode
from io import BytesIO
from django.core.files import File
from django.db import models
from django.urls import reverse
from django.contrib.auth.models import User
from django.conf import settings
class Category(models.Model):
name = models.CharField(max_length=100, verbose_name="Наименование")
class Meta:
verbose_name = "Категория"
verbose_name_plural = "Категории"
def __str__(self):
return self.name
class Supplier(models.Model):
name = models.CharField(max_length=255, verbose_name="Наименование компании")
representative_name = models.CharField(max_length=255, blank=True, null=True, verbose_name="ФИО представителя/менеджера")
phone = models.CharField(max_length=50, blank=True, null=True, verbose_name="Телефон")
email = models.EmailField(blank=True, null=True, verbose_name="Электронная почта")
contract_number = models.CharField(max_length=100, blank=True, null=True, verbose_name="Номер договора")
class Meta:
verbose_name = "Поставщик / Контрагент"
verbose_name_plural = "Поставщики / Контрагенты"
def __str__(self):
return self.name
class FleetUnit(models.Model):
STATUS_CHOICES = [
('active', 'В работе'),
('idle', 'Простаивает'),
('broken', 'Сломана'),
('repair', 'В ремонте'),
('waiting_parts', 'Ждёт деталь'),
]
name = models.CharField(max_length=255, verbose_name="Наименование")
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Категория")
model_name = models.CharField(max_length=255, verbose_name="Модель")
vin = models.CharField(max_length=100, unique=True, verbose_name="VIN / Серийный номер")
plate_number = models.CharField(max_length=50, blank=True, null=True, verbose_name="Госномер")
year = models.PositiveIntegerField(verbose_name="Год выпуска")
photo = models.ImageField(upload_to='fleet_photos/', blank=True, null=True, verbose_name="Фото")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active', verbose_name="Статус")
commissioning_date = models.DateField(verbose_name="Дата ввода в эксплуатацию")
notes = models.TextField(blank=True, null=True, verbose_name="Примечания")
qr_code = models.ImageField(upload_to='qrcodes/', blank=True, null=True, verbose_name="QR-код")
# Insurance
insurance_company = models.CharField(max_length=255, blank=True, null=True, verbose_name="Страховая компания")
insurance_policy_number = models.CharField(max_length=100, blank=True, null=True, verbose_name="Номер страхового полиса")
insurance_start_date = models.DateField(blank=True, null=True, verbose_name="Дата начала страховки")
insurance_end_date = models.DateField(blank=True, null=True, verbose_name="Дата окончания страховки")
# Supplier
supplier = models.ForeignKey(Supplier, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Поставщик / Контрагент")
supplier_name = models.CharField(max_length=255, blank=True, null=True, verbose_name="Поставщик (старое)")
supplier_contacts = models.TextField(blank=True, null=True, verbose_name="Контакты поставщика (старое)")
vehicle_documents = models.FileField(upload_to="fleet_docs/", blank=True, null=True, verbose_name="Документы на авто")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "Техника"
verbose_name_plural = "Техника"
ordering = ['-created_at']
def __str__(self):
return f"{self.name} ({self.plate_number or self.vin})"
def get_absolute_url(self):
return reverse('fleet_detail', kwargs={'pk': self.pk})
def get_status_color(self):
colors = {
'active': 'success',
'idle': 'secondary',
'broken': 'danger',
'repair': 'warning',
'waiting_parts': 'info',
}
return colors.get(self.status, 'primary')
def save(self, *args, **kwargs):
is_new = self.pk is None
super().save(*args, **kwargs)
if is_new or not self.qr_code:
self.generate_qr_code()
def generate_qr_code(self):
path = self.get_absolute_url()
qr_data = path
qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(qr_data)
qr.make(fit=True)
img = qr.make_image(fill='black', back_color='white')
buffer = BytesIO()
img.save(buffer, format='PNG')
filename = f'qr-{self.pk}.png'
self.qr_code.save(filename, File(buffer), save=False)
super().save(update_fields=['qr_code'])
class Maintenance(models.Model):
TYPE_CHOICES = [
('TO-250', 'ТО-250'),
('TO-500', 'ТО-500'),
('seasonal', 'Сезонное'),
('special', 'Спец'),
]
STATUS_CHOICES = [
('planned', 'Планируется'),
('in_progress', 'В процессе'),
('completed', 'Выполнено'),
]
fleet_unit = models.ForeignKey(FleetUnit, on_delete=models.CASCADE, related_name='maintenances', verbose_name="Техника")
m_type = models.CharField(max_length=50, choices=TYPE_CHOICES, verbose_name="Тип ТО")
planned_date = models.DateField(verbose_name="Плановая дата")
planned_runtime = models.PositiveIntegerField(verbose_name="Плановый пробег / моточасы")
checklist = models.JSONField(default=list, blank=True, verbose_name="Чек-лист")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='planned', verbose_name="Статус")
mechanic = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='assigned_maintenances', verbose_name="Исполнитель")
actual_date = models.DateField(null=True, blank=True, verbose_name="Фактическая дата")
actual_runtime = models.PositiveIntegerField(null=True, blank=True, verbose_name="Фактический пробег / моточасы")
parts_used = models.TextField(blank=True, null=True, verbose_name="Использованные запчасти")
photo = models.ImageField(upload_to='maintenance_photos/', blank=True, null=True, verbose_name="Фото")
notes = models.TextField(blank=True, null=True, verbose_name="Примечания")
class Meta:
verbose_name = "ТО"
verbose_name_plural = "ТО"
def __str__(self):
return f"{self.m_type} - {self.fleet_unit.name} ({self.planned_date})"
class Breakdown(models.Model):
STATUS_CHOICES = [
('reported', 'Заявлено'),
('repaired', 'Отремонтировано'),
('need_part', 'Нужна деталь'),
]
fleet_unit = models.ForeignKey(FleetUnit, on_delete=models.CASCADE, related_name='breakdowns', verbose_name="Техника")
date = models.DateField(auto_now_add=True, verbose_name="Дата")
system_node = models.CharField(max_length=255, verbose_name="Узел / система")
description = models.TextField(verbose_name="Описание")
photo = models.ImageField(upload_to='breakdown_photos/', blank=True, null=True, verbose_name="Фото")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='reported', verbose_name="Статус")
repair_date = models.DateField(null=True, blank=True, verbose_name="Дата ремонта")
notes = models.TextField(blank=True, null=True, verbose_name="Примечания")
cost = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name="Стоимость")
class Meta:
verbose_name = "Поломка"
verbose_name_plural = "Поломки"
def __str__(self):
return f"Поломка: {self.fleet_unit.name} - {self.system_node}"
class PartRequest(models.Model):
STATUS_CHOICES = [
('draft', 'Черновик'),
('sent', 'Отправлено'),
('ordered', 'Заказано'),
('delivered', 'Доставлено'),
]
fleet_unit = models.ForeignKey(FleetUnit, on_delete=models.CASCADE, related_name='part_requests', verbose_name="Техника")
breakdown = models.ForeignKey(Breakdown, on_delete=models.SET_NULL, null=True, blank=True, related_name='part_requests', verbose_name="Поломка")
part_name = models.CharField(max_length=255, verbose_name="Наименование детали")
article_number = models.CharField(max_length=100, blank=True, null=True, verbose_name="Артикул")
quantity = models.PositiveIntegerField(default=1, verbose_name="Количество")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft', verbose_name="Статус")
photo = models.ImageField(upload_to='part_photos/', blank=True, null=True, verbose_name="Фото")
notes = models.TextField(blank=True, null=True, verbose_name="Примечание")
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = "Заявка на запчасть"
verbose_name_plural = "Заявки на запчасти"
def __str__(self):
return f"{self.part_name} for {self.fleet_unit.name}"
class Document(models.Model):
TYPE_CHOICES = [
('passport', 'Паспорт'),
('maintenance_act', 'Акт ТО'),
('photo', 'Фото'),
('invoice', 'Счет'),
]
doc_type = models.CharField(max_length=50, choices=TYPE_CHOICES, verbose_name="Тип документа")
fleet_unit = models.ForeignKey(FleetUnit, on_delete=models.CASCADE, related_name='documents', null=True, blank=True)
maintenance = models.ForeignKey(Maintenance, on_delete=models.CASCADE, related_name='documents', null=True, blank=True)
breakdown = models.ForeignKey(Breakdown, on_delete=models.CASCADE, related_name='documents', null=True, blank=True)
part_request = models.ForeignKey(PartRequest, on_delete=models.CASCADE, related_name='documents', null=True, blank=True)
file = models.FileField(upload_to='documents/', verbose_name="Файл")
uploaded_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата добавления")
class Meta:
verbose_name = "Документ"
verbose_name_plural = "Документы"
def __str__(self):
return f"{self.get_doc_type_display()} - {self.uploaded_at}"