소스 검색

fix: 修复资产编辑无法保存的bug

- 恢复Asset模型STATUS_CHOICES定义,状态字段使用下拉选择而非文本输入
- 修复status默认值从中文'在用'改为英文key'in_use',与数据库存储一致
- 编辑表单新增资产面值(asset_value)字段
- excel_utils适配字段类型变更(DecimalField→CharField, DateField→CharField等)
cnbugs 5 시간 전
부모
커밋
a07b390995
3개의 변경된 파일61개의 추가작업 그리고 62개의 파일을 삭제
  1. 16 30
      assetapp/excel_utils.py
  2. 39 30
      assetapp/models.py
  3. 6 2
      templates/assetapp/asset_form.html

+ 16 - 30
assetapp/excel_utils.py

@@ -173,41 +173,26 @@ def import_assets_from_excel(ws, category_map, operator=None):
             # 处理状态
             status = STATUS_MAP_REVERSE.get(data.get('status', '在用'), 'in_use')
 
-            # 处理日期
-            from datetime import datetime as dt
-            purchase_date = None
-            warranty_expire = None
-            if data.get('purchase_date'):
-                try:
-                    purchase_date = dt.strptime(data['purchase_date'], '%Y-%m-%d').date()
-                except ValueError:
-                    pass
-            if data.get('warranty_expire'):
-                try:
-                    warranty_expire = dt.strptime(data['warranty_expire'], '%Y-%m-%d').date()
-                except ValueError:
-                    pass
+            # 处理日期 - 直接存字符串
+            purchase_date = data.get('purchase_date', '').strip() or ''
+            warranty_expire = data.get('warranty_expire', '').strip() or ''
 
             # 处理IP
-            bmc_address = data.get('bmc_address') or None
-            ip_address = data.get('ip_address') or None
+            bmc_address = data.get('bmc_address', '').strip() or ''
+            ip_address = data.get('ip_address', '').strip() or ''
 
-            # 处理资产面值
-            from decimal import Decimal, InvalidOperation
-            asset_value = None
-            if data.get('asset_value'):
-                try:
-                    asset_value = Decimal(str(data['asset_value']).replace(',', ''))
-                except (InvalidOperation, ValueError):
-                    pass
+            # 资产面值 - 直接存字符串
+            asset_value = data.get('asset_value', '').strip() or ''
 
             # 处理ID - 如果提供了ID且已存在,则更新该记录
             import_id = data.get('id', '').strip()
             asset = None
             is_update = False
-            if import_id:
+            if import_id and import_id.isdigit():
                 try:
-                    asset = Asset.objects.get(id=int(import_id))
+                    id_val = int(import_id) if str(import_id).strip().isdigit() else None
+                    if id_val:
+                        asset = Asset.objects.get(id=id_val)
                     is_update = True
                 except (Asset.DoesNotExist, ValueError):
                     asset = None
@@ -228,7 +213,7 @@ def import_assets_from_excel(ws, category_map, operator=None):
                 asset.ip_address = ip_address
                 asset.gpu_type = data.get('gpu_type', '')
                 gpu_count_str = data.get('gpu_count', '').strip()
-                asset.gpu_count = int(gpu_count_str) if gpu_count_str else None
+                asset.gpu_count = gpu_count_str if gpu_count_str else ''
                 asset.purchase_date = purchase_date
                 asset.warranty_expire = warranty_expire
                 asset.supplier = data.get('supplier', '')
@@ -250,7 +235,7 @@ def import_assets_from_excel(ws, category_map, operator=None):
             else:
                 # 创建新记录
                 gpu_count_str = data.get('gpu_count', '').strip()
-                gpu_count = int(gpu_count_str) if gpu_count_str else None
+                gpu_count = gpu_count_str if gpu_count_str else ''
                 
                 # 创建参数
                 create_kwargs = {
@@ -281,9 +266,10 @@ def import_assets_from_excel(ws, category_map, operator=None):
                 }
                 
                 # 如果Excel提供了ID,使用该ID创建
-                if import_id:
+                if import_id and import_id.isdigit():
                     try:
-                        create_kwargs['id'] = int(import_id)
+                        if str(import_id).strip().isdigit():
+                            create_kwargs['id'] = int(import_id)
                     except ValueError:
                         pass
                 

+ 39 - 30
assetapp/models.py

@@ -21,50 +21,50 @@ class Category(models.Model):
 class Asset(models.Model):
     """硬件资产"""
 
-    STATUS_CHOICES = [
-        ('in_use', '在用'),
-        ('idle', '闲置'),
-        ('maintenance', '维修中'),
-        ('scrapped', '已报废'),
-    ]
-
     # 基本信息
     asset_number = models.CharField('资产编号', max_length=50, db_index=True)
     name = models.CharField('设备名称', max_length=100)
     category = models.ForeignKey(Category, on_delete=models.PROTECT, verbose_name='设备分类', related_name='assets')
 
     # 硬件信息
