浏览代码

完善ERP程序

flower_mr 1 月之前
父节点
当前提交
f3a43cc3f4
共有 100 个文件被更改,包括 1639 次插入37 次删除
  1. 二进制
      db.sqlite3
  2. 二进制
      erp/__pycache__/filter.cpython-38.pyc
  3. 二进制
      erp/__pycache__/models.cpython-38.pyc
  4. 二进制
      erp/__pycache__/serializers.cpython-38.pyc
  5. 二进制
      erp/__pycache__/urls.cpython-38.pyc
  6. 二进制
      erp/__pycache__/views.cpython-38.pyc
  7. 40 2
      erp/filter.py
  8. 62 0
      erp/migrations/0006_outboundbill_outmaterialdetail.py
  9. 19 0
      erp/migrations/0007_alter_outmaterialdetail_material_entryids.py
  10. 二进制
      erp/migrations/__pycache__/0006_outboundbill_outmaterialdetail.cpython-38.pyc
  11. 二进制
      erp/migrations/__pycache__/0007_alter_outmaterialdetail_material_entryids.cpython-38.pyc
  12. 70 0
      erp/models.py
  13. 80 2
      erp/serializers.py
  14. 12 0
      erp/urls.py
  15. 281 9
      erp/views.py
  16. 747 0
      logs/boundBill.log
  17. 140 0
      logs/error.log
  18. 151 0
      logs/server.log
  19. 0 1
      templates/dist/spa/css/10.74c09acd.css
  20. 0 0
      templates/dist/spa/css/10.f721cf95.css
  21. 1 0
      templates/dist/spa/css/11.00a78ca4.css
  22. 0 1
      templates/dist/spa/css/11.fdd11090.css
  23. 1 0
      templates/dist/spa/css/12.90edc1da.css
  24. 1 0
      templates/dist/spa/css/13.0d4c4716.css
  25. 1 0
      templates/dist/spa/css/14.a65cb06f.css
  26. 1 0
      templates/dist/spa/css/15.0b5a9844.css
  27. 1 0
      templates/dist/spa/css/16.d66bc7d6.css
  28. 1 0
      templates/dist/spa/css/17.e4f041cc.css
  29. 1 0
      templates/dist/spa/css/18.7a23b7fb.css
  30. 1 0
      templates/dist/spa/css/3.48edef75.css
  31. 0 1
      templates/dist/spa/css/3.ae47b775.css
  32. 1 0
      templates/dist/spa/css/4.0ac6b60c.css
  33. 0 1
      templates/dist/spa/css/4.7ced9982.css
  34. 1 0
      templates/dist/spa/css/5.7100e471.css
  35. 0 1
      templates/dist/spa/css/5.b538e990.css
  36. 1 0
      templates/dist/spa/css/6.22759db8.css
  37. 0 1
      templates/dist/spa/css/6.a164a49b.css
  38. 1 0
      templates/dist/spa/css/7.82d60571.css
  39. 0 1
      templates/dist/spa/css/7.fc14c348.css
  40. 1 0
      templates/dist/spa/css/8.68989bda.css
  41. 0 1
      templates/dist/spa/css/9.8c912396.css
  42. 1 0
      templates/dist/spa/css/9.eeace633.css
  43. 1 1
      templates/dist/spa/index.html
  44. 0 1
      templates/dist/spa/js/10.7a3b5a7c.js
  45. 二进制
      templates/dist/spa/js/10.7a3b5a7c.js.gz
  46. 1 0
      templates/dist/spa/js/10.82a2ece5.js
  47. 二进制
      templates/dist/spa/js/10.82a2ece5.js.gz
  48. 1 0
      templates/dist/spa/js/11.c094525f.js
  49. 二进制
      templates/dist/spa/js/11.c094525f.js.gz
  50. 0 1
      templates/dist/spa/js/11.d137932a.js
  51. 二进制
      templates/dist/spa/js/11.d137932a.js.gz
  52. 1 0
      templates/dist/spa/js/12.077687d1.js
  53. 二进制
      templates/dist/spa/js/12.077687d1.js.gz
  54. 1 0
      templates/dist/spa/js/13.4cb828ed.js
  55. 二进制
      templates/dist/spa/js/13.647bce7c.js.gz
  56. 二进制
      templates/dist/spa/js/14.7f2ea93f.js.gz
  57. 1 0
      templates/dist/spa/js/14.be39ef51.js
  58. 二进制
      templates/dist/spa/js/14.be39ef51.js.gz
  59. 二进制
      templates/dist/spa/js/15.34422806.js.gz
  60. 1 0
      templates/dist/spa/js/15.f5f5f35b.js
  61. 二进制
      templates/dist/spa/js/15.f5f5f35b.js.gz
  62. 1 0
      templates/dist/spa/js/16.ed4c9435.js
  63. 二进制
      templates/dist/spa/js/16.ed4c9435.js.gz
  64. 二进制
      templates/dist/spa/js/17.3649fa32.js.gz
  65. 1 0
      templates/dist/spa/js/17.cb55703b.js
  66. 1 0
      templates/dist/spa/js/18.7ce0af4f.js
  67. 二进制
      templates/dist/spa/js/18.abcdcf47.js.gz
  68. 二进制
      templates/dist/spa/js/19.470bdfcf.js.gz
  69. 1 1
      templates/dist/spa/js/12.1cc936cf.js
  70. 1 1
      templates/dist/spa/js/13.647bce7c.js
  71. 二进制
      templates/dist/spa/js/20.0a3341c5.js.gz
  72. 二进制
      templates/dist/spa/js/20.e49bcfd3.js.gz
  73. 1 1
      templates/dist/spa/js/14.7f2ea93f.js
  74. 二进制
      templates/dist/spa/js/21.53a3e18b.js.gz
  75. 二进制
      templates/dist/spa/js/21.e266137c.js.gz
  76. 1 1
      templates/dist/spa/js/15.34422806.js
  77. 二进制
      templates/dist/spa/js/22.3054b5f5.js.gz
  78. 二进制
      templates/dist/spa/js/22.54dc6683.js.gz
  79. 1 1
      templates/dist/spa/js/16.5f33bda7.js
  80. 二进制
      templates/dist/spa/js/16.5f33bda7.js.gz
  81. 二进制
      templates/dist/spa/js/23.baedf163.js.gz
  82. 1 1
      templates/dist/spa/js/17.3649fa32.js
  83. 二进制
      templates/dist/spa/js/24.5ebb6d1b.js.gz
  84. 二进制
      templates/dist/spa/js/24.6d2f32f6.js.gz
  85. 1 1
      templates/dist/spa/js/18.abcdcf47.js
  86. 二进制
      templates/dist/spa/js/25.73320c40.js.gz
  87. 1 1
      templates/dist/spa/js/19.470bdfcf.js
  88. 二进制
      templates/dist/spa/js/26.b3d0a428.js.gz
  89. 1 1
      templates/dist/spa/js/20.e49bcfd3.js
  90. 二进制
      templates/dist/spa/js/27.e967265e.js.gz
  91. 1 1
      templates/dist/spa/js/21.e266137c.js
  92. 二进制
      templates/dist/spa/js/28.072bfae1.js.gz
  93. 0 1
      templates/dist/spa/js/29.462f2a27.js
  94. 1 1
      templates/dist/spa/js/22.54dc6683.js
  95. 二进制
      templates/dist/spa/js/29.5db67fa0.js.gz
  96. 1 0
      templates/dist/spa/js/3.4f075d5f.js
  97. 二进制
      templates/dist/spa/js/3.4f075d5f.js.gz
  98. 0 1
      templates/dist/spa/js/3.70fa2340.js
  99. 二进制
      templates/dist/spa/js/3.70fa2340.js.gz
  100. 0 0
      templates/dist/spa/js/23.baedf163.js

