container_operate.py 13 KB

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