Ver Fonte

入库规则增加

flower_mr há 3 semanas atrás
pai
commit
9c67e41e31

+ 53 - 2
bin/algorithms.py

@@ -1,6 +1,8 @@
 import copy
 import json
 from collections import defaultdict
+from .models import LocationGroupModel, alloction_pre
+from .queries import LocationQueries
 
 class AllocationAlgorithm:
     CAPACITY_MAP = {'T1':1, 'T2':2, 'T4':4, 'S4':4, 'T5':5}
@@ -28,7 +30,8 @@ class AllocationAlgorithm:
                     new_state = copy.deepcopy(layer_state)
                     new_state[layer_idx][loc_type] -= 1
                     
-                    new_pressure = [p-1 if p>0 else 0 for p in pressure]
+                    # new_pressure = [p-1 if p>0 else 0 for p in pressure]
+                    new_pressure = [p if p>0 else 0 for p in pressure]
                     allocated = min(cap, remain)
                     new_pressure[layer_idx] += allocated
 
@@ -53,4 +56,52 @@ class AllocationAlgorithm:
             if len(parts) == 2:
                 layer, loc_type = parts
                 result[layer][loc_type] += 1
-        return json.dumps(result)
+        return json.dumps(result)
+    
+    @staticmethod
+    def allocation_plan_left_right(location_type_list,batch_number,start_location,container_code):
+        locations=[]
+        existing_solution = alloction_pre.objects.filter(batch_number=batch_number).first()
+
+        for layer, location_type_dict in location_type_list.items():
+            if not location_type_dict:
+                continue
+            # 获取库位类型列表
+            location_type = list(location_type_dict.keys())
+            demand_number = sum(location_type_dict.values())
+            print (f"[1]层{layer} 需求数量: {demand_number}, 库位: {location_type}")                    
+            location_groups = LocationGroupModel.objects.filter(
+                group_type__in=location_type,
+                layer=layer,
+                status='available'
+            )
+            if not location_groups:
+                print(f"层{layer} 无库位")
+            # 根据起始位置选择排序字段
+            if start_location == '203':
+                ordered_groups = location_groups.order_by('left_priority')
+            elif start_location == '103':
+                ordered_groups = location_groups.order_by('right_priority')
+            else:
+                ordered_groups = location_groups.none()
+
+            number = 0
+            for location_group in ordered_groups:
+                if number >= demand_number:
+                    break
+                locations.append(f"{layer}_{location_group.id}")
+                number += 1
+        
+        existing_solution.layer_solution_type = locations
+        existing_solution.save()
+        print(f"[2]分配方案: {locations}")
+        return locations if locations else None
+      
+    def generate_WCS_location(location_list_cnumber):
+        allocation_target_location = (
+                            location_list_cnumber.warehouse_code + '-' 
+                            + f"{int(location_list_cnumber.row):02d}" + '-'  
+                            + f"{int(location_list_cnumber.col):02d}" + '-' 
+                            + f"{int(location_list_cnumber.layer):02d}" 
+                        )
+        return allocation_target_location

bin/location_allocator/__init__.py → bin/generate_wcs_task.py


+ 143 - 9
bin/queries.py

@@ -1,6 +1,15 @@
 from django.db import models
 from .models import *
+from django.db.models import Q
+import json
+from django.db import transaction
 from container.models import *