二进制
db.sqlite3


二进制
erp/__pycache__/filter.cpython-38.pyc


二进制
erp/__pycache__/models.cpython-38.pyc


二进制
erp/__pycache__/serializers.cpython-38.pyc


二进制
erp/__pycache__/urls.cpython-38.pyc


二进制
erp/__pycache__/views.cpython-38.pyc


+ 40 - 2
erp/filter.py

@@ -1,11 +1,29 @@
 from django_filters import FilterSet
-from .models import InboundBill, MaterialDetail
+from .models import InboundBill, MaterialDetail ,OutboundBill ,OutMaterialDetail
+
+class OutboundBillFilter(FilterSet):
+    class Meta:
+        model = OutboundBill
+        fields = {
+            "billId": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
+            "number": ['exact', 'icontains'],
+            "type": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
+            "date": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
+            "department": ['exact', 'icontains'],
+            "warehouse": ['exact', 'icontains'],
+            "creater": ['exact', 'icontains'],
+            "note": ['exact', 'icontains'],
+            "totalCount": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
+            "create_time": ['exact', 'gt', 'gte', 'lt', 'lte', 'range'],
+            "update_time": ['exact', 'gt', 'gte', 'lt', 'lte', 'range'],
+            "is_delete": ['exact'],
+            "bound_status" : ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range']
+        }
 
 class InboundBillFilter(FilterSet):
     class Meta:
         model = InboundBill
         fields = {
-           
             "billId": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
             "number": ['exact', 'icontains'],
             "type": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
@@ -42,3 +60,23 @@ class MaterialDetailFilter(FilterSet):
             "is_delete": ['exact'],
         }
 
+class OutMaterialDetailFilter(FilterSet):
+    class Meta:
+        model = OutMaterialDetail
+        fields = {
+            "id": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
+            "bound_billId": ['exact'],
+            "entryIds": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
+            "production_batch": ['exact', 'icontains'],
+            "goods_code": ['exact', 'icontains'],
+            "goods_name": ['exact', 'icontains'],
+            "goods_weight": ['exact', 'icontains'],
+            "goods_out_qty": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
+            "goods_total_weight": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
+            "goods_unit": ['exact', 'icontains'],
+            "note": ['exact', 'icontains'],
+            "create_time": ['exact', 'gt', 'gte', 'lt', 'lte', 'range'],
+            "update_time": ['exact', 'gt', 'gte', 'lt', 'lte', 'range'],
+            "is_delete": ['exact'],
+        }
+

+ 62 - 0
erp/migrations/0006_outboundbill_outmaterialdetail.py

@@ -0,0 +1,62 @@
+# Generated by Django 4.1.2 on 2025-05-04 18:40
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('erp', '0005_alter_inboundbill_options'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='OutboundBill',
+            fields=[
+                ('billId', models.IntegerField(primary_key=True, serialize=False, verbose_name='原始单据ID')),
+                ('number', models.CharField(max_length=50, unique=True, verbose_name='单据编号')),
+                ('type', models.IntegerField(choices=[(1, '订单'), (2, '其他')], verbose_name='绑定类型')),
+                ('date', models.CharField(max_length=10, verbose_name='出库时间')),
+                ('department', models.CharField(max_length=10, null=True, verbose_name='部门')),
+                ('warehouse', models.CharField(default='W01', max_length=10, verbose_name='仓库')),
+                ('creater', models.CharField(max_length=50, null=True, verbose_name='创建人')),
+                ('note', models.TextField(blank=True, null=True, verbose_name='备注说明')),
+                ('totalCount', models.IntegerField(verbose_name='总条目数')),
+                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
+                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
+                ('bound_status', models.IntegerField(default=0, verbose_name='状态')),
+                ('is_delete', models.BooleanField(default=False, verbose_name='是否删除')),
+            ],
+            options={
+                'verbose_name': '出库单',
+                'verbose_name_plural': '出库单',
+                'ordering': ['bound_status', '-create_time', '-update_time'],
+            },
+        ),
+        migrations.CreateModel(
+            name='OutMaterialDetail',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('entryIds', models.IntegerField(verbose_name='分录ID')),
+                ('production_batch', models.CharField(max_length=50, verbose_name='生产批次')),
+                ('goods_code', models.CharField(max_length=20, verbose_name='商品编码')),
+                ('goods_name', models.CharField(max_length=100, verbose_name='商品名称')),
+                ('goods_weight', models.DecimalField(decimal_places=2, default=1, max_digits=10, verbose_name='单件重量')),
+                ('goods_out_qty', models.IntegerField(verbose_name='计划数量')),
+                ('goods_total_weight', models.DecimalField(decimal_places=2, max_digits=10, null=True, verbose_name='总重量')),
+                ('goods_unit', models.CharField(max_length=20, verbose_name='计量单位')),
+                ('note', models.TextField(blank=True, verbose_name='备注')),
+                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
+                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
+                ('is_delete', models.BooleanField(default=False, verbose_name='是否删除')),
+                ('Material_entryIds', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='entry_Ids', to='erp.materialdetail', verbose_name='所属入库单详细')),
+                ('bound_billId', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bill_id', to='erp.outboundbill', verbose_name='所属出库单')),
+            ],
+            options={
+                'verbose_name': '出库物料明细',
+                'verbose_name_plural': '出库物料明细',
+                'ordering': ['-update_time', '-create_time'],
+            },
+        ),
+    ]

+ 19 - 0
erp/migrations/0007_alter_outmaterialdetail_material_entryids.py

@@ -0,0 +1,19 @@
+# Generated by Django 4.1.2 on 2025-05-04 18:56
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('erp', '0006_outboundbill_outmaterialdetail'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='outmaterialdetail',
+            name='Material_entryIds',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='entry_Ids', to='erp.materialdetail', verbose_name='所属入库单详细'),
+        ),
+    ]

二进制
erp/migrations/__pycache__/0006_outboundbill_outmaterialdetail.cpython-38.pyc


二进制
erp/migrations/__pycache__/0007_alter_outmaterialdetail_material_entryids.cpython-38.pyc


+ 70 - 0
erp/models.py

@@ -29,6 +29,39 @@ class InboundBill(models.Model):
         verbose_name = '生产入库单'
         verbose_name_plural = verbose_name
         ordering = [ 'bound_status','-create_time','-update_time']
+    
+class OutboundBill(models.Model):
+
+    BOUND_TYPE = (
+        (1, '订单'),
+        (2, '其他'),
+    )
+    STATUS_CHOICES = (
+        (0, '草稿'),
+        (1, '已审批'),
+        (2, '已入库'),
+        (3, '已取消'),
+    )
+
+    billId = models.IntegerField(primary_key=True,verbose_name='原始单据ID')
+    number = models.CharField(max_length=50, unique=True, verbose_name='单据编号')
+    type = models.IntegerField(choices=BOUND_TYPE, verbose_name='绑定类型')
+    date = models.CharField(max_length=10, verbose_name='出库时间')  
+    department = models.CharField(max_length=10, verbose_name='部门', null=True)
+    warehouse = models.CharField(max_length=10, verbose_name='仓库', default='W01')  
+    creater = models.CharField(max_length=50, verbose_name='创建人', null=True)
+    note = models.TextField(blank=True, verbose_name='备注说明', null=True)
+    totalCount = models.IntegerField(verbose_name='总条目数')
+    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
+    update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
+    bound_status = models.IntegerField(default=0, verbose_name='状态')
+    is_delete = models.BooleanField(default=False, verbose_name='是否删除')
+
+    class Meta:
+        verbose_name = '出库单'
+        verbose_name_plural = verbose_name
+        ordering = [ 'bound_status','-create_time','-update_time']
+
 
 
 class MaterialDetail(models.Model):
