| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 | from django.db.models import Sum, Ffrom .models import ContainerListModel,ContainerDetailModel,ContainerOperationModel,TaskModel,ContainerWCSModelfrom bound.models import BoundBatchModel,BoundDetailModel,BoundListModelfrom .serializers import *from rest_framework.response import Responsefrom rest_framework.views import APIViewfrom django.utils import timezonefrom decimal import Decimalimport logginglogger = 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 Falsedef 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 resultdef 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 record.count_time <= 5:                # 最后一个记录:使用当前库存作为期末数量                if i == 0:                    record.closing_quantity = current_quantity                else:                    # 前一个记录的期末数量就是当前记录的期初数量                    record.closing_quantity = history_records[i-1].opening_quantity                                # 计算期初数量(期末 + 出库 - 入库)                record.in_quantity = record.batch_log.goods_in_qty                record.out_quantity = record.batch_log.goods_out_qty                record.opening_quantity = (                    record.closing_quantity                     + record.out_quantity                     - record.in_quantity                )                                # 更新记录                record.count_time = record.count_time + 1                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("物料变动历史记录修复完成")
 |