+from .models import LocationGroupModel
+import logging
+from collections import defaultdict
+
+logger = logging.getLogger(__name__)
+
 
 class LocationQueries:
     """"
@@ -18,9 +27,6 @@ class LocationQueries:
         get_batch_info(container_code): 获取托盘批次信息
             params: container_code: 托盘编号
             return: dict
-        get_batch_info(container_code): 获取托盘批次信息
-            params: container_code: 托盘编号
-            return: dict
         get_group_capacity(): 获取库位组空闲容量
             return: list
         get_current_pressure(): 获取仓库当前工作压力
@@ -50,11 +56,19 @@ class LocationQueries:
         detail = ContainerDetailModel.objects.filter(
             container=container.id
         ).exclude(status=3).first()
-        
+        if not detail:
+            return {
+                'status': None,
+                'number': None,
+                'container': container,
+                'class': 2 ,
+            }
+            
         return {
-            'status': detail.batch.status if detail else None,
-            'number': detail.batch.bound_number if detail else None,
-            'container': container
+            'status': detail.batch.status if detail.batch else None,
+            'number': detail.batch.bound_number if detail.batch else None,
+            'container': container,
+            'class': detail.goods_class if detail else None,
         }
 
     @staticmethod
@@ -82,9 +96,129 @@ class LocationQueries:
     
     @staticmethod
     def get_pallet_count(container_code):
+        """
+        获取托盘数量
+        :param container_code: 要查询的托盘码
+        :return: 所属批次下的托盘总数
+        """
+        # 1. 通过托盘码获取容器详情
+        container = ContainerListModel.objects.filter(
+            container_code=container_code
+        ).first()
+        if not container:
+            logger.error(f"托盘 {container_code} 不存在")
+            return None
+     
+        container_detail = ContainerDetailModel.objects.filter(
+            container=container.id
+        ).exclude(status = 3).first()
+
+        batch_obj = container_detail.batch
+        return batch_obj.container_number
+
+    
+    @staticmethod
+    def get_pallet_count_by_batch( container_code):
+        """
+        根据托盘码查询批次下托盘总数
+        :param container_code: 要查询的托盘码
+        :return: 所属批次下的托盘总数
+        """
+        # 1. 通过托盘码获取容器详情
         container = ContainerListModel.objects.filter(
             container_code=container_code
         ).first()
+
         if not container:
-            return 0
-        return container.details.count()
+            logger.error(f"托盘 {container_code} 不存在")
+            return None
+     
+        container_detail = ContainerDetailModel.objects.filter(
+            container=container.id
+        ).exclude(status = 3).first()
+        if not container_detail:
+            logger.error(f"托盘 {container_code} 未组盘")
+            return None
+ 
+        batch_container = ContainerDetailModel.objects.filter(
+             batch = container_detail.batch.id,
+        ).all().exclude(status = 3)
+        # 统计批次下的不同托盘 item.contianer_id
+        batch_container_count = 0
+        container_ids = []
+        for item in batch_container:
+            if item.container_id not in container_ids: 
+                batch_container_count = batch_container_count + 1
+                container_ids.append(item.container_id)
+
+        return batch_container_count
+    
+
+    @staticmethod
+    def get_current_finish_task(container,batch_info):
+        batch = batch_info.get('number')
+
+        if not batch:
+            return None
+        solution = alloction_pre.objects.filter(batch_number=batch).first()
+        if not solution:
+            return None
+
+        return [solution.layer1_task_finish_number,solution.layer2_task_finish_number,solution.layer3_task_finish_number] 
+
+
+    @staticmethod
+    def divide_solution_by_layer(data):
+        
+
+        # 统计所有存在的层级
+        layer_counts = defaultdict(lambda: defaultdict(int))
+        existing_layers = set()
+        
+        for item in data:
+            # 分割层级和类型
+            try:
+                layer, loc_type = item.split('_')
+                layer_num = int(layer)
+                existing_layers.add(layer_num)
+                layer_counts[layer_num][loc_type] += 1
+            except (ValueError, IndexError):
+                continue  # 跳过无效格式的数据
+
+        # 确定最大层级(至少包含1层)
+        max_layer = max(existing_layers) if existing_layers else 1
+
+        # 构建包含所有层级的最终结果
+        final_result = {}
+        for layer in range(1, max_layer + 1):
+            final_result[str(layer)] = dict(layer_counts.get(layer, {}))
+        
+        return json.dumps(final_result, indent=2)
+    
+    @staticmethod
+    def get_alloction_pre_solution(batch_number):
+        """
+        获取指定批次的预留方案
+        :param batch_number: 批次号
+        :return: 预留方案
+        """
+        solution = alloction_pre.objects.filter(batch_number=batch_number).first()
+        if not solution:
+            return None
+        return solution.layer_solution_type
+    
+    @staticmethod
+    def get_left_location_group(batch_number):
+        """
+        获取指定批次的剩余库位
+        :param batch_number: 批次号
+        :return: 剩余库位
+        """
+        layer_solution_type =[]
+        location_group_obj =LocationGroupModel.objects.filter(current_batch=batch_number,status='occupied').all()
+        if not location_group_obj:
+            return None
+        else:
+            for item in location_group_obj:
+                layer_solution_type.append(f"{item.layer}_{item.id}")
+        return layer_solution_type

+ 215 - 13
bin/services.py

@@ -2,26 +2,84 @@ from django.db import transaction
 from .queries import LocationQueries
 from .updates import LocationUpdates 
 from .algorithms import AllocationAlgorithm
+import logging
+import json
+from collections import defaultdict
+from .models import *
+from bound.models import BoundBatchModel, BoundDetailModel, OutBatchModel, BoundListModel
+import datetime
+from django.utils import timezone
+
+logger = logging.getLogger(__name__)
 
 class AllocationService:
     @classmethod
     @transaction.atomic
     def allocate(cls, container_code, start_point):
+        logger.info(f"请求托盘:{container_code},请求位置:{start_point}")
+
         batch_info = LocationQueries.get_batch_info(container_code)
-        if not batch_info or not batch_info['number']:
+        if not batch_info :
             raise ValueError("无效的容器或批次信息")
-
-        if batch_info['status'] == 1:
+        if not batch_info['number']:
+            return cls._container_group_allocation(container_code,batch_info, start_point)
+        if batch_info['status'] == 1 or batch_info['status'] == 0:
             return cls._first_allocation(container_code, batch_info, start_point)
         elif batch_info['status'] == 2:
-            return cls._subsequent_allocation(batch_info)
+            return cls._subsequent_allocation(container_code, batch_info, start_point)
         else:
             raise ValueError("非法的批次状态")
 
     @classmethod
     @transaction.atomic
     def _first_allocation(cls, container_code, batch_info, start_point):
-        total = LocationQueries.get_pallet_count(container_code)
+        total = LocationQueries.get_pallet_count_by_batch(container_code)
+        current_total = LocationQueries.get_pallet_count(container_code)
+        if not total:
+                raise ValueError("无效的容器或批次信息")
+        LocationUpdates.update_pallet_count(total, batch_info['number'])
+        if current_total == 0:
+            
+            layer_cap = LocationQueries.get_group_capacity()
+            pressure = LocationQueries.get_current_pressure()
+
+            solution, new_pressure = AllocationAlgorithm.generate_plan(total, layer_cap, pressure)
+        
+            if not solution:
+                raise RuntimeError("无法生成有效分配方案")
+
+            formatted = json.loads(AllocationAlgorithm.format_solution(solution))
+            LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
+            # 使用距离最近的库位,增加切换
+            locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'] ,start_point, container_code)
+            return cls._execute_allocation(locations, start_point,container_code,batch_info)
+        else:
+            locations = LocationQueries.get_left_location_group(batch_info['number'])
+            if not locations:
+                layer_cap = LocationQueries.get_group_capacity()
+                pressure = LocationQueries.get_current_pressure()
+                finish_task_sum = sum(LocationQueries.get_current_finish_task(container_code,batch_info))
+                solution, new_pressure = AllocationAlgorithm.generate_plan(total-finish_task_sum, layer_cap, pressure)
+                
+                if not solution:
+                    raise RuntimeError("无法生成有效分配方案")
+
+                formatted = json.loads(AllocationAlgorithm.format_solution(solution))
+                LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
+                # 使用距离最近的库位,增加切换
+                locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'] ,start_point, container_code)
+                return cls._execute_allocation(locations, start_point,container_code,batch_info)
+            else:
+                return cls._execute_allocation(locations, start_point,container_code,batch_info)   
+
+
+    @classmethod
+    def _container_group_allocation(cls,container_code, batch_info, start_point):
+        total = 1
+        batch_info['number'] = 'ContainerGroup'+str(container_code)+str(timezone.now().strftime('%Y%m%d'))
+        if not total:
+            raise ValueError("无效的容器或批次信息")
+       
         layer_cap = LocationQueries.get_group_capacity()
         pressure = LocationQueries.get_current_pressure()
 
@@ -29,17 +87,161 @@ class AllocationService:
         if not solution:
             raise RuntimeError("无法生成有效分配方案")
 
-        formatted = AllocationAlgorithm.format_solution(solution)
+        formatted = json.loads(AllocationAlgorithm.format_solution(solution))
         LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
-        
-        return cls._execute_allocation(formatted, start_point)
+        # 使用距离最近的库位,增加切换
+        locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'],start_point, container_code)
+        return cls._execute_allocation_no_batch(locations, start_point,container_code,batch_info)
+    
+    @classmethod
+    def _execute_allocation_no_batch(cls, solution, start_point,container_code,batch_info):
+        LocationUpdates.update_group_status_reserved(solution)
+        print(f"[0] 库位组状态预定成功!")
+        location_min_value = AllocationService.get_location_list_remainder(solution,container_code,batch_info)
+        if not location_min_value:
+            raise ValueError("[1] 任务分配失败,无可用库位")
+        print(f"[1] 任务安排到第{location_min_value.c_number}个库位:{location_min_value}")
+
+        location_min_value.status = 'reserved'
+        location_min_value.save()
+        print(f"[2] 库位状态更新成功!")
+
+        update_location_group_status=LocationUpdates.update_location_group_status(location_min_value.location_code)
+        if not update_location_group_status:
+            raise ValueError("[3] 库位组状态更新失败")
+        print(f"[3] 库位组状态更新成功!")
+
+        update_location_group_batch = LocationUpdates.update_location_group_batch(location_min_value,batch_info['number'])
+        if not update_location_group_batch:
+            raise ValueError("[5] 批次信息更新失败")
+        print(f"[5] 批次信息更新成功!")
+
+        update_location_container_link = LocationUpdates.link_container(location_min_value.location_code, container_code)
+        if not update_location_container_link:
+            raise ValueError("[6] 容器与库位关联失败")
+        print(f"[6] 容器与库位关联成功!")
+ 
+        update_location_container_detail = LocationUpdates.update_container_detail_status(container_code, 2)
+        if not update_location_container_detail:
+            raise ValueError("[7] 托盘详情状态更新失败")
+        print(f"[7] 托盘详情状态更新成功!")
+
+        allocation_target_location = AllocationAlgorithm.generate_WCS_location(location_min_value)
+       
+        return location_min_value,allocation_target_location, batch_info
 
     @classmethod
-    def _execute_allocation(cls, solution, start_point):
-        # 实际分配执行逻辑
-        pass
+    def _execute_allocation(cls, solution, start_point,container_code,batch_info):
+
+        LocationUpdates.update_group_status_reserved(solution)
+        print(f"[0] 库位组状态预定成功!")
+        location_min_value = AllocationService.get_location_list_remainder(solution,container_code,batch_info)
+        if not location_min_value:
+            raise ValueError("[1] 任务分配失败,无可用库位")
+        print(f"[1] 任务安排到第{location_min_value.c_number}个库位:{location_min_value}")
+
+        location_min_value.status = 'reserved'
+        location_min_value.save()
+        print(f"[2] 库位状态更新成功!")
+
+        update_location_group_status=LocationUpdates.update_location_group_status(location_min_value.location_code)
+        if not update_location_group_status:
+            raise ValueError("[3] 库位组状态更新失败")
+        print(f"[3] 库位组状态更新成功!")
+
+        update_batch_status = LocationUpdates.update_batch_status(container_code, 2)
+        if not update_batch_status:
+            raise ValueError("[4] 批次状态更新失败")
+        print(f"[4] 批次状态更新成功!")
+
+        update_location_group_batch = LocationUpdates.update_location_group_batch(location_min_value,batch_info['number'])
+        if not update_location_group_batch:
+            raise ValueError("[5] 批次信息更新失败")
+        print(f"[5] 批次信息更新成功!")
+
+        update_location_container_link = LocationUpdates.link_container(location_min_value.location_code, container_code)
+        if not update_location_container_link:
+            raise ValueError("[6] 容器与库位关联失败")
+        print(f"[6] 容器与库位关联成功!")
+ 
+        update_location_container_detail = LocationUpdates.update_container_detail_status(container_code, 2)
+        if not update_location_container_detail:
+            raise ValueError("[7] 托盘详情状态更新失败")
+        print(f"[7] 托盘详情状态更新成功!")
+
+        allocation_target_location = AllocationAlgorithm.generate_WCS_location(location_min_value)
+       
+        return location_min_value,allocation_target_location, batch_info
 
     @classmethod
-    def _subsequent_allocation(cls, batch_info):
+    def _subsequent_allocation(cls, container_code, batch_info, start_point):
         # 后续分配逻辑
-        pass
+        total = LocationQueries.get_pallet_count_by_batch(container_code)
+        current_total = LocationQueries.get_pallet_count(container_code)
+        LocationUpdates.update_pallet_count(total, batch_info['number'])
+        if total == current_total:
+            locations = LocationQueries.get_left_location_group(batch_info['number'])
+            if not locations:
+                layer_cap = LocationQueries.get_group_capacity()
+                pressure = LocationQueries.get_current_pressure()
+                finish_task_sum = sum(LocationQueries.get_current_finish_task(container_code,batch_info))
+                solution, new_pressure = AllocationAlgorithm.generate_plan(total-finish_task_sum, layer_cap, pressure)
+                if not solution:
+                    raise RuntimeError("无法生成有效分配方案")
+
+                formatted = json.loads(AllocationAlgorithm.format_solution(solution))
+                LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
+                # 使用距离最近的库位,增加切换
+                locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'] ,start_point, container_code)
+                return cls._execute_allocation(locations, start_point,container_code,batch_info)
+            else:
+                return cls._execute_allocation(locations, start_point,container_code,batch_info)
+
+    @transaction.atomic
+    def get_location_list_remainder(location_group_list,container_code,batch_info):
+        """
+        获取可用库位的c_number列表
+        :param location_list: 库位对象列表
+        :return: 可用库位编号列表
+        """
+        if not location_group_list:
+            return None
+
+        current_task = LocationQueries.get_current_finish_task(container_code,batch_info)
+        print(f"[1]当前已完成任务: {current_task}")
+        # 按压力排序
+        sorted_pressure = sorted(
+            [(0, current_task[0]), (1, current_task[1]), (2, current_task[2])],
+            key=lambda x: (-x[1], x[0])
+        )
+        # 交换第一和第二个元素的位置
+        sorted_pressure[0], sorted_pressure[1] = sorted_pressure[1], sorted_pressure[0]
+        print(f"[2]任务排序: {sorted_pressure}")
+        print(f"[3]当前选择:{sorted_pressure[0][0]+1}")
+        location_type_dict = json.loads(LocationQueries.divide_solution_by_layer(location_group_list))
+        # print(f"库位类型分配方案: {location_type_dict}")
+   
+        for layer, _ in sorted_pressure:
+            if not location_type_dict.get(str(layer+1)):
+                continue
+            # print(f"当前层: {layer+1}")
+            # print(f"当前层库位组: {location_type_dict[str(layer+1)].keys()}")
+            for group_id in location_type_dict[str(layer+1)].keys():
+                location_group = LocationGroupModel.objects.filter(
+                    id=group_id,
+                ).first()
+                if not location_group:
+                    continue
+                location_list = location_group.location_items.filter(
+                    status='available'
+                ).all().order_by('c_number')
+                if not location_list:
+                    print(f"当前层库位组 {location_group.group_code} 可用库位: None")
+                    continue
+               # 提取所有库位的 c_number
+                c_numbers = [loc.c_number for loc in location_list]
+                print(f"当前层库位组 {location_group.group_code} 可用库位: {c_numbers}")
+                # 更新任务完成数目
+                current_task[layer] = current_task[layer] + 1
+                LocationUpdates.update_current_finish_task(container_code,current_task)
+                return location_list[0]

+ 174 - 5
bin/updates.py

@@ -1,7 +1,9 @@
 from django.db import transaction
 from .models import *
 from container.models import *
+import logging
 
+logger = logging.getLogger(__name__)
 class LocationUpdates:
     """
     库位相关更新
@@ -36,12 +38,16 @@ class LocationUpdates:
             container = ContainerListModel.objects.get(
                 container_code=container_code
             )
-            detail = ContainerDetailModel.objects.select_related('batch').get(
+            detail = ContainerDetailModel.objects.select_related('batch').filter(
                 container=container.id,
                 status__in=[1, 2]
-            )
-            detail.batch.status = status
-            detail.batch.save()
+                ).all()
+            if not detail:
+                print(f"托盘 {container_code} 未组盘_from update_batch_status")
+            for item in detail:
+                item.batch.status = status
+                item.batch.save()
+
             return True
         except Exception as e:
             raise RuntimeError(f"批次状态更新失败: {str(e)}")
