Доработал закрытие, техоперации автоматом переключаются, добавил выгрузку сменных заданий
All checks were successful
Deploy MES Core / deploy (push) Successful in 14s

This commit is contained in:
2026-04-25 11:58:11 +03:00
parent 6fd01c9a6e
commit 909ba05b5d
14 changed files with 998 additions and 74 deletions

View File

@@ -0,0 +1,134 @@
import logging
from django.db import transaction
from django.db.models import Q, Sum
from django.db.models.functions import Coalesce
from django.utils import timezone
from manufacturing.models import EntityOperation
from shiftflow.models import DealEntityProgress, DealItem, ProductionTask, WorkItem
logger = logging.getLogger('mes')
def _workitem_op_code(wi: WorkItem) -> str:
if getattr(wi, 'operation_id', None) and getattr(wi, 'operation', None):
code = (wi.operation.code or '').strip()
if code:
return code
return (wi.stage or '').strip()
def _target_qty_for_workitem(wi: WorkItem) -> int | None:
if getattr(wi, 'delivery_batch_id', None):
qty = (
ProductionTask.objects.filter(
deal_id=wi.deal_id,
delivery_batch_id=wi.delivery_batch_id,
entity_id=wi.entity_id,
)
.values_list('quantity_ordered', flat=True)
.first()
)
return int(qty) if qty is not None else None
di = DealItem.objects.filter(deal_id=wi.deal_id, entity_id=wi.entity_id).first()
return int(di.quantity) if di else None
@transaction.atomic
def advance_progress_and_generate_next_workitem(*, workitem_id: int) -> int | None:
wi = (
WorkItem.objects.select_for_update(of=('self',))
.select_related('operation')
.filter(id=int(workitem_id))
.first()
)
if not wi:
return None
op_code = _workitem_op_code(wi)
if not op_code:
return None
target_qty = _target_qty_for_workitem(wi)
if target_qty is None:
return None
progress, _ = DealEntityProgress.objects.select_for_update(of=('self',)).get_or_create(
deal_id=wi.deal_id,
delivery_batch_id=(int(wi.delivery_batch_id) if getattr(wi, 'delivery_batch_id', None) else None),
entity_id=wi.entity_id,
defaults={'current_seq': 1},
)
cur = int(progress.current_seq or 1)
cur_eo = EntityOperation.objects.select_related('operation').filter(entity_id=wi.entity_id, seq=cur).first()
if not cur_eo or not cur_eo.operation:
return None
cur_code = (cur_eo.operation.code or '').strip()
if cur_code != op_code:
return None
wi_qs = WorkItem.objects.filter(deal_id=wi.deal_id, entity_id=wi.entity_id).filter(Q(operation__code=op_code) | Q(stage=op_code))
if getattr(wi, 'delivery_batch_id', None):
wi_qs = wi_qs.filter(delivery_batch_id=wi.delivery_batch_id)
else:
wi_qs = wi_qs.filter(delivery_batch_id__isnull=True)
total_done = wi_qs.aggregate(s=Coalesce(Sum('quantity_done'), 0))['s']
if int(total_done or 0) < int(target_qty):
return None
progress.current_seq = cur + 1
progress.save(update_fields=['current_seq'])
next_eo = (
EntityOperation.objects.select_related('operation', 'operation__workshop')
.filter(entity_id=wi.entity_id, seq=int(progress.current_seq))
.first()
)
if not next_eo or not next_eo.operation:
return None
next_op = next_eo.operation
next_code = (next_op.code or '').strip()
planned_qs = WorkItem.objects.filter(deal_id=wi.deal_id, entity_id=wi.entity_id)
if getattr(wi, 'delivery_batch_id', None):
planned_qs = planned_qs.filter(delivery_batch_id=wi.delivery_batch_id)
else:
planned_qs = planned_qs.filter(delivery_batch_id__isnull=True)
planned_total = planned_qs.filter(Q(operation_id=next_op.id) | Q(operation__code=next_code) | Q(stage=next_code)).aggregate(
s=Coalesce(Sum('quantity_plan'), 0)
)['s']
remaining_to_plan = max(0, int(target_qty) - int(planned_total or 0))
if remaining_to_plan <= 0:
return None
created = WorkItem.objects.create(
deal_id=wi.deal_id,
delivery_batch_id=(int(wi.delivery_batch_id) if getattr(wi, 'delivery_batch_id', None) else None),
entity_id=wi.entity_id,
operation_id=next_op.id,
stage=(next_code or next_op.name or '')[:32],
workshop_id=(int(next_op.workshop_id) if getattr(next_op, 'workshop_id', None) else None),
machine_id=None,
quantity_plan=int(remaining_to_plan),
quantity_done=0,
status='planned',
date=timezone.localdate(),
)
logger.info(
'route_flow:created_next_workitem id=%s deal_id=%s batch_id=%s entity_id=%s op=%s qty=%s',
created.id,
created.deal_id,
getattr(created, 'delivery_batch_id', None),
created.entity_id,
next_code or '',
created.quantity_plan,
)
return int(created.id)