"""Cost 模块模型测试""" from datetime import date from decimal import Decimal from django.core.exceptions import ValidationError from django.db import IntegrityError from django.test import TestCase from basic_info import models as basic_models from cost import models as cost_models class CostCategoryModelTests(TestCase): def setUp(self): self.merchant = basic_models.Merchant.objects.create( name='测试商户', type=basic_models.MerchantTypeEnum.STORE, ) def test_str_representation(self): cat = cost_models.CostCategory.objects.create( merchant=self.merchant, unique_key='electricity', name='电费', ) self.assertEqual(str(cat), '电费 (electricity)') def test_unique_together_merchant_key(self): """同一商户 same unique_key 不可重复""" cost_models.CostCategory.objects.create( merchant=self.merchant, unique_key='electricity', name='电费', ) with self.assertRaises(IntegrityError): cost_models.CostCategory.objects.create( merchant=self.merchant, unique_key='electricity', name='电费2', ) def test_different_merchants_same_key_allowed(self): """不同商户 same unique_key 允许""" other = basic_models.Merchant.objects.create( name='其他商户', type=basic_models.MerchantTypeEnum.STORE, ) cat1 = cost_models.CostCategory.objects.create( merchant=self.merchant, unique_key='electricity', name='电费', ) cat2 = cost_models.CostCategory.objects.create( merchant=other, unique_key='electricity', name='电费', ) self.assertNotEqual(cat1.id, cat2.id) def test_parent_self_reference(self): """父类目 self-FK""" parent = cost_models.CostCategory.objects.create( merchant=self.merchant, unique_key='utilities', name='公共事业', ) child = cost_models.CostCategory.objects.create( merchant=self.merchant, unique_key='electricity', name='电费', parent=parent, ) self.assertEqual(child.parent_id, parent.id) self.assertEqual(list(parent.children.all()), [child]) def test_description_nullable(self): cat = cost_models.CostCategory.objects.create( merchant=self.merchant, unique_key='key', name='name', ) self.assertIsNone(cat.description) def test_ordering_by_merchant_then_name(self): other = basic_models.Merchant.objects.create( name='其他商户', type=basic_models.MerchantTypeEnum.STORE, ) cost_models.CostCategory.objects.create( merchant=self.merchant, unique_key='z', name='Z类目', ) cost_models.CostCategory.objects.create( merchant=self.merchant, unique_key='a', name='A类目', ) cost_models.CostCategory.objects.create( merchant=other, unique_key='m', name='M类目', ) # ordering = ('merchant', 'name') — 同一个 merchant 内按 name 排序 own = cost_models.CostCategory.objects.filter(merchant=self.merchant) own_names = [c.name for c in own] self.assertEqual(own_names, ['A类目', 'Z类目']) # name 升序 class CostEntryModelTests(TestCase): def setUp(self): self.merchant = basic_models.Merchant.objects.create( name='测试商户', type=basic_models.MerchantTypeEnum.STORE, ) self.category = cost_models.CostCategory.objects.create( merchant=self.merchant, unique_key='transport', name='运输费', ) def test_str_representation(self): entry = cost_models.CostEntry.objects.create( merchant=self.merchant, category=self.category, amount=Decimal('150.00'), occurred_at=date(2026, 6, 1), ) self.assertIn('运输费', str(entry)) self.assertIn('150.00', str(entry)) def test_default_ordering_by_occurred_at_desc(self): e1 = cost_models.CostEntry.objects.create( merchant=self.merchant, category=self.category, amount=Decimal('100.00'), occurred_at=date(2026, 6, 1), ) e2 = cost_models.CostEntry.objects.create( merchant=self.merchant, category=self.category, amount=Decimal('200.00'), occurred_at=date(2026, 6, 10), ) entries = list(cost_models.CostEntry.objects.all()) self.assertEqual(entries[0].id, e2.id) self.assertEqual(entries[1].id, e1.id) def test_operator_nullable(self): entry = cost_models.CostEntry.objects.create( merchant=self.merchant, category=self.category, amount=Decimal('100.00'), occurred_at=date(2026, 6, 1), operator=None, ) self.assertIsNone(entry.operator_id) def test_source_fields_nullable(self): entry = cost_models.CostEntry.objects.create( merchant=self.merchant, category=self.category, amount=Decimal('100.00'), occurred_at=date(2026, 6, 1), ) self.assertIsNone(entry.source_module) self.assertIsNone(entry.source_id) def test_image_fields_nullable(self): entry = cost_models.CostEntry.objects.create( merchant=self.merchant, category=self.category, amount=Decimal('100.00'), occurred_at=date(2026, 6, 1), ) self.assertFalse(bool(entry.image1)) self.assertFalse(bool(entry.image2))