@@ -61,4 +67,167 @@ class LocationUpdates:
             alloction_pre.objects.update_or_create(
                 batch_number=batch_number,
                 defaults={'layer_pre_type': solution}
-            )
+            )
+
+    @staticmethod
+    def update_pallet_count(batch_container_count,bound_number):
+        batch_item = BoundBatchModel.objects.filter(bound_number = bound_number).first()
+        if not batch_item:
+            print(f"批次号获取失败!")
+            return None
+        batch_item.container_number = batch_container_count
+        batch_item.save()
+        return True
+    
+    @staticmethod
+    def update_group_status_reserved(location_group_list):
+        """
+        更新库位组状态
+        :param location_group_list: 库位组对象列表
+        :return:
+        """
+        try:
+            for location_group in location_group_list:
+                # 1. 获取库位组
+                if not location_group:
+                    print(f"库位组获取失败!")
+                    return False
+                # 2. 更新库位组状态
+                location_group_id = location_group.split('_')[1]
+                location_group_item = LocationGroupModel.objects.filter(
+                    id=location_group_id
+                ).first()
+                if not location_group_item:
+                    print(f"库位组 {location_group} 不存在")
+                    return False
+                # 3. 更新库位组状态
+                location_group_item.status = 'reserved'
+                location_group_item.save()
+
+            return True
+        except Exception as e:       
+            logger.error(f"更新库位组状态失败:{str(e)}")
+            print(f"更新库位组状态失败:{str(e)}")
+            return False    
+        
+    def update_current_finish_task(container,task_finish_number):
+        """
+        更新当前完成任务数
+        :param container: 托盘号
+        :param task_finish_number: 完成任务数
+        :return: Boolean
+        """
+        from .queries import LocationQueries
+        batch = LocationQueries.get_batch_info(container).get('number') 
+
+        if not batch:
+            return None
+        solution = alloction_pre.objects.filter(batch_number=batch).first()
+        if not solution:
+            return None
+        solution.layer1_task_finish_number = task_finish_number[0]
+        solution.layer2_task_finish_number = task_finish_number[1]
+        solution.layer3_task_finish_number = task_finish_number[2]
+        solution.save()
+        return True
+    
+    @staticmethod
+    @transaction.atomic
+    def update_location_group_status( location_code):
+        """
+        更新库位组状态
+        :param location_code: 库位编码
+        :return:
+        """
+        try:
+            # 1. 获取库位
+            location = LocationModel.objects.filter(
+                location_code=location_code
+            ).first()
+            if not location:
+                print(f"库位获取失败!")
+                return False
+            
+            # 2. 获取库位组
+            location_group = LocationGroupModel.objects.filter(
+                group_code=location.location_group
+            ).first()
+            if not location_group:
+                print(f"库位组获取失败!")
+                return False
+            current=0
+            for location_item in location_group.location_items.all():
+                if location_item.status != 'available':
+                    current=current + 1
+            # 3. 更新库位组状态
+            if current == 0:
+                location_group.status = 'available'
+            elif current == location_group.max_capacity:
+                location_group.status = 'full'
+            else:
+                location_group.status = 'occupied'
+            
+            location_group.current_goods_quantity = sum(
+                    [loc.current_quantity for loc in location_group.location_items.all()]
+                )
+            location_group.current_quantity = current
+                
+            location_group.save()
+            
+            return True
+        except Exception as e:       
+            logger.error(f"更新库位组状态失败:{str(e)}")
+            print(f"更新库位组状态失败:{str(e)}")
+
+    @staticmethod
+    @transaction.atomic
+    def update_location_group_batch(location,bound_number):
+        """
+        :param location: 库位对象
+        :param 
+
+
+        :return:
+        """
+        try:
+            # 1. 获取库位组
+            location_group = LocationGroupModel.objects.filter(
+                group_code=location.location_group
+            ).first()
+            if not location_group:
+                print(f"库位组获取失败!")
+                return False
+            # 2. 更新库位组的批次
+            location_group.current_batch = bound_number
+            location_group.save()
+            print(f"更新库位组的批次成功!")
+            return True
+        except Exception as e:       
+            logger.error(f"更新库位组的批次失败:{str(e)}")
+            print(f"更新库位组的批次失败:{str(e)}")
+            return False
+        
+    def update_container_detail_status(container_code,status):
+        try:
+            # 1. 获取托盘
+            container = ContainerListModel.objects.filter(
+                container_code=container_code
+            ).first()
+            if not container:
+                print(f"托盘 {container_code} 不存在")
+                return False
+            # 2. 更新托盘状态
+            container_detail = ContainerDetailModel.objects.filter(
+                container=container.id
+            ).exclude(status=3).all()
+            if not container_detail:
+                print(f"托盘 {container_code} 未组盘_from update_container_detail_status")
+                return True
+            for detail in container_detail:
+                detail.status = status
+                detail.save()
+            return True
+        except Exception as e:       
+            logger.error(f"更新托盘状态失败:{str(e)}")
+            print(f"更新托盘状态失败:{str(e)}")
+            return False

+ 18 - 0
bound/migrations/0012_boundbatchmodel_goods_package.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.2 on 2025-05-15 16:27
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('bound', '0011_boundlistmodel_audit_status'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='boundbatchmodel',
+            name='goods_package',
+            field=models.CharField(default='待填写', max_length=255, verbose_name='商品包装'),
+        ),
+    ]

+ 1 - 0
bound/models.py

@@ -66,6 +66,7 @@ class BoundBatchModel(models.Model):
     goods_std = models.CharField(default='待填写', max_length=255, verbose_name="商品标准",blank=True, null=True)
     goods_unit = models.CharField(default='待填写', max_length=255, verbose_name="商品单位")
     goods_qty = models.BigIntegerField(default=0, verbose_name="商品数量")
+    goods_package = models.CharField(default='待填写', max_length=255, verbose_name="商品包装")
     goods_in_qty = models.BigIntegerField(default=0, verbose_name="入库数量")
     goods_in_location_qty = models.BigIntegerField(default=0, verbose_name="库位上入库数量")
     goods_out_qty = models.BigIntegerField(default=0, verbose_name="出库数量")

+ 25 - 25
container/container_operate.py

@@ -105,7 +105,7 @@ class BatchOperator:
             logger
         )
         
-        # 更新批次状态
+        # 更新批次状态(新增数目)
         update_result = BatchStatusUpdater.update_status(
             bound_obj, 
             batch['goods_qty'] * batch['goods_group'],
@@ -116,8 +116,8 @@ class BatchOperator:
         RecordCreator.create_related_records(
             data=data,
             bound_obj=bound_obj,
+            batch_data= batch,
             qty_diff=update_result['added_qty'],
-            expected_qty=update_result['expected_qty'],
             logger=logger
         )
 
@@ -145,8 +145,7 @@ class BatchStatusUpdater:
         bound_obj.save()
         return {
             'last_qty': last_qty,
-            'added_qty': added_qty,
-            'expected_qty': bound_obj.goods_qty
+            'added_qty': added_qty
         }
     @staticmethod
     def update_container_status(container_obj, status, container_code):
@@ -158,8 +157,7 @@ class BatchStatusUpdater:
         container_obj.save()
         return {
             'last_qty': 1,
-            'added_qty': 1,
-            'expected_qty': 1
+            'added_qty': 1
         }
   
 
@@ -201,7 +199,7 @@ class ListStatusUpdater:
 
 class RecordCreator:
     @staticmethod
-    def create_container_related_records(data, container_obj, qty_diff=1, expected_qty=1):
+    def create_container_related_records(data, container_obj, qty_diff=1):
         """记录创建批次"""
         # 生成明细记录
         detail_data = RecordBuilder.build_detail_data_container_group(
@@ -215,28 +213,30 @@ class RecordCreator:
         # 生成操作记录
         operation_data = RecordBuilder.build_operation_data_container_group(
             detail_data=detail_data,
-            qty_diff=qty_diff,
-            expected_qty=expected_qty
+            qty_diff=qty_diff
         )
 
         OperationRecordCreator.create_container_related_operation_records(operation_data)
 
     @staticmethod
-    def create_related_records(data, bound_obj, qty_diff, expected_qty, logger):
+    def create_related_records(data, bound_obj, batch_data, qty_diff, logger):
         """记录创建批次"""
         # 生成明细记录
-        detail_data = RecordBuilder.build_detail_data(
-            data=data,
-            bound_obj=bound_obj,
-            qty_diff=qty_diff
-        )
-        DetailRecordCreator.create_record(detail_data)
+
+        goods_group = batch_data.get('goods_group')
+        goods_qty = batch_data.get('goods_qty')
+        for i in range(goods_group):
+            detail_data = RecordBuilder.build_detail_data(
+                data=data,
+                bound_obj=bound_obj,
+                qty_diff=goods_qty
+            )
+            DetailRecordCreator.create_record(detail_data)
         
         # 生成操作记录
         operation_data = RecordBuilder.build_operation_data(
             detail_data=detail_data,
-            qty_diff=qty_diff,
-            expected_qty=expected_qty
+            qty_diff=qty_diff
         )
         OperationRecordCreator.create_operation_record(operation_data)
 
@@ -255,7 +255,7 @@ class RecordBuilder:
             "goods_weight": bound_obj.goods_weight,
             "status": 1,
             "month": data['month'],
-            "creater": data.get('creater', 'zl')
+            "creater": data.get('creater', 'wms')
         }
     
     @staticmethod
@@ -269,13 +269,13 @@ class RecordBuilder:
             "goods_weight": 0,
             "status": 1,
             "month": data['month'],
-            "creater": data.get('creater', 'zl')
+            "creater": data.get('creater', 'wms')
         }
 
     @staticmethod
-    def build_operation_data(detail_data, qty_diff, expected_qty):
+    def build_operation_data(detail_data, qty_diff):
         """构建操作记录数据"""
-        note_type = "(数量不一致)" if qty_diff != expected_qty else ""
+
         return {
             "month": detail_data['month'],
             "container": detail_data['container'],
@@ -284,15 +284,15 @@ class RecordBuilder:
             "goods_code": detail_data['goods_code'],
             "goods_desc": detail_data['goods_desc'],
             "goods_qty": qty_diff,
-            "goods_weight": detail_data['goods_weight'],
+            "goods_weight": qty_diff,
             "operator": detail_data['creater'],
             "timestamp": timezone.now(),
             "from_location": "container",
             "to_location": "container",
-            "memo": f"每组数目{detail_data['goods_qty']}入库PDA组盘{note_type}{detail_data['goods_code']}数量{qty_diff}"
+            "memo": f"每组{detail_data['goods_code']}数目{detail_data['goods_qty']},PDA组盘共计组盘{qty_diff}"
         }
     
-    def build_operation_data_container_group(detail_data, qty_diff, expected_qty):
+    def build_operation_data_container_group(detail_data, qty_diff):
         """构建操作记录数据"""
 
         return {

+ 30 - 0
container/migrations/0016_containerwcsmodel_batch_number_and_more.py

@@ -0,0 +1,30 @@
+# Generated by Django 4.1.2 on 2025-05-15 23:42
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('bound', '0012_boundbatchmodel_goods_package'),
+        ('container', '0015_containerlistmodel_available'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='containerwcsmodel',
+            name='batch_number',
+            field=models.CharField(blank=True, max_length=50, null=True, verbose_name='批次号'),
+        ),
+        migrations.AlterField(
+            model_name='containerwcsmodel',
+            name='batch',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='bound.boundbatchmodel', verbose_name='关联批次'),
+        ),
+        migrations.AlterField(
+            model_name='containerwcsmodel',
+            name='bound_list',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='bound.boundlistmodel', verbose_name='关联出库单'),
+        ),
+    ]

+ 4 - 3
container/models.py

@@ -37,7 +37,7 @@ class ContainerDetailModel(models.Model):
     )
     BATCH_CLASS = (
         (1, '成品'),
-        (2, '设备'),
+        (2, '空盘'),
         (3, '成品'),
     )
     month = models.IntegerField(verbose_name='月份')
@@ -107,9 +107,10 @@ class ContainerWCSModel(models.Model):
         (400, '失败'),
     )
     taskid = models.CharField(max_length=50, verbose_name='任务ID')
-    batch = models.ForeignKey(BoundBatchModel, on_delete=models.CASCADE, verbose_name='关联批次')
+    batch = models.ForeignKey(BoundBatchModel, on_delete=models.CASCADE, verbose_name='关联批次',null=True, blank=True )
     batch_out = models.ForeignKey(OutBatchModel, on_delete=models.CASCADE, verbose_name='出库批次' , null=True, blank=True )
-    bound_list = models.ForeignKey(BoundListModel, on_delete=models.CASCADE, verbose_name='关联出库单')
+    bound_list = models.ForeignKey(BoundListModel, on_delete=models.CASCADE, verbose_name='关联出库单',null=True, blank=True )
+    batch_number = models.CharField(max_length=50, verbose_name='批次号',null=True, blank=True )
     sequence = models.BigIntegerField(verbose_name='任务顺序')
     priority = models.IntegerField(default=100, verbose_name='优先级')
     month = models.IntegerField(verbose_name='月份')

+ 1 - 0
container/serializers.py

@@ -43,6 +43,7 @@ class ContainerDetailGetSerializer(serializers.ModelSerializer):
     goods_desc = serializers.CharField(read_only=True, required=False)
     goods_qty = serializers.IntegerField(read_only=True, required=False)
     goods_weight = serializers.DecimalField(read_only=True, required=False, max_digits=10, decimal_places=2)
