views.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. from rest_framework import viewsets
  2. from utils.page import MyPageNumberPagination
  3. from utils.datasolve import sumOfList, transportation_calculate
  4. from utils.md5 import Md5
  5. from rest_framework.filters import OrderingFilter
  6. from django_filters.rest_framework import DjangoFilterBackend
  7. from rest_framework.response import Response
  8. from rest_framework.exceptions import APIException
  9. from django.utils import timezone
  10. from django.db import transaction
  11. import logging
  12. from rest_framework import status
  13. from .models import DeviceModel,LocationModel,LocationGroupModel,LocationContainerLink,LocationChangeLog
  14. from bound.models import BoundBatchModel,BoundDetailModel,BoundListModel
  15. from .filter import DeviceFilter,LocationFilter,LocationContainerLinkFilter,LocationChangeLogFilter,LocationGroupFilter
  16. from .serializers import LocationListSerializer,LocationPostSerializer
  17. from .serializers import LocationGroupListSerializer,LocationGroupPostSerializer
  18. # 以后添加模块时,只需要在这里添加即可
  19. from rest_framework.permissions import AllowAny
  20. from container.models import ContainerListModel,ContainerDetailModel,ContainerOperationModel,TaskModel
  21. logger = logging.getLogger(__name__)
  22. # 库位分配
  23. # 入库规则函数
  24. # 逻辑根据批次下的托盘数目来找满足区间范围的库位,按照优先级排序,
  25. class LocationAllocation:
  26. # 入库规则函数
  27. # fun:get_pallet_count_by_batch: 根据托盘码查询批次下托盘总数
  28. # fun:get_location_type: 根据托盘数目获取库位类型
  29. # fun:updata_location_container_link: 更新库位和托盘的关联关系
  30. # fun:get_batch_status: 获取批次状态
  31. # fun:get_batch: 获取批次
  32. # fun:get_location_list_remainder: 获取可用库位的c_number列表
  33. # fun:get_min_list_index: 获取最小的库位
  34. # fun:get_location_by_type_remainder: 根据库位类型获取库位
  35. # fun:get_location_by_type: 第一次入库,根据库位类型获取库位
  36. # fun:get_location_by_status: 根据库位状态获取库位
  37. @transaction.atomic
  38. def get_pallet_count_by_batch(self, container_code):
  39. """
  40. 根据托盘码查询批次下托盘总数
  41. :param container_code: 要查询的托盘码
  42. :return: 所属批次下的托盘总数
  43. """
  44. # 1. 通过托盘码获取容器详情
  45. container = ContainerListModel.objects.filter(
  46. container_code=container_code
  47. ).first()
  48. if not container:
  49. logger.error(f"托盘 {container_code} 不存在")
  50. print(f"托盘 {container_code} 不存在")
  51. return None
  52. # 2. 获取关联的批次明细
  53. container_detail = ContainerDetailModel.objects.filter(
  54. container=container.id,
  55. status=1
  56. ).first()
  57. if not container_detail:
  58. print (f"容器 {container_code} 未组盘")
  59. logger.error(f"容器 {container_code} 未组盘")
  60. return None
  61. else:
  62. print (f"容器 {container_code} 已组盘")
  63. batch_container_count = ContainerDetailModel.objects.filter(
  64. batch = container_detail.batch.id,
  65. status = 1
  66. ).count()
  67. return batch_container_count
  68. def get_location_type(self,container_count):
  69. """
  70. 根据托盘数目获取库位类型
  71. :param container_count: 托盘数目
  72. :return: 库位类型
  73. """
  74. if container_count <= 1:
  75. return ["T1"]
  76. elif container_count <= 2:
  77. return ["T2"]
  78. elif container_count <= 4:
  79. return ["S4","T4"]
  80. else:
  81. return ["T5"]
  82. @transaction.atomic
  83. def updata_location_container_link(self,location_code,container_code):
  84. """
  85. 更新库位和托盘的关联关系
  86. :param location_code: 库位编码
  87. :param container_code: 托盘编码
  88. :return:
  89. """
  90. try:
  91. # 1. 获取库位和托盘的关联关系
  92. location = LocationModel.objects.filter(
  93. location_code=location_code
  94. ).first()
  95. container = ContainerListModel.objects.filter(
  96. container_code=container_code
  97. ).first()
  98. # 2. 如果库位和托盘的关联关系不存在,创建新的关联关系
  99. if not LocationContainerLink.objects.filter(location=location).exists():
  100. location_container_link = LocationContainerLink(
  101. location=location,
  102. container=container
  103. )
  104. location_container_link.save()
  105. print(f"更新库位和托盘的关联关系成功!")
  106. # 3. 更新库位和托盘的关联关系
  107. else:
  108. LocationContainerLink.objects.filter(location=location).update(location=location, container=container)
  109. print(f"更新库位和托盘的关联关系成功!")
  110. return True
  111. except Exception as e:
  112. logger.error(f"更新库位和托盘的关联关系失败:{str(e)}")
  113. print(f"更新库位和托盘的关联关系失败:{str(e)}")
  114. return False
  115. def get_batch_status(self,container_code):
  116. """
  117. 获取批次状态
  118. :param container_code: 托盘码
  119. :return: 批次状态
  120. """
  121. # 1. 通过托盘码获取容器详情
  122. container = ContainerListModel.objects.filter(
  123. container_code=container_code
  124. ).first()
  125. if not container:
  126. logger.error(f"托盘 {container_code} 不存在")
  127. print(f"托盘 {container_code} 不存在")
  128. return None
  129. # 2. 获取关联的批次明细
  130. container_detail = ContainerDetailModel.objects.filter(
  131. container=container.id,
  132. status=1
  133. ).first()
  134. if not container_detail:
  135. print (f"容器 {container_code} 未组盘")
  136. logger.error(f"容器 {container_code} 未组盘")
  137. return None
  138. else:
  139. print (f"容器 {container_code} 已组盘")
  140. batch_status = container_detail.batch.status
  141. return batch_status
  142. def get_batch(self,container_code):
  143. """
  144. 获取批次
  145. :param container_code: 托盘码
  146. :return: 批次
  147. """
  148. # 1. 通过托盘码获取容器详情
  149. container = ContainerListModel.objects.filter(
  150. container_code=container_code
  151. ).first()
  152. if not container:
  153. logger.error(f"托盘 {container_code} 不存在")
  154. print(f"托盘 {container_code} 不存在")
  155. return None
  156. # 2. 获取关联的批次明细
  157. container_detail = ContainerDetailModel.objects.filter(
  158. container=container.id,
  159. status=1
  160. ).first()
  161. if not container_detail:
  162. print (f"容器 {container_code} 未组盘")
  163. logger.error(f"容器 {container_code} 未组盘")
  164. return None
  165. else:
  166. print (f"容器 {container_code} 已组盘")
  167. batch = container_detail.batch.bound_number
  168. return batch
  169. @transaction.atomic
  170. def get_location_list_remainder(self, location_list):
  171. """
  172. 获取可用库位的c_number列表
  173. :param location_list: 库位对象列表
  174. :return: 可用库位编号列表
  175. """
  176. if not location_list:
  177. return None
  178. available_c_numbers = [
  179. loc.c_number for loc in location_list
  180. if loc.status == 'available'
  181. ]
  182. return available_c_numbers if available_c_numbers else None
  183. def get_min_list_index(self,list):
  184. """
  185. 获取最小的库位
  186. :param list: 库位列表
  187. :return: 最小库位
  188. """
  189. if not list:
  190. return None
  191. min_index = 0
  192. for i in range(len(list)):
  193. if list[i] < list[min_index]:
  194. min_index = i
  195. return min_index
  196. @transaction.atomic
  197. def get_location_by_type_remainder(self, batch, layer):
  198. """
  199. 根据库位类型获取库位
  200. :param batch: 批次号
  201. :param layer: 层数限制
  202. :return: 符合条件的库位列表
  203. """
  204. # 获取符合条件的库位组并按优先级排序
  205. location_groups = LocationGroupModel.objects.filter(
  206. current_batch=batch,
  207. layer=layer,
  208. ).first()
  209. if not location_groups:
  210. return None
  211. # 合并所有库位组中的库位
  212. locations = []
  213. locations.extend(location_groups.location_items.all()) # 假设location_items是关联字段
  214. return locations if locations else None
  215. @transaction.atomic
  216. def get_location_by_type(self, location_type_list, start_location, layer):
  217. """
  218. 第一次入库,根据库位类型获取库位
  219. :param location_type_list: 库位类型列表
  220. :param start_location: 起始位置(决定优先级排序方式)
  221. :param layer: 层数限制
  222. :return: 符合条件的库位列表
  223. """
  224. # 获取符合条件的库位组并按优先级排序
  225. location_groups = LocationGroupModel.objects.filter(
  226. group_type__in=location_type_list,
  227. layer=layer,
  228. status='available'
  229. )
  230. # 根据起始位置选择排序字段
  231. if start_location == 'in1':
  232. ordered_groups = location_groups.order_by('left_priority')
  233. elif start_location == 'in2':
  234. ordered_groups = location_groups.order_by('right_priority')
  235. else:
  236. ordered_groups = location_groups.none()
  237. # 合并所有库位组中的库位
  238. locations = []
  239. locations.extend(ordered_groups.first().location_items.all()) # 假设location_items是关联字段
  240. return locations if locations else None
  241. @transaction.atomic
  242. def get_location_by_status(self,container_code,start_location,layer):
  243. """
  244. 根据库位状态获取库位
  245. :param location_type: 库位类型
  246. :param start_location: 起始库位 if in1 优先考虑left_priority, if in2 优先考虑right_priority 就是获取库位组列表之后进行排序
  247. :param layer: 层数 限定层数
  248. :return: 库位列表
  249. """
  250. # 1. 获取批次状态 102 为已组盘 103 为部分入库 104 为全部入库
  251. status = self.get_batch_status(container_code)
  252. #
  253. if status == 1:
  254. # 2. 获取库位组
  255. print (f"第一次入库")
  256. container_number = self.get_pallet_count_by_batch(container_code)
  257. print (f"该批次下托盘总数:{container_number}")
  258. location_type_list = self.get_location_type(container_number)
  259. print(f"库位类型:{location_type_list}")
  260. location_list = self.get_location_by_type(location_type_list,start_location,layer)
  261. print(f"库位列表:{location_list}")
  262. location_list_remainder = self.get_location_list_remainder(location_list)
  263. print(f"库位列表剩余:{location_list_remainder}")
  264. if not location_list_remainder:
  265. # 库位已满,返回None
  266. return None
  267. else:
  268. location_index = self.get_min_list_index(location_list_remainder)
  269. # 返回和托盘关联的库位、库位剩余、库位类型
  270. return location_list[location_index]
  271. elif status == 2:
  272. print(f"部分入库")
  273. # 2. 获取库位组
  274. location_list = self.get_location_by_type_remainder(self.get_batch(container_code),layer)
  275. location_list_remainder = self.get_location_list_remainder(location_list)
  276. if not location_list_remainder:
  277. # 库位已满,返回None
  278. return None
  279. else:
  280. location_index = self.get_min_list_index(location_list_remainder)
  281. # 返回和托盘关联的库位、库位剩余、库位类型
  282. return location_list[location_index]
  283. class locationViewSet(viewsets.ModelViewSet):
  284. """
  285. retrieve:
  286. Response a data list(get)
  287. list:
  288. Response a data list(all)
  289. create:
  290. Create a data line(post)
  291. delete:
  292. Delete a data line(delete)
  293. """
  294. # authentication_classes = [] # 禁用所有认证类
  295. # permission_classes = [AllowAny] # 允许任意访问
  296. pagination_class = MyPageNumberPagination
  297. filter_backends = [DjangoFilterBackend, OrderingFilter, ]
  298. ordering_fields = ['id', "create_time", "update_time", ]
  299. filter_class = LocationFilter
  300. def get_project(self):
  301. try:
  302. id = self.kwargs.get('pk')
  303. return id
  304. except:
  305. return None
  306. def get_queryset(self):
  307. id = self.get_project()
  308. if self.request.user:
  309. if id is None:
  310. return LocationModel.objects.filter()
  311. else:
  312. return LocationModel.objects.filter( id=id)
  313. else:
  314. return LocationModel.objects.none()
  315. def get_serializer_class(self):
  316. if self.action == 'list':
  317. return LocationListSerializer
  318. elif self.action == 'update':
  319. return LocationPostSerializer
  320. elif self.action =='retrieve':
  321. return LocationListSerializer
  322. def update(self, request, *args, **kwargs):
  323. qs = self.get_object()
  324. data = self.request.data
  325. location_code = data.get('location_code')
  326. # 处理库位对象
  327. location_obj = LocationModel.objects.filter(location_code=location_code).first()
  328. if not location_obj:
  329. logger.info(f"库位 {location_code} 不存在")
  330. return Response(
  331. {'code': '400', 'message': '库位不存在', 'data': None},
  332. status=status.HTTP_400_BAD_REQUEST
  333. )
  334. else:
  335. data['id'] = location_obj.id
  336. logger.info(f"库位 {location_code} 已存在")
  337. serializer = self.get_serializer(qs, data=data)
  338. serializer.is_valid(raise_exception=True)
  339. serializer.save()
  340. headers = self.get_success_headers(serializer.data)
  341. self.handle_group_location_status(location_code,location_obj.location_group)
  342. return Response(serializer.data, status=200, headers=headers)
  343. def handle_group_location_status(self,location_code,location_group):
  344. """
  345. 处理库位组和库位的关联关系
  346. :param location_code: 库位编码
  347. :param location_group: 库位组编码
  348. :return:
  349. """
  350. # 1. 获取库位空闲状态的库位数目
  351. location_obj_number = LocationModel.objects.filter(
  352. location_group=location_group,
  353. status='available'
  354. ).all().count()
  355. # 2. 获取库位组对象
  356. logger.info(f"库位组 {location_group} 下的库位数目:{location_obj_number}")
  357. # 1. 获取库位和库位组的关联关系
  358. location_group_obj = LocationGroupModel.objects.filter(
  359. group_code=location_group
  360. ).first()
  361. if not location_group_obj:
  362. logger.info(f"库位组 {location_group} 不存在")
  363. return None
  364. else:
  365. if location_obj_number == 0:
  366. # 库位组库位已满,更新库位组状态为full
  367. location_group_obj.status = 'full'
  368. location_group_obj.save()
  369. elif location_obj_number < location_group_obj.max_capacity:
  370. location_group_obj.status = 'occupied'
  371. location_group_obj.save()
  372. else:
  373. location_group_obj.status = 'available'
  374. location_group_obj.save()
  375. class locationGroupViewSet(viewsets.ModelViewSet):
  376. """
  377. retrieve:
  378. Response a data list(get)
  379. list:
  380. Response a data list(all)
  381. create:
  382. Create a data line(post)
  383. delete:
  384. Delete a data line(delete)
  385. """
  386. # authentication_classes = [] # 禁用所有认证类
  387. # permission_classes = [AllowAny] # 允许任意访问
  388. pagination_class = MyPageNumberPagination
  389. filter_backends = [DjangoFilterBackend, OrderingFilter, ]
  390. ordering_fields = ['id', "create_time", "update_time", ]
  391. filter_class = LocationGroupFilter
  392. def get_project(self):
  393. try:
  394. id = self.kwargs.get('pk')
  395. return id
  396. except:
  397. return None
  398. def get_queryset(self):
  399. id = self.get_project()
  400. if self.request.user:
  401. if id is None:
  402. return LocationGroupModel.objects.filter()
  403. else:
  404. return LocationGroupModel.objects.filter(id=id)
  405. else:
  406. return LocationGroupModel.objects.none()
  407. def get_serializer_class(self):
  408. if self.action == 'list':
  409. return LocationGroupListSerializer
  410. elif self.action == 'update':
  411. return LocationGroupPostSerializer
  412. elif self.action =='retrieve':
  413. return LocationGroupListSerializer
  414. def update(self, request, *args, **kwargs):
  415. data = self.request.data
  416. order_month = str(timezone.now().strftime('%Y%m'))
  417. data['month'] = order_month
  418. group_code = data.get('group_code')
  419. # 处理库位组对象
  420. group_obj = LocationGroupModel.objects.filter(group_code=group_code).first()
  421. if group_obj:
  422. data['id'] = group_obj.id
  423. logger.info(f"库位组 {group_code} 已存在")
  424. else:
  425. logger.info(f"库位组 {group_code} 不存在,创建库位组对象")
  426. serializer_list = LocationGroupPostSerializer(data=data)
  427. serializer_list.is_valid(raise_exception=True)
  428. serializer_list.save()
  429. data['id'] = serializer_list.data.get('id')
  430. return Response(data, status=status.HTTP_201_CREATED)