1
0
forked from erp-dev/erp

feat: added type field into warehouse model

This commit is contained in:
2025-11-20 15:44:35 +08:00
parent 6091280fdc
commit 2a838199fa
22 changed files with 1384 additions and 38 deletions

View File

@@ -102,11 +102,27 @@ class SupplierSerializer(BaseSerializer):
class EmployeeSerializer(BaseSerializer):
job_type = serializers.CharField(read_only=True)
class Meta:
model = basic_models.Employee
fields = '__all__'
class EmployeeTypeSerializer(BaseSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 移除 unique_together 验证器中对 merchant 的要求
# 因为 merchant 会在 perform_create 中自动设置
for validator in self.validators:
if hasattr(validator, 'fields') and 'merchant' in validator.fields:
validator.fields = tuple(f for f in validator.fields if f != 'merchant')
class Meta:
model = basic_models.EmployeeType
fields = '__all__'
class CustomerSerializer(BaseSerializer):
created_by = serializers.PrimaryKeyRelatedField(
queryset=basic_models.Employee.objects.all(),

View File

@@ -1,3 +1,305 @@
from django.test import TestCase
from django.contrib.auth.models import User
from rest_framework.test import APIClient
from rest_framework import status
from basic_info.models import (
Merchant, MerchantTypeEnum, WareHouse, WarehouseTypeEnum,
Employee, EmployeeType, EmployeeStatusEnum
)
# Create your tests here.
class WarehouseAPITestCase(TestCase):
"""测试仓库 API"""
def setUp(self):
"""设置测试数据"""
# 创建商户
self.merchant = Merchant.objects.create(
name='测试商户',
type=MerchantTypeEnum.STORE
)
# 创建用户和员工
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.employee = Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='测试员工'
)
# 设置 API 客户端
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_create_warehouse_with_default_type(self):
"""测试创建仓库时使用默认类型"""
data = {
'name': '测试仓库1',
'location': '测试地址',
}
response = self.client.post('/api/backend/warehouses/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['type'], WarehouseTypeEnum.WHOLE.value)
# 验证数据库
warehouse = WareHouse.objects.get(id=response.data['id'])
self.assertEqual(warehouse.type, WarehouseTypeEnum.WHOLE)
def test_create_warehouse_with_whole_type(self):
"""测试创建整仓"""
data = {
'name': '整仓测试',
'type': WarehouseTypeEnum.WHOLE.value,
}
response = self.client.post('/api/backend/warehouses/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['type'], WarehouseTypeEnum.WHOLE.value)
def test_create_warehouse_with_scattered_type(self):
"""测试创建散仓"""
data = {
'name': '散仓测试',
'type': WarehouseTypeEnum.SCATTERED.value,
}
response = self.client.post('/api/backend/warehouses/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['type'], WarehouseTypeEnum.SCATTERED.value)
def test_list_warehouses_includes_type(self):
"""测试列表接口返回类型字段"""
WareHouse.objects.create(
merchant=self.merchant,
name='仓库1',
type=WarehouseTypeEnum.WHOLE
)
WareHouse.objects.create(
merchant=self.merchant,
name='仓库2',
type=WarehouseTypeEnum.SCATTERED
)
response = self.client.get('/api/backend/warehouses/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 2)
# 验证返回的数据包含 type 字段
for warehouse in results:
self.assertIn('type', warehouse)
self.assertIn(warehouse['type'], [WarehouseTypeEnum.WHOLE.value, WarehouseTypeEnum.SCATTERED.value])
def test_update_warehouse_type(self):
"""测试更新仓库类型"""
warehouse = WareHouse.objects.create(
merchant=self.merchant,
name='待更新仓库',
type=WarehouseTypeEnum.WHOLE
)
data = {'type': WarehouseTypeEnum.SCATTERED.value}
response = self.client.patch(f'/api/backend/warehouses/{warehouse.id}/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['type'], WarehouseTypeEnum.SCATTERED.value)
# 验证数据库
warehouse.refresh_from_db()
self.assertEqual(warehouse.type, WarehouseTypeEnum.SCATTERED)
def test_retrieve_warehouse_includes_type(self):
"""测试检索单个仓库时包含类型字段"""
warehouse = WareHouse.objects.create(
merchant=self.merchant,
name='单个仓库',
type=WarehouseTypeEnum.SCATTERED
)
response = self.client.get(f'/api/backend/warehouses/{warehouse.id}/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['type'], WarehouseTypeEnum.SCATTERED.value)
self.assertEqual(response.data['name'], '单个仓库')
class EmployeeTypeAPITestCase(TestCase):
"""测试员工职位类型 API"""
def setUp(self):
"""设置测试数据"""
# 创建商户
self.merchant = Merchant.objects.create(
name='测试商户',
type=MerchantTypeEnum.STORE
)
# 创建用户和员工
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.employee = Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='测试员工'
)
# 设置 API 客户端
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_create_employee_type(self):
"""测试创建职位类型"""
data = {
'title': '打纸工',
'description': '负责打纸工作'
}
response = self.client.post('/api/backend/employee-types/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['title'], '打纸工')
self.assertEqual(response.data['description'], '负责打纸工作')
def test_list_employee_types(self):
"""测试列出所有职位类型"""
EmployeeType.objects.create(merchant=self.merchant, title='打纸工')
EmployeeType.objects.create(merchant=self.merchant, title='滚筒工')
response = self.client.get('/api/backend/employee-types/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 2)
def test_update_employee_type(self):
"""测试更新职位类型"""
emp_type = EmployeeType.objects.create(
merchant=self.merchant,
title='仓库管理员'
)
data = {'description': '新的描述'}
response = self.client.patch(f'/api/backend/employee-types/{emp_type.id}/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['description'], '新的描述')
def test_delete_employee_type(self):
"""测试删除职位类型"""
emp_type = EmployeeType.objects.create(
merchant=self.merchant,
title='临时工'
)
response = self.client.delete(f'/api/backend/employee-types/{emp_type.id}/')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertFalse(EmployeeType.objects.filter(id=emp_type.id).exists())
class EmployeeAPITestCase(TestCase):
"""测试员工 API包含 job_type 兼容性)"""
def setUp(self):
"""设置测试数据"""
# 创建商户
self.merchant = Merchant.objects.create(
name='测试商户',
type=MerchantTypeEnum.STORE
)
# 创建用户和员工
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.employee = Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='测试员工'
)
# 创建职位类型
self.emp_type = EmployeeType.objects.create(
merchant=self.merchant,
title='打纸工'
)
# 设置 API 客户端
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_create_employee_with_position(self):
"""测试创建带职位的员工"""
data = {
'name': '张三',
'position': self.emp_type.id,
'mobile': '13800138000'
}
response = self.client.post('/api/backend/employees/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['position'], self.emp_type.id)
self.assertEqual(response.data['job_type'], '打纸工') # 验证兼容性字段
def test_create_employee_without_position(self):
"""测试创建不带职位的员工"""
data = {
'name': '李四',
'mobile': '13900139000'
}
response = self.client.post('/api/backend/employees/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertIsNone(response.data['position'])
self.assertEqual(response.data['job_type'], '') # 验证返回空字符串
def test_list_employees_includes_job_type(self):
"""测试列表接口包含 job_type 字段"""
Employee.objects.create(
merchant=self.merchant,
name='员工1',
position=self.emp_type
)
Employee.objects.create(
merchant=self.merchant,
name='员工2'
)
response = self.client.get('/api/backend/employees/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
# 验证 job_type 字段存在
for emp in results:
self.assertIn('job_type', emp)
def test_update_employee_position(self):
"""测试更新员工职位"""
new_type = EmployeeType.objects.create(
merchant=self.merchant,
title='滚筒工'
)
employee = Employee.objects.create(
merchant=self.merchant,
name='王五',
position=self.emp_type
)
data = {'position': new_type.id}
response = self.client.patch(f'/api/backend/employees/{employee.id}/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['position'], new_type.id)
self.assertEqual(response.data['job_type'], '滚筒工')
def test_job_type_is_read_only(self):
"""测试 job_type 是只读字段"""
employee = Employee.objects.create(
merchant=self.merchant,
name='赵六',
position=self.emp_type
)
# 尝试直接更新 job_type应该被忽略
data = {'job_type': '其他职位'}
response = self.client.patch(f'/api/backend/employees/{employee.id}/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['job_type'], '打纸工') # 应该保持不变

View File

@@ -20,6 +20,9 @@ supplier_router.register(prefix='', viewset=views.SupplierViewSet)
employee_router = routers.DefaultRouter()
employee_router.register(prefix='', viewset=views.EmployeeViewSet)
employee_type_router = routers.DefaultRouter()
employee_type_router.register(prefix='', viewset=views.EmployeeTypeViewSet)
customer_router = routers.DefaultRouter()
customer_router.register(prefix='', viewset=views.CustomerViewSet)
@@ -43,6 +46,7 @@ urlpatterns = [
path('product-categories/', include(product_category_router.urls)),
path('suppliers/', include(supplier_router.urls)),
path('employees/', include(employee_router.urls)),
path('employee-types/', include(employee_type_router.urls)),
path('customers/', include(customer_router.urls)),
path('vehicle-types/', include(vehicle_type_router.urls)),
path('bank-accounts/', include(bank_account_router.urls)),

View File

@@ -60,6 +60,11 @@ class EmployeeViewSet(BaseViewSet):
serializer_class = serializers.EmployeeSerializer
class EmployeeTypeViewSet(BaseViewSet):
queryset = serializers.basic_models.EmployeeType.objects
serializer_class = serializers.EmployeeTypeSerializer
class CustomerViewSet(BaseViewSet):
queryset = serializers.basic_models.Customer.objects
serializer_class = serializers.CustomerSerializer