|
|
@@ -2572,54 +2572,229 @@ class OutDetailViewSet(viewsets.ModelViewSet):
|
|
|
return Response({"code": "500", "message": str(e)}, status=status.HTTP_200_OK)
|
|
|
|
|
|
def confirm_out_batch_detail(self, request):
|
|
|
- """确认出库"""
|
|
|
+ """确认出库 - 支持按批次和数量部分确认"""
|
|
|
try:
|
|
|
-
|
|
|
- container_code = request.query_params.get('container_code')
|
|
|
+ container_code = request.data.get('container_code')
|
|
|
+ confirm_items = request.data.get('confirm_items', []) # 格式: [{"detail_id": 1, "confirm_qty": 10}, ...]
|
|
|
+
|
|
|
+ if not container_code:
|
|
|
+ return Response({"code": "500", "message": "缺少托盘编码参数"}, status=status.HTTP_200_OK)
|
|
|
+
|
|
|
container_obj = ContainerListModel.objects.filter(container_code=container_code).first()
|
|
|
if not container_obj:
|
|
|
return Response({"code": "500", "message": f"托盘 {container_code} 不存在"}, status=status.HTTP_200_OK)
|
|
|
- out_batch_detail_all = out_batch_detail.objects.filter(container=container_obj,working=1,is_delete=False).all()
|
|
|
- if not out_batch_detail_all:
|
|
|
- return Response({"code": "500", "message": f"托盘 {container_code} 无出库明细"}, status=status.HTTP_200_OK)
|
|
|
- for obj in out_batch_detail_all:
|
|
|
- obj.working = 0
|
|
|
- obj.save()
|
|
|
+
|
|
|
+ # 如果没有指定确认项,则确认所有出库明细(保持向后兼容)
|
|
|
+ if not confirm_items:
|
|
|
+ out_batch_detail_all = out_batch_detail.objects.filter(container=container_obj,working=1,is_delete=False).order_by('-id').all()
|
|
|
+ if not out_batch_detail_all:
|
|
|
+ return Response({"code": "500", "message": f"托盘 {container_code} 无出库明细"}, status=status.HTTP_200_OK)
|
|
|
+ for obj in out_batch_detail_all:
|
|
|
+ obj.working = 0
|
|
|
+ obj.save()
|
|
|
+ BatchOperateLogModel.objects.create(
|
|
|
+ batch_id = obj.container_detail.batch,
|
|
|
+ log_type = 1,
|
|
|
+ log_date = timezone.now(),
|
|
|
+ goods_code = obj.container_detail.batch.goods_code,
|
|
|
+ goods_desc = obj.container_detail.batch.goods_desc,
|
|
|
+ goods_qty = obj.out_goods_qty,
|
|
|
+ log_content = f"出库托盘 {container_code} 批次 {obj.container_detail.batch.id} 数量 {obj.out_goods_qty}",
|
|
|
+ creater = "WMS",
|
|
|
+ openid = "WMS"
|
|
|
+ )
|
|
|
+ return Response({"code": "200", "message": "出库成功"}, status=status.HTTP_200_OK)
|
|
|
+
|
|
|
+ # 按批次和数量部分确认
|
|
|
+ from decimal import Decimal
|
|
|
+
|
|
|
+ for item in confirm_items:
|
|
|
+ out_detail_id = item.get('detail_id') # 这是 out_batch_detail 的 ID
|
|
|
+ confirm_qty = Decimal(str(item.get('confirm_qty', 0)))
|
|
|
+
|
|
|
+ if not out_detail_id or confirm_qty <= 0:
|
|
|
+ continue
|
|
|
+
|
|
|
+ # 确保 out_detail_id 是整数类型
|
|
|
+ try:
|
|
|
+ out_detail_id = int(out_detail_id)
|
|
|
+ except (ValueError, TypeError):
|
|
|
+ logger.error(f"无效的 out_detail_id: {out_detail_id}")
|
|
|
+ continue
|
|
|
+
|
|
|
+ # 添加调试日志
|
|
|
+ logger.info(f"查询出库明细: container={container_obj.container_code}, out_batch_detail_id={out_detail_id}")
|
|
|
+
|
|
|
+ # 直接通过 out_batch_detail 的 ID 查找记录
|
|
|
+ out_detail = out_batch_detail.objects.filter(
|
|
|
+ id=out_detail_id,
|
|
|
+ container_id=container_obj.id,
|
|
|
+ working=1,
|
|
|
+ is_delete=False
|
|
|
+ ).first()
|
|
|
+
|
|
|
+ if not out_detail:
|
|
|
+ # 尝试不限制 working 状态查找
|
|
|
+ out_detail_any = out_batch_detail.objects.filter(
|
|
|
+ id=out_detail_id,
|
|
|
+ container_id=container_obj.id,
|
|
|
+ is_delete=False
|
|
|
+ ).first()
|
|
|
+
|
|
|
+ if out_detail_any:
|
|
|
+ if out_detail_any.working == 0:
|
|
|
+ logger.warning(f"出库明细 {out_detail_id} 的 working 状态为 0,可能已经被处理过了")
|
|
|
+ else:
|
|
|
+ logger.warning(f"出库明细 {out_detail_id} 的 working 状态为 {out_detail_any.working},不是 1")
|
|
|
+ else:
|
|
|
+ logger.error(f"未找到出库明细: out_batch_detail_id={out_detail_id}, container={container_obj.container_code}")
|
|
|
+ continue
|
|
|
+
|
|
|
+ # 计算本次要确认的数量(不能超过该出库明细的数量)
|
|
|
+ out_qty_to_confirm = min(confirm_qty, out_detail.out_goods_qty)
|
|
|
+
|
|
|
+ # 如果确认数量等于或大于出库数量,则标记为已完成
|
|
|
+ if out_qty_to_confirm >= out_detail.out_goods_qty:
|
|
|
+ out_detail.working = 0
|
|
|
+ out_detail.save()
|
|
|
+ else:
|
|
|
+ # 部分确认,创建新的确认记录并减少剩余数量
|
|
|
+ # 创建一个新的 out_batch_detail 记录用于剩余数量
|
|
|
+ remaining_qty = out_detail.out_goods_qty - out_qty_to_confirm
|
|
|
+ out_detail.out_goods_qty = out_qty_to_confirm
|
|
|
+ out_detail.working = 0
|
|
|
+ out_detail.save()
|
|
|
+
|
|
|
+ # 创建剩余数量的记录(如果需要)
|
|
|
+ if remaining_qty > 0:
|
|
|
+ out_batch_detail.objects.create(
|
|
|
+ out_bound_id=out_detail.out_bound_id,
|
|
|
+ container_id=out_detail.container_id,
|
|
|
+ container_detail_id=out_detail.container_detail_id,
|
|
|
+ out_goods_qty=remaining_qty,
|
|
|
+ last_out_goods_qty=out_detail.last_out_goods_qty,
|
|
|
+ working=1,
|
|
|
+ is_delete=False
|
|
|
+ )
|
|
|
+
|
|
|
+ # 创建操作日志
|
|
|
BatchOperateLogModel.objects.create(
|
|
|
- batch_id = obj.container_detail.batch,
|
|
|
+ batch_id = out_detail.container_detail.batch,
|
|
|
log_type = 1,
|
|
|
log_date = timezone.now(),
|
|
|
- goods_code = obj.container_detail.batch.goods_code,
|
|
|
- goods_desc = obj.container_detail.batch.goods_desc,
|
|
|
- goods_qty = obj.out_goods_qty,
|
|
|
- log_content = f"出库托盘 {container_code} 批次 {obj.container_detail.batch.id} 数量 {obj.out_goods_qty}",
|
|
|
+ goods_code = out_detail.container_detail.batch.goods_code,
|
|
|
+ goods_desc = out_detail.container_detail.batch.goods_desc,
|
|
|
+ goods_qty = out_qty_to_confirm,
|
|
|
+ log_content = f"出库托盘 {container_code} 批次 {out_detail.container_detail.batch.id} 数量 {out_qty_to_confirm}",
|
|
|
creater = "WMS",
|
|
|
openid = "WMS"
|
|
|
-
|
|
|
)
|
|
|
+ logger.info(f"确认出库明细 {out_detail_id}: 确认数量={out_qty_to_confirm}, 剩余数量={out_detail.out_goods_qty}")
|
|
|
+
|
|
|
return Response({"code": "200", "message": "出库成功"}, status=status.HTTP_200_OK)
|
|
|
+
|
|
|
except Exception as e:
|
|
|
+ logger.error(f"确认出库失败: {str(e)}", exc_info=True)
|
|
|
return Response({"code": "500", "message": str(e)}, status=status.HTTP_200_OK)
|
|
|
|
|
|
def cancel_out_batch_detail(self, request):
|
|
|
- """取消出库"""
|
|
|
+ """取消出库 - 支持按批次和数量部分取消"""
|
|
|
try:
|
|
|
- container_code = request.query_params.get('container_code')
|
|
|
+ container_code = request.data.get('container_code')
|
|
|
+ cancel_items = request.data.get('cancel_items', []) # 格式: [{"detail_id": 1, "cancel_qty": 10}, ...]
|
|
|
+
|
|
|
+ if not container_code:
|
|
|
+ return Response({"code": "500", "message": "缺少托盘编码参数"}, status=status.HTTP_200_OK)
|
|
|
+
|
|
|
container_obj = ContainerListModel.objects.filter(container_code=container_code).first()
|
|
|
if not container_obj:
|
|
|
return Response({"code": "500", "message": f"托盘 {container_code} 不存在"}, status=status.HTTP_200_OK)
|
|
|
- out_batch_detail_all = out_batch_detail.objects.filter(container=container_obj,working=1,is_delete=False).order_by('-id').all()
|
|
|
- if not out_batch_detail_all:
|
|
|
- return Response({"code": "500", "message": f"托盘 {container_code} 无出库明细"}, status=status.HTTP_200_OK)
|
|
|
- for obj in out_batch_detail_all:
|
|
|
- obj.container_detail.goods_out_qty = obj.last_out_goods_qty
|
|
|
- # print(f"取消出库托盘 {container_code} 批次 {obj.container_detail.batch.id},详情 {obj.container_detail.id} 变化前数量 {obj.container_detail.goods_out_qty} 变化后数量 {obj.last_out_goods_qty}")
|
|
|
- obj.container_detail.save()
|
|
|
- obj.is_delete = True
|
|
|
- obj.working = 0
|
|
|
- obj.save()
|
|
|
+
|
|
|
+ # 如果没有指定取消项,则取消所有出库明细(保持向后兼容)
|
|
|
+ if not cancel_items:
|
|
|
+ out_batch_detail_all = out_batch_detail.objects.filter(container=container_obj,working=1,is_delete=False).order_by('-id').all()
|
|
|
+ if not out_batch_detail_all:
|
|
|
+ return Response({"code": "500", "message": f"托盘 {container_code} 无出库明细"}, status=status.HTTP_200_OK)
|
|
|
+ for obj in out_batch_detail_all:
|
|
|
+ obj.container_detail.goods_out_qty = obj.last_out_goods_qty
|
|
|
+ obj.container_detail.save()
|
|
|
+ obj.is_delete = True
|
|
|
+ obj.working = 0
|
|
|
+ obj.save()
|
|
|
+ return Response({"code": "200", "message": "出库取消成功"}, status=status.HTTP_200_OK)
|
|
|
+
|
|
|
+ # 按批次和数量部分取消
|
|
|
+ from decimal import Decimal
|
|
|
+
|
|
|
+ for item in cancel_items:
|
|
|
+ out_detail_id = item.get('detail_id') # 这是 out_batch_detail 的 ID
|
|
|
+ cancel_qty = Decimal(str(item.get('cancel_qty', 0)))
|
|
|
+
|
|
|
+ if not out_detail_id or cancel_qty <= 0:
|
|
|
+ continue
|
|
|
+
|
|
|
+ # 确保 out_detail_id 是整数类型
|
|
|
+ try:
|
|
|
+ out_detail_id = int(out_detail_id)
|
|
|
+ except (ValueError, TypeError):
|
|
|
+ logger.error(f"无效的 out_detail_id: {out_detail_id}")
|
|
|
+ continue
|
|
|
+
|
|
|
+ # 添加调试日志
|
|
|
+ logger.info(f"查询出库明细: container={container_obj.container_code}, out_batch_detail_id={out_detail_id}")
|
|
|
+
|
|
|
+ # 直接通过 out_batch_detail 的 ID 查找记录
|
|
|
+ out_detail = out_batch_detail.objects.filter(
|
|
|
+ id=out_detail_id,
|
|
|
+ container_id=container_obj.id,
|
|
|
+ working=1,
|
|
|
+ is_delete=False
|
|
|
+ ).first()
|
|
|
+
|
|
|
+ if not out_detail:
|
|
|
+ # 尝试不限制 working 状态查找
|
|
|
+ out_detail_any = out_batch_detail.objects.filter(
|
|
|
+ id=out_detail_id,
|
|
|
+ container_id=container_obj.id,
|
|
|
+ is_delete=False
|
|
|
+ ).first()
|
|
|
+
|
|
|
+ if out_detail_any:
|
|
|
+ if out_detail_any.working == 0:
|
|
|
+ logger.warning(f"出库明细 {out_detail_id} 的 working 状态为 0,可能已经被处理过了")
|
|
|
+ else:
|
|
|
+ logger.warning(f"出库明细 {out_detail_id} 的 working 状态为 {out_detail_any.working},不是 1")
|
|
|
+ else:
|
|
|
+ logger.error(f"未找到出库明细: out_batch_detail_id={out_detail_id}, container={container_obj.container_code}")
|
|
|
+ continue
|
|
|
+
|
|
|
+ # 计算本次要取消的数量(不能超过该出库明细的数量)
|
|
|
+ out_qty_to_cancel = min(cancel_qty, out_detail.out_goods_qty)
|
|
|
+
|
|
|
+ # 更新容器明细的出库数量
|
|
|
+ out_detail.container_detail.goods_out_qty -= out_qty_to_cancel
|
|
|
+ out_detail.container_detail.goods_out_qty = max(
|
|
|
+ out_detail.container_detail.goods_out_qty,
|
|
|
+ out_detail.last_out_goods_qty
|
|
|
+ )
|
|
|
+ out_detail.container_detail.save()
|
|
|
+
|
|
|
+ # 如果全部取消,则删除或标记该出库明细
|
|
|
+ if out_detail.out_goods_qty <= out_qty_to_cancel:
|
|
|
+ out_detail.is_delete = True
|
|
|
+ out_detail.working = 0
|
|
|
+ out_detail.out_goods_qty = Decimal('0') # 清零
|
|
|
+ else:
|
|
|
+ # 部分取消,减少出库数量
|
|
|
+ out_detail.out_goods_qty -= out_qty_to_cancel
|
|
|
+
|
|
|
+ out_detail.save()
|
|
|
+ logger.info(f"取消出库明细 {out_detail_id}: 取消数量={out_qty_to_cancel}, 剩余数量={out_detail.out_goods_qty}")
|
|
|
+
|
|
|
return Response({"code": "200", "message": "出库取消成功"}, status=status.HTTP_200_OK)
|
|
|
+
|
|
|
except Exception as e:
|
|
|
+ logger.error(f"取消出库失败: {str(e)}", exc_info=True)
|
|
|
return Response({"code": "500", "message": str(e)}, status=status.HTTP_200_OK)
|
|
|
|
|
|
def get_contianer_detail(self, request):
|