@@ -58,3 +91,40 @@ class MaterialDetail(models.Model):
         verbose_name_plural = verbose_name
         unique_together = [('bound_billId', 'entryIds')]
         ordering = ['-update_time', '-create_time']
+
+
+class OutMaterialDetail(models.Model):
+    """物料明细模型"""
+    bound_billId = models.ForeignKey(
+        OutboundBill,
+        on_delete=models.CASCADE,
+        related_name='bill_id',
+        verbose_name='所属出库单'
+    )
+    Material_entryIds = models.ForeignKey(
+        MaterialDetail,
+        on_delete=models.CASCADE,
+        related_name='entry_Ids',
+        verbose_name='所属入库单详细',
+        null=True,
+        blank=True
+    )
+    entryIds = models.IntegerField(verbose_name='分录ID')
+    production_batch = models.CharField(max_length=50, verbose_name='生产批次')
+    goods_code = models.CharField(max_length=20, verbose_name='商品编码')
+    goods_name = models.CharField(max_length=100, verbose_name='商品名称')
+
+    goods_weight = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='单件重量', default=1)
+    goods_out_qty = models.IntegerField(verbose_name='计划数量')
+    goods_total_weight = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='总重量', null=True)
+    goods_unit = models.CharField(max_length=20, verbose_name='计量单位')
+    note = models.TextField(blank=True, verbose_name='备注')
+    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
+    update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
+    is_delete = models.BooleanField(default=False, verbose_name='是否删除')
+
+    class Meta:
+        verbose_name = '出库物料明细'
+        verbose_name_plural = verbose_name
+        ordering = ['-update_time', '-create_time']
+

+ 80 - 2
erp/serializers.py

@@ -1,5 +1,5 @@
 from rest_framework import serializers
-from .models import InboundBill, MaterialDetail
+from .models import InboundBill, MaterialDetail ,OutboundBill, OutMaterialDetail
 from utils import datasolve
 
 class InboundApplySerializer(serializers.ModelSerializer):
@@ -12,6 +12,16 @@ class InboundApplySerializer(serializers.ModelSerializer):
         ordering_fields = ['update_time', 'create_time']
         read_only_fields = ['billId', 'number', 'type', 'date', 'department', 'warehouse', 'creater', 'note', 'totalCount']
 
+class OutboundApplySerializer(serializers.ModelSerializer):
+    """
+    生产出库申请序列化器
+    """
+    class Meta:
+        model = OutboundBill
+        fields = '__all__'
+        ordering_fields = ['update_time', 'create_time']
+        read_only_fields = ['billId', 'number', 'type', 'date', 'department', 'warehouse', 'creater', 'note', 'totalCount']
+
 class boundPostSerializer(serializers.Serializer):
     """
     生产入库申请序列化器
@@ -79,4 +89,72 @@ class MaterialDetailSerializer(serializers.ModelSerializer):
     class Meta:
         model = MaterialDetail
         exclude = ['id', 'note']
-        read_only_fields = ['entryIds', 'production_batch', 'goods_code', 'goods_name', 'goods_std', 'goods_weight', 'plan_qty', 'goods_total_weight', 'goods_unit', 'note']
+        read_only_fields = ['entryIds', 'production_batch', 'goods_code', 'goods_name', 'goods_std', 'goods_weight', 'plan_qty', 'goods_total_weight', 'goods_unit', 'note']
+class OutMaterialDetailSerializer(serializers.ModelSerializer):
+    """
+    物料明细序列化器
+    """
+    class Meta:
+        model = OutMaterialDetail
+        exclude = ['id', 'note']
+        read_only_fields = ['entryIds', 'production_batch', 'goods_code', 'goods_name', 'goods_weight', 'goods_out_qty', 'goods_total_weight', 'goods_unit', 'note']
+
+class outboundPostSerializer(serializers.Serializer):
+    """
+    生产入库申请序列化器
+    """
+    billId = serializers.IntegerField(required=True, label='原始单据ID')
+    number = serializers.CharField(required=True, label='单据编号')
+    type = serializers.IntegerField(required=True, label='绑定类型')
+    date = serializers.CharField(required=True, label='出库时间')
+    department = serializers.CharField(required=True, label='部门')
+    warehouse = serializers.CharField(required=True, label='仓库')
+    creater = serializers.CharField(required=True, label='创建人')
+    note = serializers.CharField(required=False, label='备注说明')
+    totalCount = serializers.IntegerField(required=True, label='总条目数')
+    materials = serializers.ListField(required=True, label='物料明细')
+    class Meta:
+        fields = ['billId', 'number', 'type', 'date', 'department', 'warehouse', 'creater', 'note', 'totalCount','materials']
+
+    def validate_materials(self, value):
+        """
+        增强版物料明细验证
+        返回带具体缺失字段的错误信息
+        """
+        required_fields = [
+            'entryIds', 'production_batch', 
+            'goods_code', 'goods_name',
+            'goods_out_qty',
+            'goods_unit'
+        ]
+        
+        errors = []
+        
+        for index, item in enumerate(value):
+            # 识别缺失字段
+            missing_fields = [field for field in required_fields if field not in item]
+            
+            if missing_fields:
+                errors.append({
+                    "index": index,
+                    "entryIds": item.get('entryIds'),  # 保留原始标识
+                    "production_batch": item.get('production_batch'),
+                    "missing_fields": missing_fields,
+                    "message": f"第{index+1}条物料缺少必填字段: {', '.join(missing_fields)}"
+                })
+        
+        if errors:
+            # 抛出结构化错误
+            raise serializers.ValidationError([
+                {
+                    "detail": error["message"],
+                    "metadata": {
+                        "entryIds": error["entryIds"],
+                        "production_batch": error["production_batch"],
+                        "missing_fields": error["missing_fields"]
+                    }
+                } for error in errors
+            ])
+        
+        return value
+    

+ 12 - 0
erp/urls.py

@@ -14,10 +14,22 @@ urlpatterns = [
         'get': 'retrieve'
     }), name="inboundBills_1"),
 
+    path('outboundBills/', views.OutboundBills.as_view({"get": "list"}),name="outboutBills"),
+    re_path(r'^outboundBills/(?P<pk>\d+)/$', views.OutboundBills.as_view({
+        'get': 'retrieve'
+    }), name="outboundBills_1"),
+
     path('materials/', views.Materials.as_view({"get": "list"}),name="Materials"),
     re_path(r'^materials/(?P<pk>\d+)/$', views.Materials.as_view({
         'get': 'retrieve',
 
     }), name="Materials_1"),
 
+    path('outmaterials/', views.OutMaterials.as_view({"get": "list"}),name="OutMaterials"),
+    re_path(r'^outmaterials/(?P<pk>\d+)/$', views.OutMaterials.as_view({
+        'get': 'retrieve',
+
+    }), name="OutMaterials_1"),
+
+
 ]

+ 281 - 9
erp/views.py

@@ -262,7 +262,6 @@ class InboundApplyCreate(APIView):
                 )
         return material_detail
 
-
 class GenerateInbound(APIView):
     """
     生产入库单生成接口
