123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471 |
- 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)
|