| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- from django.db.models import Sum, F
- from .models import ContainerListModel,ContainerDetailModel,ContainerOperationModel,TaskModel,ContainerWCSModel
- from bound.models import BoundBatchModel,BoundDetailModel,BoundListModel
- from .serializers import *
- from rest_framework.response import Response
- from rest_framework.views import APIView
- from django.utils import timezone
- from decimal import Decimal
- import logging
- logger = logging.getLogger(__name__)
- def update_container_category_for_container(container_id):
- """
- 按需更新单个托盘的分类和批次信息
- """
- try:
- container = ContainerListModel.objects.get(id=container_id)
-
- # 获取托盘所有有效物料详情
- details = container.details.filter(
- is_delete=False
- ).exclude(status=3).annotate(
- available_qty=F('goods_qty') - F('goods_out_qty')
- ).filter(available_qty__gt=0)
-
- # 计算总物料量
- total_qty = details.aggregate(total=Sum('available_qty'))['total'] or 0
-
- # 收集批次信息
- batch_info = []
- for detail in details:
- batch = detail.batch
- if batch:
- batch_info.append({
- "batch_id": batch.id,
- "check_status": batch.check_status,
- "bound_number": batch.bound_number,
- "qty": float(detail.available_qty) # 转换为 float
- })
-
- # 确定托盘分类
- if total_qty == 0:
- # 空盘
- category = 2 if container.status == 2 else 3 # 2=在库, 3=不在库
- else:
- # 有物料
- batch_count = details.values('batch').distinct().count()
- category = 0 if batch_count == 1 else 1 # 0=整盘, 1=散盘
-
- # 更新
- container.category = category
- container.batch_info = batch_info
- container.save(update_fields=['category', 'batch_info'])
-
- return True
- except Exception as e:
- logger.error(f"更新托盘分类时出错: {e}", exc_info=True)
- return False
- def batch_update_container_categories(container_ids):
- """
- 批量更新多个托盘的分类和批次信息
- """
- updated_count = 0
- error_ids = []
-
- for container_id in container_ids:
- try:
- container = ContainerListModel.objects.get(id=container_id)
-
- # 获取托盘所有有效物料详情
- details = container.details.filter(
- is_delete=False
- ).exclude(status=3).annotate(
- available_qty=F('goods_qty') - F('goods_out_qty')
- ).filter(available_qty__gt=0)
-
- # 计算总物料量
- total_qty = details.aggregate(total=Sum('available_qty'))['total'] or 0
-
- # 收集批次信息,合并批次数量
- batch_info = []
- batch_map = {}
- for detail in details:
- batch = detail.batch
- if batch:
- if batch.id in batch_map:
- batch_map[batch.id]['qty'] += float(detail.available_qty) # 转换为 float
- else:
- batch_map[batch.id] = {
- "batch_id": batch.id,
- "check_status": batch.check_status,
- "bound_number": batch.bound_number,
- "qty": float(detail.available_qty) # 转换为 float
- }
-
- batch_info = list(batch_map.values())
- # 确定托盘分类
- if total_qty == 0:
- # 空盘
- category = 2 if container.current_location not in ['N/A', '103','203'] else 3 # 2=在库, 3=不在库
- else:
- # 有物料
- batch_count = details.values('batch').distinct().count()
- category = 0 if batch_count == 1 else 1 # 0=整盘, 1=散盘
-
- # 更新
- container.category = category
- container.batch_info = batch_info
- container.save(update_fields=['category', 'batch_info'])
-
- updated_count += 1
- except Exception as e:
- logger.error(f"更新托盘 {container_id} 分类时出错: {e}", exc_info=True)
- error_ids.append(container_id)
-
- return {
- "updated": updated_count,
- "errors": error_ids,
- "total": len(container_ids)
- }
- def update_container_categories_task():
- """后台任务:更新所有托盘的分类和批次信息"""
- container_ids = ContainerListModel.objects.values_list('id', flat=True)
- result = batch_update_container_categories(list(container_ids))
- logger.info(f"后台任务更新托盘分类: 成功 {result['updated']}, 失败 {len(result['errors'])}")
- logger.info(f"后台任务更新托盘分类: 成功 {result['updated']}, 失败 {len(result['errors'])}")
- return result
- def reconcile_material_history():
- """从后往前修复物料变动历史记录"""
- from container.models import MaterialStatistics, MaterialChangeHistory
- all_materials = MaterialStatistics.objects.all()
-
- for material in all_materials:
- # 获取该物料的所有变动历史记录(按时间倒序)
- history_records = MaterialChangeHistory.objects.filter(
- goods_code = material.goods_code
- ).order_by('-change_time')
-
- if not history_records.exists():
- continue
-
- # 从物料统计中获取当前库存作为最后一个记录的期末数量
- current_quantity = material.total_quantity
-
- # 从后往前处理每个历史记录
- for i, record in enumerate(history_records):
- # 最后一个记录:使用当前库存作为期末数量
- if i == 0:
- record.closing_quantity = current_quantity
- else:
- # 前一个记录的期末数量就是当前记录的期初数量
- record.closing_quantity = history_records[i-1].opening_quantity
-
- # 计算期初数量(期末 + 出库 - 入库)
- record.opening_quantity = (
- record.closing_quantity
- + record.out_quantity
- - record.in_quantity
- )
-
- # 更新记录
- record.save()
-
- # 更新当前数量为当前记录的期初数量(用于下一个记录)
- current_quantity = record.opening_quantity
-
- # 验证第一个记录的期初数量是否合理
- first_record = history_records.last()
- if first_record.opening_quantity < 0:
- logger.info(f"警告:物料 {material.goods_code} 的期初库存为负值: {first_record.opening_quantity}")
-
- # # 更新物料统计的总库存(应该与最后一个记录的期末数量一致)
- # material.total_quantity = history_records.first().closing_quantity
- # material.save()
-
- logger.info("物料变动历史记录修复完成")
|