views.py 33 KB

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