All checks were successful
Deploy MES Core / deploy (push) Successful in 11s
265 lines
9.8 KiB
Python
265 lines
9.8 KiB
Python
import os
|
||
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',) # Поиск по имени
|
||
|
||
class DealItemInline(admin.TabularInline):
|
||
model = DealItem
|
||
fields = ('entity', 'quantity')
|
||
autocomplete_fields = ('entity',)
|
||
extra = 10
|
||
|
||
|
||
# --- Настройка отображения Сделок ---
|
||
@admin.register(Deal)
|
||
class DealAdmin(admin.ModelAdmin):
|
||
"""
|
||
Панель администрирования Сделок
|
||
"""
|
||
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', '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__ для доступа к полям базы)
|
||
list_display = ('date', 'machine', 'get_deal', 'get_drawing', 'quantity_plan', 'quantity_fact', 'status', 'is_synced_1c')
|
||
# Фильтры справа
|
||
list_filter = ('date', 'machine', 'status', 'is_synced_1c', 'task__deal')
|
||
# Поиск по номеру сделки и названию детали через связь task
|
||
search_fields = ('task__drawing_name', 'task__deal__number')
|
||
|
||
# Группируем поля в форме редактирования
|
||
fieldsets = (
|
||
('Связь с заданием', {
|
||
'fields': ('task', 'date', 'machine')
|
||
}),
|
||
('Исполнение', {
|
||
'fields': ('quantity_plan', 'quantity_fact', 'status', 'is_synced_1c')
|
||
}),
|
||
('Отходы и материалы', {
|
||
'fields': ('material_taken', 'usable_waste', 'scrap_weight')
|
||
}),
|
||
)
|
||
|
||
def get_deal(self, obj):
|
||
return obj.task.deal if obj.task else "-"
|
||
get_deal.short_description = 'Сделка'
|
||
|
||
def get_drawing(self, obj):
|
||
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', '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',) |