Kaynağa Gözat

拆盘bug完善

flower_mr 6 gün önce
ebeveyn
işleme
b887a1904e

+ 56 - 0
container/migrations/0022_containerdetaillogmodel_and_more.py

@@ -0,0 +1,56 @@
+# Generated by Django 4.1.2 on 2025-06-03 00:29
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('container', '0021_out_batch_detail_last_out_goods_qty'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ContainerDetailLogModel',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('log_type', models.CharField(choices=[('create', '创建'), ('update', '更新'), ('delete', '删除'), ('out', '出库'), ('status_change', '状态变更')], max_length=20, verbose_name='日志类型')),
+                ('old_goods_qty', models.IntegerField(blank=True, null=True, verbose_name='原数量')),
+                ('old_goods_out_qty', models.IntegerField(blank=True, null=True, verbose_name='原出库数量')),
+                ('old_status', models.IntegerField(blank=True, choices=[(0, '空盘'), (1, '组盘'), (2, '在库'), (3, '已出库')], null=True, verbose_name='原状态')),
+                ('new_goods_qty', models.IntegerField(blank=True, null=True, verbose_name='新数量')),
+                ('new_goods_out_qty', models.IntegerField(blank=True, null=True, verbose_name='新出库数量')),
+                ('new_status', models.IntegerField(blank=True, choices=[(0, '空盘'), (1, '组盘'), (2, '在库'), (3, '已出库')], null=True, verbose_name='新状态')),
+                ('creater', models.CharField(max_length=50, verbose_name='操作人')),
+                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='操作时间')),
+            ],
+            options={
+                'verbose_name': '容器明细变更日志',
+                'verbose_name_plural': '容器明细变更日志',
+                'db_table': 'container_detail_log',
+                'ordering': ['-create_time'],
+            },
+        ),
+        migrations.AddIndex(
+            model_name='containerdetailmodel',
+            index=models.Index(fields=['container'], name='container_d_contain_125dbf_idx'),
+        ),
+        migrations.AddIndex(
+            model_name='containerdetailmodel',
+            index=models.Index(fields=['batch'], name='container_d_batch_i_165f57_idx'),
+        ),
+        migrations.AddIndex(
+            model_name='containerdetailmodel',
+            index=models.Index(fields=['goods_code'], name='container_d_goods_c_24ac97_idx'),
+        ),
+        migrations.AddIndex(
+            model_name='containerdetailmodel',
+            index=models.Index(fields=['status'], name='container_d_status_a3efbb_idx'),
+        ),
+        migrations.AddField(
+            model_name='containerdetaillogmodel',
+            name='container_detail',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='logs', to='container.containerdetailmodel'),
+        ),
+    ]

+ 17 - 0
container/migrations/0023_alter_containerdetaillogmodel_options.py

@@ -0,0 +1,17 @@
+# Generated by Django 4.1.2 on 2025-06-03 00:49
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('container', '0022_containerdetaillogmodel_and_more'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='containerdetaillogmodel',
+            options={'ordering': ['-create_time'], 'verbose_name': '托盘明细变更日志', 'verbose_name_plural': '托盘明细变更日志'},
+        ),
+    ]

+ 170 - 17
container/models.py

@@ -1,6 +1,8 @@
 from django.db import models
 from bound.models import BoundBatchModel,BoundDetailModel,OutBatchModel,BoundListModel,BoundListModel
-
+from django.utils import timezone
+from django.db.models.signals import pre_save, post_save
+from django.dispatch import receiver
 
 # Create your models here.
 # 主表:托盘数据
@@ -27,7 +29,6 @@ class ContainerListModel(models.Model):
         verbose_name_plural = "ContainerList"
         ordering = ['-container_code']
 
-# 明细表:托盘详细数据记录当前组盘的 批次 数量 使用托盘码和状态来获取、托盘上的物料信息,
 class ContainerDetailModel(models.Model):
     BATCH_STATUS=(
         (0, '空盘'),
@@ -40,6 +41,7 @@ class ContainerDetailModel(models.Model):
         (2, '空盘'),
         (3, '散盘'),
     )
+    
     month = models.IntegerField(verbose_name='月份')
     container = models.ForeignKey(ContainerListModel, on_delete=models.CASCADE, related_name='details')
     batch = models.ForeignKey(BoundBatchModel, on_delete=models.CASCADE, verbose_name='批次',blank=True, null=True)
@@ -60,9 +62,15 @@ class ContainerDetailModel(models.Model):
         verbose_name = 'ContainerDetail'
         verbose_name_plural = "ContainerDetail"
         ordering = ['-id']
+        indexes = [
+            models.Index(fields=['container']),
+            models.Index(fields=['batch']),
+            models.Index(fields=['goods_code']),
+            models.Index(fields=['status']),
+        ]
 
     def __str__(self):
