container_operate.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. from django.db import transaction
  2. from rest_framework.response import Response
  3. from rest_framework.views import APIView
  4. from django.utils import timezone
  5. from .models import ContainerListModel,ContainerDetailModel,ContainerOperationModel,TaskModel,ContainerWCSModel
  6. from bound.models import BoundBatchModel,BoundDetailModel,BoundListModel
  7. from .serializers import *
  8. class ContainerService:
  9. """
  10. 托盘管理服务
  11. function:
  12. create_container_operation: 创建托盘操作记录
  13. """
  14. @staticmethod
  15. @transaction.atomic
  16. def create_container_operation(data, logger):
  17. # 初始化基础数据
  18. data['month'] = timezone.now().strftime('%Y%m')
  19. # 处理容器
  20. container = ContainerManager.get_or_create_container(
  21. container_code=data.get('container'),
  22. logger=logger
  23. )
  24. data['container_code'] = container.container_code
  25. data['container'] = container.id
  26. # 处理批次数据
  27. if data.get('goods_class') == 2:
  28. return BatchProcessor.handle_special_batches(data, logger)
  29. return BatchProcessor.handle_normal_batches(data, logger)
  30. class ContainerManager:
  31. @staticmethod
  32. def get_or_create_container(container_code, logger):
  33. """统一管理容器对象"""
  34. if container := ContainerListModel.objects.filter(container_code=container_code).first():
  35. logger.info(f"托盘 {container_code} 已存在")
  36. return container
  37. logger.info(f"创建新托盘 {container_code}")
  38. serializer = ContainerListPostSerializer(data={'container_code': container_code})
  39. serializer.status = 5
  40. serializer.is_valid(raise_exception=True)
  41. serializer.save()
  42. return ContainerListModel.objects.filter(container_code=container_code).first()
  43. class BatchProcessor:
  44. @staticmethod
  45. def handle_normal_batches(data, logger):
  46. """标准批次处理流程"""
  47. for batch in data.get('batches', []):
  48. try:
  49. BatchOperator.process_single_batch(data, batch, logger)
  50. except Exception as e:
  51. logger.error(f"批次处理失败: {str(e)}")
  52. continue
  53. return Response({"status": "success"})
  54. @staticmethod
  55. def handle_special_batches(data, logger):
  56. """托盘组处理流程"""
  57. for batch in data.get('batches', []):
  58. try:
  59. BatchOperator.process_single_container(data, batch, logger)
  60. except Exception as e:
  61. logger.error(f"托盘组处理失败: {str(e)}")
  62. continue
  63. return Response({"status": "success"})
  64. class BatchOperator:
  65. @staticmethod
  66. def process_single_container(data, batch, logger):
  67. """单个批次处理托盘"""
  68. # 获取批次对象
  69. container_obj = ContainerManager.get_or_create_container(
  70. batch.get('goods_code'),
  71. logger
  72. )
  73. # 更新批次状态
  74. update_result = BatchStatusUpdater.update_container_status(
  75. container_obj,
  76. 5,
  77. data.get('container_code'),
  78. )
  79. print(update_result)
  80. # 创建托盘关联记录
  81. RecordCreator.create_container_related_records(
  82. data=data,
  83. container_obj=container_obj
  84. )
  85. @staticmethod
  86. def process_single_batch(data, batch, logger):
  87. """单个批次处理批次"""
  88. # 获取批次对象
  89. bound_obj = BatchValidator.get_valid_batch(
  90. batch.get('goods_code'),
  91. logger
  92. )
  93. # 更新批次状态(新增数目)
  94. update_result = BatchStatusUpdater.update_status(
  95. bound_obj,
  96. batch['goods_qty'] * batch['goods_group'],
  97. logger
  98. )
  99. # 创建关联记录
  100. RecordCreator.create_related_records(
  101. data=data,
  102. bound_obj=bound_obj,
  103. batch_data= batch,
  104. qty_diff=update_result['added_qty'],
  105. logger=logger
  106. )
  107. class BatchValidator:
  108. @staticmethod
  109. def get_valid_batch(bound_number, logger):
  110. """批次校验模块"""
  111. if not (bound_obj := BoundBatchModel.objects.filter(bound_number=bound_number).first()):
  112. logger.error(f"批次 {bound_number} 不存在")
  113. raise ValueError("无效批次号")
  114. return bound_obj
  115. class BatchStatusUpdater:
  116. @staticmethod
  117. def update_status(bound_obj, added_qty, logger):
  118. """批次状态管理"""
  119. last_qty = bound_obj.goods_in_qty
  120. bound_obj.goods_in_qty += added_qty
  121. if bound_obj.goods_in_qty >= bound_obj.goods_qty:
  122. FullBatchHandler.mark_as_completed(bound_obj, logger)
  123. else:
  124. bound_obj.status = 0
  125. bound_obj.save()
  126. return {
  127. 'last_qty': last_qty,
  128. 'added_qty': added_qty
  129. }
  130. @staticmethod
  131. def update_container_status(container_obj, status, container_code):
  132. """批次状态管理"""
  133. container_obj.status = status
  134. container_obj.available = False
  135. container_obj.current_location = '托盘'+str(container_code)
  136. container_obj.save()
  137. return {
  138. 'last_qty': 1,
  139. 'added_qty': 1
  140. }
  141. class FullBatchHandler:
  142. @staticmethod
  143. def mark_as_completed(bound_obj, logger):
  144. """满批次处理"""
  145. # bound_obj.goods_in_qty = bound_obj.goods_qty
  146. bound_obj.status = 1
  147. BatchRelationUpdater.update_related_records(bound_obj, logger)
  148. class BatchRelationUpdater:
  149. @staticmethod
  150. def update_related_records(bound_obj, logger):
  151. """更新关联单据"""
  152. if detail := BoundDetailModel.objects.filter(bound_batch=bound_obj.id).first():
  153. detail.status = 1
  154. detail.save()
  155. CompletionChecker.check_list_completion(detail.bound_list_id, logger)
  156. class CompletionChecker:
  157. @staticmethod
  158. def check_list_completion(list_id, logger):
  159. """完成度检查"""
  160. completed = BoundDetailModel.objects.filter(bound_list=list_id, status=1).count()
  161. total = BoundDetailModel.objects.filter(bound_list=list_id).count()
  162. if completed == total:
  163. ListStatusUpdater.update_list_status(list_id, 102, logger)
  164. class ListStatusUpdater:
  165. @staticmethod
  166. def update_list_status(list_id, status, logger):
  167. """更新单据状态"""
  168. if list_obj := BoundListModel.objects.filter(id=list_id).first():
  169. list_obj.bound_status = status
  170. list_obj.save()
  171. logger.info("入库申请全部完成")
  172. class RecordCreator:
  173. @staticmethod
  174. def create_container_related_records(data, container_obj, qty_diff=1):
  175. """记录创建批次"""
  176. # 生成明细记录
  177. detail_data = RecordBuilder.build_detail_data_container_group(
  178. data=data,
  179. container_obj=container_obj,
  180. qty_diff=qty_diff
  181. )
  182. DetailRecordCreator.create_container_related_records(detail_data)
  183. # 生成操作记录
  184. operation_data = RecordBuilder.build_operation_data_container_group(
  185. detail_data=detail_data,
  186. qty_diff=qty_diff
  187. )
  188. OperationRecordCreator.create_container_related_operation_records(operation_data)
  189. @staticmethod
  190. def create_related_records(data, bound_obj, batch_data, qty_diff, logger):
  191. """记录创建批次"""
  192. # 生成明细记录
  193. goods_group = batch_data.get('goods_group')
  194. goods_qty = batch_data.get('goods_qty')
  195. for i in range(goods_group):
  196. detail_data = RecordBuilder.build_detail_data(
  197. data=data,
  198. bound_obj=bound_obj,
  199. qty_diff=goods_qty
  200. )
  201. DetailRecordCreator.create_record(detail_data)
  202. # 生成操作记录
  203. operation_data = RecordBuilder.build_operation_data(
  204. detail_data=detail_data,
  205. qty_diff=qty_diff
  206. )
  207. OperationRecordCreator.create_operation_record(operation_data)
  208. class RecordBuilder:
  209. @staticmethod
  210. def build_detail_data(data, bound_obj, qty_diff):
  211. """构建明细数据结构"""
  212. return {
  213. "container": data['container'],
  214. "batch": bound_obj.id,
  215. "goods_code": bound_obj.goods_code,
  216. "goods_desc": bound_obj.goods_desc,
  217. "goods_qty": qty_diff,
  218. "goods_weight": bound_obj.goods_weight,
  219. "status": 1,
  220. "month": data['month'],
  221. "creater": data.get('creater', 'wms')
  222. }
  223. @staticmethod
  224. def build_detail_data_container_group(data, container_obj, qty_diff=1):
  225. """构建明细数据结构"""
  226. return {
  227. "container": data['container'],
  228. "goods_code": container_obj.container_code,
  229. "goods_desc": '托盘组',
  230. "goods_qty": qty_diff,
  231. "goods_weight": 0,
  232. "status": 1,
  233. "month": data['month'],
  234. "creater": data.get('creater', 'wms')
  235. }
  236. @staticmethod
  237. def build_operation_data(detail_data, qty_diff):
  238. """构建操作记录数据"""
  239. return {
  240. "month": detail_data['month'],
  241. "container": detail_data['container'],
  242. "operation_type": 'container',
  243. "batch": detail_data['batch'],
  244. "goods_code": detail_data['goods_code'],
  245. "goods_desc": detail_data['goods_desc'],
  246. "goods_qty": qty_diff,
  247. "goods_weight": qty_diff,
  248. "operator": detail_data['creater'],
  249. "timestamp": timezone.now(),
  250. "from_location": "container",
  251. "to_location": "container",
  252. "memo": f"每组{detail_data['goods_code']}数目{detail_data['goods_qty']},PDA组盘共计组盘{qty_diff}"
  253. }
  254. def build_operation_data_container_group(detail_data, qty_diff):
  255. """构建操作记录数据"""
  256. return {
  257. "month": detail_data['month'],
  258. "container": detail_data['container'],
  259. "operation_type": 'container',
  260. "goods_code": detail_data['goods_code'],
  261. "goods_desc": detail_data['goods_desc'],
  262. "goods_qty": qty_diff,
  263. "goods_weight": detail_data['goods_weight'],
  264. "operator": detail_data['creater'],
  265. "timestamp": timezone.now(),
  266. "from_location": "container",
  267. "to_location": "container",
  268. "memo": f"托盘组下的:{detail_data['goods_code']}合盘"
  269. }
  270. class DetailRecordCreator:
  271. @staticmethod
  272. def create_record(data):
  273. """统一创建明细记录"""
  274. serializer = ContainerDetailPostSerializer(data=data)
  275. serializer.is_valid(raise_exception=True)
  276. serializer.save()
  277. def create_container_related_records(data):
  278. """记录创建批次"""
  279. # 生成明细记录
  280. container_obj = ContainerListModel.objects.filter(id=data['container']).first()
  281. container_obj.status = 5
  282. container_obj.save()
  283. if ContainerDetailModel.objects.filter(container=container_obj,goods_code=data['goods_code'],status=1,is_delete=False).first():
  284. return
  285. ContainerDetailModel.objects.create(
  286. container=container_obj,
  287. goods_code=data['goods_code'],
  288. goods_desc=data['goods_desc'],
  289. goods_qty=data['goods_qty'],
  290. goods_weight=data['goods_weight'],
  291. goods_class=2,
  292. status=data['status'],
  293. month=data['month'],
  294. creater=data['creater']
  295. )
  296. class OperationRecordCreator:
  297. @staticmethod
  298. def create_operation_record(data):
  299. """统一创建操作记录"""
  300. serializer = ContainerOperationPostSerializer(data=data)
  301. serializer.is_valid(raise_exception=True)
  302. serializer.save()
  303. @staticmethod
  304. def create_container_related_operation_records(data):
  305. container_obj = ContainerListModel.objects.filter(id=data['container']).first()
  306. if ContainerOperationModel.objects.filter(container_id=container_obj.id,goods_code=data['goods_code'],is_delete=False).first():
  307. return
  308. ContainerOperationModel.objects.create(
  309. month=data['month'],
  310. container_id=container_obj.id,
  311. operation_type=data['operation_type'],
  312. bound_id=None,
  313. goods_code=data['goods_code'],
  314. goods_desc=data['goods_desc'],
  315. goods_qty=data['goods_qty'],
  316. goods_weight=data['goods_weight'],
  317. operator=data['operator'],
  318. timestamp=data['timestamp'],
  319. from_location=data['from_location'],
  320. to_location=data['to_location'],
  321. memo=data['memo']
  322. )
  323. # 最终视图类
  324. class OperationView(APIView):
  325. def create(self, request, *args, **kwargs):
  326. try:
  327. return ContainerService.create_container_operation(
  328. data=request.data,
  329. logger=self.get_logger() # 假设有日志方法
  330. )
  331. except Exception as e:
  332. return Response({"error": str(e)}, status=400)