flower_bs пре 2 дана
родитељ
комит
75e016c32f

+ 2 - 2
bin/views.py

@@ -160,13 +160,13 @@ class locationViewSet(viewsets.ModelViewSet):
             )
         )
         
-        # 收集所有激活链接的容器ID
+        # 收集所有激活链接的托盘ID
         container_ids = set()
         for loc in locations:
             if loc.active_links:  # 每个库位最多只有一个激活链接
                 container_ids.add(loc.active_links[0].container_id)
         
-        # 批量查询容器详情及其批次状态
+        # 批量查询托盘详情及其批次状态
         container_batch_status = defaultdict(dict)  # 改为字典存储,避免重复记录
         if container_ids:
             container_details = ContainerDetailModel.objects.filter(

+ 17 - 0
bound/migrations/0025_alter_batchlogmodel_options.py

@@ -0,0 +1,17 @@
+# Generated by Django 4.1.2 on 2025-07-23 16:32
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('bound', '0024_alter_batchlogmodel_goods_qty_and_more'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='batchlogmodel',
+            options={'ordering': ['-id'], 'verbose_name': 'Batch Log', 'verbose_name_plural': 'Batch Log'},
+        ),
+    ]

+ 1 - 0
bound/models.py

@@ -363,6 +363,7 @@ class BatchLogModel(models.Model):
         db_table = 'batchlog'
         verbose_name = 'Batch Log'
         verbose_name_plural = "Batch Log"
+        ordering = ['-id']
 
 # 利用创建好的批次来与申请单相对应       
 class BoundDetailModel(models.Model):

+ 19 - 1
container/filter.py

@@ -1,8 +1,26 @@
 from django_filters import FilterSet, NumberFilter, CharFilter
-from .models import ContainerListModel,ContainerDetailModel,ContainerOperationModel,TaskModel,ContainerWCSModel,ContainerDetailLogModel
+from .models import ContainerListModel,ContainerDetailModel,ContainerOperationModel,TaskModel,ContainerWCSModel,ContainerDetailLogModel,batchLogModel
 
 from django_filters import rest_framework as filters
 
+
+class batchLogFilter(filters.FilterSet):
+    batch_code__icontains = filters.CharFilter(field_name='batch__bound_number', lookup_expr='icontains')
+    bound_code__icontains = filters.CharFilter(field_name='bound__bound_code', lookup_expr='icontains')
+    class Meta:
+        model = batchLogModel
+        fields = {
+            "id": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
+            "bound": ['exact'],
+            "batch": ['exact'],
+            "log_type": ['exact', 'icontains'],
+            "goods_code": ['exact', 'icontains'],
+            "goods_desc": ['exact', 'icontains'],
+            "goods_in_qty": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
+            "goods_out_qty": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
+            "detail_logs": ['exact'],
+            "create_time": ['exact', 'gt', 'gte', 'lt', 'lte', 'range'],
+        }
 class ContainerDetailLogFilter(filters.FilterSet):
     goods_code = filters.CharFilter(field_name='container_detail__goods_code', lookup_expr='exact')
     goods_code__icontains = filters.CharFilter(field_name='container_detail__goods_code', lookup_expr='icontains')

+ 2 - 2
container/migrations/0022_containerdetaillogmodel_and_more.py

@@ -26,8 +26,8 @@ class Migration(migrations.Migration):
                 ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='操作时间')),
             ],
             options={
-                'verbose_name': '容器明细变更日志',
-                'verbose_name_plural': '容器明细变更日志',
+                'verbose_name': '托盘明细变更日志',
+                'verbose_name_plural': '托盘明细变更日志',
                 'db_table': 'container_detail_log',
                 'ordering': ['-create_time'],
             },

+ 20 - 0
container/migrations/0028_batchlogmodel_bound.py

@@ -0,0 +1,20 @@
+# Generated by Django 4.1.2 on 2025-07-23 14:29
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('bound', '0024_alter_batchlogmodel_goods_qty_and_more'),
+        ('container', '0027_containerdetaillogmodel_tobatchlog'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='batchlogmodel',
+            name='bound',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='logs', to='bound.boundlistmodel'),
+        ),
+    ]

+ 32 - 0
container/migrations/0029_remove_batchlogmodel_new_goods_qty_and_more.py

@@ -0,0 +1,32 @@
+# Generated by Django 4.1.2 on 2025-07-23 14:53
+
+from decimal import Decimal
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('container', '0028_batchlogmodel_bound'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='batchlogmodel',
+            name='new_goods_qty',
+        ),
+        migrations.RemoveField(
+            model_name='batchlogmodel',
+            name='old_goods_qty',
+        ),
+        migrations.AddField(
+            model_name='batchlogmodel',
+            name='goods_in_qty',
+            field=models.DecimalField(blank=True, decimal_places=3, default=Decimal('0'), max_digits=10, null=True, verbose_name='新入数量'),
+        ),
+        migrations.AddField(
+            model_name='batchlogmodel',
+            name='goods_out_qty',
+            field=models.DecimalField(blank=True, decimal_places=3, default=Decimal('0'), max_digits=10, null=True, verbose_name='新出数量'),
+        ),
+    ]

+ 22 - 0
container/migrations/0030_batchlogmodel_detail_logs_alter_batchlogmodel_table.py

@@ -0,0 +1,22 @@
+# Generated by Django 4.1.2 on 2025-07-23 16:32
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('container', '0029_remove_batchlogmodel_new_goods_qty_and_more'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='batchlogmodel',
+            name='detail_logs',
+            field=models.ManyToManyField(related_name='batch_logs', to='container.containerdetaillogmodel', verbose_name='关联托盘日志'),
+        ),
+        migrations.AlterModelTable(
+            name='batchlogmodel',
+            table='batch_log_from_container_log',
+        ),
+    ]

+ 223 - 77
container/models.py

@@ -4,6 +4,12 @@ from django.utils import timezone
 from django.db.models.signals import pre_save, post_save
 from django.dispatch import receiver
 from decimal import Decimal
+from django.db.models import Sum
+from datetime import timedelta 
+from django.db.models import Q
+import logging
+
+logger = logging.getLogger(__name__)
 # Create your models here.
 # 主表:托盘数据
 class ContainerListModel(models.Model):
@@ -161,32 +167,7 @@ class ContainerDetailModel(models.Model):
 
 
 
-class batchLogModel(models.Model):
-    """批次日志模型"""
-    LOG_TYPES = (
-        ('create', '创建'),
-        ('update', '更新'),
-        ('delete', '删除'),
-        ('out', '出库'),
-        ('cancel_out', '取消出库'),
-        ('status_change', '状态变更'),
-    )
 
-    batch = models.ForeignKey(BoundBatchModel, on_delete=models.CASCADE, related_name='logs')
-    log_type = models.CharField(max_length=20, choices=LOG_TYPES, verbose_name='日志类型')
-    goods_code = models.CharField(max_length=50, verbose_name='货品编码')
-    goods_desc = models.CharField(max_length=100, verbose_name='货品描述')
-    old_goods_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='原数量', null=True, blank=True)
-    new_goods_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='新数量', null=True, blank=True)
-    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
-    class Meta:
-        db_table = 'batch_log'
-        verbose_name = '批次日志'
-        verbose_name_plural = "批次日志"
-        ordering = ['-create_time']
-    
-    def __str__(self):
-        return f"{self.batch} - {self.get_log_type_display()} - {self.create_time.strftime('%Y-%m-%d %H:%M:%S')}"  
     
     
    
@@ -250,77 +231,242 @@ class ContainerDetailLogModel(models.Model):
     def __str__(self):
         return f"{self.container_detail} - {self.get_log_type_display()} - {self.create_time.strftime('%Y-%m-%d %H:%M:%S')}"  
 
