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 DeviceModel,LocationModel,LocationGroupModel,LocationContainerLink,LocationChangeLog from bound.models import BoundBatchModel,BoundDetailModel,BoundListModel from .filter import DeviceFilter,LocationFilter,LocationContainerLinkFilter,LocationChangeLogFilter,LocationGroupFilter from .serializers import LocationListSerializer,LocationPostSerializer from .serializers import LocationGroupListSerializer,LocationGroupPostSerializer # 以后添加模块时,只需要在这里添加即可 from rest_framework.permissions import AllowAny from container.models import ContainerListModel,ContainerDetailModel,ContainerOperationModel,TaskModel logger = logging.getLogger(__name__) # 库位分配 # 入库规则函数 # 逻辑根据批次下的托盘数目来找满足区间范围的库位,按照优先级排序, class LocationAllocation: # 入库规则函数 # fun:get_pallet_count_by_batch: 根据托盘码查询批次下托盘总数 # fun:get_location_type: 根据托盘数目获取库位类型 # fun:updata_location_container_link: 更新库位和托盘的关联关系 # fun:get_batch_status: 获取批次状态 # fun:get_batch: 获取批次 # fun:get_location_list_remainder: 获取可用库位的c_number列表 # fun:get_min_list_index: 获取最小的库位 # fun:get_location_by_type_remainder: 根据库位类型获取库位 # fun:get_location_by_type: 第一次入库,根据库位类型获取库位 # fun:get_location_by_status: 根据库位状态获取库位 @transaction.atomic def get_pallet_count_by_batch(self, container_code): """ 根据托盘码查询批次下托盘总数 :param container_code: 要查询的托盘码 :return: 所属批次下的托盘总数 """ # 1. 通过托盘码获取容器详情 container = ContainerListModel.objects.filter( container_code=container_code ).first() if not container: logger.error(f"托盘 {container_code} 不存在") print(f"托盘 {container_code} 不存在") return None # 2. 获取关联的批次明细 container_detail = ContainerDetailModel.objects.filter( container=container.id, status=1 ).first() if not container_detail: print (f"容器 {container_code} 未组盘") logger.error(f"容器 {container_code} 未组盘") return None else: print (f"容器 {container_code} 已组盘") batch_container_count = ContainerDetailModel.objects.filter( batch = container_detail.batch.id, status = 1 ).count() return batch_container_count def get_location_type(self,container_count): """ 根据托盘数目获取库位类型 :param container_count: 托盘数目 :return: 库位类型 """ if container_count <= 1: return ["T1"] elif container_count <= 2: return ["T2"] elif container_count <= 4: return ["S4","T4"] else: return ["T5"] @transaction.atomic def updata_location_container_link(self,location_code,container_code): """ 更新库位和托盘的关联关系 :param location_code: 库位编码 :param container_code: 托盘编码 :return: """ try: # 1. 获取库位和托盘的关联关系 location = LocationModel.objects.filter( location_code=location_code ).first() container = ContainerListModel.objects.filter( container_code=container_code ).first() # 2. 如果库位和托盘的关联关系不存在,创建新的关联关系 if not LocationContainerLink.objects.filter(location=location).exists(): location_container_link = LocationContainerLink( location=location, container=container ) location_container_link.save() print(f"更新库位和托盘的关联关系成功!") # 3. 更新库位和托盘的关联关系 else: LocationContainerLink.objects.filter(location=location).update(location=location, container=container) print(f"更新库位和托盘的关联关系成功!") return True except Exception as e: logger.error(f"更新库位和托盘的关联关系失败:{str(e)}") print(f"更新库位和托盘的关联关系失败:{str(e)}") return False def get_batch_status(self,container_code): """ 获取批次状态 :param container_code: 托盘码 :return: 批次状态 """ # 1. 通过托盘码获取容器详情 container = ContainerListModel.objects.filter( container_code=container_code ).first() if not container: logger.error(f"托盘 {container_code} 不存在") print(f"托盘 {container_code} 不存在") return None # 2. 获取关联的批次明细 container_detail = ContainerDetailModel.objects.filter( container=container.id, status=1 ).first() if not container_detail: print (f"容器 {container_code} 未组盘") logger.error(f"容器 {container_code} 未组盘") return None else: print (f"容器 {container_code} 已组盘") batch_status = container_detail.batch.status return batch_status def get_batch(self,container_code): """ 获取批次 :param container_code: 托盘码 :return: 批次 """ # 1. 通过托盘码获取容器详情 container = ContainerListModel.objects.filter( container_code=container_code ).first() if not container: logger.error(f"托盘 {container_code} 不存在") print(f"托盘 {container_code} 不存在") return None # 2. 获取关联的批次明细 container_detail = ContainerDetailModel.objects.filter( container=container.id, status=1 ).first() if not container_detail: print (f"容器 {container_code} 未组盘") logger.error(f"容器 {container_code} 未组盘") return None else: print (f"容器 {container_code} 已组盘") batch = container_detail.batch.bound_number return batch @transaction.atomic def get_location_list_remainder(self, location_list): """ 获取可用库位的c_number列表 :param location_list: 库位对象列表 :return: 可用库位编号列表 """ if not location_list: return None available_c_numbers = [ loc.c_number for loc in location_list if loc.status == 'available' ] return available_c_numbers if available_c_numbers else None def get_min_list_index(self,list): """ 获取最小的库位 :param list: 库位列表 :return: 最小库位 """ if not list: return None min_index = 0 for i in range(len(list)): if list[i] < list[min_index]: min_index = i return min_index @transaction.atomic def get_location_by_type_remainder(self, batch, layer): """ 根据库位类型获取库位 :param batch: 批次号 :param layer: 层数限制 :return: 符合条件的库位列表 """ # 获取符合条件的库位组并按优先级排序 location_groups = LocationGroupModel.objects.filter( current_batch=batch, layer=layer, ).first() if not location_groups: return None # 合并所有库位组中的库位 locations = [] locations.extend(location_groups.location_items.all()) # 假设location_items是关联字段 return locations if locations else None @transaction.atomic def get_location_by_type(self, location_type_list, start_location, layer): """ 第一次入库,根据库位类型获取库位 :param location_type_list: 库位类型列表 :param start_location: 起始位置(决定优先级排序方式) :param layer: 层数限制 :return: 符合条件的库位列表 """ # 获取符合条件的库位组并按优先级排序 location_groups = LocationGroupModel.objects.filter( group_type__in=location_type_list, layer=layer, status='available' ) # 根据起始位置选择排序字段 if start_location == 'in1': ordered_groups = location_groups.order_by('left_priority') elif start_location == 'in2': ordered_groups = location_groups.order_by('right_priority') else: ordered_groups = location_groups.none() # 合并所有库位组中的库位 locations = [] locations.extend(ordered_groups.first().location_items.all()) # 假设location_items是关联字段 return locations if locations else None @transaction.atomic def get_location_by_status(self,container_code,start_location,layer): """ 根据库位状态获取库位 :param location_type: 库位类型 :param start_location: 起始库位 if in1 优先考虑left_priority, if in2 优先考虑right_priority 就是获取库位组列表之后进行排序 :param layer: 层数 限定层数 :return: 库位列表 """ # 1. 获取批次状态 102 为已组盘 103 为部分入库 104 为全部入库 status = self.get_batch_status(container_code) # if status == 1: # 2. 获取库位组 print (f"第一次入库") container_number = self.get_pallet_count_by_batch(container_code) print (f"该批次下托盘总数:{container_number}") location_type_list = self.get_location_type(container_number) print(f"库位类型:{location_type_list}") location_list = self.get_location_by_type(location_type_list,start_location,layer) print(f"库位列表:{location_list}") location_list_remainder = self.get_location_list_remainder(location_list) print(f"库位列表剩余:{location_list_remainder}") if not location_list_remainder: # 库位已满,返回None return None else: location_index = self.get_min_list_index(location_list_remainder) # 返回和托盘关联的库位、库位剩余、库位类型 return location_list[location_index] elif status == 2: print(f"部分入库") # 2. 获取库位组 location_list = self.get_location_by_type_remainder(self.get_batch(container_code),layer) location_list_remainder = self.get_location_list_remainder(location_list) if not location_list_remainder: # 库位已满,返回None return None else: location_index = self.get_min_list_index(location_list_remainder) # 返回和托盘关联的库位、库位剩余、库位类型 return location_list[location_index] class locationViewSet(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 = LocationFilter 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 LocationModel.objects.filter() else: return LocationModel.objects.filter( id=id) else: return LocationModel.objects.none() def get_serializer_class(self): if self.action == 'list': return LocationListSerializer elif self.action == 'update': return LocationPostSerializer elif self.action =='retrieve': return LocationListSerializer def update(self, request, *args, **kwargs): qs = self.get_object() data = self.request.data location_code = data.get('location_code') # 处理库位对象 location_obj = LocationModel.objects.filter(location_code=location_code).first() if not location_obj: logger.info(f"库位 {location_code} 不存在") return Response( {'code': '400', 'message': '库位不存在', 'data': None}, status=status.HTTP_400_BAD_REQUEST ) else: data['id'] = location_obj.id logger.info(f"库位 {location_code} 已存在") serializer = self.get_serializer(qs, data=data) serializer.is_valid(raise_exception=True) serializer.save() headers = self.get_success_headers(serializer.data) self.handle_group_location_status(location_code,location_obj.location_group) return Response(serializer.data, status=200, headers=headers) def handle_group_location_status(self,location_code,location_group): """ 处理库位组和库位的关联关系 :param location_code: 库位编码 :param location_group: 库位组编码 :return: """ # 1. 获取库位空闲状态的库位数目 location_obj_number = LocationModel.objects.filter( location_group=location_group, status='available' ).all().count() # 2. 获取库位组对象 logger.info(f"库位组 {location_group} 下的库位数目:{location_obj_number}") # 1. 获取库位和库位组的关联关系 location_group_obj = LocationGroupModel.objects.filter( group_code=location_group ).first() if not location_group_obj: logger.info(f"库位组 {location_group} 不存在") return None else: if location_obj_number == 0: # 库位组库位已满,更新库位组状态为full location_group_obj.status = 'full' location_group_obj.save() elif location_obj_number < location_group_obj.max_capacity: location_group_obj.status = 'occupied' location_group_obj.save() else: location_group_obj.status = 'available' location_group_obj.save() class locationGroupViewSet(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 = LocationGroupFilter 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 LocationGroupModel.objects.filter() else: return LocationGroupModel.objects.filter(id=id) else: return LocationGroupModel.objects.none() def get_serializer_class(self): if self.action == 'list': return LocationGroupListSerializer elif self.action == 'update': return LocationGroupPostSerializer elif self.action =='retrieve': return LocationGroupListSerializer def update(self, request, *args, **kwargs): data = self.request.data order_month = str(timezone.now().strftime('%Y%m')) data['month'] = order_month group_code = data.get('group_code') # 处理库位组对象 group_obj = LocationGroupModel.objects.filter(group_code=group_code).first() if group_obj: data['id'] = group_obj.id logger.info(f"库位组 {group_code} 已存在") else: logger.info(f"库位组 {group_code} 不存在,创建库位组对象") serializer_list = LocationGroupPostSerializer(data=data) serializer_list.is_valid(raise_exception=True) serializer_list.save() data['id'] = serializer_list.data.get('id') return Response(data, status=status.HTTP_201_CREATED)