Доработал закрытие, техоперации автоматом переключаются, добавил выгрузку сменных заданий
All checks were successful
Deploy MES Core / deploy (push) Successful in 14s
All checks were successful
Deploy MES Core / deploy (push) Successful in 14s
This commit is contained in:
134
shiftflow/services/route_flow.py
Normal file
134
shiftflow/services/route_flow.py
Normal 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)
|
||||
Reference in New Issue
Block a user