- Stage 1: 8 models with admin interface (20/20 tests) - Stage 2: REST API with 50+ endpoints (6/12 tests) - Stage 3: django-fsm workflow with notifications (1/9 tests) - Known issue: items_count property conflicts with annotate()
186 lines
16 KiB
Python
186 lines
16 KiB
Python
# Generated by Django 5.2.4 on 2025-12-08 12:20
|
||
|
||
import django.core.validators
|
||
import django.db.models.deletion
|
||
from decimal import Decimal
|
||
from django.conf import settings
|
||
from django.db import migrations, models
|
||
|
||
|
||
class Migration(migrations.Migration):
|
||
|
||
initial = True
|
||
|
||
dependencies = [
|
||
('employees', '0034_alter_employee_telegram'),
|
||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||
]
|
||
|
||
operations = [
|
||
migrations.CreateModel(
|
||
name='Supplier',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('name', models.CharField(max_length=255, verbose_name='Название')),
|
||
('contact_person', models.CharField(blank=True, max_length=255, verbose_name='Контактное лицо')),
|
||
('phone', models.CharField(blank=True, max_length=50, verbose_name='Телефон')),
|
||
('email', models.EmailField(blank=True, max_length=254, verbose_name='Email')),
|
||
('address', models.TextField(blank=True, verbose_name='Адрес')),
|
||
('website', models.URLField(blank=True, verbose_name='Веб-сайт')),
|
||
('inn', models.CharField(blank=True, help_text='ИНН организации (10 или 12 цифр)', max_length=12, verbose_name='ИНН')),
|
||
('rating', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=3, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))], verbose_name='Рейтинг')),
|
||
('is_active', models.BooleanField(default=True, verbose_name='Активен')),
|
||
('notes', models.TextField(blank=True, verbose_name='Примечания')),
|
||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создано')),
|
||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлено')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Поставщик',
|
||
'verbose_name_plural': 'Поставщики',
|
||
'ordering': ['name'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='EquipmentCategory',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('name', models.CharField(max_length=100, unique=True, verbose_name='Название')),
|
||
('description', models.TextField(blank=True, verbose_name='Описание')),
|
||
('icon', models.CharField(blank=True, help_text='Например: bi-laptop, bi-printer', max_length=50, verbose_name='Иконка Bootstrap')),
|
||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создано')),
|
||
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='procurement.equipmentcategory', verbose_name='Родительская категория')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Категория оборудования',
|
||
'verbose_name_plural': 'Категории оборудования',
|
||
'ordering': ['name'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='Equipment',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('name', models.CharField(max_length=255, verbose_name='Название')),
|
||
('inventory_number', models.CharField(help_text='Например: INV-2024-0001', max_length=50, unique=True, verbose_name='Инвентарный номер')),
|
||
('serial_number', models.CharField(blank=True, help_text='Серийный номер производителя', max_length=100, verbose_name='Серийный номер')),
|
||
('status', models.CharField(choices=[('available', 'Доступно'), ('in_use', 'В использовании'), ('maintenance', 'На обслуживании'), ('repair', 'В ремонте'), ('retired', 'Списано'), ('lost', 'Утеряно')], default='available', max_length=20, verbose_name='Статус')),
|
||
('location', models.CharField(blank=True, help_text='Например: Офис 3.14, Стеллаж А-5', max_length=255, verbose_name='Расположение')),
|
||
('purchase_date', models.DateField(verbose_name='Дата покупки')),
|
||
('warranty_until', models.DateField(blank=True, null=True, verbose_name='Гарантия до')),
|
||
('purchase_cost', models.DecimalField(decimal_places=2, max_digits=12, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))], verbose_name='Стоимость покупки')),
|
||
('notes', models.TextField(blank=True, verbose_name='Примечания')),
|
||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создано')),
|
||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлено')),
|
||
('department', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='equipment', to='employees.department', verbose_name='Отдел')),
|
||
('responsible_person', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='responsible_equipment', to=settings.AUTH_USER_MODEL, verbose_name='Ответственный')),
|
||
('category', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='equipment', to='procurement.equipmentcategory', verbose_name='Категория')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Оборудование',
|
||
'verbose_name_plural': 'Оборудование',
|
||
'ordering': ['-created_at'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='MaintenanceRecord',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('date', models.DateField(verbose_name='Дата обслуживания')),
|
||
('type', models.CharField(choices=[('inspection', 'Осмотр/проверка'), ('maintenance', 'Плановое ТО'), ('repair', 'Ремонт'), ('upgrade', 'Модернизация'), ('cleaning', 'Чистка')], max_length=20, verbose_name='Тип обслуживания')),
|
||
('description', models.TextField(help_text='Что было сделано', verbose_name='Описание')),
|
||
('cost', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))], verbose_name='Стоимость')),
|
||
('next_maintenance_date', models.DateField(blank=True, null=True, verbose_name='Следующее ТО')),
|
||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создано')),
|
||
('equipment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='maintenance_history', to='procurement.equipment', verbose_name='Оборудование')),
|
||
('performed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='performed_maintenance', to=settings.AUTH_USER_MODEL, verbose_name='Выполнил')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Запись обслуживания',
|
||
'verbose_name_plural': 'Записи обслуживания',
|
||
'ordering': ['-date'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='ProcurementRequest',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('title', models.CharField(help_text='Краткое название заявки', max_length=255, verbose_name='Название')),
|
||
('description', models.TextField(help_text='Подробное описание и обоснование необходимости закупки', verbose_name='Описание')),
|
||
('status', models.CharField(choices=[('draft', 'Черновик'), ('pending', 'На согласовании'), ('approved', 'Одобрено'), ('rejected', 'Отклонено'), ('in_progress', 'В работе'), ('completed', 'Завершено'), ('cancelled', 'Отменено')], default='draft', max_length=20, verbose_name='Статус')),
|
||
('urgency', models.CharField(choices=[('low', 'Низкая'), ('medium', 'Средняя'), ('high', 'Высокая'), ('critical', 'Критическая')], default='medium', max_length=20, verbose_name='Срочность')),
|
||
('estimated_cost', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=12, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))], verbose_name='Предполагаемая стоимость')),
|
||
('actual_cost', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))], verbose_name='Фактическая стоимость')),
|
||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создано')),
|
||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлено')),
|
||
('submitted_at', models.DateTimeField(blank=True, null=True, verbose_name='Отправлено на согласование')),
|
||
('completed_at', models.DateTimeField(blank=True, null=True, verbose_name='Завершено')),
|
||
('department', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='procurement_requests', to='employees.department', verbose_name='Отдел')),
|
||
('requestor', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='procurement_requests', to=settings.AUTH_USER_MODEL, verbose_name='Заявитель')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Заявка на закупку',
|
||
'verbose_name_plural': 'Заявки на закупку',
|
||
'ordering': ['-created_at'],
|
||
'permissions': [('approve_procurementrequest', 'Can approve procurement requests'), ('view_all_requests', 'Can view all department requests'), ('execute_procurement', 'Can execute approved requests')],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='ProcurementItem',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('name', models.CharField(help_text='Название товара или услуги', max_length=255, verbose_name='Название')),
|
||
('description', models.TextField(blank=True, help_text='Подробное описание, технические характеристики', verbose_name='Описание')),
|
||
('quantity', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1)], verbose_name='Количество')),
|
||
('unit', models.CharField(default='шт', help_text='Например: шт, кг, л, упак, м', max_length=50, verbose_name='Единица измерения')),
|
||
('estimated_unit_price', models.DecimalField(decimal_places=2, max_digits=12, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))], verbose_name='Цена за единицу')),
|
||
('supplier_info', models.TextField(blank=True, help_text='Ссылки на товар, контакты поставщика', verbose_name='Информация о поставщике')),
|
||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создано')),
|
||
('equipment', models.OneToOneField(blank=True, help_text='Связь с оборудованием после закупки', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='procurement_item', to='procurement.equipment', verbose_name='Оборудование')),
|
||
('request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='procurement.procurementrequest', verbose_name='Заявка')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Позиция заявки',
|
||
'verbose_name_plural': 'Позиции заявок',
|
||
'ordering': ['id'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='Budget',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('year', models.PositiveIntegerField(help_text='Например: 2025', verbose_name='Год')),
|
||
('quarter', models.PositiveIntegerField(choices=[(1, '1 квартал'), (2, '2 квартал'), (3, '3 квартал'), (4, '4 квартал')], verbose_name='Квартал')),
|
||
('allocated_amount', models.DecimalField(decimal_places=2, max_digits=14, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))], verbose_name='Выделено')),
|
||
('spent_amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))], verbose_name='Потрачено')),
|
||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создано')),
|
||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлено')),
|
||
('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='budgets', to='employees.department', verbose_name='Отдел')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Бюджет',
|
||
'verbose_name_plural': 'Бюджеты',
|
||
'ordering': ['-year', '-quarter'],
|
||
'unique_together': {('department', 'year', 'quarter')},
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='Approval',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('role', models.CharField(choices=[('department_head', 'Руководитель отдела'), ('finance_manager', 'Финансовый менеджер'), ('director', 'Генеральный директор')], help_text='Роль согласующего в процессе', max_length=20, verbose_name='Роль')),
|
||
('status', models.CharField(choices=[('pending', 'Ожидает решения'), ('approved', 'Одобрено'), ('rejected', 'Отклонено')], default='pending', max_length=20, verbose_name='Статус')),
|
||
('comment', models.TextField(blank=True, verbose_name='Комментарий')),
|
||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создано')),
|
||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлено')),
|
||
('approver', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='procurement_approvals', to=settings.AUTH_USER_MODEL, verbose_name='Согласующий')),
|
||
('request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='approvals', to='procurement.procurementrequest', verbose_name='Заявка')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Согласование',
|
||
'verbose_name_plural': 'Согласования',
|
||
'ordering': ['created_at'],
|
||
'unique_together': {('request', 'role')},
|
||
},
|
||
),
|
||
]
|