views.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  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 ContainerListModel,ContainerDetailModel,ContainerOperationModel,ContainerWCSModel,TaskModel
  14. from bound.models import BoundBatchModel,BoundDetailModel,BoundListModel
  15. # from .files import FileListRenderCN, FileDetailRenderCN
  16. from .serializers import ContainerDetailGetSerializer,ContainerDetailPostSerializer
  17. from .serializers import ContainerListGetSerializer,ContainerListPostSerializer
  18. from .serializers import ContainerOperationGetSerializer,ContainerOperationPostSerializer
  19. from .serializers import TaskGetSerializer,TaskPostSerializer
  20. from .filter import ContainerDetailFilter,ContainerListFilter,ContainerOperationFilter,TaskFilter
  21. # 以后添加模
  22. from warehouse.models import ListModel as warehouse
  23. from staff.models import ListModel as staff
  24. from rest_framework.permissions import AllowAny
  25. logger = logging.getLogger(__name__)
  26. class ContainerListViewSet(viewsets.ModelViewSet):
  27. """
  28. retrieve:
  29. Response a data list(get)
  30. list:
  31. Response a data list(all)
  32. create:
  33. Create a data line(post)
  34. delete:
  35. Delete a data line(delete)
  36. """
  37. # authentication_classes = [] # 禁用所有认证类
  38. # permission_classes = [AllowAny] # 允许任意访问
  39. pagination_class = MyPageNumberPagination
  40. filter_backends = [DjangoFilterBackend, OrderingFilter, ]
  41. ordering_fields = ['id', "create_time", "update_time", ]
  42. filter_class = ContainerListFilter
  43. def get_project(self):
  44. try:
  45. id = self.kwargs.get('pk')
  46. return id
  47. except:
  48. return None
  49. def get_queryset(self):
  50. id = self.get_project()
  51. if self.request.user:
  52. if id is None:
  53. return ContainerListModel.objects.filter()
  54. else:
  55. return ContainerListModel.objects.filter( id=id)
  56. else:
  57. return ContainerListModel.objects.none()
  58. def get_serializer_class(self):
  59. if self.action in ['list', 'destroy','retrieve']:
  60. return ContainerListGetSerializer
  61. elif self.action in ['create', 'update']:
  62. return ContainerListPostSerializer
  63. else:
  64. return self.http_method_not_allowed(request=self.request)
  65. def create(self, request, *args, **kwargs):
  66. data = self.request.data
  67. order_month = str(timezone.now().strftime('%Y%m'))
  68. data['month'] = order_month
  69. data['last_operate'] = str(timezone.now())
  70. serializer = self.get_serializer(data=data)
  71. serializer.is_valid(raise_exception=True)
  72. serializer.save()
  73. headers = self.get_success_headers(serializer.data)
  74. return Response(serializer.data, status=200, headers=headers)
  75. def update(self, request, pk):
  76. qs = self.get_object()
  77. data = self.request.data
  78. serializer = self.get_serializer(qs, data=data)
  79. serializer.is_valid(raise_exception=True)
  80. serializer.save()
  81. headers = self.get_success_headers(serializer.data)
  82. return Response(serializer.data, status=200, headers=headers)
  83. class TaskViewSet(viewsets.ModelViewSet):
  84. """
  85. retrieve:
  86. Response a data list(get)
  87. list:
  88. Response a data list(all)
  89. create:
  90. Create a data line(post)
  91. delete:
  92. Delete a data line(delete)
  93. """
  94. pagination_class = MyPageNumberPagination
  95. filter_backends = [DjangoFilterBackend, OrderingFilter, ]
  96. ordering_fields = ['id', "create_time", "update_time", ]
  97. filter_class = TaskFilter
  98. def get_project(self):
  99. try:
  100. id = self.kwargs.get('pk')
  101. return id
  102. except:
  103. return None
  104. def get_queryset(self):
  105. id = self.get_project()
  106. if self.request.user:
  107. if id is None:
  108. return TaskModel.objects.filter()
  109. else:
  110. return TaskModel.objects.filter( id=id)
  111. else:
  112. return TaskModel.objects.none()
  113. def get_serializer_class(self):
  114. if self.action in ['list', 'destroy','retrieve']:
  115. return TaskGetSerializer
  116. elif self.action in ['create', 'update']:
  117. return TaskPostSerializer
  118. else:
  119. return self.http_method_not_allowed(request=self.request)
  120. def create(self, request, *args, **kwargs):
  121. data = self.request.data
  122. return Response(data, status=200, headers=headers)
  123. def update(self, request, pk):
  124. qs = self.get_object()
  125. data = self.request.data
  126. serializer = self.get_serializer(qs, data=data)
  127. serializer.is_valid(raise_exception=True)
  128. serializer.save()
  129. headers = self.get_success_headers(serializer.data)
  130. return Response(serializer.data, status=200, headers=headers)
  131. class ContainerWCSViewSet(viewsets.ModelViewSet):
  132. """
  133. retrieve:
  134. Response a data list(get)
  135. list:
  136. Response a data list(all)
  137. create:
  138. Create a data line(post)
  139. delete:
  140. Delete a data line(delete)
  141. """
  142. authentication_classes = [] # 禁用所有认证类
  143. permission_classes = [AllowAny] # 允许任意访问
  144. def get_container_wcs(self, request, *args, **kwargs):
  145. data = self.request.data
  146. container = data.get('container_number')
  147. current_location = data.get('current_location')
  148. data_return = {}
  149. try:
  150. container_obj = ContainerListModel.objects.filter(container_code=container).first()
  151. if not container_obj:
  152. data_return = {
  153. 'code': '400',
  154. 'message': '托盘编码不存在',
  155. 'data': data
  156. }
  157. return Response(data_return, status=status.HTTP_400_BAD_REQUEST)
  158. # 更新容器数据(部分更新)
  159. serializer = ContainerListPostSerializer(
  160. container_obj,
  161. data=data,
  162. partial=True # 允许部分字段更新
  163. )
  164. serializer.is_valid(raise_exception=True)
  165. serializer.save()
  166. # 库位分配
  167. # 检查是否已在目标位置
  168. if current_location == str(container_obj.target_location):
  169. logger.info(f"托盘 {container} 已在目标位置")
  170. data_return = {
  171. 'code': '200',
  172. 'message': '当前位置已是目标位置',
  173. 'data': data
  174. }
  175. else:
  176. current_task = ContainerWCSModel.objects.filter(
  177. container=container,
  178. tasktype='inbound'
  179. ).first()
  180. if current_task:
  181. data_return = {
  182. 'code': '200',
  183. 'message': '任务已存在,重新下发',
  184. 'data': current_task.to_dict()
  185. }
  186. else:
  187. self.generate_task(container, current_location, container_obj.target_location)
  188. current_task = ContainerWCSModel.objects.get(
  189. container=container,
  190. tasktype='inbound'
  191. )
  192. data_return = {
  193. 'code': '200',
  194. 'message': '任务下发成功',
  195. 'data': current_task.to_dict()
  196. }
  197. self.inport_update_task(current_task.id, container_obj.id)
  198. http_status = status.HTTP_200_OK if data_return['code'] == '200' else status.HTTP_400_BAD_REQUEST
  199. return Response(data_return, status=http_status)
  200. except Exception as e:
  201. logger.error(f"处理请求时发生错误: {str(e)}", exc_info=True)
  202. return Response(
  203. {'code': '500', 'message': '服务器内部错误', 'data': None},
  204. status=status.HTTP_500_INTERNAL_SERVER_ERROR
  205. )
  206. @transaction.atomic
  207. def generate_task(self, container, current_location, target_location):
  208. data_tosave = {
  209. 'container': container,
  210. 'current_location': current_location,
  211. 'month': timezone.now().strftime('%Y%m'),
  212. 'target_location': target_location,
  213. 'tasktype': 'inbound',
  214. 'status': 103,
  215. 'is_delete': False
  216. }
  217. # 生成唯一递增的 taskid
  218. last_task = ContainerWCSModel.objects.filter(
  219. month=data_tosave['month'],
  220. tasktype='inbound'
  221. ).order_by('-taskid').first()
  222. if last_task:
  223. last_id = int(last_task.taskid.split('-')[-1])
  224. new_id = f"{last_id + 1:04}"
  225. else:
  226. new_id = "0001"
  227. data_tosave['taskid'] = f"inbound-{data_tosave['month']}-{new_id}"
  228. logger.info(f"生成入库任务: {data_tosave['taskid']}")
  229. # 每月生成唯一递增的 taskNumber
  230. data_tosave['tasknumber'] = f"{data_tosave['month']}{new_id}"
  231. ContainerWCSModel.objects.create(**data_tosave)
  232. @transaction.atomic
  233. def inport_update_task(self, wcs_id,container_id):
  234. try:
  235. task_obj = ContainerWCSModel.objects.filter(id=wcs_id).first()
  236. if task_obj:
  237. container_detail_obj = ContainerDetailModel.objects.filter(container=container_id).all()
  238. if container_detail_obj:
  239. for detail in container_detail_obj:
  240. # 保存到数据库
  241. batch = BoundDetailModel.objects.filter(bound_batch_id=detail.batch.id).first()
  242. TaskModel.objects.create(
  243. task_wcs = task_obj,
  244. container_detail = detail,
  245. batch_detail = batch
  246. )
  247. logger.info(f"入库任务 {wcs_id} 已更新")
  248. else:
  249. logger.info(f"入库任务 {container_id} 批次不存在")
  250. else:
  251. logger.info(f"入库任务 {wcs_id} 不存在")
  252. except Exception as e:
  253. logger.error(f"处理入库任务时发生错误: {str(e)}", exc_info=True)
  254. return Response(
  255. {'code': '500', 'message': '服务器内部错误', 'data': None},
  256. status=status.HTTP_500_INTERNAL_SERVER_ERROR
  257. )
  258. # PDA组盘入库 将扫描到的托盘编码和批次信息保存到数据库
  259. # 1. 先查询托盘对象,如果不存在,则创建托盘对象
  260. # 2. 循环处理每个批次,查询批次对象,
  261. # 3. 更新批次数据(根据业务规则)
  262. # 4. 保存到数据库
  263. # 5. 保存操作记录到数据库
  264. class ContainerDetailViewSet(viewsets.ModelViewSet):
  265. """
  266. retrieve:
  267. Response a data list(get)
  268. list:
  269. Response a data list(all)
  270. create:
  271. Create a data line(post)
  272. delete:
  273. Delete a data line(delete)
  274. """
  275. # authentication_classes = [] # 禁用所有认证类
  276. # permission_classes = [AllowAny] # 允许任意访问
  277. pagination_class = MyPageNumberPagination
  278. filter_backends = [DjangoFilterBackend, OrderingFilter, ]
  279. ordering_fields = ['id', "create_time", "update_time", ]
  280. filter_class = ContainerDetailFilter
  281. def get_project(self):
  282. try:
  283. id = self.kwargs.get('pk')
  284. return id
  285. except:
  286. return None
  287. def get_queryset(self):
  288. id = self.get_project()
  289. if self.request.user:
  290. if id is None:
  291. return ContainerDetailModel.objects.filter( is_delete=False)
  292. else:
  293. return ContainerDetailModel.objects.filter( id=id, is_delete=False)
  294. else:
  295. return ContainerDetailModel.objects.none()
  296. def get_serializer_class(self):
  297. if self.action in ['list', 'destroy','retrieve']:
  298. return ContainerDetailGetSerializer
  299. elif self.action in ['create', 'update']:
  300. return ContainerDetailPostSerializer
  301. else:
  302. return self.http_method_not_allowed(request=self.request)
  303. def create(self, request, *args, **kwargs):
  304. data = self.request.data
  305. order_month = str(timezone.now().strftime('%Y%m'))
  306. data['month'] = order_month
  307. container_code = data.get('container')
  308. batches = data.get('batches', []) # 确保有默认空列表
  309. print('扫描到的托盘编码', container_code)
  310. # 处理托盘对象
  311. container_obj = ContainerListModel.objects.filter(container_code=container_code).first()
  312. if container_obj:
  313. data['container'] = container_obj.id
  314. logger.info(f"托盘 {container_code} 已存在")
  315. else:
  316. logger.info(f"托盘 {container_code} 不存在,创建托盘对象")
  317. serializer_list = ContainerListPostSerializer(data={'container_code': container_code})
  318. serializer_list.is_valid(raise_exception=True)
  319. serializer_list.save()
  320. data['container'] = serializer_list.data.get('id')
  321. # 循环处理每个批次
  322. for batch in batches:
  323. bound_number = batch.get('goods_code')
  324. goods_qty = batch.get('goods_qty')
  325. # 查询商品对象
  326. bound_obj = BoundBatchModel.objects.filter(bound_number=bound_number).first()
  327. if not bound_obj:
  328. # 如果商品不存在,返回错误,这里暂时在程序中进行提醒,后续需要改为前端弹窗提醒
  329. logger.error(f"批次 {bound_number} 不存在")
  330. # 跳出此次循环
  331. continue
  332. # return Response({"error": f"商品编码 {bound_number} 不存在"}, status=400)
  333. # 3. 更新批次数据(根据业务规则)
  334. try:
  335. last_qty = bound_obj.goods_in_qty
  336. bound_obj.goods_in_qty += batch.get("goods_qty", 0)
  337. if bound_obj.goods_in_qty >= bound_obj.goods_qty:
  338. bound_obj.goods_in_qty = bound_obj.goods_qty
  339. bound_obj.status = 1 # 批次状态为组盘完成
  340. print('批次id',bound_obj.id)
  341. bound_detail_obj = BoundDetailModel.objects.filter(bound_batch=bound_obj.id).first()
  342. if bound_detail_obj:
  343. bound_detail_obj.status = 1
  344. bound_detail_obj.save()
  345. print('入库申请id',bound_detail_obj.bound_list_id)
  346. # 入库申请全部批次入库完成
  347. bound_batch_all = BoundDetailModel.objects.filter(bound_list=bound_detail_obj.bound_list_id).all()
  348. if bound_batch_all.count() == bound_batch_all.filter(status=1).count():
  349. bound_list_obj = BoundListModel.objects.filter(id=bound_detail_obj.bound_list_id).first()
  350. print('当前状态',bound_list_obj.bound_status)
  351. bound_list_obj.bound_status = 102
  352. print('更新状态',bound_list_obj.bound_status)
  353. bound_list_obj.save()
  354. print('入库申请全部批次组盘完成')
  355. else:
  356. print('入库申请部分批次组盘完成')
  357. else:
  358. bound_obj.status = 0
  359. bound_obj.save() # 保存到数据库
  360. # 创建托盘详情记录(每个批次独立)
  361. print('新增个数',bound_obj.goods_in_qty-last_qty)
  362. if bound_obj.goods_in_qty-last_qty == goods_qty:
  363. detail_data = {
  364. "container": data['container'], # 托盘ID
  365. "batch": bound_obj.id, # 外键关联批次
  366. "goods_code": bound_obj.goods_code,
  367. "goods_desc": bound_obj.goods_desc,
  368. "goods_qty": goods_qty,
  369. "goods_weight": bound_obj.goods_weight,
  370. "status": 1,
  371. "month": data['month'],
  372. "creater": data.get('creater', 'zl') # 默认值兜底
  373. }
  374. serializer = self.get_serializer(data=detail_data)
  375. serializer.is_valid(raise_exception=True)
  376. serializer.save() # 必须保存到数据库
  377. operate_data = {
  378. "month" : data['month'],
  379. "container": data['container'], # 托盘ID
  380. "operation_type" : 'container',
  381. "batch" : bound_obj.id, # 外键关联批次
  382. "goods_code": bound_obj.goods_code,
  383. "goods_desc": bound_obj.goods_desc,
  384. "goods_qty": goods_qty,
  385. "goods_weight": bound_obj.goods_weight,
  386. "operator": data.get('creater', 'zl'), # 默认值兜底
  387. "timestamp": timezone.now(),
  388. "from_location": "container",
  389. "to_location": "container",
  390. "memo": "入库PDA组盘,pda入库"+str(bound_obj.goods_code)+"数量"+str(goods_qty)
  391. }
  392. serializer_operate = ContainerOperationPostSerializer(data=operate_data)
  393. serializer_operate.is_valid(raise_exception=True)
  394. serializer_operate.save() # 必须保存到数据库
  395. elif bound_obj.goods_in_qty-last_qty > 0:
  396. print('批次数量不一致')
  397. detail_data = {
  398. "container": data['container'], # 托盘ID
  399. "batch": bound_obj.id, # 外键关联批次
  400. "goods_code": bound_obj.goods_code,
  401. "goods_desc": bound_obj.goods_desc,
  402. "goods_qty": bound_obj.goods_in_qty-last_qty,
  403. "goods_weight": bound_obj.goods_weight,
  404. "status": 1,
  405. "month": data['month'],
  406. "creater": data.get('creater', 'zl') # 默认值兜底
  407. }
  408. serializer = self.get_serializer(data=detail_data)
  409. serializer.is_valid(raise_exception=True)
  410. serializer.save() # 必须保存到数据库
  411. operate_data = {
  412. "month" : data['month'],
  413. "container": data['container'], # 托盘ID
  414. "operation_type" : 'container',
  415. "batch" : bound_obj.id, # 外键关联批次
  416. "goods_code": bound_obj.goods_code,
  417. "goods_desc": bound_obj.goods_desc,
  418. "goods_qty": bound_obj.goods_in_qty-last_qty,
  419. "goods_weight": bound_obj.goods_weight,
  420. "operator": data.get('creater', 'zl'), # 默认值兜底
  421. "timestamp": timezone.now(),
  422. "from_location": "container",
  423. "to_location": "container",
  424. "memo": "入库PDA组盘,(数量不一致)pda入库"+str(bound_obj.goods_code)+"数量"+str(goods_qty)
  425. }
  426. serializer_operate = ContainerOperationPostSerializer(data=operate_data)
  427. serializer_operate.is_valid(raise_exception=True)
  428. serializer_operate.save() # 必须保存到数据库
  429. else :
  430. print('重复组盘')
  431. except Exception as e:
  432. print(f"更新批次 {bound_number} 失败: {str(e)}")
  433. continue
  434. # 将处理后的数据返回(或根据业务需求保存到数据库)
  435. res_data={
  436. "code": "200",
  437. "msg": "Success Create",
  438. "data": data
  439. }
  440. return Response(res_data, status=200)
  441. def update(self, request, pk):
  442. qs = self.get_object()
  443. data = self.request.data
  444. serializer = self.get_serializer(qs, data=data)
  445. serializer.is_valid(raise_exception=True)
  446. serializer.save()
  447. headers = self.get_success_headers(serializer.data)
  448. return Response(serializer.data, status=200, headers=headers)
  449. class ContainerOperateViewSet(viewsets.ModelViewSet):
  450. """
  451. retrieve:
  452. Response a data list(get)
  453. list:
  454. Response a data list(all)
  455. create:
  456. Create a data line(post)
  457. delete:
  458. Delete a data line(delete)
  459. """
  460. # authentication_classes = [] # 禁用所有认证类
  461. # permission_classes = [AllowAny] # 允许任意访问
  462. pagination_class = MyPageNumberPagination
  463. filter_backends = [DjangoFilterBackend, OrderingFilter, ]
  464. ordering_fields = ['id', "timestamp" ]
  465. filter_class = ContainerOperationFilter
  466. def get_project(self):
  467. try:
  468. id = self.kwargs.get('pk')
  469. return id
  470. except:
  471. return None
  472. def get_queryset(self):
  473. id = self.get_project()
  474. if self.request.user:
  475. if id is None:
  476. return ContainerOperationModel.objects.filter( is_delete=False)
  477. else:
  478. return ContainerOperationModel.objects.filter( id=id, is_delete=False)
  479. else:
  480. return ContainerOperationModel.objects.none()
  481. def get_serializer_class(self):
  482. if self.action in ['list', 'destroy','retrieve']:
  483. return ContainerOperationGetSerializer
  484. elif self.action in ['create', 'update']:
  485. return ContainerOperationPostSerializer
  486. else:
  487. return self.http_method_not_allowed(request=self.request)
  488. def create(self, request, *args, **kwargs):
  489. data = self.request.data
  490. serializer = self.get_serializer(data=data)
  491. serializer.is_valid(raise_exception=True)
  492. serializer.save()
  493. headers = self.get_success_headers(serializer.data)
  494. return Response(serializer.data, status=200, headers=headers)
  495. def update(self, request, pk):
  496. qs = self.get_object()
  497. data = self.request.data
  498. serializer = self.get_serializer(qs, data=data)
  499. serializer.is_valid(raise_exception=True)
  500. serializer.save()
  501. headers = self.get_success_headers(serializer.data)
  502. return Response(serializer.data, status=200, headers=headers)