+    goods_class = serializers.IntegerField(read_only=True, required=False)
     status = serializers.IntegerField(read_only=True, required=False)
     creater = serializers.CharField(read_only=True, required=False)
     create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M', required=False)

+ 89 - 74
container/views.py

@@ -28,6 +28,7 @@ from .filter import ContainerDetailFilter,ContainerListFilter,ContainerOperation
 from rest_framework.permissions import AllowAny
 import threading
 from django.db import close_old_connections
+from bin.services import AllocationService
 logger = logging.getLogger(__name__)
 class ContainerListViewSet(viewsets.ModelViewSet):
     """
@@ -309,84 +310,20 @@ class ContainerWCSViewSet(viewsets.ModelViewSet):
                         'data': current_task.to_dict()
                     }
                 else:
-                     # 库位分配
-                    container_code = container
-                    print(f"开始生成库位,托盘编码:{container_code}")
-                    allocator = LocationAllocation()  # 创建实例
-
-                    location_list_cnumber = allocator.get_location_by_status(container_code, current_location)  # 获取库位列表
-                    if not location_list_cnumber:
-                        print("❌ 通用库位获取失败,请检查托盘编码")
-                        return
-                    print(f"[1]库位:{location_list_cnumber}")
-                    
-                    update_location_status = allocator.update_location_status(location_list_cnumber.location_code, 'reserved')  # 更新库位状态
-                    if not update_location_status:
-                        print("❌ 库位状态更新失败,请检查托盘编码")
-                        return
-                    print(f"[2]发送任务,库位状态更新成功!")
-                    update_location_group_status = allocator.update_location_group_status(location_list_cnumber.location_code)  # 更新库位组状态
-                    if not update_location_group_status:
-                        print("❌ 库位组状态更新失败,请检查托盘编码")
-                        return
-                    print(f"[3]库位组状态更新成功!")
-
-                    update_batch_status = allocator.update_batch_status(container_code, '2')  # 更新批次状态
-                    if not update_batch_status:
-                        print("❌ 批次状态更新失败,请检查批次号")
-                        return
-                    print(f"[4]批次状态更新成功!")
-
-                    update_location_group_batch = allocator.update_location_group_batch(location_list_cnumber, container_code)  # 更新库位组的批次
-                    if not update_location_group_batch:
-                        print("❌ 库位组批次更新失败,请检查托盘编码")
-                        return
-                    print(f"[5]库位组批次更新成功!")
-
-                    update_location_container_link = allocator.update_location_container_link(location_list_cnumber.location_code, container_code)  # 更新库位和托盘的关联关系
-                    if not update_location_container_link:
-                        print("❌ 库位和托盘的关联关系更新失败,请检查托盘编码")
-                        return
-                    print(f"[7]库位和托盘的关联关系更新成功!")
-
-                    update_location_container_detail = allocator.update_container_detail_status(container_code,2)  # 更新库位和托盘的关联关系
-                    if not update_location_container_detail:
-                        print("❌ 库位和托盘的关联关系更新失败,请检查托盘编码")
-                        return
-                    print(f"[8]托盘的关联关系更新成功!")
-                    
+                    location_min_value,allocation_target_location, batch_info = AllocationService.allocate(container, current_location)
+                    batch_id = batch_info['number']
+                    if batch_info['class'] == 2:
+                        self.generate_task_no_batch(container, current_location, allocation_target_location,batch_id,location_min_value.c_number) 
+                        self.generate_container_operate_no_batch(container_obj, batch_id, allocation_target_location)
+                    else:   
+                        self.generate_task(container, current_location, allocation_target_location,batch_id,location_min_value.c_number)  # 生成任务
+                        self.generate_container_operate(container_obj, batch_id, allocation_target_location)
 
-                    allocation_target_location = (
-                            location_list_cnumber.warehouse_code + '-' 
-                            + f"{int(location_list_cnumber.row):02d}" + '-'  
-                            + f"{int(location_list_cnumber.col):02d}" + '-' 
-                            + f"{int(location_list_cnumber.layer):02d}" 
-                        )
-                    batch_id = allocator.get_batch(container_code)
-                    self.generate_task(container, current_location, allocation_target_location,batch_id,location_list_cnumber.c_number)  # 生成任务
                     current_task = ContainerWCSModel.objects.get(
                         container=container, 
                         tasktype='inbound',
                         working=1,
                     )
-                    batch_obj = BoundBatchModel.objects.filter(bound_number=batch_id).first()
-                    ContainerOperationModel.objects.create(
-                    month = int(timezone.now().strftime("%Y%m")),
-                    container = container_obj,
-                    goods_code = batch_obj.goods_code,
-                    goods_desc = batch_obj.goods_desc,
-                    operation_type ="inbound",
-                    batch_id = batch_obj.id,
-                    
-                    goods_qty = batch_obj.goods_qty,
-                    goods_weight = batch_obj.goods_qty,
-                    from_location = container_obj.current_location,
-                    to_location= allocation_target_location,
-                    timestamp=timezone.now(),
-                    operator="WMS",
-                    memo=f"WCS入库: 批次: {batch_id}, 数量: {batch_obj.goods_qty}"
-                    )
-
                     data_return = {
                         'code': '200',
                         'message': '任务下发成功',
@@ -394,7 +331,8 @@ class ContainerWCSViewSet(viewsets.ModelViewSet):
                     }
                     container_obj.target_location = allocation_target_location
                     container_obj.save()
-                    self.inport_update_task(current_task.id, container_obj.id)
+                    if batch_info['class'] == 1:
+                        self.inport_update_task(current_task.id, container_obj.id)
 
 
             http_status = status.HTTP_200_OK if data_return['code'] == '200' else status.HTTP_400_BAD_REQUEST
@@ -408,6 +346,42 @@ class ContainerWCSViewSet(viewsets.ModelViewSet):
             )
 
     @transaction.atomic
+    def generate_container_operate(self, container_obj, bound_number,allocation_target_location):
+        batch_obj = BoundBatchModel.objects.filter(bound_number=bound_number).first()
+        ContainerOperationModel.objects.create(
+            month = int(timezone.now().strftime("%Y%m")),
+            container = container_obj,
+            goods_code = batch_obj.goods_code,
+            goods_desc = batch_obj.goods_desc,
+            operation_type ="inbound",
+            batch_id = batch_obj.id,
+            
+            goods_qty = batch_obj.goods_qty,
+            goods_weight = batch_obj.goods_qty,
+            from_location = container_obj.current_location,
+            to_location= allocation_target_location,
+            timestamp=timezone.now(),
+            operator="WMS",
+            memo=f"WCS入库: 批次: {bound_number}, 数量: {batch_obj.goods_qty}"
+        )
+    @transaction.atomic
+    def generate_container_operate_no_batch(self, container_obj, bound_number,allocation_target_location):
+       
+        ContainerOperationModel.objects.create(
+            month = int(timezone.now().strftime("%Y%m")),
+            container = container_obj,
+            goods_code = 'container',
+            goods_desc = '托盘组',
+            operation_type ="inbound",
+            goods_qty = 1,
+            goods_weight = 0,
+            from_location = container_obj.current_location,
+            to_location= allocation_target_location,
+            timestamp=timezone.now(),
+            memo=f"WCS入库: 批次: {bound_number}, 数量: 1"
+
+        )
+    @transaction.atomic
     def generate_task(self, container, current_location, target_location,batch_id,location_c_number):
         batch = BoundBatchModel.objects.filter(bound_number=batch_id).first()
         batch_detail = BoundDetailModel.objects.filter(bound_batch=batch).first()
@@ -417,6 +391,7 @@ class ContainerWCSViewSet(viewsets.ModelViewSet):
         data_tosave = {
             'container': container,
             'batch': batch,
+            'batch_number': batch_id,
             'batch_out': None,
             'bound_list': batch_detail.bound_list,
             'sequence': 1,
@@ -448,8 +423,46 @@ class ContainerWCSViewSet(viewsets.ModelViewSet):
         # 每月生成唯一递增的 taskNumber
         data_tosave['tasknumber'] = number_id
         ContainerWCSModel.objects.create(**data_tosave)
-        
 
+    def generate_task_no_batch(self, container, current_location, target_location,batch_id,location_c_number):
+    
+ 
+        data_tosave = {
+            'container': container,
+            'batch': None,
+            'batch_number': batch_id,
+            'batch_out': None,
+            'bound_list': None,
+            'sequence': 1,
+            'order_number' :location_c_number,
+            'priority': 1,
+            'current_location': current_location,
+            'month': timezone.now().strftime('%Y%m'),
+            'target_location': target_location,  
+            'tasktype': 'inbound',
+            'status': 103,
+            'is_delete': False
+        }
+
+        # 生成唯一递增的 taskid
+        last_task = ContainerWCSModel.objects.filter(
+            month=data_tosave['month'],
+        ).order_by('-tasknumber').first()
+
+        if last_task:
+            
+            number_id = last_task.tasknumber + 1
+            new_id = f"{number_id:05d}"
+        else:
+            new_id = "00001"
+            number_id = f"{data_tosave['month']}{new_id}"
+        
+        data_tosave['taskid'] = f"inbound-{data_tosave['month']}-{new_id}"
+        logger.info(f"生成入库任务: {data_tosave['taskid']}")
+        # 每月生成唯一递增的 taskNumber
+        data_tosave['tasknumber'] = number_id
+        ContainerWCSModel.objects.create(**data_tosave)
+        
     def update_container_wcs(self, request, *args, **kwargs):
         data = self.request.data
         logger.info(f"请求托盘:{data.get('container_number')}, 请求位置:{data.get('current_location')}, 任务号:{data.get('taskNumber')}")
@@ -517,6 +530,8 @@ class ContainerWCSViewSet(viewsets.ModelViewSet):
 
     def is_already_at_target(self, container_obj, current_location):
         """检查是否已在目标位置"""
+        print (current_location)
+        print (str(container_obj.target_location))
         return current_location == str(container_obj.target_location)
 
     def handle_target_reached(self, container_obj, data):

+ 192 - 192
data_base/product.csv

@@ -1,192 +1,192 @@
-id,product_code,product_name,product_std,product_unit,creater,is_delete
-1,D/DA06,地奥司明,STP,kg,first,0
-2,DAP01,地奥司明,STP,kg,first,0
-3,DAP02,地奥司明,STP,kg,first,0
-4,DAP04,地奥司明,STP,kg,first,0
-5,DAP05,地奥司明,STP,kg,first,0
-6,DAP06,地奥司明,STP,kg,first,0
-7,DAP08,柑橘黄酮,STP,kg,first,0
-8,DAP09,地奥司明,STP,kg,first,0
-9,DAP10,地奥司明,STP,kg,first,0
-10,DAP11,地奥司明,STP,kg,first,0
-11,DAP08,柑橘黄酮,STP,kg,first,0
-12,DBP08,柑橘黄酮,STP,kg,first,0
-13,DBP01,地奥司明微粉,STP,kg,first,0
-14,DBP04,地奥司明微粉,STP,kg,first,0
-15,DBP05,地奥司明微粉,STP,kg,first,0
-16,DBP06,地奥司明微粉,STP,kg,first,0
-17,DBP09,地奥司明微粉,STP,kg,first,0
-18,DBP10,地奥司明微粉,STP,kg,first,0
-19,DBP11,地奥司明微粉,STP,kg,first,0
-20,DBP12,地奥司明微粉,STP,kg,first,0
-21,DFF03,地奥司明A/SG,STP,kg,first,0
-22,DBP07,MPFF,STP,kg,first,0
-23,DAF01,地奥司明,STP,kg,first,0
-24,DAF02,地奥司明,STP,kg,first,0
-25,DAF03,地奥司明,STP,kg,first,0
-26,DAF04,地奥司明,STP,kg,first,0
-27,DAF05,地奥司明,STP,kg,first,0
-28,DBF01,地奥司明微粉,STP,kg,first,0
-29,DBF02,地奥司明微粉,STP,kg,first,0
-30,DBF03,地奥司明微粉,STP,kg,first,0
-31,DBF04,地奥司明微粉,STP,kg,first,0
-32,DBF05,地奥司明微粉,STP,kg,first,0
-33,D/RAF01,芦丁,STP,kg,first,0
-34,D/RCF01,芦丁,STP,kg,first,0
-35,D/RDF01,芦丁,STP,kg,first,0
-36,D/RDF02,芦丁,STP,kg,first,0
-37,D/RF02,芦丁,STP,kg,first,0
-38,D/RDP01,芦丁,STP,kg,first,0
-39,D/RI01,芦丁(S),STP,kg,first,0
-40,RDP01,芦丁(S),STP,kg,first,0
-41,RDP02,芦丁(S),STP,kg,first,0
-42,RF02,芦丁A,STP,kg,first,0
-43,RF04,芦丁E,STP,kg,first,0
-44,D/TAF01,曲克芦丁,STP,kg,first,0
-45,D/UAF01,L-鼠李糖,STP,kg,first,0
-46,DHF02,香叶木素,STP,kg,first,0
-47,DHF02,香叶木素,STP,kg,first,0
-48,D/NAF01,新橙皮苷,STP,kg,first,0
-49,D/NCF01,柚苷,STP,kg,first,0
-50,D/NDF01,柚皮苷二氢查耳酮,STP,kg,first,0
-51,CCF01,鹰嘴豆蛋白,STP,kg,first,0
-52,PBF01,根皮素,STP,kg,first,0
-53,PAF01,普鲁宁,STP,kg,first,0
-54,EG01,蛋黄粉(脱脂),STP,kg,first,0
-55,GAF01,染料木素,STP,kg,first,0
-56,HDF01,橙皮素,STP,kg,first,0
-57,GBF01,葡糖基芦丁(AGR),STP,kg,first,0
-58,D/EG02,(鸡)蛋黄粉,STP,kg,first,0
-59,EG02,(鸡)蛋黄粉,STP,kg,first,0
-60,RHF01,白藜芦醇,STP,kg,first,0
-61,LBF01,左旋多巴,STP,kg,first,0
-62,BDF01,苦荞黄酮,STP,kg,first,0
-63,SBF01,甜橙皮提取物,STP,kg,first,0
-64,D/SBF01,甜橙皮提取物,STP,kg,first,0
-65,LCF01,圣草次苷,STP,kg,first,0
-66,AAP01,青蒿素,STP,kg,first,0
-67,ABP01,双氢青蒿素,STP,kg,first,0
-68,ADP01,蒿甲醚,STP,kg,first,0
-69,ACP01,青蒿琥酯,STP,kg,first,0
-70,DAF04,地奥司明,F-STP,kg,first,0
-71,DCF02,地奥司明:橙皮苷(90:10),F-STP,kg,first,0
-72,DCF03,地奥司明:橙皮苷(90:10),F-STP,kg,first,0
-73,DCF04,地奥司明:橙皮苷(90:10),F-STP,kg,first,0
-74,DCF06,地奥司明:橙皮苷(90:10),F-STP,kg,first,0
-75,DDF02,地奥司明:橙皮苷(90:10)微粉,F-STP,kg,first,0
-76,DDF03,地奥司明:橙皮苷(90:10)微粉,F-STP,kg,first,0
-77,DGF01,地奥司明:橙皮苷(90:10)颗粒,F-STP,kg,first,0
-78,LDF01,柠檬黄酮,F-STP,kg,first,0
-79,QHF01,二氢槲皮素,F-STP,kg,first,0
-80,QM02,芦丁水解中和浓缩液,F-STP,kg,first,0
-81,GBF01,葡萄柚黄酮,F-STP,kg,first,0
-82,GBF02,葡萄柚黄酮,F-STP,kg,first,0
-83,GBF03,葡萄柚黄酮,F-STP,kg,first,0
-84,BAF01,盐酸小檗碱,F-STP,kg,first,0
-85,BAF02,盐酸小檗碱,F-STP,kg,first,0
-86,BAF03,盐酸小檗碱,F-STP,kg,first,0
-87,BBF04,盐酸小檗碱颗粒,F-STP,kg,first,0
-88,BBF05,盐酸小檗碱颗粒,F-STP,kg,first,0
-89,BCF03,高密度盐酸小檗碱,F-STP,kg,first,0
-90,D/NBF05,新甲基橙皮苷二氢查耳酮,F-STP,kg,first,0
-91,NBF01,新甲基橙皮苷二氢查耳酮,F-STP,kg,first,0
-92,NBF05,新甲基橙皮苷二氢查耳酮,F-STP,kg,first,0
-93,D/QAF01,二水槲皮素,F-STP,kg,first,0
-94,D/QAF03,二水槲皮素,F-STP,kg,first,0
-95,D/QAF08,二水槲皮素,F-STP,kg,first,0
-96,D/QBF03,二水槲皮素颗粒,F-STP,kg,first,0
-97,D/QDF02,无水槲皮素,F-STP,kg,first,0
-98,D/QDF02,无水槲皮素(HM-02),F-STP,kg,first,0
-99,D/QDF03,无水槲皮素,F-STP,kg,first,0
-100,D/QDF04,无水槲皮素,F-STP,kg,first,0
-101,D/QEF01,无水槲皮素颗粒,F-STP,kg,first,0
-102,QAF01,二水槲皮素,F-STP,kg,first,0
-103,QAF03,二水槲皮素,F-STP,kg,first,0
-104,QAF05,二水槲皮素,F-STP,kg,first,0
-105,QAF08,二水槲皮素,F-STP,kg,first,0
-106,QBF03,二水槲皮素颗粒,F-STP,kg,first,0
-107,QCF02,二水高密度槲皮素,F-STP,kg,first,0
-108,QCF03,二水高密度槲皮素,F-STP,kg,first,0
-109,QCF05,二水高密度槲皮素,F-STP,kg,first,0
-110,QCF06,二水高密度槲皮素,F-STP,kg,first,0
-111,QDF02,无水槲皮素(HM-02),F-STP,kg,first,0
-112,QDF02,无水槲皮素,F-STP,kg,first,0
-113,QDF03,无水槲皮素,F-STP,kg,first,0
-114,QDF04,无水槲皮素,F-STP,kg,first,0
-115,QDF05,无水槲皮素,F-STP,kg,first,0
-116,QEF01,无水槲皮素颗粒,F-STP,kg,first,0
-117,QEF02,无水槲皮素颗粒,F-STP,kg,first,0
-118,QEF03,无水槲皮素颗粒,F-STP,kg,first,0
-119,QEF04,无水槲皮素颗粒,F-STP,kg,first,0
-120,QFF02,无水高密度槲皮素,F-STP,kg,first,0
-121,QFF04,无水高密度槲皮素,F-STP,kg,first,0
-122,QGF01,槐树花浸膏,F-STP,kg,first,0
-123,RGF01,白藜芦醇颗粒,F-STP,kg,first,0
-124,D/IAF01,异槲皮苷,F-STP,kg,first,0
-125,D/IAF02,异槲皮苷,F-STP,kg,first,0
-126,QHF01,二氢槲皮素,F-STP,kg,first,0
-127,IAF01,异槲皮苷,F-STP,kg,first,0
-128,IAF02,异槲皮苷,F-STP,kg,first,0
-129,HAF01,橙皮苷,F-STP,kg,first,0
-130,HAF02,橙皮苷,F-STP,kg,first,0
-131,HAF03,橙皮苷,F-STP,kg,first,0
-132,HAF04,橙皮苷,F-STP,kg,first,0
-133,HBF01,橙皮苷微粉,F-STP,kg,first,0
-134,HBF02,橙皮苷微粉,F-STP,kg,first,0
-135,HBF03,橙皮苷微粉,F-STP,kg,first,0
-136,HBP01,橙皮苷微粉,F-STP,kg,first,0
-137,HCF01,橙皮苷颗粒,F-STP,kg,first,0
-138,HCF02,橙皮苷颗粒,F-STP,kg,first,0
-139,HCF03,橙皮苷颗粒,F-STP,kg,first,0
-140,HAF01,橙皮苷,F-STP,kg,first,0
-141,CAF01,枳实黄酮,F-STP,kg,first,0
-142,CAF02,枳实黄酮,F-STP,kg,first,0
-143,CAF03,枳实黄酮,F-STP,kg,first,0
-144,CAF04,枳实黄酮,F-STP,kg,first,0
-145,CAF05,枳实黄酮,F-STP,kg,first,0
-146,CAF06,枳实黄酮,F-STP,kg,first,0
-147,CAF07,枳实黄酮,F-STP,kg,first,0
-148,CAF08,枳实黄酮,F-STP,kg,first,0
-149,CBF01,枳实黄酮颗粒,F-STP,kg,first,0
-150,CBF02,枳实黄酮颗粒,F-STP,kg,first,0
-151,CBF03,枳实黄酮颗粒,F-STP,kg,first,0
-152,RI01,芦丁(S),F-STP,kg,first,0
-153,RI02,芦丁(S),F-STP,kg,first,0
-154,TAF01,曲克芦丁,F-STP,kg,first,0
-155,TAF03,曲克芦丁,F-STP,kg,first,0
-156,TAF04,曲克芦丁,F-STP,kg,first,0
-157,TBF01,曲克芦丁,F-STP,kg,first,0
-158,RFF01,水溶性芦丁,F-STP,kg,first,0
-159,RAF01,芦丁,F-STP,kg,first,0
-160,RCF01,芦丁,F-STP,kg,first,0
-161,RDF01,芦丁,F-STP,kg,first,0
-162,RDF02,芦丁,F-STP,kg,first,0
-163,RDF03,芦丁,F-STP,kg,first,0
-164,RDF04,芦丁,F-STP,kg,first,0
-165,RDF05,芦丁,F-STP,kg,first,0
-166,REF01,芦丁颗粒,F-STP,kg,first,0
-167,NCF01,柚苷,F-STP,kg,first,0
-168,MBF01,橙皮苷甲基查耳酮,F-STP,kg,first,0
-169,HDF01,橙皮素,F-STP,kg,first,0
-170,NDF01,柚皮苷二氢查耳酮,F-STP,kg,first,0
-171,NGF01,柚皮素,F-STP,kg,first,0
-172,NAF01,新橙皮苷,F-STP,kg,first,0
-173,NAF02,新橙皮苷,F-STP,kg,first,0
-174,DHF01,香叶木素,F-STP,kg,first,0
-175,DHF02,香叶木素,F-STP,kg,first,0
-176,DFF01,地奥司明香叶木素颗粒,F-STP,kg,first,0
-177,DFF02,地奥司明香叶木素颗粒,F-STP,kg,first,0
-178,DEF01,地奥司明颗粒,F-STP,kg,first,0
-179,DEF02,地奥司明颗粒,F-STP,kg,first,0
-180,DEF03,地奥司明颗粒,F-STP,kg,first,0
-181,DEF04,地奥司明颗粒,F-STP,kg,first,0
-182,DFF03,地奥司明A/SG,F-STP,kg,first,0
-183,HFF01,柑橘提取物,F-STP,kg,first,0
-184,D/UAF01,L-鼠李糖,F-STP,kg,first,0
-185,UAF01,L-鼠李糖,F-STP,kg,first,0
-186,NFF01,柚苷颗粒,F-STP,kg,first,0
-187,LUF01,木犀草素,F-STP,kg,first,0
-188,GAF01,染料木素,F-STP,kg,first,0
-189,NGF01,柚皮苷,F-STP,kg,first,0
-190,LCF01,圣草次苷,F-STP,kg,first,0
-191,FAF01,漆黄素,F-STP,kg,first,0
+id,product_code,product_name,product_std,product_unit,product_package,creater,is_delete
+1,D/DA06,地奥司明,STP,kg,箱,first,0
+2,DAP01,地奥司明,STP,kg,桶,first,0
+3,DAP02,地奥司明,STP,kg,袋,first,0
+4,DAP04,地奥司明,STP,kg,箱,first,0
+5,DAP05,地奥司明,STP,kg,桶,first,0
+6,DAP06,地奥司明,STP,kg,袋,first,0
+7,DAP08,柑橘黄酮,STP,kg,箱,first,0
+8,DAP09,地奥司明,STP,kg,桶,first,0
+9,DAP10,地奥司明,STP,kg,袋,first,0
+10,DAP11,地奥司明,STP,kg,箱,first,0
+11,DAP08,柑橘黄酮,STP,kg,桶,first,0
+12,DBP08,柑橘黄酮,STP,kg,袋,first,0
+13,DBP01,地奥司明微粉,STP,kg,箱,first,0
+14,DBP04,地奥司明微粉,STP,kg,桶,first,0
+15,DBP05,地奥司明微粉,STP,kg,袋,first,0
+16,DBP06,地奥司明微粉,STP,kg,箱,first,0
+17,DBP09,地奥司明微粉,STP,kg,桶,first,0
+18,DBP10,地奥司明微粉,STP,kg,袋,first,0
+19,DBP11,地奥司明微粉,STP,kg,箱,first,0
+20,DBP12,地奥司明微粉,STP,kg,桶,first,0
+21,DFF03,地奥司明A/SG,STP,kg,袋,first,0
+22,DBP07,MPFF,STP,kg,箱,first,0
+23,DAF01,地奥司明,STP,kg,桶,first,0
+24,DAF02,地奥司明,STP,kg,袋,first,0
+25,DAF03,地奥司明,STP,kg,箱,first,0
+26,DAF04,地奥司明,STP,kg,桶,first,0
+27,DAF05,地奥司明,STP,kg,袋,first,0
+28,DBF01,地奥司明微粉,STP,kg,箱,first,0
+29,DBF02,地奥司明微粉,STP,kg,桶,first,0
+30,DBF03,地奥司明微粉,STP,kg,袋,first,0
+31,DBF04,地奥司明微粉,STP,kg,箱,first,0
+32,DBF05,地奥司明微粉,STP,kg,桶,first,0
+33,D/RAF01,芦丁,STP,kg,袋,first,0
+34,D/RCF01,芦丁,STP,kg,箱,first,0
+35,D/RDF01,芦丁,STP,kg,桶,first,0
+36,D/RDF02,芦丁,STP,kg,袋,first,0
+37,D/RF02,芦丁,STP,kg,箱,first,0
+38,D/RDP01,芦丁,STP,kg,桶,first,0
+39,D/RI01,芦丁(S),STP,kg,袋,first,0
+40,RDP01,芦丁(S),STP,kg,箱,first,0
+41,RDP02,芦丁(S),STP,kg,桶,first,0
+42,RF02,芦丁A,STP,kg,袋,first,0
+43,RF04,芦丁E,STP,kg,箱,first,0
+44,D/TAF01,曲克芦丁,STP,kg,桶,first,0
+45,D/UAF01,L-鼠李糖,STP,kg,袋,first,0
+46,DHF02,香叶木素,STP,kg,箱,first,0
+47,DHF02,香叶木素,STP,kg,桶,first,0
+48,D/NAF01,新橙皮苷,STP,kg,袋,first,0
+49,D/NCF01,柚苷,STP,kg,箱,first,0
+50,D/NDF01,柚皮苷二氢查耳酮,STP,kg,桶,first,0
+51,CCF01,鹰嘴豆蛋白,STP,kg,袋,first,0
+52,PBF01,根皮素,STP,kg,箱,first,0
+53,PAF01,普鲁宁,STP,kg,桶,first,0
+54,EG01,蛋黄粉(脱脂),STP,kg,袋,first,0
+55,GAF01,染料木素,STP,kg,箱,first,0
+56,HDF01,橙皮素,STP,kg,桶,first,0
+57,GBF01,葡糖基芦丁(AGR),STP,kg,袋,first,0
+58,D/EG02,(鸡)蛋黄粉,STP,kg,箱,first,0
+59,EG02,(鸡)蛋黄粉,STP,kg,桶,first,0
+60,RHF01,白藜芦醇,STP,kg,袋,first,0
+61,LBF01,左旋多巴,STP,kg,箱,first,0
+62,BDF01,苦荞黄酮,STP,kg,桶,first,0
+63,SBF01,甜橙皮提取物,STP,kg,袋,first,0
+64,D/SBF01,甜橙皮提取物,STP,kg,箱,first,0
+65,LCF01,圣草次苷,STP,kg,桶,first,0
+66,AAP01,青蒿素,STP,kg,袋,first,0
+67,ABP01,双氢青蒿素,STP,kg,箱,first,0
+68,ADP01,蒿甲醚,STP,kg,桶,first,0
+69,ACP01,青蒿琥酯,STP,kg,袋,first,0
+70,DAF04,地奥司明,F-STP,kg,箱,first,0
+71,DCF02,地奥司明:橙皮苷(90:10),F-STP,kg,桶,first,0
+72,DCF03,地奥司明:橙皮苷(90:10),F-STP,kg,袋,first,0
+73,DCF04,地奥司明:橙皮苷(90:10),F-STP,kg,箱,first,0
+74,DCF06,地奥司明:橙皮苷(90:10),F-STP,kg,桶,first,0
+75,DDF02,地奥司明:橙皮苷(90:10)微粉,F-STP,kg,袋,first,0
+76,DDF03,地奥司明:橙皮苷(90:10)微粉,F-STP,kg,箱,first,0
+77,DGF01,地奥司明:橙皮苷(90:10)颗粒,F-STP,kg,桶,first,0
+78,LDF01,柠檬黄酮,F-STP,kg,袋,first,0
+79,QHF01,二氢槲皮素,F-STP,kg,箱,first,0
+80,QM02,芦丁水解中和浓缩液,F-STP,kg,桶,first,0
+81,GBF01,葡萄柚黄酮,F-STP,kg,袋,first,0
+82,GBF02,葡萄柚黄酮,F-STP,kg,箱,first,0
+83,GBF03,葡萄柚黄酮,F-STP,kg,桶,first,0
+84,BAF01,盐酸小檗碱,F-STP,kg,袋,first,0
+85,BAF02,盐酸小檗碱,F-STP,kg,箱,first,0
+86,BAF03,盐酸小檗碱,F-STP,kg,桶,first,0
+87,BBF04,盐酸小檗碱颗粒,F-STP,kg,袋,first,0
+88,BBF05,盐酸小檗碱颗粒,F-STP,kg,箱,first,0
+89,BCF03,高密度盐酸小檗碱,F-STP,kg,桶,first,0
+90,D/NBF05,新甲基橙皮苷二氢查耳酮,F-STP,kg,袋,first,0
+91,NBF01,新甲基橙皮苷二氢查耳酮,F-STP,kg,箱,first,0
+92,NBF05,新甲基橙皮苷二氢查耳酮,F-STP,kg,桶,first,0
+93,D/QAF01,二水槲皮素,F-STP,kg,袋,first,0
+94,D/QAF03,二水槲皮素,F-STP,kg,箱,first,0
+95,D/QAF08,二水槲皮素,F-STP,kg,桶,first,0
+96,D/QBF03,二水槲皮素颗粒,F-STP,kg,袋,first,0
+97,D/QDF02,无水槲皮素,F-STP,kg,箱,first,0
+98,D/QDF02,无水槲皮素(HM-02),F-STP,kg,桶,first,0
+99,D/QDF03,无水槲皮素,F-STP,kg,袋,first,0
+100,D/QDF04,无水槲皮素,F-STP,kg,箱,first,0
+101,D/QEF01,无水槲皮素颗粒,F-STP,kg,桶,first,0
+102,QAF01,二水槲皮素,F-STP,kg,袋,first,0
+103,QAF03,二水槲皮素,F-STP,kg,箱,first,0
+104,QAF05,二水槲皮素,F-STP,kg,桶,first,0
+105,QAF08,二水槲皮素,F-STP,kg,袋,first,0
+106,QBF03,二水槲皮素颗粒,F-STP,kg,箱,first,0
+107,QCF02,二水高密度槲皮素,F-STP,kg,桶,first,0
+108,QCF03,二水高密度槲皮素,F-STP,kg,袋,first,0
+109,QCF05,二水高密度槲皮素,F-STP,kg,箱,first,0
+110,QCF06,二水高密度槲皮素,F-STP,kg,桶,first,0
+111,QDF02,无水槲皮素(HM-02),F-STP,kg,袋,first,0
+112,QDF02,无水槲皮素,F-STP,kg,箱,first,0
+113,QDF03,无水槲皮素,F-STP,kg,桶,first,0
+114,QDF04,无水槲皮素,F-STP,kg,袋,first,0
+115,QDF05,无水槲皮素,F-STP,kg,箱,first,0
+116,QEF01,无水槲皮素颗粒,F-STP,kg,桶,first,0
+117,QEF02,无水槲皮素颗粒,F-STP,kg,袋,first,0
+118,QEF03,无水槲皮素颗粒,F-STP,kg,箱,first,0
+119,QEF04,无水槲皮素颗粒,F-STP,kg,桶,first,0
+120,QFF02,无水高密度槲皮素,F-STP,kg,袋,first,0
+121,QFF04,无水高密度槲皮素,F-STP,kg,箱,first,0
+122,QGF01,槐树花浸膏,F-STP,kg,桶,first,0
+123,RGF01,白藜芦醇颗粒,F-STP,kg,袋,first,0
+124,D/IAF01,异槲皮苷,F-STP,kg,箱,first,0
+125,D/IAF02,异槲皮苷,F-STP,kg,桶,first,0
+126,QHF01,二氢槲皮素,F-STP,kg,袋,first,0
+127,IAF01,异槲皮苷,F-STP,kg,箱,first,0
+128,IAF02,异槲皮苷,F-STP,kg,桶,first,0
+129,HAF01,橙皮苷,F-STP,kg,袋,first,0
+130,HAF02,橙皮苷,F-STP,kg,箱,first,0
+131,HAF03,橙皮苷,F-STP,kg,桶,first,0
+132,HAF04,橙皮苷,F-STP,kg,袋,first,0
+133,HBF01,橙皮苷微粉,F-STP,kg,箱,first,0
+134,HBF02,橙皮苷微粉,F-STP,kg,桶,first,0
+135,HBF03,橙皮苷微粉,F-STP,kg,袋,first,0
+136,HBP01,橙皮苷微粉,F-STP,kg,箱,first,0
+137,HCF01,橙皮苷颗粒,F-STP,kg,桶,first,0
+138,HCF02,橙皮苷颗粒,F-STP,kg,袋,first,0
+139,HCF03,橙皮苷颗粒,F-STP,kg,箱,first,0
+140,HAF01,橙皮苷,F-STP,kg,桶,first,0
+141,CAF01,枳实黄酮,F-STP,kg,袋,first,0
+142,CAF02,枳实黄酮,F-STP,kg,箱,first,0
+143,CAF03,枳实黄酮,F-STP,kg,桶,first,0
+144,CAF04,枳实黄酮,F-STP,kg,袋,first,0
+145,CAF05,枳实黄酮,F-STP,kg,箱,first,0
+146,CAF06,枳实黄酮,F-STP,kg,桶,first,0
+147,CAF07,枳实黄酮,F-STP,kg,袋,first,0
+148,CAF08,枳实黄酮,F-STP,kg,箱,first,0
+149,CBF01,枳实黄酮颗粒,F-STP,kg,桶,first,0
+150,CBF02,枳实黄酮颗粒,F-STP,kg,袋,first,0
+151,CBF03,枳实黄酮颗粒,F-STP,kg,箱,first,0
+152,RI01,芦丁(S),F-STP,kg,桶,first,0
+153,RI02,芦丁(S),F-STP,kg,袋,first,0
+154,TAF01,曲克芦丁,F-STP,kg,箱,first,0
+155,TAF03,曲克芦丁,F-STP,kg,桶,first,0
+156,TAF04,曲克芦丁,F-STP,kg,袋,first,0
+157,TBF01,曲克芦丁,F-STP,kg,箱,first,0
+158,RFF01,水溶性芦丁,F-STP,kg,桶,first,0
+159,RAF01,芦丁,F-STP,kg,袋,first,0
+160,RCF01,芦丁,F-STP,kg,箱,first,0
+161,RDF01,芦丁,F-STP,kg,桶,first,0
+162,RDF02,芦丁,F-STP,kg,袋,first,0
+163,RDF03,芦丁,F-STP,kg,箱,first,0
+164,RDF04,芦丁,F-STP,kg,桶,first,0
+165,RDF05,芦丁,F-STP,kg,袋,first,0
+166,REF01,芦丁颗粒,F-STP,kg,箱,first,0
+167,NCF01,柚苷,F-STP,kg,桶,first,0
+168,MBF01,橙皮苷甲基查耳酮,F-STP,kg,袋,first,0
+169,HDF01,橙皮素,F-STP,kg,箱,first,0
+170,NDF01,柚皮苷二氢查耳酮,F-STP,kg,桶,first,0
+171,NGF01,柚皮素,F-STP,kg,袋,first,0
+172,NAF01,新橙皮苷,F-STP,kg,箱,first,0
+173,NAF02,新橙皮苷,F-STP,kg,桶,first,0
+174,DHF01,香叶木素,F-STP,kg,袋,first,0
+175,DHF02,香叶木素,F-STP,kg,箱,first,0
+176,DFF01,地奥司明香叶木素颗粒,F-STP,kg,桶,first,0
+177,DFF02,地奥司明香叶木素颗粒,F-STP,kg,袋,first,0
+178,DEF01,地奥司明颗粒,F-STP,kg,箱,first,0
+179,DEF02,地奥司明颗粒,F-STP,kg,桶,first,0
+180,DEF03,地奥司明颗粒,F-STP,kg,袋,first,0
+181,DEF04,地奥司明颗粒,F-STP,kg,箱,first,0
+182,DFF03,地奥司明A/SG,F-STP,kg,桶,first,0
+183,HFF01,柑橘提取物,F-STP,kg,袋,first,0
+184,D/UAF01,L-鼠李糖,F-STP,kg,箱,first,0
+185,UAF01,L-鼠李糖,F-STP,kg,桶,first,0
+186,NFF01,柚苷颗粒,F-STP,kg,袋,first,0
+187,LUF01,木犀草素,F-STP,kg,箱,first,0
+188,GAF01,染料木素,F-STP,kg,桶,first,0
+189,NGF01,柚皮苷,F-STP,kg,袋,first,0
+190,LCF01,圣草次苷,F-STP,kg,箱,first,0
+191,FAF01,漆黄素,F-STP,kg,桶,first,0

Diff do ficheiro suprimidas por serem muito extensas
+ 2279 - 0
logs/error.log


Diff do ficheiro suprimidas por serem muito extensas
+ 3311 - 0
logs/server.log


+ 186 - 87
templates/src/pages/inbound/asn.vue

@@ -519,6 +519,7 @@
                 transition-show="scale"
                 transition-hide="scale"
                 :rules="[(val) => (val && val.length > 0) || error1]"
+                :onClick="assignGoodsCode()"
               />
             </div>
             <div class="col column q-gutter-y-md">
@@ -549,7 +550,7 @@
             option-value="value"
             emit-value
             map-options
-            :readonly="true"
+            :readonly="false"
             transition-show="scale"
             transition-hide="scale"
             :rules="[(val) => (val && val.length > 0) || error1]"
@@ -566,7 +567,7 @@
 
           <div class="row q-gutter-x-md">
             <div class="col column q-gutter-y-md">
-              <q-input
+              <!-- <q-input
                 dense
                 outlined
                 square
@@ -574,15 +575,28 @@
                 :label="'单重'"
                 type="number"
                 :rules="[(val) => (val && val > 0) || error1]"
+              /> -->
+              <q-input
+                dense
+                outlined
+                square
+                v-model.number="newBatchFormData.goods_qty"
+                :label="'数量'"
+                type="number"
+                :rules="[(val) => (val && val > 0) || error1]"
               />
               <q-input
                 dense
                 outlined
                 square
-                v-model="newBatchFormData.goods_std"
-                :label="'规格/备注'"
+                v-model="newBatchFormData.goods_package"
+                :label="'包装'"
                 :rules="[(val) => (val && val.length > 0) || error1]"
               />
+              <q-toggle
+                v-model="isorder"
+                :label="isorder ? '自动编码' : '手动编码(建议)'"
+              />
             </div>
             <div class="col column q-gutter-y-md">
               <q-input
@@ -593,9 +607,47 @@
                 :label="'单位'"
                 :rules="[(val) => (val && val.length > 0) || error1]"
               />
-              <q-toggle
-                v-model="isorder"
-                :label="isorder ? '自动编码' : '手动编码(建议)'"
+              <q-input
+                dense
+                outlined
+                square
+                v-model="newBatchFormData.goods_std"
+                :label="'规格/备注'"
+                :rules="[(val) => (val && val.length > 0) || error1]"
+              />
+            </div>
+          </div>
+          <div class="row q-gutter-x-md" v-show="!isorder">
+            <div class="col column q-gutter-y-md">
+              <q-input
+                dense
+                outlined
+                square
+                v-model="batch_number_year"
+                type="number"
+                :label="'年'"
+                :rules="[(val) => (val && val > 0) || error1]"
+              />
+            </div>
+            <div class="col column q-gutter-y-md">
+              <q-input
+                dense
+                outlined
+                square
+                v-model="batch_number_month"
+                :label="'月'"
+                type="number"
+                :rules="[(val) => (val && val > 0) || error1]"
+              />
+            </div>
+            <div class="col column q-gutter-y-md">
+              <q-input
+                dense
+                outlined
+                v-model="batch_number_batch"
+                type="number"
+                :label="'批'"
+                :rules="[(val) => (val && val > 0) || '请输入正确的批号']"
               />
             </div>
           </div>
@@ -606,15 +658,7 @@
             square
             v-model.number="newBatchFormData.bound_batch_order"
             :label="'批号——年月第几批(20250409)'"
-            type="number"
-            :rules="[(val) => (val && val > 0) || error1]"
-          />
-          <q-input
-            dense
-            outlined
-            square
-            v-model.number="newBatchFormData.goods_qty"
-            :label="'数量'"
+            class="centered-input"
             type="number"
             :rules="[(val) => (val && val > 0) || error1]"
           />
@@ -648,7 +692,7 @@
       transition-show="jump-down"
       transition-hide="jump-up"
     >
-      <q-card style="min-width: 900px">
+      <q-card style="min-width: 1150px">
         <q-bar
           class="bg-light-blue-10 text-white rounded-borders"
           style="height: 50px"
@@ -722,73 +766,31 @@
             <q-card-section>
               <template>
                 <div class="text-h6 q-mb-md">{{ "批次信息" }}</div>
-                <template v-if="batch_detail.length > 0">
-                  <div
-                    v-for="(item, index) in batch_detail"
-                    :key="index"
-                    class="row q-col-gutter-md q-mb-sm"
-                  >
-                    <div class="col-1">
-                      <!-- 修改打印按钮,使用v-print指令 -->
+                <q-table
+                  v-if="batch_detail.length > 0"
+                  :data="batch_detail"
+                  :columns="columns_batch"
+                  row-key="id"
+                  flat
+                  bordered
+                  hide-pagination
+                  class="my-sticky-table"
+                >
+                  <!-- 自定义单元格内容 -->
+                  <template v-slot:body-cell-actions="props">
+                    <q-td :props="props">
                       <q-btn
                         icon="print"
                         flat
-                        v-print="getPrintConfig(item)"
-                        @click="setCurrentBatch(item)"
+                        v-print="getPrintConfig(props.row)"
+                        @click="setCurrentBatch(props.row)"
                       >
                         <q-tooltip>打印条码</q-tooltip>
                       </q-btn>
-                    </div>
-                    <div class="col-3">
-                      <q-input
-                        v-model="item.bound_batch.sourced_number"
-                        :label="'批次'"
-                        :readonly="onlyread"
-                        dense
-                        outlined
-                      />
-                    </div>
-                    <div class="col-4">
-                      <q-input
-                        v-model="item.bound_batch.goods_desc"
-                        :label="'货物'"
-                        :readonly="onlyread"
-                        dense
-                        outlined
-                      />
-                    </div>
-
-                    <div class="col-2">
-                      <q-input
-                        v-model="item.bound_batch.goods_in_qty"
-                        :label="'数量'"
-                        :readonly="onlyread"
-                        dense
-                        outlined
-                      />
-                    </div>
-                    <div class="col-2">
-                      <q-input
-                        v-model="item.bound_batch.goods_unit"
-                        :label="'单位'"
-                        :readonly="onlyread"
-                        dense
-                        outlined
-                      />
-                    </div>
-                    <!-- <div class="col" style="max-width: 50px">
-                      <q-btn
-                        v-if="!onlyread"
-                        icon="delete"
-                        flat
-                        dense
-                        color="primary"
-                        @click="addbatch(table_detail.id)"
-                        style="margin-top: 1px"
-                      />
-                    </div> -->
-                  </div>
-                </template>
+                    </q-td>
+                  </template>
+
+                </q-table>
               </template>
             </q-card-section>
           </q-card>
@@ -817,9 +819,9 @@
 
 <script>
 import { getauth, postauth, putauth, deleteauth } from 'boot/axios_request'
+
 import JsBarcode from 'jsbarcode'
 import { date, exportFile, LocalStorage, QToggle } from 'quasar'
-
 export default {
   components: {
     QToggle
@@ -869,6 +871,9 @@ export default {
       bound_status_map: [],
       product_list: [],
       product_map: [],
+      batch_number_year: '',
+      batch_number_month: '',
+      batch_number_batch: '',
 
       columns: [
         { name: 'detail', label: '详情', field: 'detail', align: 'center' },
@@ -917,6 +922,55 @@ export default {
         },
         { name: 'action', label: '操作', align: 'center' }
       ],
+      // 列定义
+      columns_batch: [
+        {
+          name: 'actions',
+          label: '操作',
+          align: 'center',
+          style: 'width: 80px'
+        },
+        {
+          name: 'bound_batch_order',
+          label: '批次',
+          field: (row) => row.bound_batch.bound_batch_order,
+          align: 'center'
+        },
+        {
+          name: 'goods_desc',
+          label: '货物',
+          field: (row) => row.bound_batch.goods_desc,
+          align: 'center'
+        },
+        {
+          name: 'plan_weight',
+          label: '计划入库重量',
+          field: (row) => row.bound_batch.goods_qty, // 确保字段路径正确
+          align: 'center',
+          style: 'width: 150px'
+        },
+        {
+          name: 'actual_weight',
+          label: '实际入库重量',
+          field: (row) => row.bound_batch.goods_in_qty, // 根据实际字段调整
+          align: 'center',
+          style: 'width: 150px'
+        },
+        {
+          name: 'unit',
+          label: '单位',
+          field: (row) => row.bound_batch.goods_unit,
+          align: 'center',
+          style: 'width: 10px'
+        },
+        {
+          name: 'package',
+          label: '包装',
+          field: (row) => row.bound_batch.goods_package,
+          align: 'center',
+          style: 'width: 10px'
+        }
+      ],
       filter: '',
       product_filter: '',
       pagination: {
@@ -948,7 +1002,7 @@ export default {
       activeTab: 'tab1',
       isorder: false,
       order: 'false',
-      currentBarcode: '',
+
       printConfig: {
         id: 'printBarcode'
       },
@@ -967,6 +1021,27 @@ export default {
     }
   },
   methods: {
+    assignGoodsCode () {
+      console.log('data', this.newBatchFormData.goods_code)
+      console.log(
+        'product_map',
+        this.product_map[this.newBatchFormData.goods_code]
+      )
+      if (this.product_map[this.newBatchFormData.goods_code]) {
+        this.newBatchFormData.goods_desc =
+          this.product_map[this.newBatchFormData.goods_code].product_name
+        this.newBatchFormData.goods_std =
+          this.product_map[this.newBatchFormData.goods_code].product_std
+        this.newBatchFormData.goods_unit =
+          this.product_map[this.newBatchFormData.goods_code].product_unit
+        this.newBatchFormData.goods_package =
+          this.product_map[this.newBatchFormData.goods_code].product_package
+        this.newBatchFormData.goods_weight = 1
+      }
+      // this.newBatchFormData.goods_code = this.product_map[this.newBatchFormData.goods_code].product_code
+      // this.newBatchFormData.goods_std = this.product_map[this.newBatchFormData.goods_code].goods_std
+      // this.newBatchFormData.goods_package = this.product_map[this.newBatchFormData.goods_code].goods_package
+    },
     setCurrentBatch (item) {
       this.currentBarcode = item.bound_batch?.bound_number || ''
       this.currentgoods = item.bound_batch
@@ -1073,7 +1148,7 @@ export default {
             value: item.product_code
           }))
           _this.product_map = res.results.reduce((acc, item) => {
-            acc[item.product_code] = item.product_name
+            acc[item.product_code] = item
             return acc
           }, {})
         })
@@ -1201,7 +1276,7 @@ export default {
         .then((res) => {
           _this.getSearchList()
           _this.newDataCancel()
-          if (res.status_code != 500) {
+          if (res.status_code !== 500) {
             _this.$q.notify({
               message: '成功新增数据',
               icon: 'check',
@@ -1227,13 +1302,13 @@ export default {
       console.log('当前的order是', _this.newBatchFormData.order)
       postauth('bound/batch/', _this.newBatchFormData)
         .then((res) => {
-          if (res.status_code != 500) {
+          if (res.status_code !== 500) {
             _this.newDetailFormData.bound_batch = res.id
             _this.newDetailFormData.creater = _this.login_name
 
             postauth('bound/detail/', _this.newDetailFormData).then(
               (res) => {
-                if (res.status_code != 500) {
+                if (res.status_code !== 500) {
                   _this.detailData(_this.newDetailFormData)
 
                   _this.$q.notify({
@@ -1275,6 +1350,9 @@ export default {
     addbatch (bound_number) {
       var _this = this
       _this.newBatchForm = true
+      _this.batch_number_year = _this.date.slice(0, 4)
+      _this.batch_number_month = _this.date.slice(5, 7)
+      _this.newBatchFormData.creater = _this.login_name
       _this.newDetailFormData = {
         id: bound_number,
         bound_list: bound_number
@@ -1316,7 +1394,7 @@ export default {
         .then((res) => {
           _this.editDataCancel()
           _this.getSearchList()
-          if (res.status_code != 500) {
+          if (res.status_code !== 500) {
             _this.$q.notify({
               message: '开始入库',
               icon: 'check',
@@ -1340,7 +1418,7 @@ export default {
         .then((res) => {
           _this.editDataCancel()
           _this.getSearchList()
-          if (res.status_code != 500) {
+          if (res.status_code !== 500) {
             _this.$q.notify({
               message: '成功编辑数据',
               icon: 'check',
@@ -1589,7 +1667,11 @@ export default {
         this.order = 'false'
       }
     },
-
+    batch_number_batch: function (val) {
+      console.log(val)
+      this.newBatchFormData.bound_batch_order =
+        this.batch_number_year * 1000 + this.batch_number_month * 10 + val
+    },
     createDate1 (val) {
       if (val) {
         if (val.to) {
@@ -1702,4 +1784,21 @@ export default {
     gap: 1mm;
   }
 }
+
+/* 在样式文件中添加 */
+.centered-input ::v-deep .q-field__control {
+  justify-content: center;
+}
+
+.centered-input ::v-deep input {
+  text-align: center;
+  -moz-appearance: textfield; /* 隐藏Firefox的数字箭头 */
+}
+
+/* 隐藏Chrome/Safari的数字箭头 */
+.centered-input ::v-deep input::-webkit-outer-spin-button,
+.centered-input ::v-deep input::-webkit-inner-spin-button {
+  -webkit-appearance: none;
+  margin: 0;
+}
 </style>

Diff do ficheiro suprimidas por serem muito extensas
+ 603 - 373
templates/src/pages/warehouse/product.vue


+ 20 - 2
warehouse/migrations/0001_initial.py

@@ -1,4 +1,4 @@
-# Generated by Django 4.1.2 on 2025-03-28 10:18
+# Generated by Django 4.1.2 on 2025-05-15 13:32
 
 from django.db import migrations, models
 
@@ -11,6 +11,22 @@ class Migration(migrations.Migration):
     ]
 
     operations = [
+        migrations.CreateModel(
+            name='baseset',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('set_name', models.CharField(max_length=255, verbose_name='Set Name')),
+                ('set_value', models.CharField(max_length=9999, verbose_name='Set Value')),
+                ('set_desc', models.CharField(blank=True, max_length=255, null=True, verbose_name='Set Description')),
+                ('is_delete', models.BooleanField(default=False, verbose_name='Delete Label')),
+            ],
+            options={
+                'verbose_name': 'Base Set',
+                'verbose_name_plural': 'Base Set',
+                'db_table': 'baseset',
+                'ordering': ['-id'],
+            },
+        ),
         migrations.CreateModel(
             name='BoundBSListModel',
             fields=[
@@ -137,7 +153,9 @@ class Migration(migrations.Migration):
                 ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('product_code', models.CharField(max_length=255, verbose_name='Product Code')),
                 ('product_name', models.CharField(max_length=255, verbose_name='Product Name')),
-                ('product_std', models.CharField(max_length=255, verbose_name='Product Description')),
+                ('product_std', models.CharField(blank=True, max_length=255, null=True, verbose_name='Product Description')),
+                ('product_unit', models.CharField(blank=True, default='KG', max_length=255, null=True, verbose_name='Product Unit')),
+                ('product_package', models.CharField(blank=True, default='箱', max_length=255, null=True, verbose_name='Product Package')),
                 ('creater', models.CharField(default='first', max_length=255, verbose_name='Who Created')),
                 ('is_delete', models.BooleanField(default=False, verbose_name='Delete Label')),
             ],

+ 0 - 18
warehouse/migrations/0002_productlistmodel_product_unit.py

@@ -1,18 +0,0 @@
-# Generated by Django 4.1.2 on 2025-05-07 10:10
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('warehouse', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='productlistmodel',
-            name='product_unit',
-            field=models.CharField(default='KG', max_length=255, verbose_name='Product Unit'),
-        ),
-    ]

+ 0 - 18
warehouse/migrations/0003_alter_productlistmodel_product_unit.py

@@ -1,18 +0,0 @@
-# Generated by Django 4.1.2 on 2025-05-08 09:05
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('warehouse', '0002_productlistmodel_product_unit'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='productlistmodel',
-            name='product_unit',
-            field=models.CharField(blank=True, default='KG', max_length=255, null=True, verbose_name='Product Unit'),
-        ),
-    ]

+ 0 - 18
warehouse/migrations/0004_alter_productlistmodel_product_std.py

@@ -1,18 +0,0 @@
-# Generated by Django 4.1.2 on 2025-05-08 09:09
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('warehouse', '0003_alter_productlistmodel_product_unit'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='productlistmodel',
-            name='product_std',
-            field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Product Description'),
-        ),
-    ]

+ 0 - 28
warehouse/migrations/0005_baseset.py

@@ -1,28 +0,0 @@
-# Generated by Django 4.1.2 on 2025-05-08 20:40
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('warehouse', '0004_alter_productlistmodel_product_std'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='baseset',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('set_name', models.CharField(max_length=255, verbose_name='Set Name')),
-                ('set_value', models.CharField(max_length=9999, verbose_name='Set Value')),
-                ('set_desc', models.CharField(blank=True, max_length=255, null=True, verbose_name='Set Description')),
-            ],
-            options={
-                'verbose_name': 'Base Set',
-                'verbose_name_plural': 'Base Set',
-                'db_table': 'baseset',
-                'ordering': ['-id'],
-            },
-        ),
-    ]

+ 0 - 18
warehouse/migrations/0006_baseset_is_delete.py

@@ -1,18 +0,0 @@
-# Generated by Django 4.1.2 on 2025-05-12 09:48
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('warehouse', '0005_baseset'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='baseset',
-            name='is_delete',
-            field=models.BooleanField(default=False, verbose_name='Delete Label'),
-        ),
-    ]

+ 2 - 1
warehouse/models.py

@@ -106,7 +106,8 @@ class ProductListModel(models.Model):
     product_name = models.CharField(max_length=255, verbose_name="Product Name")
     product_std = models.CharField(max_length=255, verbose_name="Product Description", blank=True, null=True)
     product_unit = models.CharField(default='KG', max_length=255, verbose_name="Product Unit", blank=True, null=True)
-
+    product_package = models.CharField(default='箱', max_length=255, verbose_name="Product Package", blank=True, null=True)
+    
     creater = models.CharField(default='first', max_length=255, verbose_name="Who Created")
     is_delete = models.BooleanField(default=False, verbose_name='Delete Label')