from django.db import transaction from django.utils import timezone from shiftflow.models import ( CuttingSession, Item, ProductionReportConsumption, ProductionReportRemnant, ShiftItem, ) from shiftflow.services.sessions import close_cutting_session from warehouse.models import StockItem @transaction.atomic def apply_closing( *, user_id: int, machine_id: int, material_id: int, item_actions: dict[int, dict], consumptions: dict[int, float], remnants: list[dict], ) -> None: items = list( Item.objects.select_for_update(of=('self',)) .select_related('task', 'task__deal', 'task__material', 'machine') .filter(id__in=list(item_actions.keys()), machine_id=machine_id, status='work', task__material_id=material_id) ) if not items: raise RuntimeError('Не найдено пунктов сменки для закрытия.') report = CuttingSession.objects.create( operator_id=user_id, machine_id=machine_id, used_stock_item=None, date=timezone.localdate(), is_closed=False, ) for it in items: spec = item_actions.get(it.id) or {} action = (spec.get('action') or '').strip() fact = int(spec.get('fact') or 0) if action not in ['done', 'partial']: continue plan = int(it.quantity_plan or 0) if plan <= 0: continue if action == 'done': fact = plan else: fact = max(0, min(fact, plan)) if fact <= 0: raise RuntimeError('При частичном закрытии факт должен быть больше 0.') ShiftItem.objects.create(session=report, task=it.task, quantity_fact=fact) for stock_item_id, qty in consumptions.items(): if qty <= 0: continue ProductionReportConsumption.objects.create( report=report, stock_item_id=stock_item_id, material_id=None, quantity=float(qty), ) for r in remnants: qty = float(r.get('quantity') or 0) if qty <= 0: continue ProductionReportRemnant.objects.create( report=report, material_id=material_id, quantity=qty, current_length=r.get('current_length'), current_width=r.get('current_width'), unique_id=None, ) close_cutting_session(report.id) for it in items: spec = item_actions.get(it.id) or {} action = (spec.get('action') or '').strip() fact = int(spec.get('fact') or 0) if action not in ['done', 'partial']: continue plan = int(it.quantity_plan or 0) if plan <= 0: continue if action == 'done': it.quantity_fact = plan it.status = 'done' it.save(update_fields=['quantity_fact', 'status']) continue fact = max(0, min(fact, plan)) residual = plan - fact it.quantity_fact = fact it.status = 'partial' it.save(update_fields=['quantity_fact', 'status']) if residual > 0: Item.objects.create( task=it.task, date=it.date, machine=it.machine, quantity_plan=residual, quantity_fact=0, status='leftover', is_synced_1c=False, )