@@ -298,16 +297,29 @@ class GenerateInbound(APIView):
 
                 logger.info(f"入库单生成成功 | billId: {bill_id} -> boundCode: {bound_list.bound_code}")
                 return Response({
-                    "bound_code": bound_list.bound_code,
-                    "batch_count": bill_obj.totalCount
-                }, status=status.HTTP_201_CREATED)
+                    "code": 200,
+                    "count": 1,
+                    "next": "null",
+                    "previous": "null",
+                    "results":{
+                        "bound_code": bound_list.bound_code,
+                        "batch_count": bill_obj.totalCount
+                    }
+                    
+                }, status=status.HTTP_200_OK)
 
         except InboundBill.DoesNotExist:
             logger.error(f"原始单据不存在 | billId: {bill_id}")
-            return Response({"error": "原始单据不存在"}, status=status.HTTP_404_NOT_FOUND)
+            return Response({
+                "code": 404,
+                "error": "原始单据不存在"
+                }, status=status.HTTP_404_NOT_FOUND)
         except Exception as e:
             logger.exception(f"入库单生成异常 | billId: {bill_id}")
-            return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+            return Response({
+                "code": 400,
+                "error": str(e)
+                }, status=status.HTTP_200_OK)
 
     def validate_and_lock(self, bill_id):
         """验证并锁定相关资源"""
@@ -317,6 +329,8 @@ class GenerateInbound(APIView):
             is_delete=False
         )
 
+        logger.info(f"锁定原始单据成功 | billId: {bill_id}")
+        logger.info(f"原始单据状态: {bill_obj.bound_status}") 
         # 状态校验
         if bill_obj.bound_status == 1:
             logger.warning(f"单据已生成过入库单 | status: {bill_obj.bound_status}")
@@ -457,11 +471,193 @@ class OutboundApplyCreate(APIView):
     """
     生产出库申请
     """
-    authentication_classes = []  # 禁用所有认证类
-    permission_classes = [AllowAny]  # 允许任意访问
+    authentication_classes = []
+    permission_classes = [AllowAny]
+
     def post(self, request):
+        logger.info('生产出库申请请求 | 原始数据: %s', request.data)
+        
+        try:
+            total_count = len(request.data.get('materials', []))
+            if total_count == 0 :
+                return WMSResponse.error(
+                    message="物料清单不能为空",
+                    total=0,
+                    fail_count=0
+                )
+            if total_count != request.data.get('totalCount', 0):
+                return WMSResponse.error(
+                    message="物料数量不匹配",
+                    total=total_count,
+                    fail_count=total_count
+                )
+            
+            serializer = outboundPostSerializer(data=request.data)
+            if not serializer.is_valid():
+                print("出错",serializer.errors)
+                return self._handle_validation_error(serializer.errors, total_count)
+
+            unique_result,error_details = self.find_unique_billid_and_number(serializer)
+            if not unique_result:
+                return WMSResponse.error(
+                    message="单据编号或原始单据ID重复",
+                    total=total_count,
+                    fail_count=total_count,
+                    fail_materials=[{
+                        "entryIds": None,
+                        "production_batch": None,
+                        "errors": error_details
+                    }]
+                )
+            # 保存或更新入库单
+            bound_bill = self.save_or_update_inbound_bill(serializer)
+
+            # 保存或更新物料明细
+            self.save_or_update_material_detail(bound_bill, serializer)
+            return WMSResponse.success( 
+                data=serializer.data,
+                total=total_count,
+                success=total_count
+            )
+
+        except Exception as e:
+            logger.exception("服务器内部错误")
+            return WMSResponse.error(
+                message="系统处理异常",
+                total=total_count,
+                fail_count=total_count,
+                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
+                exception=e
+            )
+
+    def _handle_validation_error(self, errors, total_count):
+        """增强错误解析"""
+        fail_materials = []
+        
+        # 提取嵌套错误信息
+        material_errors = errors.get('materials', [])
+        
+        for error in material_errors:
+            # 解析DRF的错误结构
+            if isinstance(error, dict) and 'metadata' in error:
+                fail_materials.append({
+                    "entryIds": error['metadata'].get('entryIds'),
+                    "production_batch": error['metadata'].get('production_batch'),
+                    "errors": {
+                        "missing_fields": error['metadata']['missing_fields'],
+                        "message": error['detail']
+                    }
+                })
+        
+        return WMSResponse.error(
+            message="物料数据不完整",
+            total=total_count,
+            fail_count=len(fail_materials),
+            fail_materials=fail_materials)
+
+    def _format_material_errors(self, error_dict):
+        """格式化单个物料的错误信息"""
+        return {
+            field: details[0] if isinstance(details, list) else details
+            for field, details in error_dict.items()
+        }
+
+    def find_unique_billid_and_number(self, serializer):
+        """增强版唯一性验证"""
+        bill_id = serializer.validated_data['billId']
+        number = serializer.validated_data['number']
+        
+        # 使用Q对象进行联合查询
+        duplicates_id = OutboundBill.objects.filter(
+            Q(billId=bill_id) 
+        ).only('billId')
+        # 使用Q对象进行联合查询
+        duplicates_nu = OutboundBill.objects.filter(
+            Q(number=number)
+        ).only( 'number')
+        
+        error_details = {}
+        
+        # 检查单据编号重复
+        if any(obj.billId != bill_id for obj in duplicates_nu):
+            error_details['number'] = ["number出库单编码已存在,但是系统中与之前出库单单据ID不一致,请检查"]
+        
+        
+        # 检查业务编号重复
+        if any(obj.number != number for obj in duplicates_id):
+            
+            error_details['billId'] = ["billId出库库单单据ID已存在,但是系统中与之前出库申请单编码不一致,请检查"]
+        
+    
+        if error_details:
+            return False,error_details
+        return True,None
+        
+    def save_or_update_inbound_bill(self, serializer):
+        """保存或更新出库单"""
+        try:
+            bound_bill = OutboundBill.objects.get(billId=serializer.validated_data['billId'])
+            bound_bill.number = serializer.validated_data['number']
+            bound_bill.type = serializer.validated_data['type']
+            bound_bill.date = serializer.validated_data['date']
+            bound_bill.department = serializer.validated_data['department']
+            bound_bill.warehouse = serializer.validated_data['warehouse']
+            bound_bill.creater = serializer.validated_data['creater']
+            bound_bill.note = (serializer.validated_data['note'] if 'note' in serializer.validated_data else '')
+            bound_bill.totalCount = serializer.validated_data['totalCount']
+            bound_bill.update_time = timezone.now()
+            bound_bill.save()
+        except OutboundBill.DoesNotExist:
+            bound_bill = OutboundBill.objects.create(
+                billId=serializer.validated_data['billId'],
+                number=serializer.validated_data['number'],
+                type=serializer.validated_data['type'],
+                date=serializer.validated_data['date'],
+                department=serializer.validated_data['department'],
+                warehouse=serializer.validated_data['warehouse'],
+                creater=serializer.validated_data['creater'],
+                note=(serializer.validated_data['note'] if 'note' in serializer.validated_data else ''),
+                totalCount=serializer.validated_data['totalCount'],
+                create_time=timezone.now(),
+                update_time=timezone.now()
+            )
+        return bound_bill
+
+    def save_or_update_material_detail(self, bound_bill, serializer):
+        """保存或更新物料明细"""
+        for item in serializer.validated_data['materials']:
+            try:
+                material_detail = OutMaterialDetail.objects.get(bound_billId=bound_bill, entryIds=item['entryIds'])
+                material_detail.production_batch = item['production_batch']
+        
+                material_detail.goods_code = item['goods_code']
+                material_detail.goods_name = item['goods_name']
+        
+         
+                material_detail.goods_out_qty = item['goods_out_qty']
+                material_detail.goods_total_weight = item['goods_out_qty']
+                material_detail.goods_unit = item['goods_unit']
+                material_detail.note = (item['note'] if 'note' in item else '')
+                material_detail.update_time = timezone.now()
+                material_detail.save()
+            except OutMaterialDetail.DoesNotExist:
+                material_detail = OutMaterialDetail.objects.create(
+                    bound_billId=bound_bill,
+                    entryIds=item['entryIds'],
+                    production_batch=item['production_batch'],
+                    goods_code=item['goods_code'],
+                    goods_name=item['goods_name'],
+      
+                    goods_weight=1,
+                    goods_out_qty=item['goods_out_qty'],
+                    goods_total_weight=item['goods_out_qty'],
+                    goods_unit=item['goods_unit'],
+                    note=(item['note'] if 'note' in item else ''),
+                    create_time=timezone.now(),
+                    update_time=timezone.now()
+                )
+        return material_detail
 
