container_operate.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  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. from bin.queries import LocationQueries
  54. LocationQueries.get_batch_info(data.get('container_code'))
  55. return Response({"status": "success"})
  56. @staticmethod
  57. def handle_special_batches(data, logger):
  58. """托盘组处理流程"""
  59. for batch in data.get('batches', []):
  60. try:
  61. BatchOperator.process_single_container(data, batch, logger)
  62. except Exception as e:
  63. logger.error(f"托盘组处理失败: {str(e)}")
  64. continue
  65. return Response({"status": "success"})
  66. class BatchOperator:
  67. @staticmethod
  68. def process_single_container(data, batch, logger):
  69. """单个批次处理托盘"""
  70. # 获取批次对象
  71. container_obj = ContainerManager.get_or_create_container(
  72. batch.get('goods_code'),
  73. logger
  74. )
  75. # 更新批次状态
  76. update_result = BatchStatusUpdater.update_container_status(
  77. container_obj,
  78. 5,
  79. data.get('container_code'),
  80. )
  81. print(update_result)
  82. # 创建托盘关联记录
  83. RecordCreator.create_container_related_records(
  84. data=data,
  85. container_obj=container_obj
  86. )
  87. @staticmethod
  88. def process_single_batch(data, batch, logger):
  89. """单个批次处理批次"""
  90. # 获取批次对象
  91. bound_obj = BatchValidator.get_valid_batch(
  92. batch.get('goods_code'),
  93. logger
  94. )
  95. # 更新批次状态(新增数目)
  96. update_result = BatchStatusUpdater.update_status(
  97. bound_obj,
  98. batch['goods_qty'] * batch['goods_group'],
  99. logger
  100. )
  101. # 创建关联记录
  102. RecordCreator.create_related_records(
  103. data=data,
  104. bound_obj=bound_obj,
  105. batch_data= batch,
  106. qty_diff=update_result['added_qty'],
  107. logger=logger
  108. )
  109. class BatchValidator:
  110. @staticmethod
  111. def get_valid_batch(bound_number, logger):
  112. """批次校验模块"""
  113. if not (bound_obj := BoundBatchModel.objects.filter(bound_number=bound_number).first()):
  114. logger.error(f"批次 {bound_number} 不存在")
  115. raise ValueError("无效批次号")
  116. return bound_obj
  117. class BatchStatusUpdater:
  118. @staticmethod
  119. def update_status(bound_obj, added_qty, logger):
  120. """批次状态管理"""
  121. last_qty = bound_obj.goods_in_qty
  122. bound_obj.goods_in_qty += added_qty
  123. if bound_obj.goods_in_qty >= bound_obj.goods_qty:
  124. FullBatchHandler.mark_as_completed(bound_obj, logger)
  125. else:
  126. bound_obj.status = 0
  127. bound_obj.save()
  128. return {
  129. 'last_qty': last_qty,
  130. 'added_qty': added_qty
  131. }
  132. @staticmethod
  133. def update_container_status(container_obj, status, container_code):
  134. """批次状态管理"""
  135. container_obj.status = status
  136. container_obj.available = False
  137. container_obj.current_location = '托盘'+str(container_code)
  138. container_obj.save()
  139. return {
  140. 'last_qty': 1,
  141. 'added_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):
  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. )
  190. OperationRecordCreator.create_container_related_operation_records(operation_data)
  191. @staticmethod
  192. def create_related_records(data, bound_obj, batch_data, qty_diff, logger):
  193. """记录创建批次"""
  194. # 生成明细记录
  195. goods_group = batch_data.get('goods_group')
  196. goods_qty = batch_data.get('goods_qty')
  197. for i in range(goods_group):
  198. detail_data = RecordBuilder.build_detail_data(
  199. data=data,
  200. bound_obj=bound_obj,
  201. qty_diff=goods_qty
  202. )
  203. DetailRecordCreator.create_record(detail_data)
  204. # 生成操作记录
  205. operation_data = RecordBuilder.build_operation_data(
  206. detail_data=detail_data,
  207. qty_diff=qty_diff
  208. )
  209. OperationRecordCreator.create_operation_record(operation_data)
  210. class RecordBuilder:
  211. @staticmethod
  212. def build_detail_data(data, bound_obj, qty_diff):
  213. """构建明细数据结构"""
  214. return {
  215. "container": data['container'],
  216. "batch": bound_obj.id,
  217. "goods_code": bound_obj.goods_code,
  218. "goods_desc": bound_obj.goods_desc,
  219. "goods_qty": qty_diff,
  220. "goods_weight": bound_obj.goods_weight,
  221. "status": 1,
  222. "month": data['month'],
  223. "creater": data.get('creater', 'wms')
  224. }
  225. @staticmethod
  226. def build_detail_data_container_group(data, container_obj, qty_diff=1):
  227. """构建明细数据结构"""
  228. return {
  229. "container": data['container'],
  230. "goods_code": container_obj.container_code,
  231. "goods_desc": '托盘组',
  232. "goods_qty": qty_diff,
  233. "goods_weight": 0,
  234. "status": 1,
  235. "month": data['month'],
  236. "creater": data.get('creater', 'wms')
  237. }
  238. @staticmethod
  239. def build_operation_data(detail_data, qty_diff):
  240. """构建操作记录数据"""
  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": qty_diff,
  250. "operator": detail_data['creater'],
  251. "timestamp": timezone.now(),
  252. "from_location": "container",
  253. "to_location": "container",
  254. "memo": f"每组{detail_data['goods_code']}数目{detail_data['goods_qty']},PDA组盘共计组盘{qty_diff}"
  255. }
  256. def build_operation_data_container_group(detail_data, qty_diff):
  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,is_delete=False).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)