Огромная замена логики
All checks were successful
Deploy MES Core / deploy (push) Successful in 11s

This commit is contained in:
2026-04-06 08:06:37 +03:00
parent 0e8497ab1f
commit e88b861f68
48 changed files with 3833 additions and 175 deletions

View File

@@ -1,28 +1,94 @@
import os
from django.contrib import admin
from .models import Company, EmployeeProfile, Machine, Deal, ProductionTask, Item
from django.contrib import admin, messages
from shiftflow.services.sessions import close_cutting_session
from warehouse.models import StockItem
from .models import (
Company,
CuttingSession,
Deal,
DealItem,
DxfPreviewJob,
DxfPreviewSettings,
EmployeeProfile,
Item,
Machine,
MaterialRequirement,
ProductionReportConsumption,
ProductionReportRemnant,
ProductionTask,
ShiftItem,
Workshop,
)
# --- Настройка отображения Компаний ---
@admin.register(Company)
class CompanyAdmin(admin.ModelAdmin):
list_display = ('name', 'description')
search_fields = ('name',)
"""
Панель администрирования Компаний
"""
list_display = ('name', 'description') # Что видим в общем списке
search_fields = ('name',) # Поиск по имени
class DealItemInline(admin.TabularInline):
model = DealItem
fields = ('entity', 'quantity')
autocomplete_fields = ('entity',)
extra = 10
# --- Настройка отображения Сделок ---
@admin.register(Deal)
class DealAdmin(admin.ModelAdmin):
list_display = ('number', 'status', 'company')
"""
Панель администрирования Сделок
"""
list_display = ('number', 'id', 'status', 'company')
list_display_links = ('number',)
search_fields = ('number', 'company__name')
list_filter = ('status', 'company')
inlines = (DealItemInline,)
# --- Задания на производство (База) ---
"""
Панель администрирования Заданий на производство
"""
@admin.register(ProductionTask)
class ProductionTaskAdmin(admin.ModelAdmin):
list_display = ('drawing_name', 'deal', 'material', 'quantity_ordered', 'created_at')
search_fields = ('drawing_name', 'deal__number')
list_display = ('drawing_name', 'deal', 'entity', 'material', 'quantity_ordered', 'created_at')
search_fields = ('drawing_name', 'deal__number', 'entity__name', 'entity__drawing_number')
list_filter = ('deal', 'material', 'is_bend')
autocomplete_fields = ('deal', 'entity', 'material')
# --- Сменные задания (Выполнение) ---
"""
Панель администрирования Сделочных элементов
"""
@admin.register(DealItem)
class DealItemAdmin(admin.ModelAdmin):
"""
Панель администрирования Сделочных элементов
"""
list_display = ('deal', 'entity', 'quantity')
search_fields = ('deal__number', 'entity__name', 'entity__drawing_number')
list_filter = ('deal',)
autocomplete_fields = ('deal', 'entity')
@admin.register(MaterialRequirement)
class MaterialRequirementAdmin(admin.ModelAdmin):
"""
Панель администрирования Требований к Материалам
"""
list_display = ('deal', 'material', 'required_qty', 'unit', 'status')
search_fields = ('deal__number', 'material__name', 'material__full_name')
list_filter = ('status', 'unit', 'material__category')
autocomplete_fields = ('deal', 'material')
"""
Панель администрирования Сменных задания (Выполнение)
"""
@admin.register(Item)
class ItemAdmin(admin.ModelAdmin):
# Что видим в общем списке (используем task__ для доступа к полям базы)
@@ -53,13 +119,147 @@ class ItemAdmin(admin.ModelAdmin):
return obj.task.drawing_name if obj.task else "-"
get_drawing.short_description = 'Деталь'
@admin.register(Workshop)
class WorkshopAdmin(admin.ModelAdmin):
list_display = ('name', 'location')
search_fields = ('name',)
list_filter = ('location',)
@admin.register(Machine)
class MachineAdmin(admin.ModelAdmin):
list_display = ('name', 'machine_type')
list_filter = ('machine_type',)
list_display = ('name', 'machine_type', 'workshop', 'location')
list_display_links = ('name',)
list_filter = ('machine_type', 'workshop')
search_fields = ('name',)
fields = ('name', 'machine_type', 'workshop', 'location')
class ProductionReportLineInline(admin.TabularInline):
model = ShiftItem
fk_name = 'session'
fields = ('task', 'quantity_fact', 'material_substitution')
extra = 5
class ProductionReportConsumptionInline(admin.TabularInline):
model = ProductionReportConsumption
fk_name = 'report'
fields = ('stock_item', 'quantity')
autocomplete_fields = ('stock_item',)
extra = 3
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'stock_item':
report = getattr(request, '_production_report_obj', None)
if report and getattr(report, 'machine_id', None):
machine = report.machine
work_location = None
if getattr(machine, 'workshop_id', None) and getattr(machine.workshop, 'location_id', None):
work_location = machine.workshop.location
elif getattr(machine, 'location_id', None):
work_location = machine.location
if work_location:
kwargs['queryset'] = StockItem.objects.filter(location=work_location, material__isnull=False)
else:
kwargs['queryset'] = StockItem.objects.none()
return super().formfield_for_foreignkey(db_field, request, **kwargs)
class ProductionReportRemnantInline(admin.TabularInline):
model = ProductionReportRemnant
fk_name = 'report'
fields = ('material', 'quantity', 'current_length', 'current_width')
autocomplete_fields = ('material',)
extra = 3
@admin.register(CuttingSession)
class CuttingSessionAdmin(admin.ModelAdmin):
"""
Панель администрирования Производственных отчетов.
Ограничение по складу:
- списание сырья доступно только со склада цеха выбранного станка.
"""
list_display = ('date', 'id', 'machine', 'operator', 'used_stock_item', 'is_closed')
list_display_links = ('date',)
list_filter = ('date', 'machine', 'is_closed')
search_fields = ('operator__username',)
actions = ('action_close_sessions',)
inlines = (ProductionReportLineInline, ProductionReportConsumptionInline, ProductionReportRemnantInline)
def get_form(self, request, obj=None, **kwargs):
request._production_report_obj = obj
return super().get_form(request, obj, **kwargs)
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'used_stock_item':
report = getattr(request, '_production_report_obj', None)
if report and getattr(report, 'machine_id', None):
machine = report.machine
work_location = None
if getattr(machine, 'workshop_id', None) and getattr(machine.workshop, 'location_id', None):
work_location = machine.workshop.location
elif getattr(machine, 'location_id', None):
work_location = machine.location
if work_location:
kwargs['queryset'] = StockItem.objects.filter(location=work_location, material__isnull=False)
else:
kwargs['queryset'] = StockItem.objects.none()
return super().formfield_for_foreignkey(db_field, request, **kwargs)
@admin.action(description='Закрыть производственный отчет')
def action_close_sessions(self, request, queryset):
ok = 0
skipped = 0
failed = 0
for s in queryset:
try:
if s.is_closed:
skipped += 1
continue
close_cutting_session(s.id)
ok += 1
except Exception as e:
failed += 1
self.message_user(request, f'Отчет id={s.id}: {e}', level=messages.ERROR)
if ok:
self.message_user(request, f'Закрыто: {ok}.', level=messages.SUCCESS)
if skipped:
self.message_user(request, f'Пропущено (уже закрыто): {skipped}.', level=messages.WARNING)
if failed:
self.message_user(request, f'Ошибок: {failed}.', level=messages.ERROR)
@admin.register(ShiftItem)
class ShiftItemAdmin(admin.ModelAdmin):
list_display = ('session', 'task', 'quantity_fact', 'material_substitution')
list_filter = ('material_substitution',)
autocomplete_fields = ('session', 'task')
class DxfPreviewSettingsAdmin(admin.ModelAdmin):
list_display = (
'line_color',
'lineweight_scaling',
'min_lineweight',
'keep_original_colors',
'per_task_timeout_sec',
'updated_at',
)
@admin.register(DxfPreviewJob)
class DxfPreviewJobAdmin(admin.ModelAdmin):
list_display = ('id', 'status', 'created_by', 'processed', 'total', 'updated', 'errors', 'started_at', 'finished_at')
list_filter = ('status',)
search_fields = ('last_message',)
@admin.register(EmployeeProfile)
class EmployeeProfileAdmin(admin.ModelAdmin):
list_display = ('user', 'role')
filter_horizontal = ('machines',) # Красивый выбор станков двумя колонками
filter_horizontal = ('machines',)