| 
					
				 | 
			
			
				@@ -1,7 +1,7 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from django.db import models 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from erp.models import InboundBill, MaterialDetail, OutboundBill,OutMaterialDetail 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from django.db.models import Sum 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-from django.db.models.signals import post_save, post_delete 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from django.db.models.signals import post_save, post_delete, pre_save 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from django.dispatch import receiver 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from decimal import Decimal 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class BoundListModel(models.Model): 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -154,36 +154,74 @@ class MaterialStatistics(models.Model): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         verbose_name_plural = "物料统计" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         ordering = ['goods_code'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@receiver(pre_save, sender=BoundBatchModel) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def store_original_goods_code(sender, instance, **kwargs): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """保存前记录原始物料编码(仅更新时生效)""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if not instance.pk:  # 新建对象无原始值 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        original = BoundBatchModel.objects.get(pk=instance.pk) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # 将原始值存储在实例属性中供post_save使用 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        instance._original_goods_code = original.goods_code   
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    except BoundBatchModel.DoesNotExist: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        pass 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 @receiver([post_save, post_delete], sender=BoundBatchModel) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 def update_material_statistics(sender, instance, **kwargs): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    goods_code = instance.goods_code 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    stats, created = MaterialStatistics.objects.get_or_create( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        goods_code=goods_code, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        defaults={ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            'goods_desc': instance.goods_desc, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            'goods_std': instance.goods_std or '待填写', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            'goods_unit': instance.goods_unit or '待填写', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    # 更新物料信息为最新批次的信息(可选) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    stats.goods_desc = instance.goods_desc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if instance.goods_std and instance.goods_std != '待填写': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        stats.goods_std = instance.goods_std 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if instance.goods_unit and instance.goods_unit != '待填写': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        stats.goods_unit = instance.goods_unit 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    stats.save() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    # 计算总数量 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    total = BoundBatchModel.objects.filter(goods_code=goods_code).aggregate( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        total=Sum('goods_in_location_qty') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    )['total'] or 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    # 更新物料统计的出库数目 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    total_out = BoundBatchModel.objects.filter(goods_code=goods_code).aggregate( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        total_out=Sum('goods_out_qty') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    )['total_out'] or 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    stats.total_quantity = total 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    stats.save() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    # 处理删除/创建/更新操作的核心逻辑 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def refresh_stats(target_goods_code, force_desc=False): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """刷新指定物料的统计数据""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        stats, created = MaterialStatistics.objects.get_or_create( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            goods_code=target_goods_code, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            defaults={ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                'goods_desc': instance.goods_desc, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                'goods_std': instance.goods_std or '待填写', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                'goods_unit': instance.goods_unit or '待填写', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # 仅强制更新描述(用于当前物料)或新建时初始化 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if force_desc or created: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            stats.goods_desc = instance.goods_desc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if instance.goods_std and instance.goods_std != '待填写': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stats.goods_std = instance.goods_std 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if instance.goods_unit and instance.goods_unit != '待填写': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stats.goods_unit = instance.goods_unit 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # 重新聚合统计数据(始终执行) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        agg = BoundBatchModel.objects.filter(goods_code=target_goods_code).aggregate( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            total=Sum('goods_in_location_qty'), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            total_out=Sum('goods_out_qty') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        stats.total_quantity = agg['total'] or 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # 如果有出库统计字段需更新,可在此添加 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        stats.save() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    # 情况1:删除操作直接更新当前物料 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if kwargs.get('signal') == post_delete: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        refresh_stats(instance.goods_code) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    # 情况2:创建操作直接更新新物料 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if kwargs.get('created'): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        refresh_stats(instance.goods_code, force_desc=True) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    # 情况3:更新操作(检测物料编码变更) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    original_code = getattr(instance, '_original_goods_code', None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    current_code = instance.goods_code 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    # 物料编码未变化时仅更新当前物料 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if original_code == current_code: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        refresh_stats(current_code, force_desc=True) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    # 物料编码变化时更新新旧两个物料 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        refresh_stats(original_code)      # 更新旧物料(仅刷新数量) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        refresh_stats(current_code, force_desc=True)  # 更新新物料(全量刷新) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 |