-        return f"{self.container_code} - {self.batch.bound_number} - {self.goods_code} - {self.goods_desc} - {self.goods_qty}"
+        return f"{self.container.container_code} - {self.batch.bound_number if self.batch else 'N/A'} - {self.goods_code}"
     
     def save(self, *args, **kwargs):
         """
@@ -70,31 +78,176 @@ class ContainerDetailModel(models.Model):
         goods_in_qty(组盘数目)
         ,goods_in_location_qty(在库数目)
         ,goods_out_qty(出库数目)
-    
         """
+        # 在保存前调用信号处理器
+        if self.pk:
+            # 获取状态变化前的状态
+            original = ContainerDetailModel.objects.get(pk=self.pk)
+            original_status = original.status
+            
+        super().save(*args, **kwargs)
+        
+        # 更新批次数据
         if self.batch:
-          
-            super().save(*args, **kwargs)
-
-            if self.goods_qty - self.goods_out_qty <= 0:
-                self.status = 3
+            # 避免循环更新 - 仅在数量相关字段变更时才更新
+            if 'update_fields' not in kwargs or any(field in kwargs['update_fields'] for field in ['goods_qty', 'goods_out_qty']):
+                self.update_batch_stats()
+            
+            # # 根据出库数量更新状态
+            # if self.goods_out_qty > self.goods_qty:
+            #     # 出库数量不能大于总数量
+            #     self.goods_out_qty = self.goods_qty
+            #     self.save(update_fields=['goods_out_qty'])
+                
+            # 根据当前数量状态更新状态
+            if self.goods_out_qty >= self.goods_qty:
+                self.status = 3  # 已出库
                 super().save(*args, **kwargs)
+                
             elif self.goods_qty - self.goods_out_qty > 0 and self.goods_out_qty > 0:
-                self.status = 2
+                self.status = 2  # 在库
                 super().save(*args, **kwargs)
+
+              
             if self.status == 3 and self.goods_qty - self.goods_out_qty > 0 :
-                self.status = 2
+                self.status = 2  # 在库
                 super().save(*args, **kwargs)
+    
+    def update_batch_stats(self):
+        """更新批次的统计数据"""
+        if not self.batch:
+            return
+            
+        # 聚合托盘明细数据
+        stats = ContainerDetailModel.objects.filter(
+            batch=self.batch,
+            is_delete=False
+        ).aggregate(
+            total_qty=models.Sum('goods_qty'),
+            total_out_qty=models.Sum('goods_out_qty')
+        )
+        
+        # 更新批次数据
+        self.batch.goods_in_qty = stats['total_qty'] or 0
+        self.batch.goods_in_location_qty = (stats['total_qty'] or 0) - (stats['total_out_qty'] or 0)
+        self.batch.goods_out_qty = stats['total_out_qty'] or 0
+        self.batch.save()
+        
+
 
-            container_all_qty = ContainerDetailModel.objects.filter(batch=self.batch,is_delete=False).aggregate(total_qty=models.Sum('goods_qty'))['total_qty']
-            container_all_out_qty = ContainerDetailModel.objects.filter(batch=self.batch,is_delete=False).aggregate(total_out_qty=models.Sum('goods_out_qty'))['total_out_qty']
-            self.batch.goods_in_qty = container_all_qty
-            self.batch.goods_in_location_qty = container_all_qty - container_all_out_qty
-            self.batch.goods_out_qty = container_all_out_qty
-            self.batch.save()
 
 
+
+class ContainerDetailLogModel(models.Model):
+    """托盘明细变更日志模型"""
+    LOG_TYPES = (
+        ('create', '创建'),
+        ('update', '更新'),
+        ('delete', '删除'),
+        ('out', '出库'),
+        ('status_change', '状态变更'),
+    )
+    
+    # 关联的托盘明细
+    container_detail = models.ForeignKey(
+        ContainerDetailModel, 
+        on_delete=models.CASCADE,
+        related_name='logs'
+    )
+    
+    # 日志类型
+    log_type = models.CharField(
+        max_length=20,
+        choices=LOG_TYPES,
+        verbose_name='日志类型'
+    )
+    
+    # 原值
+    old_goods_qty = models.IntegerField(verbose_name='原数量', null=True, blank=True)
+    old_goods_out_qty = models.IntegerField(verbose_name='原出库数量', null=True, blank=True)
+    old_status = models.IntegerField(
+        choices=ContainerDetailModel.BATCH_STATUS,
+        null=True,
+        blank=True,
+        verbose_name='原状态'
+    )
+    
+    # 新值
+    new_goods_qty = models.IntegerField(verbose_name='新数量', null=True, blank=True)
+    new_goods_out_qty = models.IntegerField(verbose_name='新出库数量', null=True, blank=True)
+    new_status = models.IntegerField(
+        choices=ContainerDetailModel.BATCH_STATUS,
+        null=True,
+        blank=True,
+        verbose_name='新状态'
+    )
+    
+    # 元信息
+    creater = models.CharField(max_length=50, verbose_name='操作人')
+    create_time = models.DateTimeField(auto_now_add=True, verbose_name='操作时间')
+    
+    class Meta:
+        db_table = 'container_detail_log'
+        verbose_name = '托盘明细变更日志'
+        verbose_name_plural = "托盘明细变更日志"
+        ordering = ['-create_time']
+    
+    def __str__(self):
+        return f"{self.container_detail} - {self.get_log_type_display()} - {self.create_time.strftime('%Y-%m-%d %H:%M:%S')}"  
+
+
+
+@receiver(pre_save, sender=ContainerDetailModel)
+def container_detail_pre_save(sender, instance, **kwargs):
+    """在托盘明细保存前记录变更"""
+    # 检查是否是新建对象
+    if instance.pk:
+        try:
+            # 获取数据库中的当前对象
+            old_instance = ContainerDetailModel.objects.get(pk=instance.pk)
+        except ContainerDetailModel.DoesNotExist:
+            return
+            
+        # 创建日志记录
+        log = ContainerDetailLogModel(container_detail=instance, creater=instance.creater)
         