-        return Response(status=status.HTTP_200_OK)
 
 class BatchUpdate(APIView):
     """
@@ -526,6 +722,46 @@ class InboundBills(viewsets.ModelViewSet):
         else:
             return self.http_method_not_allowed(request=self.request)
 
+class OutboundBills(viewsets.ModelViewSet):
+    """
+        retrieve:
+            Response a data list(get)
+        list:
+            Response a data list(all)
+    """
+    # authentication_classes = []  # 禁用所有认证类
+    # permission_classes = [AllowAny]  # 允许任意访问
+    
+    pagination_class = MyPageNumberPagination
+    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
+    ordering_fields = ["update_time", "create_time"]
+    filter_class = OutboundBillFilter
+
+    def get_project(self):
+        # 获取项目ID,如果不存在则返回None
+        try:
+            id = self.kwargs.get('pk')
+            return id
+        except:
+            return None
+
+    def get_queryset(self):
+        # 根据请求用户过滤查询集
+        id = self.get_project()
+        if self.request.user:
+            if id is None:
+                return OutboundBill.objects.filter(is_delete=False)
+            else:
+                return OutboundBill.objects.filter(billId=id, is_delete=False)
+        else:
+            return OutboundBill.objects.none()
+
+    def get_serializer_class(self):
+        # 根据操作类型选择合适的序列化器
+        if self.action in ['list', 'retrieve', ]:
+            return InboundApplySerializer
+        else:
+            return self.http_method_not_allowed(request=self.request)
 
 class Materials(viewsets.ModelViewSet):
     """
@@ -565,6 +801,42 @@ class Materials(viewsets.ModelViewSet):
         else:
             return self.http_method_not_allowed(request=self.request)   
 
+class OutMaterials(viewsets.ModelViewSet):
+    """
+        retrieve:
+            Response a data list(get)
+        list:
+            Response a data list(all)
+    """
+    # authentication_classes = []  # 禁用所有认证类
+    # permission_classes = [AllowAny]  # 允许任意访问
+    
+    pagination_class = MyPageNumberPagination   
+    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
+    ordering_fields = ['id', "create_time", "update_time", ]
+    filter_class = OutMaterialDetailFilter
+
+    def get_project(self):
+        try:
+            id = self.kwargs.get('pk')
+            return id
+        except:
+            return None
+
+    def get_queryset(self):
+        id = self.get_project()
+        if self.request.user:
+            if id is None:
+                return OutMaterialDetail.objects.filter(is_delete=False)
+            else:
+                return OutMaterialDetail.objects.filter(id=id)
+        else:
+            return OutMaterialDetail.objects.none()
 
+    def get_serializer_class(self):
+        if self.action in ['retrieve', 'list']:
+            return OutMaterialDetailSerializer
+        else:
+            return self.http_method_not_allowed(request=self.request)   
 
 

文件差异内容过多而无法显示
+ 747 - 0
logs/boundBill.log


+ 140 - 0
logs/error.log

