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. qty_diff=update_result['added_qty'],
  104. expected_qty=update_result['expected_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. 'expected_qty': bound_obj.goods_qty
  130. }
  131. @staticmethod
  132. def update_container_status(container_obj, status, container_code):
  133. """批次状态管理"""
  134. container_obj.status = status
  135. container_obj.available = False
  136. container_obj.current_location = '托盘'+str(container_code)
  137. container_obj.save()
  138. return {
  139. 'last_qty': 1,
  140. 'added_qty': 1,
  141. 'expected_qty': 1
  142. }
  143. class FullBatchHandler:
  144. @staticmethod
  145. def mark_as_completed(bound_obj, logger):
  146. """满批次处理"""
  147. # bound_obj.goods_in_qty = bound_obj.goods_qty
  148. bound_obj.status = 1
  149. BatchRelationUpdater.update_related_records(bound_obj, logger)
  150. class BatchRelationUpdater:
  151. @staticmethod
  152. def update_related_records(bound_obj, logger):
  153. """更新关联单据"""
  154. if detail := BoundDetailModel.objects.filter(bound_batch=bound_obj.id).first():
  155. detail.status = 1
  156. detail.save()
  157. CompletionChecker.check_list_completion(detail.bound_list_id, logger)
  158. class CompletionChecker:
  159. @staticmethod
  160. def check_list_completion(list_id, logger):
  161. """完成度检查"""
  162. completed = BoundDetailModel.objects.filter(bound_list=list_id, status=1).count()
  163. total = BoundDetailModel.objects.filter(bound_list=list_id).count()
  164. if completed == total:
  165. ListStatusUpdater.update_list_status(list_id, 102, logger)
  166. class ListStatusUpdater:
  167. @staticmethod
  168. def update_list_status(list_id, status, logger):
  169. """更新单据状态"""
  170. if list_obj := BoundListModel.objects.filter(id=list_id).first():
  171. list_obj.bound_status = status
  172. list_obj.save()
  173. logger.info("入库申请全部完成")
  174. class RecordCreator:
  175. @staticmethod
  176. def create_container_related_records(data, container_obj, qty_diff=1, expected_qty=1):
  177. """记录创建批次"""
  178. # 生成明细记录
  179. detail_data = RecordBuilder.build_detail_data_container_group(
  180. data=data,
  181. container_obj=container_obj,
  182. qty_diff=qty_diff
  183. )
  184. DetailRecordCreator.create_container_related_records(detail_data)
  185. # 生成操作记录
  186. operation_data = RecordBuilder.build_operation_data_container_group(
  187. detail_data=detail_data,
  188. qty_diff=qty_diff,
  189. expected_qty=expected_qty
  190. )
  191. OperationRecordCreator.create_container_related_operation_records(operation_data)
  192. @staticmethod
  193. def create_related_records(data, bound_obj, qty_diff, expected_qty, logger):
  194. """记录创建批次"""
  195. # 生成明细记录
  196. detail_data = RecordBuilder.build_detail_data(
  197. data=data,
  198. bound_obj=bound_obj,
  199. qty_diff=qty_diff
  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. expected_qty=expected_qty
  207. )
  208. OperationRecordCreator.create_operation_record(operation_data)
  209. class RecordBuilder:
  210. @staticmethod
  211. def build_detail_data(data, bound_obj, qty_diff):
  212. """构建明细数据结构"""
  213. return {
  214. "container": data['container'],
  215. "batch": bound_obj.id,
  216. "goods_code": bound_obj.goods_code,
  217. "goods_desc": bound_obj.goods_desc,
  218. "goods_qty": qty_diff,
  219. "goods_weight": bound_obj.goods_weight,
  220. "status": 1,
  221. "month": data['month'],
  222. "creater": data.get('creater', 'zl')
  223. }
  224. @staticmethod
  225. def build_detail_data_container_group(data, container_obj, qty_diff=1):
  226. """构建明细数据结构"""
  227. return {
  228. "container": data['container'],
  229. "goods_code": container_obj.container_code,
  230. "goods_desc": '托盘组',
  231. "goods_qty": qty_diff,
  232. "goods_weight": 0,
  233. "status": 1,
  234. "month": data['month'],
  235. "creater": data.get('creater', 'zl')
  236. }
  237. @staticmethod
  238. def build_operation_data(detail_data, qty_diff, expected_qty):
  239. """构建操作记录数据"""
  240. note_type = "(数量不一致)" if qty_diff != expected_qty else ""
  241. return {
  242. "month": detail_data['month'],
  243. "container": detail_data['container'],
  244. "operation_type": 'container',
  245. "batch": detail_data['batch'],
  246. "goods_code": detail_data['goods_code'],
  247. "goods_desc": detail_data['goods_desc'],
  248. "goods_qty": qty_diff,
  249. "goods_weight": detail_data['goods_weight'],
  250. "operator": detail_data['creater'],
  251. "timestamp": timezone.now(),
  252. "from_location": "container",
  253. "to_location": "container",
  254. "memo": f"每组数目{detail_data['goods_qty']}入库PDA组盘{note_type}{detail_data['goods_code']}数量{qty_diff}"
  255. }
  256. def build_operation_data_container_group(detail_data, qty_diff, expected_qty):
  257. """构建操作记录数据"""
  258. return {
  259. "month": detail_data['month'],
  260. "container": detail_data['container'],
  261. "operation_type": 'container',
  262. "goods_code": detail_data['goods_code'],
  263. "goods_desc": detail_data['goods_desc'],
  264. "goods_qty": qty_diff,
  265. "goods_weight": detail_data['goods_weight'],
  266. "operator": detail_data['creater'],
  267. "timestamp": timezone.now(),
  268. "from_location": "container",
  269. "to_location": "container",
  270. "memo": f"托盘组下的:{detail_data['goods_code']}合盘"
  271. }
  272. class DetailRecordCreator:
  273. @staticmethod
  274. def create_record(data):
  275. """统一创建明细记录"""
  276. serializer = ContainerDetailPostSerializer(data=data)
  277. serializer.is_valid(raise_exception=True)
  278. serializer.save()
  279. def create_container_related_records(data):
  280. """记录创建批次"""
  281. # 生成明细记录
  282. container_obj = ContainerListModel.objects.filter(id=data['container']).first()
  283. container_obj.status = 5
  284. container_obj.save()
  285. if ContainerDetailModel.objects.filter(container=container_obj,goods_code=data['goods_code'],status=1).first():
  286. return
  287. ContainerDetailModel.objects.create(
  288. container=container_obj,
  289. goods_code=data['goods_code'],
  290. goods_desc=data['goods_desc'],
  291. goods_qty=data['goods_qty'],
  292. goods_weight=data['goods_weight'],
  293. goods_class=2,
  294. status=data['status'],
  295. month=data['month'],
  296. creater=data['creater']
  297. )
  298. class OperationRecordCreator:
  299. @staticmethod
  300. def create_operation_record(data):
  301. """统一创建操作记录"""
  302. serializer = ContainerOperationPostSerializer(data=data)
  303. serializer.is_valid(raise_exception=True)
  304. serializer.save()
  305. @staticmethod
  306. def create_container_related_operation_records(data):
  307. container_obj = ContainerListModel.objects.filter(id=data['container']).first()
  308. if ContainerOperationModel.objects.filter(container_id=container_obj.id,goods_code=data['goods_code'],is_delete=False).first():
  309. return
  310. ContainerOperationModel.objects.create(
  311. month=data['month'],
  312. container_id=container_obj.id,
  313. operation_type=data['operation_type'],
  314. bound_id=None,
  315. goods_code=data['goods_code'],
  316. goods_desc=data['goods_desc'],
  317. goods_qty=data['goods_qty'],
  318. goods_weight=data['goods_weight'],
  319. operator=data['operator'],
  320. timestamp=data['timestamp'],
  321. from_location=data['from_location'],
  322. to_location=data['to_location'],
  323. memo=data['memo']
  324. )
  325. # 最终视图类
  326. class OperationView(APIView):
  327. def create(self, request, *args, **kwargs):
  328. try:
  329. return ContainerService.create_container_operation(
  330. data=request.data,
  331. logger=self.get_logger() # 假设有日志方法
  332. )
  333. except Exception as e:
  334. return Response({"error": str(e)}, status=400)