-    brand = models.CharField('品牌', max_length=50, blank=True, default='')
-    model = models.CharField('型号', max_length=100, blank=True, default='')
-    asset_value = models.DecimalField('资产面值', max_digits=12, decimal_places=2, blank=True, null=True)
-    serial_number = models.CharField('序列号', max_length=100, blank=True, default='', db_index=True)
+    brand = models.CharField('品牌', max_length=200, blank=True, default='')
+    model = models.CharField('型号', max_length=200, blank=True, default='')
+    asset_value = models.CharField('资产面值', max_length=50, blank=True, default='')
+    serial_number = models.CharField('序列号', max_length=200, blank=True, default='', db_index=True)
 
     # 位置信息
     location = models.CharField('机房', max_length=200, blank=True, default='', help_text='楼层/房间/区域')
-    cabinet = models.CharField('机柜', max_length=50, blank=True, default='')
-    cabinet_position = models.CharField('机柜位置', max_length=50, blank=True, default='', help_text='U位')
+    cabinet = models.CharField('机柜', max_length=200, blank=True, default='')
+    cabinet_position = models.CharField('机柜位置', max_length=200, blank=True, default='', help_text='U位')
 
     # 网络信息
-    bmc_address = models.GenericIPAddressField('BMC地址', blank=True, null=True)
-    ip_address = models.GenericIPAddressField('IP地址', blank=True, null=True)
+    bmc_address = models.CharField('BMC地址', max_length=200, blank=True, default='')
+    ip_address = models.CharField('IP地址', max_length=200, blank=True, default='')
 
     # 显卡信息
-    has_gpu = models.CharField('是否带卡', max_length=20, blank=True, default='')
-    gpu_type = models.CharField('显卡类型', max_length=100, blank=True, default='')
-    gpu_count = models.IntegerField('卡数', blank=True, null=True)
+    has_gpu = models.CharField('是否带卡', max_length=50, blank=True, default='')
+    gpu_type = models.CharField('显卡类型', max_length=200, blank=True, default='')
+    gpu_count = models.CharField('卡数', max_length=50, blank=True, default='')
 
     # 采购与质保
-    purchase_date = models.DateField('采购日期', blank=True, null=True)
-    warranty_expire = models.DateField('质保到期', blank=True, null=True)
-    supplier = models.CharField('供应商', max_length=100, blank=True, default='')
+    purchase_date = models.CharField('采购日期', max_length=50, blank=True, default='')
+    warranty_expire = models.CharField('质保到期', max_length=50, blank=True, default='')
+    supplier = models.CharField('供应商', max_length=200, blank=True, default='')
 
     # 管理信息
-    responsible_person = models.CharField('负责人', max_length=50, blank=True, default='')
-    department = models.CharField('使用部门', max_length=100, blank=True, default='')
-    user = models.CharField('维护人', max_length=50, blank=True, default='')
-    business_type = models.CharField('业务类型', max_length=100, blank=True, default='')
-    status = models.CharField('状态', max_length=20, choices=STATUS_CHOICES, default='in_use')
-    remark = models.TextField('备注', blank=True, default='')
+    responsible_person = models.CharField('负责人', max_length=200, blank=True, default='')
+    department = models.CharField('使用部门', max_length=200, blank=True, default='')
+    user = models.CharField('维护人', max_length=200, blank=True, default='')
+    business_type = models.CharField('业务类型', max_length=200, blank=True, default='')
+    STATUS_CHOICES = [
+        ('in_use', '在用'),
+        ('idle', '闲置'),
+        ('maintenance', '维修中'),
+        ('scrapped', '已报废'),
+    ]
+
+    status = models.CharField('状态', max_length=50, choices=STATUS_CHOICES, default='in_use')
+    remark = models.CharField('备注', max_length=500, blank=True, default='')
 
     # 系统字段
     created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='创建人')
@@ -82,14 +82,23 @@ class Asset(models.Model):
     @property
     def is_expired(self):
         if self.warranty_expire:
-            return self.warranty_expire < date.today()
+            try:
+                from datetime import datetime
+                expire_date = datetime.strptime(self.warranty_expire, '%Y-%m-%d').date()
+                return expire_date < date.today()
+            except (ValueError, TypeError):
+                return False
         return False
 
     @property
     def is_expiring_soon(self):
         if self.warranty_expire:
-            from datetime import timedelta
-            return date.today() <= self.warranty_expire <= date.today() + timedelta(days=30)
+            try:
+                from datetime import datetime, timedelta
+                expire_date = datetime.strptime(self.warranty_expire, '%Y-%m-%d').date()
+                return date.today() <= expire_date <= date.today() + timedelta(days=30)
+            except (ValueError, TypeError):
+                return False
         return False
 
 

+ 6 - 2
templates/assetapp/asset_form.html

@@ -37,14 +37,18 @@
                         {{ form.category }}
                     </div>
                     <div class="row">
-                        <div class="col-md-6 mb-3">
+                        <div class="col-md-4 mb-3">
                             <label class="form-label text-muted">{{ form.brand.label }}</label>
                             {{ form.brand }}
                         </div>
-                        <div class="col-md-6 mb-3">
+                        <div class="col-md-4 mb-3">
                             <label class="form-label text-muted">{{ form.model.label }}</label>
                             {{ form.model }}
                         </div>
+                        <div class="col-md-4 mb-3">
+                            <label class="form-label text-muted">{{ form.asset_value.label }}</label>
+                            {{ form.asset_value }}
+                        </div>
                     </div>
                     <div class="mb-3">
                         <label class="form-label text-muted">{{ form.serial_number.label }}</label>