@@ -2171,3 +2171,143 @@ django.core.exceptions.FieldError: Cannot resolve keyword 'id' into field. Choic
 [2025-04-29 22:12:02,509][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
 [2025-04-29 22:13:06,549][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
 [2025-04-29 22:25:54,858][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-04-30 09:18:35,510][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-04-30 09:21:27,159][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-04-30 09:21:52,458][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-04-30 09:22:25,546][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-04-30 16:47:58,102][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:03:01,635][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:10:34,067][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:15:24,609][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:16:20,704][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:18:11,949][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:28:04,138][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:31:32,224][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:31:51,952][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:32:17,984][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:32:39,699][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:33:46,437][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:34:07,168][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:34:26,036][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:35:04,285][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:35:09,320][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:35:20,125][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:35:27,289][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:40:00,633][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:40:25,743][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:40:44,020][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:41:30,751][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 17:47:28,834][django.request.log_response():241] [ERROR] Internal Server Error: /wms/createOutboundApply
+Traceback (most recent call last):
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 472, in thread_handler
+    raise exc_info[1]
+  File "d:\language\python38\lib\site-packages\django\core\handlers\exception.py", line 42, in inner
+    response = await get_response(request)
+  File "d:\language\python38\lib\site-packages\django\core\handlers\base.py", line 253, in _get_response_async
+    response = await wrapped_callback(
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 435, in __call__
+    ret = await asyncio.wait_for(future, timeout=None)
+  File "d:\language\python38\lib\asyncio\tasks.py", line 455, in wait_for
+    return await fut
+  File "d:\language\python38\lib\concurrent\futures\thread.py", line 57, in run
+    result = self.fn(*self.args, **self.kwargs)
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 476, in thread_handler
+    return func(*args, **kwargs)
+  File "d:\language\python38\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
+    return view_func(*args, **kwargs)
+  File "d:\language\python38\lib\site-packages\django\views\generic\base.py", line 103, in view
+    return self.dispatch(request, *args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 509, in dispatch
+    response = self.handle_exception(exc)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
+    self.raise_uncaught_exception(exc)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
+    raise exc
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 497, in dispatch
+    self.initial(request, *args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 416, in initial
+    self.check_throttles(request)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 359, in check_throttles
+    if not throttle.allow_request(request, self):
+  File "D:\Document\code\vue\greater_wms\.\utils\throttle.py", line 18, in allow_request
+    openid = request.auth.openid
+AttributeError: 'NoneType' object has no attribute 'openid'
+[2025-05-04 18:52:52,148][django.request.log_response():241] [ERROR] Internal Server Error: /wms/createOutboundApply
+[2025-05-04 19:08:48,599][django.request.log_response():241] [ERROR] Internal Server Error: /wms/outboundBills/
+Traceback (most recent call last):
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 472, in thread_handler
+    raise exc_info[1]
+  File "d:\language\python38\lib\site-packages\django\core\handlers\exception.py", line 42, in inner
+    response = await get_response(request)
+  File "d:\language\python38\lib\site-packages\django\core\handlers\base.py", line 253, in _get_response_async
+    response = await wrapped_callback(
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 435, in __call__
+    ret = await asyncio.wait_for(future, timeout=None)
+  File "d:\language\python38\lib\asyncio\tasks.py", line 455, in wait_for
+    return await fut
+  File "d:\language\python38\lib\concurrent\futures\thread.py", line 57, in run
+    result = self.fn(*self.args, **self.kwargs)
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 476, in thread_handler
+    return func(*args, **kwargs)
+  File "d:\language\python38\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
+    return view_func(*args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\viewsets.py", line 125, in view
+    return self.dispatch(request, *args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 509, in dispatch
+    response = self.handle_exception(exc)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
+    self.raise_uncaught_exception(exc)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
+    raise exc
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 506, in dispatch
+    response = handler(request, *args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\mixins.py", line 38, in list
+    queryset = self.filter_queryset(self.get_queryset())
+  File "d:\language\python38\lib\site-packages\rest_framework\generics.py", line 150, in filter_queryset
+    queryset = backend().filter_queryset(self.request, queryset, self)
+  File "d:\language\python38\lib\site-packages\django_filters\rest_framework\backends.py", line 90, in filter_queryset
+    filterset = self.get_filterset(request, queryset, view)
+  File "d:\language\python38\lib\site-packages\django_filters\rest_framework\backends.py", line 31, in get_filterset
+    filterset_class = self.get_filterset_class(view, queryset)
+  File "d:\language\python38\lib\site-packages\django_filters\rest_framework\backends.py", line 64, in get_filterset_class
+    assert issubclass(queryset.model, filterset_model), \
+AssertionError: FilterSet model <class 'erp.models.InboundBill'> does not match queryset model <class 'erp.models.OutboundBill'>
+[2025-05-04 19:18:55,600][django.request.log_response():241] [ERROR] Internal Server Error: /wms/outmaterials/
+Traceback (most recent call last):
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 472, in thread_handler
+    raise exc_info[1]
+  File "d:\language\python38\lib\site-packages\django\core\handlers\exception.py", line 42, in inner
+    response = await get_response(request)
+  File "d:\language\python38\lib\site-packages\django\core\handlers\base.py", line 253, in _get_response_async
+    response = await wrapped_callback(
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 435, in __call__
+    ret = await asyncio.wait_for(future, timeout=None)
+  File "d:\language\python38\lib\asyncio\tasks.py", line 455, in wait_for
+    return await fut
+  File "d:\language\python38\lib\concurrent\futures\thread.py", line 57, in run
+    result = self.fn(*self.args, **self.kwargs)
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 476, in thread_handler
+    return func(*args, **kwargs)
+  File "d:\language\python38\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
+    return view_func(*args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\viewsets.py", line 125, in view
+    return self.dispatch(request, *args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 509, in dispatch
+    response = self.handle_exception(exc)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
+    self.raise_uncaught_exception(exc)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
+    raise exc
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 506, in dispatch
+    response = handler(request, *args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\mixins.py", line 38, in list
+    queryset = self.filter_queryset(self.get_queryset())
+  File "d:\language\python38\lib\site-packages\rest_framework\generics.py", line 150, in filter_queryset
+    queryset = backend().filter_queryset(self.request, queryset, self)
+  File "d:\language\python38\lib\site-packages\django_filters\rest_framework\backends.py", line 90, in filter_queryset
+    filterset = self.get_filterset(request, queryset, view)
+  File "d:\language\python38\lib\site-packages\django_filters\rest_framework\backends.py", line 31, in get_filterset
+    filterset_class = self.get_filterset_class(view, queryset)
+  File "d:\language\python38\lib\site-packages\django_filters\rest_framework\backends.py", line 64, in get_filterset_class
+    assert issubclass(queryset.model, filterset_model), \
+AssertionError: FilterSet model <class 'erp.models.OutMaterialDetail'> does not match queryset model <class 'erp.models.MaterialDetail'>

+ 151 - 0
logs/server.log

@@ -2340,3 +2340,154 @@ django.core.exceptions.FieldError: Cannot resolve keyword 'id' into field. Choic
 [2025-04-29 22:29:41,857][django.request.log_response():241] [WARNING] Not Found: /cyclecount/qtyrecorviewset/
 [2025-04-29 22:29:49,712][django.request.log_response():241] [WARNING] Not Found: /cyclecount/qtyrecorviewset/
 [2025-04-29 22:30:16,481][django.request.log_response():241] [WARNING] Not Found: /cyclecount/qtyrecorviewset/
+[2025-04-30 09:17:50,822][django.request.log_response():241] [WARNING] Not Found: /wms/generateinbound
+[2025-04-30 09:18:35,510][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-04-30 09:21:27,159][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-04-30 09:21:52,458][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-04-30 09:22:25,546][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-04-30 16:47:58,102][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-04-30 17:15:13,729][django.request.log_response():241] [WARNING] Not Found: /cyclecount/qtyrecorviewset/
+[2025-04-30 17:15:28,915][django.request.log_response():241] [WARNING] Not Found: /asn/list/
+[2025-05-04 16:03:01,635][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:10:34,067][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:15:24,609][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:16:20,704][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:18:11,949][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:28:04,138][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:31:32,224][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:31:51,952][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:32:17,984][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:32:39,699][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:33:46,437][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:34:07,168][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:34:26,036][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:35:04,285][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:35:09,320][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:35:20,125][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:35:27,289][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:40:00,633][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:40:25,743][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:40:44,020][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:41:30,751][django.request.log_response():241] [ERROR] Internal Server Error: /wms/generateinbound
+[2025-05-04 16:42:40,492][django.request.log_response():241] [WARNING] Bad Request: /wms/generateinbound
+[2025-05-04 16:43:05,684][django.request.log_response():241] [WARNING] Bad Request: /wms/generateinbound
+[2025-05-04 16:43:12,574][django.request.log_response():241] [WARNING] Bad Request: /wms/generateinbound
+[2025-05-04 17:39:16,800][django.request.log_response():241] [WARNING] Not Found: /cyclecount/qtyrecorviewset/
+[2025-05-04 17:47:28,834][django.request.log_response():241] [ERROR] Internal Server Error: /wms/createOutboundApply
+Traceback (most recent call last):
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 472, in thread_handler
+    raise exc_info[1]
+  File "d:\language\python38\lib\site-packages\django\core\handlers\exception.py", line 42, in inner
+    response = await get_response(request)
+  File "d:\language\python38\lib\site-packages\django\core\handlers\base.py", line 253, in _get_response_async
+    response = await wrapped_callback(
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 435, in __call__
+    ret = await asyncio.wait_for(future, timeout=None)
+  File "d:\language\python38\lib\asyncio\tasks.py", line 455, in wait_for
+    return await fut
+  File "d:\language\python38\lib\concurrent\futures\thread.py", line 57, in run
+    result = self.fn(*self.args, **self.kwargs)
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 476, in thread_handler
+    return func(*args, **kwargs)
+  File "d:\language\python38\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
+    return view_func(*args, **kwargs)
+  File "d:\language\python38\lib\site-packages\django\views\generic\base.py", line 103, in view
+    return self.dispatch(request, *args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 509, in dispatch
+    response = self.handle_exception(exc)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
+    self.raise_uncaught_exception(exc)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
+    raise exc
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 497, in dispatch
+    self.initial(request, *args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 416, in initial
+    self.check_throttles(request)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 359, in check_throttles
+    if not throttle.allow_request(request, self):
+  File "D:\Document\code\vue\greater_wms\.\utils\throttle.py", line 18, in allow_request
+    openid = request.auth.openid
+AttributeError: 'NoneType' object has no attribute 'openid'
+[2025-05-04 18:40:24,966][django.request.log_response():241] [WARNING] Bad Request: /wms/createOutboundApply
+[2025-05-04 18:41:50,943][django.request.log_response():241] [WARNING] Bad Request: /wms/createOutboundApply
+[2025-05-04 18:50:04,871][django.request.log_response():241] [WARNING] Bad Request: /wms/createOutboundApply
+[2025-05-04 18:52:04,955][django.request.log_response():241] [WARNING] Bad Request: /wms/createOutboundApply
+[2025-05-04 18:52:52,148][django.request.log_response():241] [ERROR] Internal Server Error: /wms/createOutboundApply
+[2025-05-04 19:08:48,599][django.request.log_response():241] [ERROR] Internal Server Error: /wms/outboundBills/
+Traceback (most recent call last):
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 472, in thread_handler
+    raise exc_info[1]
+  File "d:\language\python38\lib\site-packages\django\core\handlers\exception.py", line 42, in inner
+    response = await get_response(request)
+  File "d:\language\python38\lib\site-packages\django\core\handlers\base.py", line 253, in _get_response_async
+    response = await wrapped_callback(
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 435, in __call__
+    ret = await asyncio.wait_for(future, timeout=None)
+  File "d:\language\python38\lib\asyncio\tasks.py", line 455, in wait_for
+    return await fut
+  File "d:\language\python38\lib\concurrent\futures\thread.py", line 57, in run
+    result = self.fn(*self.args, **self.kwargs)
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 476, in thread_handler
+    return func(*args, **kwargs)
+  File "d:\language\python38\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
+    return view_func(*args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\viewsets.py", line 125, in view
+    return self.dispatch(request, *args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 509, in dispatch
+    response = self.handle_exception(exc)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
+    self.raise_uncaught_exception(exc)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
+    raise exc
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 506, in dispatch
+    response = handler(request, *args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\mixins.py", line 38, in list
+    queryset = self.filter_queryset(self.get_queryset())
+  File "d:\language\python38\lib\site-packages\rest_framework\generics.py", line 150, in filter_queryset
+    queryset = backend().filter_queryset(self.request, queryset, self)
+  File "d:\language\python38\lib\site-packages\django_filters\rest_framework\backends.py", line 90, in filter_queryset
+    filterset = self.get_filterset(request, queryset, view)
+  File "d:\language\python38\lib\site-packages\django_filters\rest_framework\backends.py", line 31, in get_filterset
+    filterset_class = self.get_filterset_class(view, queryset)
+  File "d:\language\python38\lib\site-packages\django_filters\rest_framework\backends.py", line 64, in get_filterset_class
+    assert issubclass(queryset.model, filterset_model), \
+AssertionError: FilterSet model <class 'erp.models.InboundBill'> does not match queryset model <class 'erp.models.OutboundBill'>
+[2025-05-04 19:18:55,600][django.request.log_response():241] [ERROR] Internal Server Error: /wms/outmaterials/
+Traceback (most recent call last):
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 472, in thread_handler
+    raise exc_info[1]
+  File "d:\language\python38\lib\site-packages\django\core\handlers\exception.py", line 42, in inner
+    response = await get_response(request)
+  File "d:\language\python38\lib\site-packages\django\core\handlers\base.py", line 253, in _get_response_async
+    response = await wrapped_callback(
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 435, in __call__
+    ret = await asyncio.wait_for(future, timeout=None)
+  File "d:\language\python38\lib\asyncio\tasks.py", line 455, in wait_for
+    return await fut
+  File "d:\language\python38\lib\concurrent\futures\thread.py", line 57, in run
+    result = self.fn(*self.args, **self.kwargs)
+  File "d:\language\python38\lib\site-packages\asgiref\sync.py", line 476, in thread_handler
+    return func(*args, **kwargs)
+  File "d:\language\python38\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
+    return view_func(*args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\viewsets.py", line 125, in view
+    return self.dispatch(request, *args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 509, in dispatch
+    response = self.handle_exception(exc)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
+    self.raise_uncaught_exception(exc)
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
+    raise exc
+  File "d:\language\python38\lib\site-packages\rest_framework\views.py", line 506, in dispatch
+    response = handler(request, *args, **kwargs)
+  File "d:\language\python38\lib\site-packages\rest_framework\mixins.py", line 38, in list
+    queryset = self.filter_queryset(self.get_queryset())
+  File "d:\language\python38\lib\site-packages\rest_framework\generics.py", line 150, in filter_queryset
+    queryset = backend().filter_queryset(self.request, queryset, self)
+  File "d:\language\python38\lib\site-packages\django_filters\rest_framework\backends.py", line 90, in filter_queryset
+    filterset = self.get_filterset(request, queryset, view)
+  File "d:\language\python38\lib\site-packages\django_filters\rest_framework\backends.py", line 31, in get_filterset
+    filterset_class = self.get_filterset_class(view, queryset)
+  File "d:\language\python38\lib\site-packages\django_filters\rest_framework\backends.py", line 64, in get_filterset_class
+    assert issubclass(queryset.model, filterset_model), \
+AssertionError: FilterSet model <class 'erp.models.OutMaterialDetail'> does not match queryset model <class 'erp.models.MaterialDetail'>

+ 0 - 1
templates/dist/spa/css/10.74c09acd.css

@@ -1 +0,0 @@
-.tabs .q-tab__indicator{width:25%;height:1.5px;margin:auto;color:#d6d7d7}.tabs .absolute-bottom{bottom:8px}

templates/dist/spa/css/8.f721cf95.css → templates/dist/spa/css/10.f721cf95.css


+ 1 - 0
templates/dist/spa/css/11.00a78ca4.css

@@ -0,0 +1 @@
+.q-date__calendar-item--selected[data-v-2c7b2e73]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-2c7b2e73]{background-color:rgba(25,118,210,0.1)}

+ 0 - 1
templates/dist/spa/css/11.fdd11090.css

@@ -1 +0,0 @@
-.q-date__calendar-item--selected[data-v-9c5341a8]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-9c5341a8]{background-color:rgba(25,118,210,0.1)}

+ 1 - 0
templates/dist/spa/css/12.90edc1da.css

@@ -0,0 +1 @@
+.q-date__calendar-item--selected[data-v-0cca92ed]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-0cca92ed]{background-color:rgba(25,118,210,0.1)}[data-v-0cca92ed] .q-field__label{margin-top:8px;align-self:center}[data-v-0cca92ed] .q-field__control-container{padding-left:50px;margin-top:-5px}

文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/css/13.0d4c4716.css


+ 1 - 0
templates/dist/spa/css/14.a65cb06f.css

@@ -0,0 +1 @@
+.q-date__calendar-item--selected[data-v-358efdde]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-358efdde]{background-color:rgba(25,118,210,0.1)}[data-v-358efdde] .q-field__label{margin-top:8px;align-self:center}[data-v-358efdde] .q-field__control-container{padding-left:50px;margin-top:-5px}

文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/css/15.0b5a9844.css


+ 1 - 0
templates/dist/spa/css/16.d66bc7d6.css

@@ -0,0 +1 @@
+.q-date__calendar-item--selected[data-v-016cb5de]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-016cb5de]{background-color:rgba(25,118,210,0.1)}

文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/css/17.e4f041cc.css


文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/css/18.7a23b7fb.css


文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/css/3.48edef75.css


文件差异内容过多而无法显示
+ 0 - 1
templates/dist/spa/css/3.ae47b775.css


+ 1 - 0
templates/dist/spa/css/4.0ac6b60c.css

@@ -0,0 +1 @@
+.q-date__calendar-item--selected[data-v-3b1d1346]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-3b1d1346]{background-color:rgba(25,118,210,0.1)}

+ 0 - 1
templates/dist/spa/css/4.7ced9982.css

@@ -1 +0,0 @@
-.q-date__calendar-item--selected[data-v-6801116e]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-6801116e]{background-color:rgba(25,118,210,0.1)}

+ 1 - 0
templates/dist/spa/css/5.7100e471.css

@@ -0,0 +1 @@
+.q-date__calendar-item--selected[data-v-0b572384]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-0b572384]{background-color:rgba(25,118,210,0.1)}

+ 0 - 1
templates/dist/spa/css/5.b538e990.css

@@ -1 +0,0 @@
-.q-date__calendar-item--selected[data-v-df3b7de4]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-df3b7de4]{background-color:rgba(25,118,210,0.1)}

+ 1 - 0
templates/dist/spa/css/6.22759db8.css

@@ -0,0 +1 @@
+.q-date__calendar-item--selected[data-v-1056e070]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-1056e070]{background-color:rgba(25,118,210,0.1)}

+ 0 - 1
templates/dist/spa/css/6.a164a49b.css

@@ -1 +0,0 @@
-.q-date__calendar-item--selected[data-v-76685cd4]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-76685cd4]{background-color:rgba(25,118,210,0.1)}

+ 1 - 0
templates/dist/spa/css/7.82d60571.css

@@ -0,0 +1 @@
+.q-date__calendar-item--selected[data-v-257b6ff4]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-257b6ff4]{background-color:rgba(25,118,210,0.1)}[data-v-257b6ff4] .q-field__label{margin-top:8px;align-self:center}[data-v-257b6ff4] .q-field__control-container{padding-left:50px;margin-top:-5px}

+ 0 - 1
templates/dist/spa/css/7.fc14c348.css

@@ -1 +0,0 @@
-.q-date__calendar-item--selected[data-v-f7e4cf28]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-f7e4cf28]{background-color:rgba(25,118,210,0.1)}[data-v-f7e4cf28] .q-field__label{margin-top:8px;align-self:center}[data-v-f7e4cf28] .q-field__control-container{padding-left:50px;margin-top:-5px}

+ 1 - 0
templates/dist/spa/css/8.68989bda.css

@@ -0,0 +1 @@
+.q-date__calendar-item--selected[data-v-050cf908]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-050cf908]{background-color:rgba(25,118,210,0.1)}

+ 0 - 1
templates/dist/spa/css/9.8c912396.css

@@ -1 +0,0 @@
-.q-date__calendar-item--selected[data-v-32740af8]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-32740af8]{background-color:rgba(25,118,210,0.1)}

+ 1 - 0
templates/dist/spa/css/9.eeace633.css

@@ -0,0 +1 @@
+.q-date__calendar-item--selected[data-v-248f47b8]{transition:all 0.3s ease;background-color:#1976d2!important}.q-date__range[data-v-248f47b8]{background-color:rgba(25,118,210,0.1)}[data-v-248f47b8] .q-field__label{margin-top:8px;align-self:center}[data-v-248f47b8] .q-field__control-container{padding-left:50px;margin-top:-5px}

文件差异内容过多而无法显示
+ 1 - 1
templates/dist/spa/index.html


文件差异内容过多而无法显示
+ 0 - 1
templates/dist/spa/js/10.7a3b5a7c.js


二进制
templates/dist/spa/js/10.7a3b5a7c.js.gz


文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/js/10.82a2ece5.js


二进制
templates/dist/spa/js/10.82a2ece5.js.gz


文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/js/11.c094525f.js


二进制
templates/dist/spa/js/11.c094525f.js.gz


文件差异内容过多而无法显示
+ 0 - 1
templates/dist/spa/js/11.d137932a.js


二进制
templates/dist/spa/js/11.d137932a.js.gz


文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/js/12.077687d1.js


二进制
templates/dist/spa/js/12.077687d1.js.gz


文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/js/13.4cb828ed.js


二进制
templates/dist/spa/js/13.647bce7c.js.gz


二进制
templates/dist/spa/js/14.7f2ea93f.js.gz


文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/js/14.be39ef51.js


二进制
templates/dist/spa/js/14.be39ef51.js.gz


二进制
templates/dist/spa/js/15.34422806.js.gz


文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/js/15.f5f5f35b.js


二进制
templates/dist/spa/js/15.f5f5f35b.js.gz


文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/js/16.ed4c9435.js


二进制
templates/dist/spa/js/16.ed4c9435.js.gz


二进制
templates/dist/spa/js/17.3649fa32.js.gz


文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/js/17.cb55703b.js


文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/js/18.7ce0af4f.js


二进制
templates/dist/spa/js/18.abcdcf47.js.gz


二进制
templates/dist/spa/js/19.470bdfcf.js.gz


文件差异内容过多而无法显示
+ 1 - 1
templates/dist/spa/js/12.1cc936cf.js


文件差异内容过多而无法显示
+ 1 - 1
templates/dist/spa/js/13.647bce7c.js


二进制
templates/dist/spa/js/20.0a3341c5.js.gz


二进制
templates/dist/spa/js/20.e49bcfd3.js.gz


文件差异内容过多而无法显示
+ 1 - 1
templates/dist/spa/js/14.7f2ea93f.js


二进制
templates/dist/spa/js/21.53a3e18b.js.gz


二进制
templates/dist/spa/js/21.e266137c.js.gz


文件差异内容过多而无法显示
+ 1 - 1
templates/dist/spa/js/15.34422806.js


二进制
templates/dist/spa/js/22.3054b5f5.js.gz


二进制
templates/dist/spa/js/22.54dc6683.js.gz


文件差异内容过多而无法显示
+ 1 - 1
templates/dist/spa/js/16.5f33bda7.js


二进制
templates/dist/spa/js/16.5f33bda7.js.gz


二进制
templates/dist/spa/js/23.baedf163.js.gz


文件差异内容过多而无法显示
+ 1 - 1
templates/dist/spa/js/17.3649fa32.js


二进制
templates/dist/spa/js/24.5ebb6d1b.js.gz


二进制
templates/dist/spa/js/24.6d2f32f6.js.gz


文件差异内容过多而无法显示
+ 1 - 1
templates/dist/spa/js/18.abcdcf47.js


二进制
templates/dist/spa/js/25.73320c40.js.gz


文件差异内容过多而无法显示
+ 1 - 1
templates/dist/spa/js/19.470bdfcf.js


二进制
templates/dist/spa/js/26.b3d0a428.js.gz


文件差异内容过多而无法显示
+ 1 - 1
templates/dist/spa/js/20.e49bcfd3.js


二进制
templates/dist/spa/js/27.e967265e.js.gz


文件差异内容过多而无法显示
+ 1 - 1
templates/dist/spa/js/21.e266137c.js


二进制
templates/dist/spa/js/28.072bfae1.js.gz


文件差异内容过多而无法显示
+ 0 - 1
templates/dist/spa/js/29.462f2a27.js


文件差异内容过多而无法显示
+ 1 - 1
templates/dist/spa/js/22.54dc6683.js


二进制
templates/dist/spa/js/29.5db67fa0.js.gz


文件差异内容过多而无法显示
+ 1 - 0
templates/dist/spa/js/3.4f075d5f.js


二进制
templates/dist/spa/js/3.4f075d5f.js.gz


文件差异内容过多而无法显示
+ 0 - 1
templates/dist/spa/js/3.70fa2340.js


二进制
templates/dist/spa/js/3.70fa2340.js.gz


+ 0 - 0
templates/dist/spa/js/23.baedf163.js


部分文件因为文件数量过多而无法显示