|
@@ -1,5 +1,5 @@
|
|
from wsgiref import headers
|
|
from wsgiref import headers
|
|
-from rest_framework.views import APIView
|
|
|
|
|
|
+from rest_framework.viewsets import ViewSet
|
|
from rest_framework import viewsets
|
|
from rest_framework import viewsets
|
|
from utils.page import MyPageNumberPagination
|
|
from utils.page import MyPageNumberPagination
|
|
from django.db.models import Prefetch
|
|
from django.db.models import Prefetch
|
|
@@ -886,6 +886,13 @@ class ContainerWCSViewSet(viewsets.ModelViewSet):
|
|
return Response({'code': '500', 'message': '出库状态更新失败', 'data': None},
|
|
return Response({'code': '500', 'message': '出库状态更新失败', 'data': None},
|
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
OutboundService.process_next_task()
|
|
OutboundService.process_next_task()
|
|
|
|
+
|
|
|
|
+ if task and task.tasktype == 'check' and task.status == 300:
|
|
|
|
+ success = self.handle_outbound_completion(container_obj, task)
|
|
|
|
+ if not success:
|
|
|
|
+ return Response({'code': '500', 'message': '出库状态更新失败', 'data': None},
|
|
|
|
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
+ OutboundService.process_next_task()
|
|
|
|
|
|
return Response({
|
|
return Response({
|
|
'code': '200',
|
|
'code': '200',
|
|
@@ -1084,32 +1091,35 @@ class ContainerWCSViewSet(viewsets.ModelViewSet):
|
|
try:
|
|
try:
|
|
allocator = LocationAllocation()
|
|
allocator = LocationAllocation()
|
|
location_task = task.current_location
|
|
location_task = task.current_location
|
|
- location_row = location_task.split('-')[1]
|
|
|
|
- location_col = location_task.split('-')[2]
|
|
|
|
- location_layer = location_task.split('-')[3]
|
|
|
|
- location= LocationModel.objects.filter(row=location_row, col=location_col, layer=location_layer).first()
|
|
|
|
- location_code = location.location_code
|
|
|
|
-
|
|
|
|
|
|
+ if location_task == '103' or location_task == '203':
|
|
|
|
+ return True
|
|
|
|
+ else:
|
|
|
|
+ location_row = location_task.split('-')[1]
|
|
|
|
+ location_col = location_task.split('-')[2]
|
|
|
|
+ location_layer = location_task.split('-')[3]
|
|
|
|
+ location= LocationModel.objects.filter(row=location_row, col=location_col, layer=location_layer).first()
|
|
|
|
+ location_code = location.location_code
|
|
|
|
+
|
|
|
|
|
|
- # 事务确保原子性
|
|
|
|
- with transaction.atomic():
|
|
|
|
- # 解除库位与托盘的关联
|
|
|
|
- if not allocator.release_location(location_code):
|
|
|
|
- raise Exception("解除库位关联失败")
|
|
|
|
|
|
+ # 事务确保原子性
|
|
|
|
+ with transaction.atomic():
|
|
|
|
+ # 解除库位与托盘的关联
|
|
|
|
+ if not allocator.release_location(location_code):
|
|
|
|
+ raise Exception("解除库位关联失败")
|
|
|
|
|
|
- # 更新库位状态为可用
|
|
|
|
- if not allocator.update_location_status(location_code, 'available'):
|
|
|
|
- raise Exception("库位状态更新失败")
|
|
|
|
|
|
+ # 更新库位状态为可用
|
|
|
|
+ if not allocator.update_location_status(location_code, 'available'):
|
|
|
|
+ raise Exception("库位状态更新失败")
|
|
|
|
|
|
- # 更新库位组的统计信息
|
|
|
|
- self.handle_group_location_status(location_code, location.location_group)
|
|
|
|
|
|
+ # 更新库位组的统计信息
|
|
|
|
+ self.handle_group_location_status(location_code, location.location_group)
|
|
|
|
|
|
- # 更新托盘状态为已出库(假设状态3表示已出库)
|
|
|
|
- container_obj.status = 3
|
|
|
|
- container_obj.save()
|
|
|
|
|
|
+ # 更新托盘状态为已出库(假设状态3表示已出库)
|
|
|
|
+ container_obj.status = 3
|
|
|
|
+ container_obj.save()
|
|
|
|
|
|
|
|
|
|
- return True
|
|
|
|
|
|
+ return True
|
|
except Exception as e:
|
|
except Exception as e:
|
|
logger.error(f"出库完成处理失败: {str(e)}")
|
|
logger.error(f"出库完成处理失败: {str(e)}")
|
|
return False
|
|
return False
|
|
@@ -1680,6 +1690,50 @@ class OutboundService:
|
|
container_obj.save()
|
|
container_obj.save()
|
|
ContainerWCSModel.objects.bulk_create(tasks)
|
|
ContainerWCSModel.objects.bulk_create(tasks)
|
|
logger.info(f"已创建 {len(tasks)} 个初始任务")
|
|
logger.info(f"已创建 {len(tasks)} 个初始任务")
|
|
|
|
+
|
|
|
|
+ @staticmethod
|
|
|
|
+ def create_initial_check_tasks(container_list,batch_id):
|
|
|
|
+ """生成初始任务队列"""
|
|
|
|
+ with transaction.atomic():
|
|
|
|
+ current_WCS = ContainerWCSModel.objects.filter(tasktype='check',batch_id = batch_id,is_delete=False,working=1).first()
|
|
|
|
+ if current_WCS:
|
|
|
|
+ logger.error(f"当前{batch_id}已有检查任务")
|
|
|
|
+ return False
|
|
|
|
+ tasks = []
|
|
|
|
+ start_sequence = ContainerWCSModel.objects.filter(tasktype='check').count() + 1
|
|
|
|
+ tasknumber = ContainerWCSModel.objects.filter().count()
|
|
|
|
+ tasknumber_index = 1
|
|
|
|
+ for index, container in enumerate(container_list, start=start_sequence):
|
|
|
|
+ container_obj = ContainerListModel.objects.filter(id =container['container_number']).first()
|
|
|
|
+ if container_obj.current_location != container_obj.target_location:
|
|
|
|
+ logger.error(f"托盘 {container_obj.container_code} 未到达目的地,不生成任务")
|
|
|
|
+ return False
|
|
|
|
+ batch_obj = BoundBatchModel.objects.filter(id =batch_id).first()
|
|
|
|
+ month = int(timezone.now().strftime("%Y%m"))
|
|
|
|
+ task = ContainerWCSModel(
|
|
|
|
+ taskid=OutboundService.generate_task_id(),
|
|
|
|
+ batch = batch_obj,
|
|
|
|
+ batch_out = None,
|
|
|
|
+ bound_list = None,
|
|
|
|
+ sequence=index,
|
|
|
|
+ order_number = container['c_number'],
|
|
|
|
+ priority=100,
|
|
|
|
+ tasknumber = month*100000+tasknumber_index+tasknumber,
|
|
|
|
+ container=container_obj.container_code,
|
|
|
|
+ current_location=container_obj.current_location,
|
|
|
|
+ target_location="103",
|
|
|
|
+ tasktype="check",
|
|
|
|
+ month=int(timezone.now().strftime("%Y%m")),
|
|
|
|
+ message="等待出库",
|
|
|
|
+ status=100,
|
|
|
|
+ )
|
|
|
|
+ tasknumber_index += 1
|
|
|
|
+ tasks.append(task)
|
|
|
|
+ container_obj = ContainerListModel.objects.filter(container_code=task.container).first()
|
|
|
|
+ container_obj.target_location = task.target_location
|
|
|
|
+ container_obj.save()
|
|
|
|
+ ContainerWCSModel.objects.bulk_create(tasks)
|
|
|
|
+ logger.info(f"已创建 {len(tasks)} 个初始任务")
|
|
|
|
|
|
@staticmethod
|
|
@staticmethod
|
|
def insert_new_tasks(new_tasks):
|
|
def insert_new_tasks(new_tasks):
|
|
@@ -1775,7 +1829,7 @@ class OutboundService:
|
|
|
|
|
|
|
|
|
|
# 出库任务下发
|
|
# 出库任务下发
|
|
-class OutTaskViewSet(APIView):
|
|
|
|
|
|
+class OutTaskViewSet(ViewSet):
|
|
|
|
|
|
"""
|
|
"""
|
|
# fun:get_out_task:下发出库任务
|
|
# fun:get_out_task:下发出库任务
|
|
@@ -1799,9 +1853,9 @@ class OutTaskViewSet(APIView):
|
|
|
|
|
|
if current_WCS.working == 1:
|
|
if current_WCS.working == 1:
|
|
OutboundService.process_current_task(current_WCS.taskid)
|
|
OutboundService.process_current_task(current_WCS.taskid)
|
|
- return Response({"code": "200", "msg": f"下发任务{ current_WCS.taskid }到WCS成功"}, status=200)
|
|
|
|
|
|
+ return Response({"code": 200, "msg": f"下发任务{ current_WCS.taskid }到WCS成功"}, status=200)
|
|
else :
|
|
else :
|
|
- return Response({"code": "200", "msg": f"当前任务{current_WCS.taskid}正在处理中"}, status=200)
|
|
|
|
|
|
+ return Response({"code": 200, "msg": f"当前任务{current_WCS.taskid}正在处理中"}, status=200)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1812,7 +1866,7 @@ class OutTaskViewSet(APIView):
|
|
).select_related('batch_number')
|
|
).select_related('batch_number')
|
|
|
|
|
|
if not out_batches.exists():
|
|
if not out_batches.exists():
|
|
- return Response({"code": "404", "msg": "未找到相关出库批次"}, status=404)
|
|
|
|
|
|
+ return Response({"code": 404, "msg": "未找到相关出库批次"}, status=404)
|
|
# 构建批次需求字典
|
|
# 构建批次需求字典
|
|
batch_demand = {}
|
|
batch_demand = {}
|
|
for ob in out_batches:
|
|
for ob in out_batches:
|
|
@@ -1846,14 +1900,43 @@ class OutTaskViewSet(APIView):
|
|
# 3. 立即发送第一个任务
|
|
# 3. 立即发送第一个任务
|
|
OutboundService.process_next_task()
|
|
OutboundService.process_next_task()
|
|
|
|
|
|
- return Response({"code": "200", "msg": "Success"}, status=200)
|
|
|
|
|
|
+ return Response({"code": 200, "msg": "下发任务成功"}, status=200)
|
|
except Exception as e:
|
|
except Exception as e:
|
|
logger.error(f"任务生成失败: {str(e)}")
|
|
logger.error(f"任务生成失败: {str(e)}")
|
|
- return Response({"code": "200", "msg": str(e)}, status=200)
|
|
|
|
|
|
+ return Response({"code": 200, "msg": str(e)}, status=200)
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
- # 获取出库需求
|
|
|
|
|
|
+ def post_check(self, request):
|
|
|
|
+ try:
|
|
|
|
+ data = self.request.data
|
|
|
|
+ logger.info(f"收到 出库抽检 推送数据: {data}")
|
|
|
|
+ # 从请求中获取 batch_id
|
|
|
|
+ batch_id = data.get('batch_id')
|
|
|
|
+ container_demand = int(data.get('container_demand'))
|
|
|
|
+ if not batch_id or not container_demand:
|
|
|
|
+ return Response({"code": "400", "msg": "缺少抽检数目或批次号"}, status=200)
|
|
|
|
+ current_WCS = ContainerWCSModel.objects.filter(batch=batch_id,tasktype='check',is_delete=False,working=1).first()
|
|
|
|
+ if current_WCS:
|
|
|
|
+ logger.info(f"当前{batch_id}已有出库抽检任务{current_WCS.taskid}")
|
|
|
|
+ if current_WCS.working == 1:
|
|
|
|
+ OutboundService.process_current_task(current_WCS.taskid)
|
|
|
|
+ return Response({"code": 200, "msg": f"下发任务{ current_WCS.taskid }到WCS成功"}, status=200)
|
|
|
|
+ else :
|
|
|
|
+ return Response({"code": 200, "msg": f"当前任务{current_WCS.taskid}正在处理中"}, status=200)
|
|
|
|
+ # 获取批次号
|
|
|
|
+ generate_result = self.generate_location_by_check(batch_id,container_demand)
|
|
|
|
+ if generate_result['code'] != '200':
|
|
|
|
+ return Response(generate_result, status=200)
|
|
|
|
+ # 创建并处理出库任务
|
|
|
|
+ container_list = generate_result['data']
|
|
|
|
+ # 3. 立即发送第一个任务
|
|
|
|
+ OutboundService.create_initial_check_tasks(container_list,batch_id)
|
|
|
|
+ OutboundService.process_next_task()
|
|
|
|
+ return Response({"code": 200, "msg": "下发任务成功"}, status=200)
|
|
|
|
+ except Exception as e:
|
|
|
|
+ logger.error(f"任务生成失败: {str(e)}")
|
|
|
|
+ return Response({"code": 200, "msg": str(e)}, status=200)
|
|
|
|
+
|
|
def get_batch_count_by_boundlist(self,bound_list_id):
|
|
def get_batch_count_by_boundlist(self,bound_list_id):
|
|
try:
|
|
try:
|
|
bound_list_obj_all = OutBoundDetailModel.objects.filter(bound_list=bound_list_id).all()
|
|
bound_list_obj_all = OutBoundDetailModel.objects.filter(bound_list=bound_list_id).all()
|
|
@@ -1946,7 +2029,75 @@ class OutTaskViewSet(APIView):
|
|
)
|
|
)
|
|
.order_by('container_id', '-id')
|
|
.order_by('container_id', '-id')
|
|
)
|
|
)
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ def generate_location_by_check(self,batch_id,container_demand):
|
|
|
|
+ '''
|
|
|
|
+ 根据抽检托盘数目,把相应的库位给到出库任务
|
|
|
|
+ '''
|
|
|
|
+ try:
|
|
|
|
+ return_data = []
|
|
|
|
+ # 获取已去重的托盘列表
|
|
|
|
+ container_qs = self.get_container_allocation(batch_id)
|
|
|
|
+
|
|
|
|
+ # 构建托盘信息字典(自动去重)
|
|
|
|
+ container_map = {}
|
|
|
|
+ for cd in container_qs:
|
|
|
|
+ if cd.container_id in container_map:
|
|
|
|
+ continue
|
|
|
|
+ # 获取有效库位信息
|
|
|
|
+ active_location = next(
|
|
|
|
+ (link.location for link in cd.container.active_location
|
|
|
|
+ if link.is_active),
|
|
|
|
+ None
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ container_map[cd.container_id] = {
|
|
|
|
+ 'detail': cd,
|
|
|
|
+ 'container': cd.container,
|
|
|
|
+ 'location': active_location
|
|
|
|
+ }
|
|
|
|
+ # 转换为排序列表
|
|
|
|
+ container_list = list(container_map.values())
|
|
|
|
+
|
|
|
|
+ sorted_containers = sorted(
|
|
|
|
+ container_list,
|
|
|
|
+ key=lambda x: (
|
|
|
|
+ self._get_goods_class_priority(x['detail'].goods_class),
|
|
|
|
+ -(x['location'].c_number if x['location'] else 0),
|
|
|
|
+ # -(x['location'].layer if x['location'] else 0),
|
|
|
|
+ # x['location'].row if x['location'] else 0,
|
|
|
|
+ # x['location'].col if x['location'] else 0
|
|
|
|
+ )
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ for item in sorted_containers:
|
|
|
|
+ if container_demand <= 0:
|
|
|
|
+ break
|
|
|
|
+ # 获取可分配数量
|
|
|
|
+ # 记录分配信息
|
|
|
|
+ allocate_container = {
|
|
|
|
+ "container_number": item['container'].id,
|
|
|
|
+ "batch_id": batch_id,
|
|
|
|
+ "location_code": item['location'].location_code if item['location'] else 'N/A',
|
|
|
|
+ "allocate_qty": 0,
|
|
|
|
+ "c_number": item['location'].c_number if item['location'] else 0
|
|
|
|
+ }
|
|
|
|
+ return_data.append(allocate_container)
|
|
|
|
+ container_demand -= 1
|
|
|
|
+
|
|
|
|
+ # 降重 return_data,以container_number为key
|
|
|
|
+ return_data = list({v['container_number']: v for v in return_data}.values())
|
|
|
|
+
|
|
|
|
+ # 排序
|
|
|
|
+ return_data = sorted(return_data, key=lambda x: -x['c_number'])
|
|
|
|
+
|
|
|
|
+ return {"code": "200", "msg": "Success", "data": return_data}
|
|
|
|
+
|
|
|
|
+ except Exception as e:
|
|
|
|
+ logger.error(f"出库任务生成失败: {str(e)}", exc_info=True)
|
|
|
|
+ return {"code": "500", "msg": str(e)}
|
|
|
|
+
|
|
|
|
+
|
|
def generate_location_by_demand(self, batch_demand, bound_list_id):
|
|
def generate_location_by_demand(self, batch_demand, bound_list_id):
|
|
try:
|
|
try:
|
|
return_data = []
|
|
return_data = []
|