+        # 检查数量变化
+        if old_instance.goods_qty != instance.goods_qty:
+            log.old_goods_qty = old_instance.goods_qty
+            log.new_goods_qty = instance.goods_qty
+            log.log_type = 'update'
+            log.save()
+            
+        # 检查出库数量变化
+        if old_instance.goods_out_qty != instance.goods_out_qty:
+            log.old_goods_out_qty = old_instance.goods_out_qty
+            log.new_goods_out_qty = instance.goods_out_qty
+            log.log_type = 'out'
+            log.save()
+
+            
+        # # 检查状态变化
+        # if old_instance.status != instance.status:
+        #     log.old_status = old_instance.status
+        #     log.new_status = instance.status
+        #     log.log_type = 'status_change'
+            
+        # # 保存日志记录
+        # if hasattr(log, 'log_type'):
+        #     log.save()
+
+@receiver(post_save, sender=ContainerDetailModel)
+def container_detail_post_save(sender, instance, created, **kwargs):
+    """在托盘明细保存后记录创建"""
+    if created:
+        # 为新建的对象创建日志
+        ContainerDetailLogModel.objects.create(
+            container_detail=instance,
+            log_type='create',
+            creater=instance.creater,
+            new_goods_qty=instance.goods_qty,
+            new_status=instance.status
+        )
 
     
 

+ 1 - 0
container/views.py

@@ -1943,6 +1943,7 @@ class OutDetailViewSet(viewsets.ModelViewSet):
             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)
+            logger.info(f"change_container_out_qty: {request.data}")
             for container_detail_id, out_qty in request.data.get('detail_list').items():
                 container_detail_obj = ContainerDetailModel.objects.filter(id=container_detail_id,is_delete=False).first()
                 if not container_detail_obj:

+ 6 - 0
logs/server.log

@@ -28009,3 +28009,9 @@ django.db.utils.OperationalError: no such table: user_profile
 [2025-05-31 16:48:06,455][django.server.log_message():187] [INFO] "GET /container/pda/confirmdetail/?container_code=10080 HTTP/1.1" 200 55
 [2025-05-31 16:48:57,577][django.server.log_message():187] [INFO] "GET /container/pda/confirmdetail/?container_code=10080 HTTP/1.1" 200 78
 [2025-05-31 16:49:13,628][django.server.log_message():187] [INFO] "GET /container/pda/confirmdetail/?container_code=10080 HTTP/1.1" 200 78
+[2025-06-03 01:03:38,681][django.server.log_message():187] [INFO] "GET /container/pda/containerdetail/?container_code=10061 HTTP/1.1" 200 834
+[2025-06-03 01:03:38,765][django.server.log_message():187] [INFO] "GET /container/pda/containerdetail/?container_code=10061 HTTP/1.1" 200 834
+[2025-06-03 01:03:38,815][django.server.log_message():187] [INFO] "GET /container/pda/containerdetail/?container_code=10061 HTTP/1.1" 200 834
+[2025-06-03 01:03:38,839][django.server.log_message():187] [INFO] "GET /container/pda/containerdetail/?container_code=10061 HTTP/1.1" 200 834
+[2025-06-03 01:03:38,852][django.server.log_message():187] [INFO] "GET /container/pda/containerdetail/?container_code=10061 HTTP/1.1" 200 834
+[2025-06-03 01:03:38,884][django.server.log_message():187] [INFO] "GET /container/pda/containerdetail/?container_code=10061 HTTP/1.1" 200 834