-# # 根据时间、类型来判断返回当前的主单、批次,累计出入库数量,并将tobatchlog设置为True,将日志记录转移到批次日志中
-def find_batch_by_time_and_type(ContainerDetailLogModel_obj):
-    time = ContainerDetailLogModel_obj.create_time
-    operation_type= ContainerDetailLogModel_obj.log_type
-    batch_number = ContainerDetailLogModel_obj.container_detail.batch.bound_number
-    if operation_type == 'create':
-        # 这里查找逻辑是先找到最近的批次日志,然后再找主单
-        batchlogobj = batchLogModel.objects.filter(log_type='create', batch__bound_number=batch_number, create_time__lte=time).order_by('-create_time').first()
-        if batchlogobj:
 
+class batchLogModel(models.Model):
+    """批次日志模型"""
+    LOG_TYPES = (
+        ('create', '创建'),
+        ('update', '更新'),
+        ('delete', '删除'),
+        ('out', '出库'),
+        ('cancel_out', '取消出库'),
+        ('status_change', '状态变更'),
+    )
+    bound = models.ForeignKey(BoundListModel, on_delete=models.CASCADE, related_name='logs', null=True, blank=True)
+    batch = models.ForeignKey(BoundBatchModel, on_delete=models.CASCADE, related_name='logs')
+    log_type = models.CharField(max_length=20, choices=LOG_TYPES, verbose_name='日志类型')
+    goods_code = models.CharField(max_length=50, verbose_name='货品编码')
+    goods_desc = models.CharField(max_length=100, verbose_name='货品描述')
+    goods_in_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='新入数量', null=True, blank=True)
+    goods_out_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='新出数量', null=True, blank=True)
+    detail_logs = models.ManyToManyField(
+        ContainerDetailLogModel,
+        related_name='batch_logs',
+        verbose_name='关联托盘日志'
+    )
+    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
+    class Meta:
+        db_table = 'batch_log_from_container_log'
+        verbose_name = '批次日志'
+        verbose_name_plural = "批次日志"
+        ordering = ['-create_time']
+    
+    def __str__(self):
+        return f"{self.batch} - {self.get_log_type_display()} - {self.create_time.strftime('%Y-%m-%d %H:%M:%S')}"  
+    
+    def update_from_details(self):
+        """从关联的托盘日志更新批次日志数据"""
+        # 获取所有关联的托盘日志
+        detail_logs = self.detail_logs.all()
+        
+        # 计算聚合值
+        total_old_qty = detail_logs.aggregate(total=Sum('old_goods_qty'))['total'] or 0
+        total_new_qty = detail_logs.aggregate(total=Sum('new_goods_qty'))['total'] or 0
+        total_old_out = detail_logs.aggregate(total=Sum('old_goods_out_qty'))['total'] or 0
+        total_new_out = detail_logs.aggregate(total=Sum('new_goods_out_qty'))['total'] or 0
+        
+        # 更新批次日志
+        self.goods_in_qty = total_new_qty - total_old_qty
+        self.goods_out_qty = total_new_out - total_old_out
+        self.save()
+        
+        # 同时更新批次统计数据
+        self.update_batch_stats()
+    
+    def update_batch_stats(self):
+        """更新批次的统计数据"""
+        # 聚合托盘明细数据
+        stats = ContainerDetailModel.objects.filter(
+            batch=self.batch,
+            is_delete=False
+        ).aggregate(
+            total_qty=Sum('goods_qty'),
+            total_out_qty=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()
 
+# 批次日志聚合处理器
+def aggregate_to_batch_log(container_log):
+    """将托盘日志聚合到批次日志"""
+    logger.info(f"开始聚合托盘日志: {container_log.id}")
+    
+    # 检查日志是否已处理
+    if container_log.tobatchlog:
+        logger.info(f"托盘日志 {container_log.id} 已处理过,跳过")
+        return
+    
+    try:
+        # 确定批次
+        detail = container_log.container_detail
+        if not detail:
+            logger.warning(f"托盘日志 {container_log.id} 缺少关联的托盘明细")
+            return
+            
+        batch = detail.batch
+        if not batch:
+            logger.warning(f"托盘明细 {detail.id} 缺少关联的批次")
+            return
+        
+        # 确定操作类型
+        log_type = container_log.log_type
+        
+        # 创建或获取批次日志(按类型和时间窗口分组)
+        # 设置时间窗口(5分钟)
+        time_window_start = container_log.create_time - timedelta(minutes=2)
+        time_window_end = container_log.create_time + timedelta(minutes=2)
+        
+        # 查找相同批次、相同类型、在时间窗口内的批次日志
+        existing_logs = batchLogModel.objects.filter(
+            batch=batch,
+            log_type=log_type,
+            create_time__gte=time_window_start,
+            create_time__lte=time_window_end
+        ).order_by('create_time')
+        
+        if existing_logs.exists():
+            # 使用时间窗口内最早的批次日志
+            batch_log = existing_logs.first()
+            logger.info(f"找到已有批次日志 {batch_log.id} 用于聚合")
+        else:
+            # 创建新的批次日志
+            batch_log = batchLogModel.objects.create(
+                batch=batch,
+                log_type=log_type,
+                goods_code=batch.goods_code,
+                goods_desc=batch.goods_desc,
+                create_time=container_log.create_time
+            )
+            logger.info(f"创建新批次日志 {batch_log.id}")
+        
+        # 将托盘日志关联到批次日志
+        batch_log.detail_logs.add(container_log)
+        
+        # 从关联日志更新批次日志数据
+        batch_log.update_from_details()
+        
+        # 根据日志类型添加关联单据信息
+        if log_type == 'out' and not batch_log.bound:
+            from bound.models import OutBoundDetailModel
+            bound = OutBoundDetailModel.objects.filter(
+                bound_batch_number=batch
+            ).order_by('-create_time').first()
+            if bound:
+                batch_log.bound = bound.bound_list
+                batch_log.save()
+                logger.info(f"为批次日志 {batch_log.id} 添加出库单关联")
+
+        if log_type == 'create' and not batch_log.bound:
+            from bound.models import BoundDetailModel
+            bound = BoundDetailModel.objects.filter(
+                bound_batch=batch
+            ).order_by('-create_time').first()
+            if bound:
+                batch_log.bound = bound.bound_list
+                logger.info(f"为批次日志 {batch_log.id} 添加入库单关联")
+                batch_log.save()
+    
+        
+        # 标记日志已处理
+        container_log.tobatchlog = True
+        container_log.save()
+        
+        logger.info(f"成功聚合托盘日志 {container_log.id} 到批次日志 {batch_log.id}")
+        return batch_log
+    
+    except Exception as e:
+        logger.error(f"聚合托盘日志时出错: {e}", exc_info=True)
+        raise
+        
+
+
+# 简化的信号处理器
+@receiver(post_save, sender=ContainerDetailLogModel)
+def handle_container_detail_log(sender, instance, created, **kwargs):
+    """创建托盘日志后立即关联到批次日志"""
+    if created:
+        try:
+            aggregate_to_batch_log(instance)
+        except Exception as e:
+            print(f"Error aggregating log: {e}")
 
 
 
 @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
+            logs = []
             
-        # 创建日志记录
-        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_qty != instance.goods_qty:
+                logs.append(ContainerDetailLogModel(
+                    container_detail=instance,
+                    log_type='update',
+                    old_goods_qty=old_instance.goods_qty,
+                    new_goods_qty=instance.goods_qty,
+                    creater=instance.creater
+                ))
             
-        # 检查出库数量变化
-        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
-            if log.old_goods_out_qty < log.new_goods_out_qty:
-                log.log_type = 'out'
-            else:
-                log.log_type = 'cancel_out'
-            log.save()
-
-        if old_instance.is_delete != instance.is_delete:
-            log.log_type = 'delete'
-            log.old_goods_qty = old_instance.goods_qty
-            log.old_goods_out_qty = old_instance.goods_out_qty
-            log.old_status = old_instance.status
-            log.new_goods_qty = instance.goods_qty
-            log.new_goods_out_qty = instance.goods_out_qty
-            log.new_status = instance.status
-            log.save()
+            # 出库数量变化日志
+            if old_instance.goods_out_qty != instance.goods_out_qty:
+                if old_instance.goods_out_qty < instance.goods_out_qty:
+                    log_type = 'out'
+                else:
+                    log_type = 'cancel_out'
+                
+                logs.append(ContainerDetailLogModel(
+                    container_detail=instance,
+                    log_type=log_type,
+                    old_goods_out_qty=old_instance.goods_out_qty,
+                    new_goods_out_qty=instance.goods_out_qty,
+                    creater=instance.creater
+                ))
             
-        # # 检查状态变化
-        # if old_instance.status != instance.status:
-        #     log.old_status = old_instance.status
-        #     log.new_status = instance.status
-        #     log.log_type = 'status_change'
+            # 删除日志
+            if old_instance.is_delete != instance.is_delete and instance.is_delete:
+                logs.append(ContainerDetailLogModel(
+                    container_detail=instance,
+                    log_type='delete',
+                    old_goods_qty=old_instance.goods_qty,
+                    old_goods_out_qty=old_instance.goods_out_qty,
+                    new_goods_qty = old_instance.goods_qty,
+                    new_goods_out_qty = old_instance.goods_qty,
+                    old_status=old_instance.status,
+                    creater=instance.creater
+                ))
             
-        # # 保存日志记录
-        # if hasattr(log, 'log_type'):
-        #     log.save()
+            # 批量创建日志
+            if logs:
+                created_logs = ContainerDetailLogModel.objects.bulk_create(logs)
+                for log in created_logs:
+                # 由于bulk_create不会触发信号,我们手动调用信号处理函数
+                    handle_container_detail_log(ContainerDetailLogModel, log, created=True)
+                
+        except ContainerDetailModel.DoesNotExist:
+            pass
+
 
 @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',

+ 22 - 1
container/serializers.py

@@ -1,10 +1,31 @@
 from rest_framework import serializers
 
-from .models import ContainerListModel,ContainerDetailModel,ContainerOperationModel,TaskModel,ContainerWCSModel,out_batch_detail,ContainerDetailLogModel
+from .models import ContainerListModel,ContainerDetailModel,ContainerOperationModel,TaskModel,ContainerWCSModel,out_batch_detail,ContainerDetailLogModel,batchLogModel
 from bound.models import BoundBatchModel,BoundDetailModel
 
 from utils import datasolve
 from decimal import Decimal
+
+class batchLogModelSerializer(serializers.ModelSerializer):
+
+    # 定义批次日志的序列化器,用于获取操作,字段只读
+    bound_code = serializers.SerializerMethodField()
+    batch_code = serializers.CharField(source='batch.bound_number', read_only=True)
+    goods_unit = serializers.CharField(source='batch.goods_unit', read_only=True)
+    check_status = serializers.IntegerField(source='batch.check_status', read_only=True)
+    create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M', required=False)
+    class Meta:
+        # 指定模型和排除字段
+        model = batchLogModel
+        fields= ['id', 'bound_code', 'batch_code', 'log_type', 'goods_code', 'goods_desc', 'goods_in_qty', 'goods_out_qty', 'goods_unit','check_status','create_time']
+        read_only_fields = ['id']
+
+    def get_bound_code(self, obj):
+        """ 动态序列化关联的批次数据 """
+        return obj.bound.bound_code if obj.bound else '无主单,涉及手动操作'
+    
+   
+    
 class WCSTaskGetSerializer(serializers.ModelSerializer):
     class Meta:
         # 指定模型和排除字段

+ 3 - 0
container/urls.py

@@ -60,4 +60,7 @@ re_path(r'^out_detail/(?P<pk>\d+)/$', views.OutDetailViewSet.as_view({
 
 path(r'detaillog/', views.ContainerDetailLogModelViewSet.as_view({"get": "list", "post": "create"}), name="Task"),
 
+path(r'batchdetaillog/', views.batchLogModelViewSet.as_view({"get": "list", "post": "create"}), name="Task"),
+path(r'batchdetaillog/containerlog/', views.batchLogModelViewSet.as_view({ "get": "get_container_operation_log"}), name="Task"),
+
 ]

+ 71 - 4
container/views.py

@@ -14,7 +14,7 @@ import requests
 from django.db import transaction
 import logging
 from rest_framework import status
-from .models import ContainerListModel,ContainerDetailModel,ContainerOperationModel,ContainerWCSModel,TaskModel,out_batch_detail,ContainerDetailLogModel
+from .models import ContainerListModel,ContainerDetailModel,ContainerOperationModel,ContainerWCSModel,TaskModel,out_batch_detail,ContainerDetailLogModel,batchLogModel
 from bound.models import BoundDetailModel,BoundListModel,OutBoundDetailModel
 from bin.views import LocationAllocation,base_location
 from bin.models import LocationModel,LocationContainerLink,LocationGroupModel
@@ -27,7 +27,8 @@ from .serializers import TaskGetSerializer,TaskPostSerializer
 from .serializers import WCSTaskGetSerializer
 from .serializers import OutBoundFullDetailSerializer,OutBoundDetailSerializer
 from .serializers import ContainerDetailLogSerializer
-from .filter import ContainerDetailFilter,ContainerListFilter,ContainerOperationFilter,TaskFilter,WCSTaskFilter,ContainerDetailLogFilter
+from .serializers import batchLogModelSerializer
+from .filter import ContainerDetailFilter,ContainerListFilter,ContainerOperationFilter,TaskFilter,WCSTaskFilter,ContainerDetailLogFilter,batchLogFilter
 
 from rest_framework.permissions import AllowAny
 import threading
@@ -38,6 +39,71 @@ from django.db.models import Sum
 from staff.models import ListModel as StaffListModel
 logger = logging.getLogger(__name__)
 
+# 托盘流水汇总批次流水
+class batchLogModelViewSet(viewsets.ModelViewSet):
+
+    """
+        retrieve:
+            Response a data list(get)
+        list:
+            Response a data list(all)
+        create:
+            Create a data line(post)
+
+        delete:
+            Delete a data line(delete)
+
+
+    """
+    pagination_class = MyPageNumberPagination   
+    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
+    ordering_fields = ['id', "create_time", "update_time", ]
+    filter_class = batchLogFilter
+    
+    def get_project(self):
+        try:
+            id = self.kwargs.get('pk')
+            return id
+        except:
+            return None
+
+    def get_queryset(self):
+        id = self.get_project()
+        if self.request.user:
+            if id is None:
+                return batchLogModel.objects.filter()
+            else:
+                return batchLogModel.objects.filter(id=id)
+        else:
+            return batchLogModel.objects.none()
+        
+    def get_serializer_class(self):
+        if self.action in ['list', 'destroy','retrieve']:
+            return batchLogModelSerializer
+        else:
+            return self.http_method_not_allowed(request=self.request)
+
+    def create(self, request, *args, **kwargs):
+        data = self.request.data
+        return Response(data, status=200, headers=headers)
+    
+    def update(self, request, pk):
+        qs = self.get_object()
+        data = self.request.data
+        serializer = self.get_serializer(qs, data=data)
+        serializer.is_valid(raise_exception=True)
+        serializer.save()
+        headers = self.get_success_headers(serializer.data)  
+        return Response(serializer.data, status=200, headers=headers)
+    
+    def get_container_operation_log(self,request):
+        batchlog_id = self.request.query_params.get('batchlog_id')
+        batch_obj = batchLogModel.objects.get(id=batchlog_id)
+        container_operation_log = batch_obj.detail_logs.all()
+        serializer = ContainerDetailLogSerializer(container_operation_log, many=True)
+        return Response(serializer.data, status=200)
+    
+
 # 进出库log查看
 class ContainerDetailLogModelViewSet(viewsets.ModelViewSet):
     """
@@ -460,7 +526,7 @@ class ContainerWCSViewSet(viewsets.ModelViewSet):
                         self.generate_task_no_batch(container, start_location, allocation_target_location,batch_id,location_min_value.c_number) 
                         self.generate_container_operate_no_batch(container_obj, batch_id, allocation_target_location)
                     elif batch_info['class'] == 3:
-                        self.generate_task_no_batch(container, target_location, allocation_target_location,batch_id,location_min_value.c_number) 
+                        self.generate_task_no_batch(container, start_location, allocation_target_location,batch_id,location_min_value.c_number) 
                         self.generate_move_container_operate(container_obj, allocation_target_location)
                     else:   
                         self.generate_task(container, start_location, allocation_target_location,batch_id,location_min_value.c_number)  # 生成任务
@@ -2402,11 +2468,12 @@ 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)
-            out_batch_detail_all = out_batch_detail.objects.filter(container=container_obj,working=1,is_delete=False).all()
+            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

Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
templates/dist/spa/js/chunk-common.ba1643f5.js


+ 1 - 1
templates/src/components/containercard.vue

@@ -717,7 +717,7 @@ export default {
           editing: false
         }))
       }).catch(err => {
-        console.error('获取容器详情失败:', err)
+        console.error('获取托盘详情失败:', err)
         _this.$q.notify({
           message: '获取托盘详情失败:' + (err.response?.data?.detail || err.message),
           color: 'negative'

+ 5 - 5
templates/src/pages/count/batch copy.vue

@@ -319,7 +319,7 @@
                 class="col-6"
                 v-model="editForm.check_status"
                 :options="[
-                  { label: '待审核', value: 0 },
+                  { label: '待质检', value: 0 },
                   { label: '质检合格', value: 1 },
                   { label: '质检问题', value: 2 },
                 ]"
@@ -632,7 +632,7 @@ export default {
 
     checkStatusToText (check_status) {
       const statusTexts = {
-        0: '待审核',
+        0: '待质检',
         1: '质检合格',
         2: '质检问题'
       }
@@ -642,7 +642,7 @@ export default {
     getRowStyle (row) {
       // 根据check_status值返回不同的背景色
       const statusColors = {
-        0: '#fff9c4', // 更浅的黄色 - 待审核
+        0: '#fff9c4', // 更浅的黄色 - 待质检
         1: '#c8e6c9', // 更浅的绿色 - 质检合格
         2: '#ffcdd2' // 更浅的红色 - 质检问题
       }
@@ -667,7 +667,7 @@ export default {
           ]
         case 'bound_status':
           return [
-            { label: '待审核', value: 0 },
+            { label: '待质检', value: 0 },
             { label: '质检合格', value: 1 }
           ]
         case 'bound_department':
@@ -675,7 +675,7 @@ export default {
 
         case 'check_status':
           return [
-            { label: '待审核', value: 0 },
+            { label: '待质检', value: 0 },
             { label: '质检合格', value: 1 },
             { label: '质检问题', value: 2 }
           ]

+ 346 - 90
templates/src/pages/count/predeliverystock.vue

@@ -16,10 +16,12 @@
         flat
         bordered
       >
-          <template v-slot:header-cell="props">
+        <template v-slot:header-cell="props">
           <q-th :props="props" @dblclick="handleHeaderDblClick(props.col)">
             <!-- 为特定列添加下拉选择器 -->
-            <template v-if="['bound_department'].includes(props.col.name)">
+            <template
+              v-if="['check_status', 'log_type'].includes(props.col.name)"
+            >
               <q-select
                 dense
                 outlined
@@ -59,107 +61,195 @@
           <q-space />
 
           <div class="flex items-center">
-            <q-btn-group push class="q-ml-md"> </q-btn-group>
+            <div class="q-mr-md">{{ $t("download_center.createTime") }}</div>
             <q-input
+              readonly
               outlined
-              rounded
               dense
-              debounce="300"
-              color="primary"
-              v-model="filter"
-              :placeholder="$t('search')"
-              @input="getSearchList()"
-              @keyup.enter="getSearchList()"
+              v-model="createDate2"
+              :placeholder="interval"
             >
               <template v-slot:append>
-                <q-icon name="search" @click="getSearchList()" />
+                <q-icon name="event" class="cursor-pointer">
+                  <q-popup-proxy
+                    ref="qDateProxy"
+                    transition-show="scale"
+                    transition-hide="scale"
+                  >
+                    <q-date v-model="createDate1" range>
+                      <div class="row items-center justify-end q-gutter-sm">
+                        <q-btn
+                          :label="$t('index.cancel')"
+                          color="primary"
+                          flat
+                          v-close-popup
+                        />
+                        <q-btn
+                          :label="$t('index.clear')"
+                          color="primary"
+                          @click="
+                            createDate2 = '';
+                            createDate1 = '';
+                          "
+                          v-close-popup
+                        />
+                      </div>
+                    </q-date>
+                  </q-popup-proxy>
+                </q-icon>
               </template>
             </q-input>
+            <q-btn-group push class="q-ml-md"> </q-btn-group>
+
           </div>
         </template>
 
         <template v-slot:body="props">
-          <q-tr :props="props">
+          <q-tr :props="props" :style="getRowStyle(props.row)">
             <q-td auto-width>
               <q-btn
                 size="sm"
                 round
-                :icon="props.row.expand ? 'remove' : 'ballot'"
+                :icon="props.row.expand ? 'unfold_less' : 'expand'"
                 @click="handle_row_expand(props.row)"
-              />
+              >
+                <q-tooltip
+                  content-class="bg-amber text-black shadow-4"
+                  :offset="[10, 10]"
+                  content-style="font-size: 12px"
+                >
+                  {{ "查看流水下的托盘日志" }}
+                </q-tooltip>
+              </q-btn>
             </q-td>
+
             <q-td
               v-for="col in columns.filter((c) => c.name !== 'expand')"
               :key="col.name"
               :props="props"
             >
-              {{ col.field ? props.row[col.field] : props.row[col.name] }}
+              <span v-if="col.name === 'check_status'">
+                {{ checkStatusToText(props.row[col.field]) }}
+              </span>
+              <span v-else-if="col.name === 'goods_qty'">
+                {{ props.row.goods_in_qty - props.row.goods_out_qty }}
+              </span>
+              <span v-else-if="col.name === 'log_type'">
+                {{ logtypeToText(props.row[col.field]) }}
+              </span>
+              <span
+                v-else-if="
+                  ['check_status', 'goods_qty'].indexOf(col.name) === -1
+                "
+              >
+                {{ col.field ? props.row[col.field] : props.row[col.name] }}
+              </span>
             </q-td>
           </q-tr>
 
           <!-- 第二级:时间轴 -->
-          <q-tr v-show="props.row.expand" :props="props" class="expanded-row">
-            <q-td colspan="100%">
-              <div class="q-pa-md timeline-wrapper">
-                <q-timeline
-                  color="#e0e0e0"
-                  v-if="props.row.batch_items?.length"
-                >
-                  <q-timeline-entry
-                    v-for="(batch_item, index) in props.row.batch_items"
-                    :key="index"
-                    class="custom-node"
+          <q-tr
+            v-show="props.row.expand"
+            :props="props"
+            class="expanded-row"
+            :style="getRowStyle(props.row)"
+          >
+            <template>
+              <q-td colspan="100%">
+                <div class="q-pa-md timeline-wrapper">
+                  <q-timeline
+                    color="#e0e0e0"
+                    v-if="props.row.containers?.length"
                   >
-                    <template v-slot:title>
-                      <span>
-                        <div>批次 {{ batch_item.bound_number }}</div>
-                        <div class="row">
-                          <div class="col">
-                            <div class="custom-title">
-                              {{ batch_item.goods_desc }}
-                            </div>
-                          </div>
-                          <div class="col">
-                            <div class="custom-title">
-                              计划数量:{{ batch_item.goods_qty }}
-                            </div>
+                    <q-timeline-entry
+                      v-for="(container, index) in props.row.containers"
+                      :key="index"
+                      class="custom-node"
+                    >
+                      <template v-slot:title>
+                        <span>
+                          <div>
+                            托盘 {{ container.container_code }}
+                            <span class="text-caption">
+                              操作时间:{{ container.create_time }}
+                            </span>
                           </div>
+                        </span>
+                      </template>
+
+                      <template v-slot:subtitle>
+                        <div class="text-caption">
+                          批次: {{ container.batch }} |
+                          物料编码:
+                          {{ container.goods_code }} |{{ container.goods_desc }} |
+                          <br>
+                          批次计划入库数:
+                          {{ container.batch_goods_qty }} |
+                          批次总组盘数:
+                          {{ container.batch_goods_in_qty }} |
+                          批次总出库数:
+                          {{ container.batch_goods_out_qty }} |
+                          在库数量:
+                          {{ container.batch_goods_in_location_qty }} |
+                        </div>
+                      </template>
 
-                          <div class="col">
-                            <div class="custom-title">
-                              入库数量:{{ batch_item.goods_in_qty }}
+                      <div class="timeline-content">
+                        <div class="row">
+                          <div class="col-6">
+                            <div class="text-caption">
+                              操作类型: {{ logtypeToText(container.log_type) }}
                             </div>
-                          </div>
-                          <div class="col">
-                            <div class="custom-title">
-                              实际在库数量:{{
-                                batch_item.goods_in_location_qty
-                              }}
+                            <div class="text-caption">
+                              操作人: {{ container.creater }}
                             </div>
                           </div>
-
-                          <div class="col">
-                            <div class="custom-title">
-                              预定/已出库数量:{{
-                                batch_item.goods_out_qty
-                              }}
-                            </div>
+                          <div class="col-6">
+                            数量变化:<br />
+                            <span v-if="container.log_type === 'delete'">
+                              {{ container.old_goods_qty }} →
+                              {{ container.new_goods_qty }}
+                            </span>
+                            <span v-else-if="container.log_type === 'out'">
+                              出库数量:{{ container.old_goods_out_qty }} →
+                              {{ container.new_goods_out_qty }}
+                            </span>
+                            <span v-else-if="container.log_type === 'create'">
+                              入库数量:{{ container.old_goods_qty }} →
+                              {{ container.new_goods_in_qty }}
+                            </span>
+
+                            <span v-else-if="container.log_type === 'update'">
+                              入库数量:{{ container.old_goods_qty }} →
+                              {{ container.new_goods_qty }}
+                              <br />
+                              出库数量:{{ container.old_goods_out_qty }} →
+                              {{ container.new_goods_out_qty }}
+                            </span>
+
+                            <!-- <div class="text-caption">
+                              状态:
+                              <span v-if="container.old_status !== null">
+                                {{ getStatusText(container.old_status) }} →
+                                {{ getStatusText(container.new_status) }}
+                              </span>
+                            </div> -->
                           </div>
                         </div>
-                      </span>
-                    </template>
-                  </q-timeline-entry>
-                </q-timeline>
-                <div v-else-if="props.row.loading" class="text-center q-pa-md">
-                  <q-spinner color="primary" size="2em" />
-                  <div class="q-mt-sm">正在加载信息...</div>
+                      </div>
+                    </q-timeline-entry>
+                  </q-timeline>
+                  <div v-else class="text-caption text-grey">
+                    暂无容器操作记录
+                  </div>
                 </div>
-              </div>
-            </q-td>
+              </q-td>
+            </template>
           </q-tr>
         </template>
       </q-table>
     </transition>
+
     <template>
       <div v-show="max !== 0" class="q-pa-lg flex flex-center">
         <div>{{ total }}</div>
@@ -192,12 +282,15 @@
 <router-view />
 
 <script>
-import { getauth, postauth, putauth, deleteauth } from 'boot/axios_request'
-import { filter } from 'jszip'
+import { getauth, putauth } from 'boot/axios_request'
 import { date, LocalStorage } from 'quasar'
+import containercard from 'components/containercard.vue'
 
 export default {
   name: 'PageTask',
+  components: {
+    containercard
+  },
   data () {
     return {
       createDate1: '',
@@ -211,13 +304,14 @@ export default {
       login_name: '',
       authin: '0',
       searchUrl: '',
-      pathname: 'bound/batch/count/',
+      pathname: 'container/batchdetaillog/',
       pathname_previous: '',
       pathname_next: '',
       separator: 'cell',
       loading: false,
       height: '',
       viewForm: false,
+      editDialog: false,
 
       table_list: [],
       columns: [
@@ -228,6 +322,32 @@ export default {
           headerStyle: 'width: 50px'
         },
 
+        {
+          name: 'create_time',
+          label: '操作时间',
+          field: 'create_time',
+          align: 'center',
+          headerStyle: 'width: 40px'
+        },
+        {
+          name: 'log_type',
+          label: '操作类型',
+          field: 'log_type',
+          align: 'center',
+          headerStyle: 'width: 20px'
+        },
+        {
+          name: 'bound_code',
+          label: '单据编号',
+          align: 'center',
+          field: 'bound_code'
+        },
+        {
+          name: 'batch_code',
+          label: '批号',
+          align: 'center',
+          field: 'batch_code'
+        },
         {
           name: 'goods_code',
           label: '存货编码',
@@ -242,18 +362,24 @@ export default {
         },
 
         {
-          name: 'total_quantity',
-          label: '在库数目',
-          field: 'total_quantity',
+          name: 'goods_qty',
+          label: '数目',
           align: 'center'
         },
-
         {
           name: 'goods_unit',
           label: '单位',
           field: 'goods_unit',
           align: 'center',
           headerStyle: 'width: 20px'
+        },
+
+        {
+          name: 'check_status',
+          label: '质检状态',
+          field: 'check_status',
+          align: 'center',
+          headerStyle: 'width: 20px'
         }
       ],
       filter: '',
@@ -267,12 +393,31 @@ export default {
       paginationIpt: 1,
       containers: {},
       timer: null,
-            filterModels: {
+      showInventoryDetails: false,
+      select_container_number: 0,
+      select_container_code: 0,
+      filterModels: {
         bound_department: null
       },
+      editForm: {
+        id: '',
+        bound_number: '',
+        goods_code: '',
+        goods_desc: '',
+        goods_qty: '',
+        goods_unit: '',
+        goods_package: '',
+        goods_in_qty: '',
+        goods_out_qty: '',
+        goods_std: '',
+        check_status: '',
+        check_user: '默认质检人',
+        create_time: '',
+        note: '无'
+      },
       activeSearchField: '',
       activeSearchLabel: '',
-      filterdata: {},
+      filterdata: {}
     }
   },
   computed: {
@@ -285,7 +430,82 @@ export default {
     }
   },
   methods: {
-       // 处理过滤变化
+    handleEditRow (row) {
+      this.editForm = { ...row } // 复制当前行的数据到表单
+      this.editForm.note = this.editForm.note || '无' // 防止note为空
+      this.editForm.check_user = this.editForm.check_user || '默认质检人' // 防止check_user为空
+      console.log(this.editForm)
+      this.editDialog = true // 打开对话框
+    },
+    saveEditRow () {
+      const _this = this
+      putauth(`bound/batch/${_this.editForm.id}/`, _this.editForm) // 假设修改API是这样的
+        .then((res) => {
+          _this.editDialog = false // 关闭对话框
+
+          if (res.status_code !== 400) {
+            _this.$q.notify({ message: '修改成功', color: 'positive' })
+            _this.getSearchList() // 刷新列表
+          } else {
+            // 错误信息的键值映射到中文字段名称
+            const errorFieldMap = {
+              note: '备注',
+              check_user: '质检人'
+            }
+
+            // 遍历 res 对象的属性,查找错误信息
+            let errorMessage = '修改失败'
+            for (const key in res) {
+              if (Array.isArray(res[key]) && res[key].length > 0) {
+                const fieldLabel = errorFieldMap[key] || key
+                errorMessage = `${fieldLabel}: ${res[key].join(' ')}`
+                break
+              }
+            }
+            console.error('修改失败', errorMessage)
+            _this.$q.notify({ message: errorMessage, color: 'negative' })
+          }
+        })
+        .catch((error) => {
+          console.error('修改失败', error)
+          _this.$q.notify({
+            message: '发生未知错误,请联系管理员',
+            color: 'negative'
+          })
+        })
+    },
+
+    checkStatusToText (check_status) {
+      const statusTexts = {
+        0: '待质检',
+        1: '质检合格',
+        2: '质检问题'
+      }
+
+      return statusTexts[check_status] || '未知状态'
+    },
+    logtypeToText (log) {
+      const logtypeTexts = {
+        create: '批次入库',
+        out: '批次出库',
+        delete: '批次删除',
+        update: '批次更新'
+      }
+
+      return logtypeTexts[log] || '未知状态'
+    },
+    getRowStyle (row) {
+      // 根据check_status值返回不同的背景色
+      const statusColors = {
+        0: '#fff9c4', // 更浅的黄色 - 待质检
+        1: '#c8e6c9', // 更浅的绿色 - 质检合格
+        2: '#ffcdd2' // 更浅的红色 - 质检问题
+      }
+
+      const color = statusColors[row.check_status] || ''
+      return color ? { backgroundColor: color } : {}
+    },
+    // 处理过滤变化
     handleFilterChange () {
       this.pagination.page = 1
       this.getSearchList(1)
@@ -302,15 +522,40 @@ export default {
           ]
         case 'bound_status':
           return [
-            { label: '待审核', value: 0 },
-            { label: '确认无误', value: 1 }
+            { label: '待质检', value: 0 },
+            { label: '质检合格', value: 1 }
           ]
         case 'bound_department':
           return this.bound_department_list
+
+        case 'check_status':
+          return [
+            { label: '待质检', value: 0 },
+            { label: '质检合格', value: 1 },
+            { label: '质检问题', value: 2 }
+          ]
+
+        case 'log_type':
+          return [
+            { label: '批次入库', value: 'create' },
+            { label: '批次出库', value: 'out' },
+            { label: '批次删除', value: 'delete' },
+            { label: '批次更新', value: 'update' }
+          ]
         default:
           return []
       }
     },
+    getStatusText (status) {
+      const statusMap = {
+        0: '待入库',
+        1: '在库',
+        2: '待出库',
+        3: '已出库',
+        4: '异常'
+      }
+      return statusMap[status] || '未知状态'
+    },
     handleHeaderDblClick (column) {
       // 排除不需要搜索的列
       if (['detail', 'action'].includes(column.name)) return
@@ -389,12 +634,12 @@ export default {
           icon: 'search',
           color: 'positive'
         })
-
         // 重置激活的搜索字段
         this.activeSearchField = ''
         this.activeSearchLabel = ''
       }
     },
+
     class_to_name (class_id) {
       const class_map = {
         1: '整盘',
@@ -409,10 +654,12 @@ export default {
       if (row.expand) {
         // 添加行级 loading 状态
         _this.$set(row, 'loading', true)
-        getauth('bound/batch/count/' + row.id + '/', {})
+        getauth('container/batchdetaillog/containerlog/?batchlog_id=' + row.id)
           .then((res) => {
-            _this.$set(row, 'batch_items', res.batch_items)
-            console.log('当前的', row.batch_items)
+            // 将数据存储到当前行的 containers 属性
+
+            _this.$set(row, 'containers', res)
+            console.log('当前的', row.containers)
           })
           .catch((err) => {
             _this.$q.notify({ message: err.detail, color: 'negative' })
@@ -453,7 +700,15 @@ export default {
           _this.table_list = res.results.map((item) => ({
             ...item,
             expand: false,
-            batch_items: [],
+            containers: [
+              // {
+              //   id: 0,
+              //   container_code: 0,
+              //   current_location: '0',
+              //   goods_qty: 0,
+              //   class: 0
+              // }
+            ],
             loading: false
           }))
           _this.total = res.count
@@ -485,7 +740,8 @@ export default {
       this.getSearchList(this.current)
     },
 
-        getSearchList (page = 1) {
+    // 修改搜索方法以包含过滤条件
+    getSearchList (page = 1) {
       this.current = page
       this.paginationIpt = page
 
@@ -499,9 +755,9 @@ export default {
 
       this.getList({
         number__icontains: this.filter,
-        document_date__range: this.date_range,
-        ...filterParams ,// 添加过滤条件
-        ...this.filterdata, // 添加其他过滤条件
+        create_time__range: this.date_range,
+        ...filterParams, // 添加过滤条件
+        ...this.filterdata // 添加其他过滤条件
       })
     },
 
@@ -603,22 +859,22 @@ export default {
           this.createDate2 = `${val.from} - ${val.to}`
           this.date_range = `${val.from},${val.to} `
 
-          // this.downloadhUrl = this.pathname + 'filelist/?' + 'document_date__range=' + this.date_range
+          // this.downloadhUrl = this.pathname + 'filelist/?' + 'create_time__range=' + this.date_range
         } else {
           this.createDate2 = `${val}`
           this.dateArray = val.split('/')
           this.searchUrl =
             this.pathname +
             '?' +
-            'document_date__year=' +
+            'create_time__year=' +
             this.dateArray[0] +
             '&' +
-            'document_date__month=' +
+            'create_time__month=' +
             this.dateArray[1] +
             '&' +
-            'document_date__day=' +
+            'create_time__day=' +
             this.dateArray[2]
-          // this.downloadhUrl = this.pathname + 'filelist/?' + 'document_date__year=' + this.dateArray[0] + '&' + 'document_date__month=' + this.dateArray[1] + '&' + 'document_date__day=' + this.dateArray[2]
+          // this.downloadhUrl = this.pathname + 'filelist/?' + 'create_time__year=' + this.dateArray[0] + '&' + 'create_time__month=' + this.dateArray[1] + '&' + 'create_time__day=' + this.dateArray[2]
         }
         this.date_range = this.date_range.replace(/\//g, '-')
 

Разлика између датотеке није приказан због своје велике величине
+ 1087 - 0
templates/src/pages/count/batchoperatelog.vue


+ 6 - 0
templates/src/pages/count/count.vue

@@ -9,9 +9,15 @@
         <transition appear enter-active-class="animated zoomIn">
           <q-route-tab name="batch" :label="'批次状态'"  icon="img:statics/inbound/more.png" :to="{ name: 'batch' }" exact/>
         </transition>
+        <transition appear enter-active-class="animated zoomIn">
+          <q-route-tab name="batchlog" :label="'批次流水'"  icon="img:statics/dashboard/in_and_out_statement.svg" :to="{ name: 'batchlog' }" exact/>
+        </transition>
         <transition appear enter-active-class="animated zoomIn">
           <q-route-tab name="detaillog" :label="'托盘日志'"  icon="img:statics/inbound/polist.png" :to="{ name: 'detaillog' }" exact/>
         </transition>
+        <transition appear enter-active-class="animated zoomIn">
+          <q-route-tab name="batchoperatelog" :label="'操作日志'"  icon="img:statics/inbound/polist.png" :to="{ name: 'batchoperatelog' }" exact/>
+        </transition>
         <!-- <transition appear enter-active-class="animated zoomIn">
           <q-route-tab name="shortage" :label="$t('inbound.shortage')" icon="img:statics/inbound/shortage.png" :to="{ name: 'shortage' }" exact/>
         </transition>

+ 1 - 1
templates/src/pages/count/detaillog.vue

@@ -277,7 +277,7 @@ export default {
           ]
         case 'bound_status':
           return [
-            { label: '待审核', value: 0 },
+            { label: '待质检', value: 0 },
             { label: '确认无误', value: 1 }
           ]
         case 'bound_department':

+ 0 - 287
templates/src/pages/count/preloadstock.vue

@@ -1,287 +0,0 @@
-<template>
-    <div>
-      <transition appear enter-active-class="animated fadeIn">
-      <q-table
-        class="my-sticky-header-table shadow-24"
-        :data="table_list"
-        row-key="id"
-        :separator="separator"
-        :loading="loading"
-        :filter="filter"
-        :columns="columns"
-        hide-bottom
-        :pagination.sync="pagination"
-        no-data-label="No data"
-        no-results-label="No data you want"
-        :table-style="{ height: height }"
-        flat
-        bordered
-      >
-         <template v-slot:top>
-           <q-btn-group push>
-             <q-btn :label="$t('refresh')" icon="refresh" @click="reFresh()">
-               <q-tooltip content-class="bg-amber text-black shadow-4" :offset="[10, 10]" content-style="font-size: 12px">
-                 {{ $t('refreshtip') }}
-               </q-tooltip>
-             </q-btn>
-           </q-btn-group>
-           <q-space />
-           <q-input outlined rounded dense debounce="300" color="primary" v-model="filter" :placeholder="$t('search')" @input="getSearchList()" @keyup.enter="getSearchList()">
-             <template v-slot:append>
-               <q-icon name="search" @click="getSearchList()"/>
-             </template>
-           </q-input>
-         </template>
-         <template v-slot:body="props">
-           <q-tr :props="props">
-               <q-td key="asn_code" :props="props">
-                 {{ props.row.asn_code }}
-               </q-td>
-               <q-td key="goods_code" :props="props">
-                 {{ props.row.goods_code }}
-               </q-td>
-               <q-td key="goods_desc" :props="props">
-                 {{ props.row.goods_desc }}
-               </q-td>
-               <q-td key="goods_qty" :props="props">
-                 {{ props.row.goods_qty }}
-               </q-td>
-               <q-td key="goods_weight" :props="props">
-                 {{ props.row.goods_weight }}
-               </q-td>
-             <q-td key="goods_volume" :props="props">
-               {{ props.row.goods_volume }}
-             </q-td>
-             <q-td key="supplier" :props="props">
-               {{ props.row.supplier }}
-             </q-td>
-             <q-td key="creater" :props="props">
-               {{ props.row.creater }}
-             </q-td>
-             <q-td key="create_time" :props="props">
-               {{ props.row.create_time }}
-             </q-td>
-             <q-td key="update_time" :props="props">
-               {{ props.row.update_time }}
-             </q-td>
-           </q-tr>
-         </template>
-      </q-table>
-        </transition>
-      <template>
-        <div v-show="max !== 0" class="q-pa-lg flex flex-center">
-           <div>{{ total }} </div>
-          <q-pagination
-            v-model="current"
-            color="black"
-            :max="max"
-            :max-pages="6"
-            boundary-links
-            @click="getList()"
-          />
-          <div>
-            <input
-              v-model="paginationIpt"
-              @blur="changePageEnter"
-              @keyup.enter="changePageEnter"
-              style="width: 60px; text-align: center"
-            />
-          </div>
-        </div>
-        <div v-show="max === 0" class="q-pa-lg flex flex-center">
-          <q-btn flat push color="dark" :label="$t('no_data')"></q-btn>
-        </div>
-    </template>
-    </div>
-</template>
-    <router-view />
-
-<script>
-import { getauth } from 'boot/axios_request'
-
-export default {
-  name: 'Pageasnpreload',
-  data () {
-    return {
-      openid: '',
-      login_name: '',
-      authin: '0',
-      pathname: 'asn/detail/?asn_status=2',
-      pathname_previous: '',
-      pathname_next: '',
-      separator: 'cell',
-      loading: false,
-      height: '',
-      table_list: [],
-      bin_size_list: [],
-      bin_property_list: [],
-      warehouse_list: [],
-      columns: [
-        { name: 'asn_code', required: true, label: this.$t('inbound.view_asn.asn_code'), align: 'left', field: 'asn_code' },
-        { name: 'goods_code', label: this.$t('goods.view_goodslist.goods_code'), field: 'goods_code', align: 'center' },
-        { name: 'goods_desc', label: this.$t('goods.view_goodslist.goods_desc'), field: 'goods_desc', align: 'center' },
-        { name: 'goods_qty', label: this.$t('inbound.view_asn.goods_qty'), field: 'goods_qty', align: 'center' },
-        { name: 'goods_weight', label: this.$t('inbound.view_asn.total_weight'), field: 'goods_weight', align: 'center' },
-        { name: 'goods_volume', label: this.$t('inbound.view_asn.total_volume'), field: 'goods_volume', align: 'center' },
-        { name: 'supplier', label: this.$t('baseinfo.view_supplier.supplier_name'), field: 'supplier', align: 'center' },
-        { name: 'creater', label: this.$t('creater'), field: 'creater', align: 'center' },
-        { name: 'create_time', label: this.$t('createtime'), field: 'create_time', align: 'center' },
-        { name: 'update_time', label: this.$t('updatetime'), field: 'update_time', align: 'center' }
-      ],
-      filter: '',
-      pagination: {
-        page: 1,
-        rowsPerPage: 11
-      },
-      current: 1,
-      max: 0,
-      total: 0,
-      paginationIpt: 1
-    }
-  },
-  methods: {
-    getList () {
-      var _this = this
-      if (_this.$q.localStorage.has('auth')) {
-        getauth(_this.pathname + '&page=' + '' + _this.current, {
-        }).then(res => {
-          _this.table_list = res.results
-          _this.total = res.count
-          if (res.count === 0) {
-            _this.max = 0
-          } else {
-            if (Math.ceil(res.count / _this.pagination.rowsPerPage) === 1) {
-              _this.max = 0
-            } else {
-              _this.max = Math.ceil(res.count / _this.pagination.rowsPerPage)
-            }
-          }
-          _this.pathname_previous = res.previous
-          _this.pathname_next = res.next
-        }).catch(err => {
-          _this.$q.notify({
-            message: err.detail,
-            icon: 'close',
-            color: 'negative'
-          })
-        })
-      }
-    },
-    changePageEnter(e) {
-      if (Number(this.paginationIpt) < 1) {
-        this.current = 1;
-        this.paginationIpt = 1;
-      } else if (Number(this.paginationIpt) > this.max) {
-        this.current = this.max;
-        this.paginationIpt = this.max;
-      } else {
-        this.current = Number(this.paginationIpt);
-      }
-      this.getList();
-    },
-    getSearchList () {
-      var _this = this
-      if (_this.$q.localStorage.has('auth')) {
-        _this.current = 1
-        _this.paginationIpt = 1
-        getauth(_this.pathname + '&asn_code__icontains=' + _this.filter + '&page=' + '' + _this.current, {
-        }).then(res => {
-          _this.table_list = res.results
-          _this.total = res.count
-          if (res.count === 0) {
-            _this.max = 0
-          } else {
-            if (Math.ceil(res.count / _this.pagination.rowsPerPage) === 1) {
-              _this.max = 0
-            } else {
-              _this.max = Math.ceil(res.count / _this.pagination.rowsPerPage)
-            }
-          }
-          _this.pathname_previous = res.previous
-          _this.pathname_next = res.next
-        }).catch(err => {
-          _this.$q.notify({
-            message: err.detail,
-            icon: 'close',
-            color: 'negative'
-          })
-        })
-      } else {
-      }
-    },
-    getListPrevious () {
-      var _this = this
-      if (_this.$q.localStorage.has('auth')) {
-        getauth(_this.pathname_previous, {
-        }).then(res => {
-          _this.table_list = res.results
-          _this.pathname_previous = res.previous
-          _this.pathname_next = res.next
-        }).catch(err => {
-          _this.$q.notify({
-            message: err.detail,
-            icon: 'close',
-            color: 'negative'
-          })
-        })
-      } else {
-      }
-    },
-    getListNext () {
-      var _this = this
-      if (_this.$q.localStorage.has('auth')) {
-        getauth(_this.pathname_next, {
-        }).then(res => {
-          _this.table_list = res.results
-          _this.pathname_previous = res.previous
-          _this.pathname_next = res.next
-        }).catch(err => {
-          _this.$q.notify({
-            message: err.detail,
-            icon: 'close',
-            color: 'negative'
-          })
-        })
-      } else {
-      }
-    },
-    reFresh () {
-      var _this = this
-      _this.getList()
-    }
-  },
-  created () {
-    var _this = this
-    if (_this.$q.localStorage.has('openid')) {
-      _this.openid = _this.$q.localStorage.getItem('openid')
-    } else {
-      _this.openid = ''
-      _this.$q.localStorage.set('openid', '')
-    }
-    if (_this.$q.localStorage.has('login_name')) {
-      _this.login_name = _this.$q.localStorage.getItem('login_name')
-    } else {
-      _this.login_name = ''
-      _this.$q.localStorage.set('login_name', '')
-    }
-    if (_this.$q.localStorage.has('auth')) {
-      _this.authin = '1'
-      _this.getList()
-    } else {
-      _this.authin = '0'
-    }
-  },
-  mounted () {
-    var _this = this
-    if (this.$q.platform.is.electron) {
-      _this.height = String(_this.$q.screen.height - 290) + 'px'
-    } else {
-      _this.height = _this.$q.screen.height - 290 + '' + 'px'
-    }
-  },
-  updated () {
-  },
-  destroyed () {
-  }
-}
-</script>

+ 1 - 1
templates/src/pages/count/sortstock.vue

@@ -410,7 +410,7 @@ export default {
           ]
         case 'bound_status':
           return [
-            { label: '待审核', value: 0 },
+            { label: '待质检', value: 0 },
             { label: '确认无误', value: 1 }
           ]
         case 'bound_department':

+ 1 - 1
templates/src/pages/outbound/dn copy.vue

@@ -586,7 +586,7 @@
                   </div>
                 </div>
 
-                <!-- 拖拽式条件容器 -->
+                <!-- 拖拽式条件托盘 -->
                 <draggable :list="conditions" handle=".handle">
                   <div
                     v-for="(condition, index) in conditions"

+ 1 - 1
templates/src/pages/outbound/freshorder.vue

@@ -19,7 +19,7 @@
       </div>
     </div>
 
-    <!-- 拖拽式条件容器 -->
+    <!-- 拖拽式条件托盘 -->
     <draggable :list="conditions" handle=".handle">
       <div
         v-for="(condition, index) in conditions"

+ 1 - 1
templates/src/pages/outbound/pod.vue

@@ -586,7 +586,7 @@
                   </div>
                 </div>
 
-                <!-- 拖拽式条件容器 -->
+                <!-- 拖拽式条件托盘 -->
                 <draggable :list="conditions" handle=".handle">
                   <div
                     v-for="(condition, index) in conditions"

+ 2 - 2
templates/src/pages/stock/binset.vue

@@ -226,7 +226,7 @@ export default {
 
       updateCSSVariables() {
           const root = document.documentElement
-          // 获取组件容器的实际尺寸
+          // 获取组件托盘的实际尺寸
           const dwidth = document.documentElement.clientWidth
           const dheight = document.documentElement.clientHeight
           console.log(dwidth, dheight)
@@ -352,7 +352,7 @@ export default {
 }
 
 
-/* 网格系统容器 */
+/* 网格系统托盘 */
 .grid-system {
   position: relative;
   padding-left: var(--cell-x-2);

+ 2 - 2
templates/src/pages/stock/management copy.vue

@@ -224,7 +224,7 @@ export default {
 
         updateCSSVariables() {
             const root = document.documentElement
-            // 获取组件容器的实际尺寸
+            // 获取组件托盘的实际尺寸
             const dwidth = document.documentElement.clientWidth
             const dheight = document.documentElement.clientHeight
             console.log(dwidth, dheight)
@@ -350,7 +350,7 @@ export default {
 }
 
 
-/* 网格系统容器 */
+/* 网格系统托盘 */
 .grid-system {
     position: relative;
     padding-left: var(--cell-x-2);

+ 2 - 2
templates/src/pages/stock/management.vue

@@ -464,7 +464,7 @@ export default {
 
     updateCSSVariables() {
       const root = document.documentElement;
-      // 获取组件容器的实际尺寸
+      // 获取组件托盘的实际尺寸
       const dwidth = document.documentElement.clientWidth;
       const dheight = document.documentElement.clientHeight;
 
@@ -580,7 +580,7 @@ export default {
   gap: 10px;
 }
 
-/* 网格系统容器 */
+/* 网格系统托盘 */
 .grid-system {
   position: relative;
   padding-left: var(--cell-x-2);

+ 7 - 6
templates/src/router/routes.js

@@ -192,14 +192,15 @@ const routes = [{
         component: () => import('pages/count/detaillog.vue')
       },
       {
-        path: 'predeliverystock',
-        name: 'predeliverystock',
-        component: () => import('pages/count/predeliverystock.vue')
+        path: 'batchoperatelog',
+        name: 'batchoperatelog',
+        component: () => import('pages/count/batchoperatelog.vue')
       },
       {
-        path: 'preloadstock',
-        name: 'preloadstock',
-        component: () => import('pages/count/preloadstock.vue')
+        path: 'batchlog',
+        name: 'batchlog',
+        component: () => import('pages/count/batchlog.vue')
+
       },
       {
         path: 'presortstock',