from rest_framework import viewsets from utils.page import MyPageNumberPagination from utils.datasolve import sumOfList, transportation_calculate from utils.md5 import Md5 from rest_framework.filters import OrderingFilter from django_filters.rest_framework import DjangoFilterBackend from rest_framework.response import Response from rest_framework.exceptions import APIException from django.utils import timezone from django.db import transaction import logging from rest_framework import status from .models import ContainerListModel,ContainerDetailModel,ContainerOperationModel,ContainerWCSModel,TaskModel from bound.models import BoundBatchModel,BoundDetailModel,BoundListModel from bin.views import LocationAllocation from bin.models import LocationModel # from .files import FileListRenderCN, FileDetailRenderCN from .serializers import ContainerDetailGetSerializer,ContainerDetailPostSerializer from .serializers import ContainerListGetSerializer,ContainerListPostSerializer from .serializers import ContainerOperationGetSerializer,ContainerOperationPostSerializer from .serializers import TaskGetSerializer,TaskPostSerializer from .filter import ContainerDetailFilter,ContainerListFilter,ContainerOperationFilter,TaskFilter # 以后添加模 from warehouse.models import ListModel as warehouse from staff.models import ListModel as staff from rest_framework.permissions import AllowAny logger = logging.getLogger(__name__) class ContainerListViewSet(viewsets.ModelViewSet): """ retrieve: Response a data list(get) list: Response a data list(all) create: Create a data line(post) delete: Delete a data line(delete) """ # authentication_classes = [] # 禁用所有认证类 # permission_classes = [AllowAny] # 允许任意访问 pagination_class = MyPageNumberPagination filter_backends = [DjangoFilterBackend, OrderingFilter, ] ordering_fields = ['id', "create_time", "update_time", ] filter_class = ContainerListFilter def get_project(self): try: id = self.kwargs.get('pk') return id except: return None def get_queryset(self): id = self.get_project() if self.request.user: if id is None: return ContainerListModel.objects.filter() else: return ContainerListModel.objects.filter( id=id) else: return ContainerListModel.objects.none() def get_serializer_class(self): if self.action in ['list', 'destroy','retrieve']: return ContainerListGetSerializer elif self.action in ['create', 'update']: return ContainerListPostSerializer else: return self.http_method_not_allowed(request=self.request) def create(self, request, *args, **kwargs): data = self.request.data order_month = str(timezone.now().strftime('%Y%m')) data['month'] = order_month data['last_operate'] = str(timezone.now()) serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) serializer.save() headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=200, headers=headers) def update(self, request, pk): qs = self.get_object() data = self.request.data serializer = self.get_serializer(qs, data=data) serializer.is_valid(raise_exception=True) serializer.save() headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=200, headers=headers) class TaskViewSet(viewsets.ModelViewSet): """ retrieve: Response a data list(get) list: Response a data list(all) create: Create a data line(post) delete: Delete a data line(delete) """ pagination_class = MyPageNumberPagination filter_backends = [DjangoFilterBackend, OrderingFilter, ] ordering_fields = ['id', "create_time", "update_time", ] filter_class = TaskFilter def get_project(self): try: id = self.kwargs.get('pk') return id except: return None def get_queryset(self): id = self.get_project() if self.request.user: if id is None: return TaskModel.objects.filter() else: return TaskModel.objects.filter( id=id) else: return TaskModel.objects.none() def get_serializer_class(self): if self.action in ['list', 'destroy','retrieve']: return TaskGetSerializer elif self.action in ['create', 'update']: return TaskPostSerializer else: return self.http_method_not_allowed(request=self.request) def create(self, request, *args, **kwargs): data = self.request.data return Response(data, status=200, headers=headers) def update(self, request, pk): qs = self.get_object() data = self.request.data serializer = self.get_serializer(qs, data=data) serializer.is_valid(raise_exception=True) serializer.save() headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=200, headers=headers) class ContainerWCSViewSet(viewsets.ModelViewSet): """ retrieve: Response a data list(get) list: Response a data list(all) create: Create a data line(post) delete: Delete a data line(delete) """ authentication_classes = [] # 禁用所有认证类 permission_classes = [AllowAny] # 允许任意访问 def get_container_wcs(self, request, *args, **kwargs): data = self.request.data container = data.get('container_number') current_location = data.get('current_location') logger.info(f"请求托盘:{container},请求位置:{current_location}") if current_location =="203": current_location ="in1" elif current_location=="103": current_location="in2" data_return = {} try: container_obj = ContainerListModel.objects.filter(container_code=container).first() if not container_obj: data_return = { 'code': '400', 'message': '托盘编码不存在', 'data': data } return Response(data_return, status=status.HTTP_400_BAD_REQUEST) # 更新容器数据(部分更新) serializer = ContainerListPostSerializer( container_obj, data=data, partial=True # 允许部分字段更新 ) serializer.is_valid(raise_exception=True) serializer.save() # 检查是否已在目标位置 if current_location == str(container_obj.target_location): logger.info(f"托盘 {container} 已在目标位置") data_return = { 'code': '200', 'message': '当前位置已是目标位置', 'data': data } else: current_task = ContainerWCSModel.objects.filter( container=container, tasktype='inbound' ).first() if current_task: data_return = { 'code': '200', 'message': '任务已存在,重新下发', 'data': current_task.to_dict() } else: # 库位分配 container_code = container print(f"开始生成库位,托盘编码:{container_code}") allocator = LocationAllocation() # 创建实例 location_list_cnumber = allocator.get_location_by_status(container_code, current_location) # 获取库位列表 if not location_list_cnumber: print("❌ 通用库位获取失败,请检查托盘编码") return print(f"[1]库位:{location_list_cnumber}") update_location_status = allocator.update_location_status(location_list_cnumber.location_code, 'reserved') # 更新库位状态 if not update_location_status: print("❌ 库位状态更新失败,请检查托盘编码") return print(f"[2]发送任务,库位状态更新成功!") update_location_group_status = allocator.update_location_group_status(location_list_cnumber.location_code) # 更新库位组状态 if not update_location_group_status: print("❌ 库位组状态更新失败,请检查托盘编码") return print(f"[3]库位组状态更新成功!") update_batch_status = allocator.update_batch_status(container_code, '2') # 更新批次状态 if not update_batch_status: print("❌ 批次状态更新失败,请检查批次号") return print(f"[4]批次状态更新成功!") update_location_group_batch = allocator.update_location_group_batch(location_list_cnumber, container_code) # 更新库位组的批次 if not update_location_group_batch: print("❌ 库位组批次更新失败,请检查托盘编码") return print(f"[5]库位组批次更新成功!") update_location_container_link = allocator.update_location_container_link(location_list_cnumber.location_code, container_code) # 更新库位和托盘的关联关系 if not update_location_container_link: print("❌ 库位和托盘的关联关系更新失败,请检查托盘编码") return print(f"[7]库位和托盘的关联关系更新成功!") allocation_target_location = ( location_list_cnumber.warehouse_code + '-' + f"{int(location_list_cnumber.row):02d}" + '-' + f"{int(location_list_cnumber.col):02d}" + '-' + f"{int(location_list_cnumber.layer):02d}" ) self.generate_task(container, current_location, allocation_target_location) # 生成任务 current_task = ContainerWCSModel.objects.get( container=container, tasktype='inbound' ) data_return = { 'code': '200', 'message': '任务下发成功', 'data': current_task.to_dict() } container_obj.target_location = allocation_target_location container_obj.save() self.inport_update_task(current_task.id, container_obj.id) http_status = status.HTTP_200_OK if data_return['code'] == '200' else status.HTTP_400_BAD_REQUEST return Response(data_return, status=http_status) except Exception as e: logger.error(f"处理请求时发生错误: {str(e)}", exc_info=True) return Response( {'code': '500', 'message': '服务器内部错误', 'data': None}, status=status.HTTP_500_INTERNAL_SERVER_ERROR ) @transaction.atomic def generate_task(self, container, current_location, target_location): data_tosave = { 'container': container, 'current_location': current_location, 'month': timezone.now().strftime('%Y%m'), 'target_location': target_location, 'tasktype': 'inbound', 'status': 103, 'is_delete': False } # 生成唯一递增的 taskid last_task = ContainerWCSModel.objects.filter( month=data_tosave['month'], tasktype='inbound' ).order_by('-taskid').first() if last_task: last_id = int(last_task.taskid.split('-')[-1]) new_id = f"{last_id + 1:04}" else: new_id = "0001" data_tosave['taskid'] = f"inbound-{data_tosave['month']}-{new_id}" logger.info(f"生成入库任务: {data_tosave['taskid']}") # 每月生成唯一递增的 taskNumber data_tosave['tasknumber'] = f"{data_tosave['month']}{new_id}" ContainerWCSModel.objects.create(**data_tosave) def update_container_wcs(self, request, *args, **kwargs): data = self.request.data container = data.get('container_number') current_location = data.get('current_location') logger.info(f"请求托盘:{container},请求位置:{current_location}") if current_location =="203": current_location ="in1" elif current_location=="103": current_location="in2" data_return = {} try: container_obj = ContainerListModel.objects.filter(container_code=container).first() if not container_obj: data_return = { 'code': '400', 'message': '托盘编码不存在', 'data': data } return Response(data_return, status=status.HTTP_400_BAD_REQUEST) # 更新容器数据(部分更新) serializer = ContainerListPostSerializer( container_obj, data=data, partial=True # 允许部分字段更新 ) serializer.is_valid(raise_exception=True) serializer.save() # 检查是否已在目标位置 if current_location == str(container_obj.target_location): logger.info(f"托盘 {container} 已在目标位置") data_return = { 'code': '200', 'message': '当前位置已是目标位置', 'data': data } allocator = LocationAllocation() # 创建实例 location_row = int(container_obj.target_location.split('-')[1]) location_col = int(container_obj.target_location.split('-')[2]) location_layer = int(container_obj.target_location.split('-')[3]) coordinate = f"{location_row}-{location_col}-{location_layer}" print(f"坐标:{coordinate}") location_code = LocationModel.objects.filter(coordinate=coordinate).first().location_code container_code = container update_location_status = allocator.update_location_status(location_code, 'occupied') # 更新库位状态 if not update_location_status: print("❌ 库位状态更新失败,请检查托盘编码") return print(f"[6]WCS到位,库位状态更新成功!") update_location_container_link = allocator.update_location_container_link(location_code, container_code) # 更新库位和托盘的关联关系 if not update_location_container_link: print("❌ 库位和托盘的关联关系更新失败,请检查托盘编码") return print(f"[7]库位和托盘的关联关系更新成功!") else: current_task = ContainerWCSModel.objects.filter( container=container, tasktype='inbound' ).first() if current_task: data_return = { 'code': '200', 'message': '任务已存在,重新下发', 'data': current_task.to_dict() } else: # 库位分配 container_code = container print(f"开始生成库位,托盘编码:{container_code}") allocator = LocationAllocation() # 创建实例 location_list_cnumber = allocator.get_location_by_status(container_code, current_location) # 获取库位列表 if not location_list_cnumber: print("❌ 通用库位获取失败,请检查托盘编码") return print(f"[1]库位:{location_list_cnumber}") update_location_status = allocator.update_location_status(location_list_cnumber.location_code, 'reserved') # 更新库位状态 if not update_location_status: print("❌ 库位状态更新失败,请检查托盘编码") return print(f"[2]发送任务,库位状态更新成功!") update_location_group_status = allocator.update_location_group_status(location_list_cnumber.location_code) # 更新库位组状态 if not update_location_group_status: print("❌ 库位组状态更新失败,请检查托盘编码") return print(f"[3]库位组状态更新成功!") update_batch_status = allocator.update_batch_status(container_code, '2') # 更新批次状态 if not update_batch_status: print("❌ 批次状态更新失败,请检查批次号") return print(f"[4]批次状态更新成功!") update_location_group_batch = allocator.update_location_group_batch(location_list_cnumber, container_code) # 更新库位组的批次 if not update_location_group_batch: print("❌ 库位组批次更新失败,请检查托盘编码") return print(f"[5]库位组批次更新成功!") update_location_container_link = allocator.update_location_container_link(location_list_cnumber.location_code, container_code) # 更新库位和托盘的关联关系 if not update_location_container_link: print("❌ 库位和托盘的关联关系更新失败,请检查托盘编码") return print(f"[7]库位和托盘的关联关系更新成功!") # update_location_status = allocator.update_location_status(location_list_cnumber.location_code, 'occupied') # 更新库位状态 # if not update_location_status: # print("❌ 库位状态更新失败,请检查托盘编码") # return # print(f"[6]WCS到位,库位状态更新成功!") # update_location_container_link = allocator.update_location_container_link(location_list_cnumber.location_code, container_code) # 更新库位和托盘的关联关系 # if not update_location_container_link: # print("❌ 库位和托盘的关联关系更新失败,请检查托盘编码") # return # print(f"[7]库位和托盘的关联关系更新成功!") allocation_target_location = ( location_list_cnumber.warehouse_code + '-' + f"{int(location_list_cnumber.row):02d}" + '-' # 关键修改点 + f"{int(location_list_cnumber.col):02d}" + '-' + f"{int(location_list_cnumber.layer):02d}" ) self.generate_task(container, current_location, allocation_target_location) # 生成任务 current_task = ContainerWCSModel.objects.get( container=container, tasktype='inbound' ) data_return = { 'code': '200', 'message': '任务下发成功', 'data': current_task.to_dict() } container_obj.target_location = allocation_target_location container_obj.save() self.inport_update_task(current_task.id, container_obj.id) http_status = status.HTTP_200_OK if data_return['code'] == '200' else status.HTTP_400_BAD_REQUEST return Response(data_return, status=http_status) except Exception as e: logger.error(f"处理请求时发生错误: {str(e)}", exc_info=True) return Response( {'code': '500', 'message': '服务器内部错误', 'data': None}, status=status.HTTP_500_INTERNAL_SERVER_ERROR ) @transaction.atomic def inport_update_task(self, wcs_id,container_id): try: task_obj = ContainerWCSModel.objects.filter(id=wcs_id).first() if task_obj: container_detail_obj = ContainerDetailModel.objects.filter(container=container_id).all() if container_detail_obj: for detail in container_detail_obj: # 保存到数据库 batch = BoundDetailModel.objects.filter(bound_batch_id=detail.batch.id).first() TaskModel.objects.create( task_wcs = task_obj, container_detail = detail, batch_detail = batch ) logger.info(f"入库任务 {wcs_id} 已更新") else: logger.info(f"入库任务 {container_id} 批次不存在") else: logger.info(f"入库任务 {wcs_id} 不存在") except Exception as e: logger.error(f"处理入库任务时发生错误: {str(e)}", exc_info=True) return Response( {'code': '500', 'message': '服务器内部错误', 'data': None}, status=status.HTTP_500_INTERNAL_SERVER_ERROR ) # PDA组盘入库 将扫描到的托盘编码和批次信息保存到数据库 # 1. 先查询托盘对象,如果不存在,则创建托盘对象 # 2. 循环处理每个批次,查询批次对象, # 3. 更新批次数据(根据业务规则) # 4. 保存到数据库 # 5. 保存操作记录到数据库 class ContainerDetailViewSet(viewsets.ModelViewSet): """ retrieve: Response a data list(get) list: Response a data list(all) create: Create a data line(post) delete: Delete a data line(delete) """ # authentication_classes = [] # 禁用所有认证类 # permission_classes = [AllowAny] # 允许任意访问 pagination_class = MyPageNumberPagination filter_backends = [DjangoFilterBackend, OrderingFilter, ] ordering_fields = ['id', "create_time", "update_time", ] filter_class = ContainerDetailFilter def get_project(self): try: id = self.kwargs.get('pk') return id except: return None def get_queryset(self): id = self.get_project() if self.request.user: if id is None: return ContainerDetailModel.objects.filter( is_delete=False) else: return ContainerDetailModel.objects.filter( id=id, is_delete=False) else: return ContainerDetailModel.objects.none() def get_serializer_class(self): if self.action in ['list', 'destroy','retrieve']: return ContainerDetailGetSerializer elif self.action in ['create', 'update']: return ContainerDetailPostSerializer else: return self.http_method_not_allowed(request=self.request) def create(self, request, *args, **kwargs): data = self.request.data order_month = str(timezone.now().strftime('%Y%m')) data['month'] = order_month container_code = data.get('container') batches = data.get('batches', []) # 确保有默认空列表 print('扫描到的托盘编码', container_code) # 处理托盘对象 container_obj = ContainerListModel.objects.filter(container_code=container_code).first() if container_obj: data['container'] = container_obj.id logger.info(f"托盘 {container_code} 已存在") else: logger.info(f"托盘 {container_code} 不存在,创建托盘对象") serializer_list = ContainerListPostSerializer(data={'container_code': container_code}) serializer_list.is_valid(raise_exception=True) serializer_list.save() data['container'] = serializer_list.data.get('id') # 循环处理每个批次 for batch in batches: bound_number = batch.get('goods_code') goods_qty = batch.get('goods_qty') # 查询商品对象 bound_obj = BoundBatchModel.objects.filter(bound_number=bound_number).first() if not bound_obj: # 如果商品不存在,返回错误,这里暂时在程序中进行提醒,后续需要改为前端弹窗提醒 logger.error(f"批次 {bound_number} 不存在") # 跳出此次循环 continue # return Response({"error": f"商品编码 {bound_number} 不存在"}, status=400) # 3. 更新批次数据(根据业务规则) try: last_qty = bound_obj.goods_in_qty bound_obj.goods_in_qty += batch.get("goods_qty", 0) if bound_obj.goods_in_qty >= bound_obj.goods_qty: bound_obj.goods_in_qty = bound_obj.goods_qty bound_obj.status = 1 # 批次状态为组盘完成 print('批次id',bound_obj.id) bound_detail_obj = BoundDetailModel.objects.filter(bound_batch=bound_obj.id).first() if bound_detail_obj: bound_detail_obj.status = 1 bound_detail_obj.save() print('入库申请id',bound_detail_obj.bound_list_id) # 入库申请全部批次入库完成 bound_batch_all = BoundDetailModel.objects.filter(bound_list=bound_detail_obj.bound_list_id).all() if bound_batch_all.count() == bound_batch_all.filter(status=1).count(): bound_list_obj = BoundListModel.objects.filter(id=bound_detail_obj.bound_list_id).first() print('当前状态',bound_list_obj.bound_status) bound_list_obj.bound_status = 102 print('更新状态',bound_list_obj.bound_status) bound_list_obj.save() print('入库申请全部批次组盘完成') else: print('入库申请部分批次组盘完成') else: bound_obj.status = 0 bound_obj.save() # 保存到数据库 # 创建托盘详情记录(每个批次独立) print('新增个数',bound_obj.goods_in_qty-last_qty) if bound_obj.goods_in_qty-last_qty == goods_qty: detail_data = { "container": data['container'], # 托盘ID "batch": bound_obj.id, # 外键关联批次 "goods_code": bound_obj.goods_code, "goods_desc": bound_obj.goods_desc, "goods_qty": goods_qty, "goods_weight": bound_obj.goods_weight, "status": 1, "month": data['month'], "creater": data.get('creater', 'zl') # 默认值兜底 } serializer = self.get_serializer(data=detail_data) serializer.is_valid(raise_exception=True) serializer.save() # 必须保存到数据库 operate_data = { "month" : data['month'], "container": data['container'], # 托盘ID "operation_type" : 'container', "batch" : bound_obj.id, # 外键关联批次 "goods_code": bound_obj.goods_code, "goods_desc": bound_obj.goods_desc, "goods_qty": goods_qty, "goods_weight": bound_obj.goods_weight, "operator": data.get('creater', 'zl'), # 默认值兜底 "timestamp": timezone.now(), "from_location": "container", "to_location": "container", "memo": "入库PDA组盘,pda入库"+str(bound_obj.goods_code)+"数量"+str(goods_qty) } serializer_operate = ContainerOperationPostSerializer(data=operate_data) serializer_operate.is_valid(raise_exception=True) serializer_operate.save() # 必须保存到数据库 elif bound_obj.goods_in_qty-last_qty > 0: print('批次数量不一致') detail_data = { "container": data['container'], # 托盘ID "batch": bound_obj.id, # 外键关联批次 "goods_code": bound_obj.goods_code, "goods_desc": bound_obj.goods_desc, "goods_qty": bound_obj.goods_in_qty-last_qty, "goods_weight": bound_obj.goods_weight, "status": 1, "month": data['month'], "creater": data.get('creater', 'zl') # 默认值兜底 } serializer = self.get_serializer(data=detail_data) serializer.is_valid(raise_exception=True) serializer.save() # 必须保存到数据库 operate_data = { "month" : data['month'], "container": data['container'], # 托盘ID "operation_type" : 'container', "batch" : bound_obj.id, # 外键关联批次 "goods_code": bound_obj.goods_code, "goods_desc": bound_obj.goods_desc, "goods_qty": bound_obj.goods_in_qty-last_qty, "goods_weight": bound_obj.goods_weight, "operator": data.get('creater', 'zl'), # 默认值兜底 "timestamp": timezone.now(), "from_location": "container", "to_location": "container", "memo": "入库PDA组盘,(数量不一致)pda入库"+str(bound_obj.goods_code)+"数量"+str(goods_qty) } serializer_operate = ContainerOperationPostSerializer(data=operate_data) serializer_operate.is_valid(raise_exception=True) serializer_operate.save() # 必须保存到数据库 else : print('重复组盘') except Exception as e: print(f"更新批次 {bound_number} 失败: {str(e)}") continue # 将处理后的数据返回(或根据业务需求保存到数据库) res_data={ "code": "200", "msg": "Success Create", "data": data } return Response(res_data, status=200) def update(self, request, pk): qs = self.get_object() data = self.request.data serializer = self.get_serializer(qs, data=data) serializer.is_valid(raise_exception=True) serializer.save() headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=200, headers=headers) class ContainerOperateViewSet(viewsets.ModelViewSet): """ retrieve: Response a data list(get) list: Response a data list(all) create: Create a data line(post) delete: Delete a data line(delete) """ # authentication_classes = [] # 禁用所有认证类 # permission_classes = [AllowAny] # 允许任意访问 pagination_class = MyPageNumberPagination filter_backends = [DjangoFilterBackend, OrderingFilter, ] ordering_fields = ['id', "timestamp" ] filter_class = ContainerOperationFilter def get_project(self): try: id = self.kwargs.get('pk') return id except: return None def get_queryset(self): id = self.get_project() if self.request.user: if id is None: return ContainerOperationModel.objects.filter( is_delete=False) else: return ContainerOperationModel.objects.filter( id=id, is_delete=False) else: return ContainerOperationModel.objects.none() def get_serializer_class(self): if self.action in ['list', 'destroy','retrieve']: return ContainerOperationGetSerializer elif self.action in ['create', 'update']: return ContainerOperationPostSerializer else: return self.http_method_not_allowed(request=self.request) def create(self, request, *args, **kwargs): data = self.request.data serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) serializer.save() headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=200, headers=headers) def update(self, request, pk): qs = self.get_object() data = self.request.data serializer = self.get_serializer(qs, data=data) serializer.is_valid(raise_exception=True) serializer.save() headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=200, headers=headers)