| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642 |
- 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,alloction_pre,base_location
- 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
- from django.db.models import Prefetch
- import copy
- import json
- from collections import defaultdict
- logger = logging.getLogger(__name__)
- from operation_log.views import log_operation,log_failure_operation,log_success_operation
- from operation_log.models import OperationLog
- # 库位分配
- # 入库规则函数
- # 逻辑根据批次下的托盘数目来找满足区间范围的库位,按照优先级排序,
-
- 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()
- prefetch_containers = Prefetch(
- 'container_links',
- queryset=LocationContainerLink.objects.filter(
- is_active=True
- ).select_related('container'), # 加载关联的托盘对象
- to_attr='active_links' # 新的属性名称
- )
- if self.request.auth:
- if id is None:
- log_operation(
- request=self.request,
- operation_content="查看库位列表",
- operation_level="view",
- operator=self.request.auth.name if self.request.auth else None,
- module_name="库位"
- )
- return LocationModel.objects.prefetch_related(prefetch_containers).all()
- else:
- log_operation(
- request=self.request,
- operation_content=f"查看库位详情 ID:{id}",
- operation_level="view",
- operator=self.request.auth.name if self.request.auth else None,
- object_id=id,
- module_name="库位"
- )
- return LocationModel.objects.prefetch_related(prefetch_containers).filter(id=id)
- else:
- return LocationModel.objects.none()
- def get_serializer_class(self):
- if self.action == 'list':
- return LocationListSerializer
- elif self.action == 'create':
- return LocationPostSerializer
- elif self.action == 'update':
- return LocationPostSerializer
- elif self.action =='retrieve':
- return LocationListSerializer
- def create(self, request, *args, **kwargs):
- """创建库位"""
- serializer = self.get_serializer(data=request.data)
- try:
- serializer.is_valid(raise_exception=True)
- instance = serializer.save()
- log_success_operation(
- request=self.request,
- operation_content=f"创建库位成功,库位编码: {instance.location_code}",
- operation_level="new",
- operator=self.request.auth.name if self.request.auth else None,
- object_id=instance.id,
- module_name="库位"
- )
- headers = self.get_success_headers(serializer.data)
- return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
- except Exception as e:
- log_failure_operation(
- request=self.request,
- operation_content=f"创建库位失败: {str(e)}",
- operation_level="new",
- operator=self.request.auth.name if self.request.auth else None,
- module_name="库位"
- )
- raise
- def destroy(self, request, *args, **kwargs):
- """删除库位"""
- instance = self.get_object()
- location_code = instance.location_code
- object_id = instance.id
- try:
- self.perform_destroy(instance)
- log_success_operation(
- request=self.request,
- operation_content=f"删除库位成功,库位编码: {location_code}",
- operation_level="delete",
- operator=self.request.auth.name if self.request.auth else None,
- object_id=object_id,
- module_name="库位"
- )
- return Response(status=status.HTTP_204_NO_CONTENT)
- except Exception as e:
- log_failure_operation(
- request=self.request,
- operation_content=f"删除库位失败,库位编码: {location_code}, 错误: {str(e)}",
- operation_level="delete",
- operator=self.request.auth.name if self.request.auth else None,
- object_id=object_id,
- module_name="库位"
- )
- raise
- 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:
- log_failure_operation(
- request=self.request,
- operation_content=f"更新库位失败,库位 {location_code} 不存在",
- operation_level="update",
- operator=self.request.auth.name if self.request.auth else None,
- module_name="库位"
- )
- 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()
- log_success_operation(
- request=self.request,
- operation_content=f"更新库位成功,库位 {location_code} 已更新",
- operation_level="update",
- operator=self.request.auth.name if self.request.auth else None,
- object_id=location_obj.id,
- module_name="库位"
- )
- 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()
- def batch_status_location(self, request):
- """
- 优化版:批量获取库位批次状态
- 基于模型结构优化查询
- """
- layer = request.data.get('layer')
-
- # 使用反向关系名 'container_links' 进行预取
- locations = LocationModel.objects.filter(
- layer=layer
- ).prefetch_related(
- Prefetch(
- 'container_links', # 使用模型定义的 related_name
- queryset=LocationContainerLink.objects.filter(is_active=True)
- .select_related('container'),
- to_attr='active_links'
- )
- )
-
- # 收集所有激活链接的托盘ID
- container_ids = set()
- for loc in locations:
- if loc.active_links: # 每个库位最多只有一个激活链接
- container_ids.add(loc.active_links[0].container_id)
-
- # 批量查询托盘详情及其批次状态
- container_batch_status = defaultdict(dict) # 改为字典存储,避免重复记录
- if container_ids:
- container_details = ContainerDetailModel.objects.filter(
- container_id__in=container_ids ,
- is_delete=False
- ).select_related('batch').exclude(status=3) # 排除已删除或不合格的托盘
-
- for detail in container_details:
- if detail.batch_id:
- # 创建唯一标识的键
- status_key = (
- detail.batch.check_status if detail.batch else "404",
- detail.batch.bound_number if detail.batch else "no_batch"
- )
-
- # 如果这个状态尚未添加过,或者需要更新
- if status_key not in container_batch_status[detail.container_id]:
- container_batch_status[detail.container_id][status_key] = (
- detail.batch.check_status if detail.batch else "404",
- detail.batch.check_time if detail.batch else "no_check_time",
- detail.batch.bound_number if detail.batch else "no_batch",
- detail.goods_qty-detail.goods_out_qty if detail.goods_qty-detail.goods_out_qty > 0 else 0,
-
- )
- else:
- # 如果批次状态相同,则更新库位数量
- if container_batch_status[detail.container_id][status_key][0] == detail.batch.check_status:
- container_batch_status[detail.container_id][status_key] = (
- detail.batch.check_status,
- max(container_batch_status[detail.container_id][status_key][1], detail.batch.check_time),
- detail.batch.bound_number,
- container_batch_status[detail.container_id][status_key][3] + (detail.goods_qty-detail.goods_out_qty if detail.goods_qty-detail.goods_out_qty > 0 else 0)
- )
-
- # 构造返回数据
- return_data = []
- for loc in locations:
- batch_statuses = []
-
- if loc.active_links:
- container_id = loc.active_links[0].container_id
- # 从字典中提取值并转换为列表
- if container_id in container_batch_status:
- batch_statuses = list(container_batch_status[container_id].values())
- else:
- batch_statuses = [("404", "no_check_time", "no_batch")]
-
- # 使用Django模型自带的model_to_dict转换基础字段
- from django.forms.models import model_to_dict
- location_data = model_to_dict(loc, fields=[
- "id", "shelf_type", "row", "col", "layer", "update_time",
- "empty_label", "location_code", "location_group", "location_type",
- "status", "max_capacity", "current_quantity", "c_number",
- "coordinate", "access_priority", "is_active"
- ])
-
- # 添加批次状态字段 - 存储所有信息
- location_data["batch_statuses"] = batch_statuses
-
- return_data.append(location_data)
- data = {
- "code": "200",
- "msg": "Success Create",
- "data": return_data
- }
- log_operation(
- request=self.request,
- operation_content=f"批量获取库位批次状态,层号:{layer}",
- operation_level="view",
- operator=self.request.auth.name if self.request.auth else None,
- module_name="库位"
- )
-
- return Response(data, status=200)
-
- 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.auth:
- if id is None:
- log_operation(
- request=self.request,
- operation_content="查看库位组列表",
- operation_level="view",
- operator=self.request.auth.name if self.request.auth else None,
- module_name="库位"
- )
- return LocationGroupModel.objects.filter()
- else:
- log_operation(
- request=self.request,
- operation_content=f"查看库位组详情 ID:{id}",
- operation_level="view",
- operator=self.request.auth.name if self.request.auth else None,
- object_id=id,
- module_name="库位"
- )
- return LocationGroupModel.objects.filter(id=id)
- else:
- return LocationGroupModel.objects.none()
- def get_serializer_class(self):
- if self.action == 'create':
- return LocationGroupPostSerializer
- if self.action == 'list':
- return LocationGroupListSerializer
- elif self.action == 'update':
- return LocationGroupPostSerializer
- elif self.action =='retrieve':
- return LocationGroupListSerializer
- def create(self, request, *args, **kwargs):
- """创建库位组"""
- data = self.request.data.copy()
- order_month = str(timezone.now().strftime('%Y%m'))
- data['month'] = order_month
- serializer = LocationGroupPostSerializer(data=data)
- try:
- serializer.is_valid(raise_exception=True)
- instance = serializer.save()
- log_success_operation(
- request=self.request,
- operation_content=f"创建库位组成功,库位组编码: {instance.group_code}",
- operation_level="new",
- operator=self.request.auth.name if self.request.auth else None,
- object_id=instance.id,
- module_name="库位"
- )
- headers = self.get_success_headers(serializer.data)
- return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
- except Exception as e:
- log_failure_operation(
- request=self.request,
- operation_content=f"创建库位组失败: {str(e)}",
- operation_level="new",
- operator=self.request.auth.name if self.request.auth else None,
- module_name="库位"
- )
- raise
- def destroy(self, request, *args, **kwargs):
- """删除库位组"""
- instance = self.get_object()
- group_code = instance.group_code
- object_id = instance.id
- try:
- self.perform_destroy(instance)
- log_success_operation(
- request=self.request,
- operation_content=f"删除库位组成功,库位组编码: {group_code}",
- operation_level="delete",
- operator=self.request.auth.name if self.request.auth else None,
- object_id=object_id,
- module_name="库位"
- )
- return Response(status=status.HTTP_204_NO_CONTENT)
- except Exception as e:
- log_failure_operation(
- request=self.request,
- operation_content=f"删除库位组失败,库位组编码: {group_code}, 错误: {str(e)}",
- operation_level="delete",
- operator=self.request.auth.name if self.request.auth else None,
- object_id=object_id,
- module_name="库位"
- )
- raise
-
- 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} 已存在")
- # 更新现有库位组
- serializer = LocationGroupPostSerializer(group_obj, data=data)
- try:
- serializer.is_valid(raise_exception=True)
- serializer.save()
- log_success_operation(
- request=self.request,
- operation_content=f"更新库位组成功,库位组编码: {group_code}",
- operation_level="update",
- operator=self.request.auth.name if self.request.auth else None,
- object_id=group_obj.id,
- module_name="库位"
- )
- return Response(serializer.data, status=status.HTTP_200_OK)
- except Exception as e:
- log_failure_operation(
- request=self.request,
- operation_content=f"更新库位组失败,库位组编码: {group_code}, 错误: {str(e)}",
- operation_level="update",
- operator=self.request.auth.name if self.request.auth else None,
- object_id=group_obj.id,
- module_name="库位"
- )
- raise
- else:
- logger.info(f"库位组 {group_code} 不存在,创建库位组对象")
- serializer_list = LocationGroupPostSerializer(data=data)
- serializer_list.is_valid(raise_exception=True)
- serializer_list.save()
- log_success_operation(
- request=self.request,
- operation_content=f"创建库位组成功,库位组编码: {group_code}",
- operation_level="new",
- operator=self.request.auth.name if self.request.auth else None,
- object_id=serializer_list.data.get('id'),
- module_name="库位"
- )
- data['id'] = serializer_list.data.get('id')
- return Response(data, status=status.HTTP_201_CREATED)
- class LocationAllocation:
- # 入库规则函数
- # fun:get_pallet_count_by_batch: 根据托盘码查询批次下托盘总数
- # fun:get_left_locationGroup_number_by_type: 获取每层库位组剩余数量
- # fun:get_location_type: 根据托盘数目获取库位类型
- # fun:update_location_container_link: 更新库位和托盘的关联关系
- # fun:update_location_group_batch: 更新库位组的批次
- # fun:update_batch_status: 更新批次状态yes/no
- # fun:update_location_status: 更新库位状态和
- # fun:up
- # fun:get_batch_status: 获取批次状态
- # fun:get_batch: 获取批次
- # fun:get_location_list_remainder: 获取可用库位的c_number列表
- # fun
- # fun:get_location_by_type_remainder: 根据库位类型获取库位
- # fun:get_location_by_type: 第一次入库,根据库位类型获取库位
- # fun:get_location_by_status: 根据库位状态获取库位
-
- 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} 不存在")
- return None
- # 2. 获取关联的批次明细
- container_detail = ContainerDetailModel.objects.filter(
- container=container.id,is_delete=False
- ).exclude(status = 3).first()
- if not container_detail:
- logger.error(f"托盘 {container_code} 未组盘")
- return None
-
- batch_container = ContainerDetailModel.objects.filter(
- batch = container_detail.batch.id,
- is_delete = False,
- status = 1
- ).all()
- # 统计批次下的不同托盘 item.contianer_id
- batch_container_count = 0
- container_ids = []
- for item in batch_container:
- if item.container_id not in container_ids:
- batch_container_count = batch_container_count + 1
- container_ids.append(item.container_id)
- batch_item = BoundBatchModel.objects.filter( bound_number = container_detail.batch.bound_number).first()
- if not batch_item:
- print(f"批次号获取失败!")
- return None
- batch_item.container_number = batch_container_count
- batch_item.save()
- return batch_container_count
-
- def get_left_locationGroup_number_by_type(self):
- """
- 获取每层库位组剩余数量
- :return:
- """
- try:
- # 定义库位组和层号
- group = ['T1', 'T2', 'S4', 'T4', 'T5']
- layer = [1, 2, 3]
- # 初始化结果列表,包含三个空字典对应三个层
- left_number = [{} for _ in layer]
-
- for item in group:
- for idx, layer_num in enumerate(layer):
- # 检查库位组是否存在(不考虑状态)
- exists = LocationGroupModel.objects.filter(
- group_type=item,
- layer=layer_num
- ).exists()
-
- if not exists:
- print(f"库位组 {item}_{layer_num} 不存在")
- left_number[idx][item] = 0
- else:
- # 统计可用状态的库位组数量
- count = LocationGroupModel.objects.filter(
- group_type=item,
- layer=layer_num,
- status='available'
- ).count()
- left_number[idx][item] = count
-
- return left_number
- except Exception as e:
- logger.error(f"获取库位组剩余数量失败:{str(e)}")
- print(f"获取库位组剩余数量失败:{str(e)}")
- return None
-
- def update_location_container_link(self,location_code,container_code, request=None):
- """
- 更新库位和托盘的关联关系
- :param location_code: 库位编码
- :param container_code: 托盘编码
- :param request: 请求对象(可选)
- :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"更新库位和托盘的关联关系成功!")
- # 记录操作日志
- try:
- if request:
- log_success_operation(
- request=request,
- operation_content=f"创建库位-托盘关联,库位编码: {location_code}, 托盘编码: {container_code}",
- operation_level="other",
- module_name="库位分配"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"创建库位-托盘关联,库位编码: {location_code}, 托盘编码: {container_code}",
- operation_level="other",
- operation_result="success",
- module_name="库位分配"
- )
- except Exception as log_error:
- logger.error(f"记录库位-托盘关联日志失败: {str(log_error)}")
- return True
- # 3. 更新库位和托盘的关联关系
- else:
- LocationContainerLink.objects.filter(location=location).update(location=location, container=container)
- print(f"更新库位和托盘的关联关系成功!")
- # 记录操作日志
- try:
- if request:
- log_success_operation(
- request=request,
- operation_content=f"更新库位-托盘关联,库位编码: {location_code}, 托盘编码: {container_code}",
- operation_level="update",
- module_name="库位分配"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"更新库位-托盘关联,库位编码: {location_code}, 托盘编码: {container_code}",
- operation_level="update",
- operation_result="success",
- module_name="库位分配"
- )
- except Exception as log_error:
- logger.error(f"记录库位-托盘关联日志失败: {str(log_error)}")
- return True
- except Exception as e:
- logger.error(f"更新库位和托盘的关联关系失败:{str(e)}")
- print(f"更新库位和托盘的关联关系失败:{str(e)}")
- # 记录失败日志
- try:
- if request:
- log_failure_operation(
- request=request,
- operation_content=f"更新库位-托盘关联失败,库位编码: {location_code}, 托盘编码: {container_code}, 错误: {str(e)}",
- operation_level="update",
- module_name="库位分配"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"更新库位-托盘关联失败,库位编码: {location_code}, 托盘编码: {container_code}, 错误: {str(e)}",
- operation_level="update",
- operation_result="failure",
- module_name="库位分配"
- )
- except Exception as log_error:
- logger.error(f"记录失败日志失败: {str(log_error)}")
- return False
- def update_container_detail_status(self,container_code,status):
- try:
- # 1. 获取托盘
- container = ContainerListModel.objects.filter(
- container_code=container_code
- ).first()
- if not container:
- print(f"托盘 {container_code} 不存在")
- return False
- # 2. 更新托盘状态
- container_detail = ContainerDetailModel.objects.filter(
- container=container.id,is_delete=False
- ).exclude(status=3).first()
- if not container_detail:
- print(f"托盘 {container_code} 未组盘_from update_container_detail_status")
- return False
- container_detail.status = status
- container_detail.save()
- print(f"更新托盘状态成功!")
- return True
- except Exception as e:
- logger.error(f"更新托盘状态失败:{str(e)}")
- print(f"更新托盘状态失败:{str(e)}")
- return False
- def update_location_group_batch(self,location,container_code):
- """
- :param location: 库位对象
- :param container_code: 托盘码
- :return:
- """
- try:
- # 1. 获取库位组
- location_group = LocationGroupModel.objects.filter(
- group_code=location.location_group
- ).first()
- if not location_group:
- print(f"库位组获取失败!")
- return False
- # 2. 更新库位组的批次
- bound_number=self.get_batch(container_code)
- if not bound_number:
- print(f"批次号获取失败!")
- return False
- location_group.current_batch = bound_number
- location_group.save()
- print(f"更新库位组的批次成功!")
- return True
- except Exception as e:
- logger.error(f"更新库位组的批次失败:{str(e)}")
- print(f"更新库位组的批次失败:{str(e)}")
- return False
- def update_location_status(self,location_code,status, request=None):
- """
- 更新库位状态
- :param location_code: 库位编码
- :param status: 库位状态
- :param request: 请求对象(可选)
- :return:
- """
- try:
- # 1. 获取库位
- location = LocationModel.objects.filter(
- location_code=location_code
- ).first()
- if not location:
- print(f"库位获取失败!")
- return False
- # 2. 更新库位状态
- old_status = location.status
- location.status = status
- location.save()
- print(f"更新库位状态成功!")
- # 记录操作日志(状态变更敏感操作)
- try:
- if request:
- log_success_operation(
- request=request,
- operation_content=f"更新库位状态,库位编码: {location_code}, 状态: {old_status} -> {status}",
- operation_level="update",
- object_id=str(location.id),
- module_name="库位分配"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"更新库位状态,库位编码: {location_code}, 状态: {old_status} -> {status}",
- operation_level="update",
- operation_result="success",
- object_id=str(location.id),
- module_name="库位分配"
- )
- except Exception as log_error:
- logger.error(f"记录库位状态更新日志失败: {str(log_error)}")
- return True
- except Exception as e:
- logger.error(f"更新库位状态失败:{str(e)}")
- print(f"更新库位状态失败:{str(e)}")
- # 记录失败日志
- try:
- if request:
- log_failure_operation(
- request=request,
- operation_content=f"更新库位状态失败,库位编码: {location_code}, 错误: {str(e)}",
- operation_level="update",
- module_name="库位分配"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"更新库位状态失败,库位编码: {location_code}, 错误: {str(e)}",
- operation_level="update",
- operation_result="failure",
- module_name="库位分配"
- )
- except Exception as log_error:
- logger.error(f"记录失败日志失败: {str(log_error)}")
- return False
-
- def update_group_status_reserved(self,location_group_list, request=None):
- """
- 更新库位组状态为预留
- :param location_group_list: 库位组对象列表
- :param request: 请求对象(可选)
- :return:
- """
- try:
- reserved_groups = []
- for location_group in location_group_list:
- # 1. 获取库位组
- if not location_group:
- print(f"库位组获取失败!")
- return False
- # 2. 更新库位组状态
- location_group_id = location_group.split('_')[1]
- location_group_item = LocationGroupModel.objects.filter(
- id=location_group_id
- ).first()
- if not location_group_item:
- print(f"库位组 {location_group} 不存在")
- return False
- # 3. 更新库位组状态
- old_status = location_group_item.status
- location_group_item.status = 'reserved'
- location_group_item.save()
- reserved_groups.append(f"{location_group_item.group_code}({old_status}->reserved)")
- # 记录预留库位组操作日志(敏感操作)
- try:
- if request:
- log_success_operation(
- request=request,
- operation_content=f"预留库位组,共{len(reserved_groups)}个: {', '.join(reserved_groups)}",
- operation_level="update",
- module_name="库位分配"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"预留库位组,共{len(reserved_groups)}个: {', '.join(reserved_groups)}",
- operation_level="update",
- operation_result="success",
- module_name="库位分配"
- )
- except Exception as log_error:
- logger.error(f"记录预留库位组日志失败: {str(log_error)}")
- return True
- except Exception as e:
- logger.error(f"更新库位组状态失败:{str(e)}")
- print(f"更新库位组状态失败:{str(e)}")
- # 记录失败日志
- try:
- if request:
- log_failure_operation(
- request=request,
- operation_content=f"预留库位组失败,错误: {str(e)}",
- operation_level="update",
- module_name="库位分配"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"预留库位组失败,错误: {str(e)}",
- operation_level="update",
- operation_result="failure",
- module_name="库位分配"
- )
- except Exception as log_error:
- logger.error(f"记录失败日志失败: {str(log_error)}")
- return False
-
- def update_location_group_status(self, location_code):
- """
- 更新库位组状态
- :param location_code: 库位编码
- :return:
- """
- try:
- # 1. 获取库位
- location = LocationModel.objects.filter(
- location_code=location_code
- ).first()
- if not location:
- print(f"库位获取失败!")
- return False
-
- # 2. 获取库位组
- location_group = LocationGroupModel.objects.filter(
- group_code=location.location_group
- ).first()
- if not location_group:
- print(f"库位组获取失败!")
- return False
- current=0
- for location_item in location_group.location_items.all():
- if location_item.status != 'available':
- current=current + 1
- # 3. 更新库位组状态
- if current == 0:
- location_group.status = 'available'
- elif current == location_group.max_capacity:
- location_group.status = 'full'
- else:
- location_group.status = 'occupied'
-
- location_group.current_goods_quantity = sum(
- [loc.current_quantity for loc in location_group.location_items.all()]
- )
- location_group.current_quantity = current
-
- location_group.save()
- print(f"更新库位组状态成功!")
- return True
- except Exception as e:
- logger.error(f"更新库位组状态失败:{str(e)}")
- print(f"更新库位组状态失败:{str(e)}")
- def update_batch_status(self,container_code,status, request=None):
- """
- 更新批次状态
- :param container_code: 托盘编码
- :param status: 批次状态
- :param request: 请求对象(可选)
- :return:
- """
- try:
- # 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,is_delete=False
- ).exclude(status=3).first()
- if not container_detail:
- print (f"托盘 {container_code} 未组盘")
- logger.error(f"托盘 {container_code} 未组盘_from update_batch_status")
- return None
-
- # 3. 更新批次状态
- batch = container_detail.batch
- old_status = batch.status
- batch.status = status
- batch.save()
- print(f"更新批次状态成功!")
- # 记录操作日志(批次状态变更敏感操作)
- try:
- if request:
- log_success_operation(
- request=request,
- operation_content=f"更新批次状态,托盘编码: {container_code}, 批次号: {batch.bound_number}, 状态: {old_status} -> {status}",
- operation_level="update",
- object_id=str(batch.id),
- module_name="库位分配"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"更新批次状态,托盘编码: {container_code}, 批次号: {batch.bound_number}, 状态: {old_status} -> {status}",
- operation_level="update",
- operation_result="success",
- object_id=str(batch.id),
- module_name="库位分配"
- )
- except Exception as log_error:
- logger.error(f"记录批次状态更新日志失败: {str(log_error)}")
- return True
- except Exception as e:
- logger.error(f"更新批次状态失败:{str(e)}")
- print(f"更新批次状态失败:{str(e)}")
- # 记录失败日志
- try:
- if request:
- log_failure_operation(
- request=request,
- operation_content=f"更新批次状态失败,托盘编码: {container_code}, 错误: {str(e)}",
- operation_level="update",
- module_name="库位分配"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"更新批次状态失败,托盘编码: {container_code}, 错误: {str(e)}",
- operation_level="update",
- operation_result="failure",
- module_name="库位分配"
- )
- except Exception as log_error:
- logger.error(f"记录失败日志失败: {str(log_error)}")
- return False
-
- # def update_batch_goods_in_location_qty(self,container_code,taskworking):
- # """
- # 更新批次库位入库数量
- # :param container_code: 托盘码
- # :param goods_in_location_qty: 库位入库数量
- # :return:
- # """
- # try:
- # # 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,is_delete=False
- # ).exclude(status=3).all()
- # if not container_detail:
- # print (f"托盘 {container_code} 未组盘")
- # logger.error(f"托盘 {container_code} 未组盘_from update_batch_goods_in_location_qty")
- # return None
- # for item in container_detail:
- # if item.goods_class == 2:
- # continue
- # item.batch.goods_in_location_qty += item.goods_qty * taskworking
- # item.batch.save()
- # 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,is_delete=False
- ).exclude(status=3).first()
- if not container_detail:
- print (f"托盘 {container_code} 未组盘")
- logger.error(f"托盘 {container_code} 未组盘_from get_batch_status")
- return None
- 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,is_delete=False
- ).exclude(status=3).first()
- if not container_detail:
- print (f"托盘 {container_code} 未组盘")
- logger.error(f"托盘 {container_code} 未组盘_from get_batch")
- return None
- batch = container_detail.batch.bound_number
- return batch
-
- def get_location_list_remainder(self, location_group_list,container_code):
- """
- 获取可用库位的c_number列表
- :param location_list: 库位对象列表
- :return: 可用库位编号列表
- """
- if not location_group_list:
- return None
- min_c_number=1000
- min_c_number_index=1000
-
- current_task = self.get_current_finish_task(container_code)
- print(f"[1]当前已完成任务: {current_task}")
- # 按压力排序
- sorted_pressure = sorted(
- [(0, current_task[0]), (1, current_task[1]), (2, current_task[2])],
- key=lambda x: (-x[1], x[0])
- )
- # 交换第一和第二个元素的位置
- sorted_pressure[0], sorted_pressure[1] = sorted_pressure[1], sorted_pressure[0]
- print(f"[2]任务排序: {sorted_pressure}")
- print(f"[3]当前选择:{sorted_pressure[0][0]+1}")
- location_type_dict = json.loads(self.divide_solution_by_layer(location_group_list))
- # print(f"库位类型分配方案: {location_type_dict}")
-
- for layer, _ in sorted_pressure:
- if not location_type_dict.get(str(layer+1)):
- continue
- # print(f"当前层: {layer+1}")
- # print(f"当前层库位组: {location_type_dict[str(layer+1)].keys()}")
- for group_id in location_type_dict[str(layer+1)].keys():
- location_group = LocationGroupModel.objects.filter(
- id=group_id,
- ).first()
- if not location_group:
- continue
- location_list = location_group.location_items.filter(
- status='available'
- ).all().order_by('c_number')
- if not location_list:
- print(f"当前层库位组 {location_group.group_code} 可用库位: None")
- continue
- # 提取所有库位的 c_number
- c_numbers = [loc.c_number for loc in location_list]
- print(f"当前层库位组 {location_group.group_code} 可用库位: {c_numbers}")
- # 更新任务完成数目
- current_task[layer] = current_task[layer] + 1
- self.update_current_finish_task(container_code,current_task)
- return location_list[0]
-
- def get_location_type(self, container_code, request=None):
- """
- 智能库位分配核心算法
- :param container_code: 托盘码
- :param request: 请求对象(可选)
- :return: 库位类型分配方案
- """
- try:
- batch = self.get_batch(container_code)
- if not batch:
- logger.error("批次信息获取失败")
- # 记录失败日志
- try:
- if request:
- log_failure_operation(
- request=request,
- operation_content=f"库位分配算法执行失败,托盘编码: {container_code}, 错误: 批次信息获取失败",
- operation_level="other",
- module_name="库位分配算法"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"库位分配算法执行失败,托盘编码: {container_code}, 错误: 批次信息获取失败",
- operation_level="other",
- operation_result="failure",
- module_name="库位分配算法"
- )
- except Exception as log_error:
- logger.error(f"记录算法失败日志失败: {str(log_error)}")
- return None
- # 检查已有分配方案
- existing_solution = alloction_pre.objects.filter(batch_number=batch).first()
- if existing_solution:
- # 记录使用已有分配方案
- try:
- if request:
- log_operation(
- request=request,
- operation_content=f"使用已有库位分配方案,托盘编码: {container_code}, 批次号: {batch}",
- operation_level="view",
- module_name="库位分配算法"
- )
- except Exception as log_error:
- logger.error(f"记录使用已有方案日志失败: {str(log_error)}")
- return existing_solution.layer_pre_type
- # 获取关键参数
- total_pallets = self.get_pallet_count_by_batch(container_code)
- layer_capacity = self.get_left_locationGroup_number_by_type()
- current_pressure = self.get_current_pressure()
- # 测试参数
- # total_pallets = 30
- # layer_capacity = [{'T1': 29, 'T2': 14, 'S4': 10, 'T4': 27, 'T5': 27}, {'T1': 0, 'T2': 0, 'S4': 0, 'T4': 0, 'T5': 21}, {'T1': 29, 'T2': 14, 'S4': 10, 'T4': 27, 'T5': 27}]
- # current_pressure = [1,0,0]
- print(f"[1]托盘数目: {total_pallets}")
- print(f"[2]层容量: {layer_capacity}")
- # print(f"[3]当前压力: {current_pressure}")
- # 定义库位容量表
- LOCATION_CAPACITY = {'T1':1, 'T2':2, 'T4':4, 'S4':4, 'T5':5}
-
- def allocate(remain, path, pressure,real_pressure,layer_capacity_state, depth=0):
- # 终止条件
- if remain <= 0:
- return [path,real_pressure]
- # 深拷贝当前层容量状态
- new_layer_capacity = copy.deepcopy(layer_capacity_state)
- # print(f"[2]当前剩余: {new_layer_capacity}")
- # 压力平衡系数
- balance_factor = 1.0 - (0.1 * min(depth, 5))
- # 层选择策略
- print (f"[3]当前压力: {pressure}")
- layer_priority = sorted(
- [(0, pressure[0]), (1, pressure[1]), (2, pressure[2])],
- key=lambda x: (x[1] * balance_factor, x[0])
- )
- for layer, _ in layer_priority:
- # 生成候选库位类型(按效率和容量排序)
- # 排序键函数 :
- # min(x[1], remain) 计算当前库位类型的容量 c 和剩余数量 remain 中的较小值。
- # -min(x[1], remain) 和 -x[1] 都使用了负号,这意味着排序是按降序进行的。
- # 首先按 -min(x[1], remain) 排序,即优先选择容量与剩余数量更接近的库位类型。
- # 如果有多个库位类型的容量与剩余数量相同,则按 -x[1] 排序,即优先选择容量更大的库位类型。
- print(f"[4]当前层: {layer+1}, 剩余: {remain}, 容量状态: {new_layer_capacity[layer]}")
- candidates = sorted(
- [(t, c) for t, c in LOCATION_CAPACITY.items()
- if new_layer_capacity[layer].get(t,0) > 0],
- key=lambda x: (abs(x[1]-remain), -x[1])
- )
- print(f"[4]候选库位类型: {candidates}")
- for loc_type, cap in candidates:
- # 更新容量状态
- updated_capacity = copy.deepcopy(new_layer_capacity)
- updated_capacity[layer][loc_type] -= 1 # 占用一个库位组
- # 允许适度空间浪费(当剩余<2时)
- # effective_cap = min(cap, remain) if (cap - remain) < 2 else cap
- effective_cap = min(cap, remain)
-
- if effective_cap <= remain:
- new_remain = remain - effective_cap
- new_pressure = pressure.copy()
- for i in range(0, 3):
- new_pressure[i] -=1 if new_pressure[i] > 0 else 0
- new_pressure[layer] += effective_cap # 按实际存放数计算压力,此时别的楼层压力可能降下来了
- real_pressure[layer] += effective_cap # 实际压力
- result = allocate(
- new_remain,
- path + [f"{layer+1}_{loc_type}"],
- new_pressure,
- real_pressure,
- updated_capacity,
- depth + 1
- )
- if result:
- print (f"[5]分配方案: {result}")
- return result
- return None
- # 执行分配
- allocation = allocate(total_pallets, [], [current_pressure[0], current_pressure[1],current_pressure[2]],[current_pressure[0], current_pressure[1],current_pressure[2]], layer_capacity)
-
- if not allocation:
- logger.error("无法生成有效分配方案")
- # 记录失败日志
- try:
- if request:
- log_failure_operation(
- request=request,
- operation_content=f"库位分配算法执行失败,托盘编码: {container_code}, 批次号: {batch}, 托盘数: {total_pallets}, 错误: 无法生成有效分配方案",
- operation_level="other",
- module_name="库位分配算法"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"库位分配算法执行失败,托盘编码: {container_code}, 批次号: {batch}, 托盘数: {total_pallets}, 错误: 无法生成有效分配方案",
- operation_level="other",
- operation_result="failure",
- module_name="库位分配算法"
- )
- except Exception as log_error:
- logger.error(f"记录算法失败日志失败: {str(log_error)}")
- return None
- # 保存分配方案
- allocation_json = self.divide_solution_by_layer(allocation[0])
- print(f"[6]分配方案: {allocation_json}")
- solution = alloction_pre(
- batch_number=batch,
- layer_pre_type =allocation_json
- )
- solution_pressure, created = base_location.objects.get_or_create(
- id=1,
- defaults={
- 'layer1_pressure': 0,
- 'layer2_pressure': 0,
- 'layer3_pressure': 0
- }
- )
- solution_pressure.layer1_pressure = allocation[1][0]
- solution_pressure.layer2_pressure = allocation[1][1]
- solution_pressure.layer3_pressure = allocation[1][2]
- solution.save()
- solution_pressure.save()
- # 记录算法执行成功日志(关键操作)
- try:
- allocation_summary = f"批次号: {batch}, 托盘数: {total_pallets}, 分配方案: {len(allocation[0])}个库位组, 压力分布: L1={allocation[1][0]}, L2={allocation[1][1]}, L3={allocation[1][2]}"
- if request:
- log_success_operation(
- request=request,
- operation_content=f"库位分配算法执行成功,托盘编码: {container_code}, {allocation_summary}",
- operation_level="other",
- module_name="库位分配算法"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"库位分配算法执行成功,托盘编码: {container_code}, {allocation_summary}",
- operation_level="other",
- operation_result="success",
- module_name="库位分配算法"
- )
- except Exception as log_error:
- logger.error(f"记录算法成功日志失败: {str(log_error)}")
- return [loc.split('_')[1] for loc in allocation[0]]
- except Exception as e:
- logger.error(f"分配算法异常:{str(e)}")
- # 记录异常日志
- try:
- if request:
- log_failure_operation(
- request=request,
- operation_content=f"库位分配算法执行异常,托盘编码: {container_code}, 错误: {str(e)}",
- operation_level="other",
- module_name="库位分配算法"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"库位分配算法执行异常,托盘编码: {container_code}, 错误: {str(e)}",
- operation_level="other",
- operation_result="failure",
- module_name="库位分配算法"
- )
- except Exception as log_error:
- logger.error(f"记录算法异常日志失败: {str(log_error)}")
- return None
- def divide_solution_by_layer(self, data):
- # 统计所有存在的层级
- layer_counts = defaultdict(lambda: defaultdict(int))
- existing_layers = set()
-
- for item in data:
- # 分割层级和类型
- try:
- layer, loc_type = item.split('_')
- layer_num = int(layer)
- existing_layers.add(layer_num)
- layer_counts[layer_num][loc_type] += 1
- except (ValueError, IndexError):
- continue # 跳过无效格式的数据
- # 确定最大层级(至少包含1层)
- max_layer = max(existing_layers) if existing_layers else 1
- # 构建包含所有层级的最终结果
- final_result = {}
- for layer in range(1, max_layer + 1):
- final_result[str(layer)] = dict(layer_counts.get(layer, {}))
-
- return json.dumps(final_result, indent=2)
- def get_current_pressure(self):
- """获取实时工作压力"""
- last_solution = base_location.objects.order_by('-id').first()
- if not last_solution:
- base_location.objects.create(
- layer1_pressure=0,
- layer2_pressure=0,
- layer3_pressure=0,
- ).save()
- return [
- last_solution.layer1_pressure if last_solution else 0,
- last_solution.layer2_pressure if last_solution else 0,
- last_solution.layer3_pressure if last_solution else 0,
- ]
- def get_current_finish_task(self,container):
- batch = self.get_batch(container)
- if not batch:
- return None
- solution = alloction_pre.objects.filter(batch_number=batch).first()
- if not solution:
- return None
- return [solution.layer1_task_finish_number,solution.layer2_task_finish_number,solution.layer3_task_finish_number]
- def update_current_finish_task(self,container,task_finish_number):
- batch = self.get_batch(container)
- if not batch:
- return None
- solution = alloction_pre.objects.filter(batch_number=batch).first()
- if not solution:
- return None
- solution.layer1_task_finish_number = task_finish_number[0]
- solution.layer2_task_finish_number = task_finish_number[1]
- solution.layer3_task_finish_number = task_finish_number[2]
- solution.save()
- return True
-
- def get_location_by_type(self, location_type_list, start_location, container_code):
- """
- 根据库位类型获取库位,先根据工作压力找出最空闲的层,看下这层有没有工作,如果有,就从里面找,如果没有,则跳过该层,继续找下一层
- :param location_type_list: 库位分配方案
- {
- "1": {
- "S4": 1
- },
- "2": {},
- "3": {
- "T5": 1
- }
- }
- :param start_location: 起始位置(决定优先级排序方式)
- :return: 符合条件的库位列表
- """
- locations = []
- # 检查已有分配方案
- existing_solution = alloction_pre.objects.filter(batch_number=self.get_batch(container_code)).first()
- if existing_solution.layer_solution_type:
- print(f"[0]已有库位分配方案:{existing_solution.layer_solution_type}")
- return existing_solution.layer_solution_type
- for layer, location_type_dict in location_type_list.items():
- if not location_type_dict:
- continue
- # 获取库位类型列表
- location_type = list(location_type_dict.keys())
- demand_number = sum(location_type_dict.values())
- print (f"[1]层{layer} 需求数量: {demand_number}, 库位: {location_type}")
- location_groups = LocationGroupModel.objects.filter(
- group_type__in=location_type,
- layer=layer,
- status='available'
- )
- if not location_groups:
- print(f"层{layer} 无库位")
- # 根据起始位置选择排序字段
- if start_location == '203':
- ordered_groups = location_groups.order_by('left_priority')
- elif start_location == '103':
- ordered_groups = location_groups.order_by('right_priority')
- else:
- ordered_groups = location_groups.none()
- number = 0
- for location_group in ordered_groups:
- if number >= demand_number:
- break
- locations.append(f"{layer}_{location_group.id}")
- number += 1
-
- existing_solution.layer_solution_type = locations
- existing_solution.save()
- print(f"[2]分配方案: {locations}")
- return locations if locations else None
-
- def get_location_by_status(self,container_code,start_location, request=None):
- """
- 根据库位状态获取库位
- :param container_code: 托盘编码
- :param start_location: 起始库位 if in1 优先考虑left_priority, if in2 优先考虑right_priority 就是获取库位组列表之后进行排序
- :param request: 请求对象(可选)
- :return: 库位列表
- """
- # 1. 获取批次状态 1 为已组盘 2 为部分入库 3 为全部入库
- status = self.get_batch_status(container_code)
- #
- if status == 1:
- # 2. 获取库位组
- print(f"[1]第一次入库")
- # 重新获取最新数据
- self.get_location_type(container_code, request)
-
- location_type_list = json.loads(alloction_pre.objects.filter(batch_number=self.get_batch(container_code)).first().layer_pre_type)
- location_list = self.get_location_by_type(location_type_list,start_location,container_code)
- # 预定这些库组
- self.update_group_status_reserved(location_list, request)
- location_min_value = self.get_location_list_remainder(location_list,container_code)
- print(f"库位安排到第{location_min_value.c_number}个库位:{location_min_value}")
- # 记录库位分配成功日志
- try:
- if request:
- log_success_operation(
- request=request,
- operation_content=f"库位分配成功(第一次入库),托盘编码: {container_code}, 起始位置: {start_location}, 分配库位: {location_min_value.location_code}, 库位组: {location_min_value.location_group}",
- operation_level="other",
- object_id=str(location_min_value.id),
- module_name="库位分配算法"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"库位分配成功(第一次入库),托盘编码: {container_code}, 起始位置: {start_location}, 分配库位: {location_min_value.location_code}, 库位组: {location_min_value.location_group}",
- operation_level="other",
- operation_result="success",
- object_id=str(location_min_value.id),
- module_name="库位分配算法"
- )
- except Exception as log_error:
- logger.error(f"记录库位分配日志失败: {str(log_error)}")
- return location_min_value
-
- elif status == 2:
- # 3. 获取部分入库库位
- print (f"部分入库")
- location_list = alloction_pre.objects.filter(batch_number=self.get_batch(container_code)).first().layer_solution_type
- location_min_value = self.get_location_list_remainder(location_list,container_code)
- print(f"库位安排到第{location_min_value.c_number}个库位:{location_min_value}")
- # 记录库位分配成功日志
- try:
- if request:
- log_success_operation(
- request=request,
- operation_content=f"库位分配成功(部分入库),托盘编码: {container_code}, 分配库位: {location_min_value.location_code}, 库位组: {location_min_value.location_group}",
- operation_level="other",
- object_id=str(location_min_value.id),
- module_name="库位分配算法"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"库位分配成功(部分入库),托盘编码: {container_code}, 分配库位: {location_min_value.location_code}, 库位组: {location_min_value.location_group}",
- operation_level="other",
- operation_result="success",
- object_id=str(location_min_value.id),
- module_name="库位分配算法"
- )
- except Exception as log_error:
- logger.error(f"记录库位分配日志失败: {str(log_error)}")
- return location_min_value
- def release_location(self, location_code, request=None):
- """释放库位并更新关联数据"""
- try:
- location = LocationModel.objects.get(location_code=location_code)
- links = LocationContainerLink.objects.filter(location=location, is_active=True).first()
- if not links:
- logger.error(f"库位{location_code}未关联托盘")
- return True
- container_id = links.container_id
- print(f"释放库位: {location_code}, 关联托盘: {container_id}")
- # 解除关联并标记为非活跃
- links.is_active = False
- links.save()
- # 更新库位状态为可用
- location.status = 'available'
- location.save()
- # 记录释放库位操作日志(敏感操作)
- try:
- if request:
- log_success_operation(
- request=request,
- operation_content=f"释放库位,库位编码: {location_code}, 托盘ID: {container_id}",
- operation_level="update",
- object_id=str(location.id),
- module_name="库位分配"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"释放库位,库位编码: {location_code}, 托盘ID: {container_id}",
- operation_level="update",
- operation_result="success",
- object_id=str(location.id),
- module_name="库位分配"
- )
- except Exception as log_error:
- logger.error(f"记录释放库位日志失败: {str(log_error)}")
- return True
- except Exception as e:
- logger.error(f"释放库位失败: {str(e)}")
- # 记录失败日志
- try:
- if request:
- log_failure_operation(
- request=request,
- operation_content=f"释放库位失败,库位编码: {location_code}, 错误: {str(e)}",
- operation_level="update",
- module_name="库位分配"
- )
- else:
- OperationLog.objects.create(
- operator="系统自动",
- operation_content=f"释放库位失败,库位编码: {location_code}, 错误: {str(e)}",
- operation_level="update",
- operation_result="failure",
- module_name="库位分配"
- )
- except Exception as log_error:
- logger.error(f"记录失败日志失败: {str(log_error)}")
- return False
-
-
-
|