from datetime import date from decimal import Decimal from django.contrib.auth.models import Group from django.test import TestCase from apps.it_manager.admin import InternetContractForm from apps.it_manager.billing import _build_billing_rows from apps.it_manager.models import InternetContract, InternetContractPayment, InternetProvider from apps.locations.models.base import Location from core.models.directories import Directory, DirectoryType from core.portal_access import user_can_access from core.test_utils import login_2fa, grant_all_locations from core.tests_portal_access import make_employee def _ensure_contract_status(): dtype, _ = DirectoryType.objects.get_or_create(code='contract_status', defaults={'name': 'Статус договора'}) status, _ = Directory.objects.get_or_create( directory_type=dtype, code='active', defaults={'name': 'Активный'}, ) return status def _make_location(**overrides): region_type, _ = DirectoryType.objects.get_or_create(code='location_region', defaults={'name': 'Регион'}) region, _ = Directory.objects.get_or_create(directory_type=region_type, code='test', defaults={'name': 'Тест'}) defaults = { 'address': 'Test addr', 'location_type': 'office', 'location_region': region, 'is_active': True, } defaults.update(overrides) return Location.objects.create(**defaults) def _ensure_service_type(): dtype, _ = DirectoryType.objects.get_or_create(code='contract_service_type', defaults={'name': 'Тип услуги'}) service, _ = Directory.objects.get_or_create( directory_type=dtype, code='internet', defaults={'name': 'Интернет'}, ) return service class InternetContractHierarchyTests(TestCase): def setUp(self): self.status = _ensure_contract_status() self.service = _ensure_service_type() self.provider = InternetProvider.objects.create(name='WESTELECOM') self.location = _make_location(location_type='pharmacy', address='Pharmacy addr') self.master = InternetContract.objects.create( provider=self.provider, location=None, contract_number='708052', service_type=self.service, status=self.status, start_date=date(2024, 1, 1), is_framework=True, monthly_cost=Decimal('1000.00'), ) self.child = InternetContract.objects.create( provider=self.provider, location=self.location, contract_number='70805208', service_type=self.service, status=self.status, start_date=date(2024, 1, 1), parent_contract=self.master, monthly_cost=Decimal('300.00'), ) def test_child_links_to_master(self): self.assertIsNone(self.master.location_id) self.assertEqual(self.child.parent_contract, self.master) self.assertIn(self.child, self.master.child_contracts.all()) def test_framework_cannot_have_parent(self): contract = InternetContract( provider=self.provider, location=self.location, contract_number='X', service_type=self.service, status=self.status, start_date=date(2024, 1, 1), is_framework=True, parent_contract=self.master, ) from django.core.exceptions import ValidationError with self.assertRaises(ValidationError): contract.full_clean() def test_billing_tree_nests_child_under_framework(self): rows = _build_billing_rows( [self.master, self.child], payments_by_contract={}, ) contract_rows = [r for r in rows if not r.get('is_provider_header')] self.assertEqual(len(contract_rows), 2) self.assertEqual(contract_rows[0]['contract'], self.master) self.assertEqual(contract_rows[0]['indent'], 0) self.assertTrue(contract_rows[0]['has_children']) self.assertEqual(contract_rows[1]['contract'], self.child) self.assertEqual(contract_rows[1]['indent'], 1) def test_billing_tree_infers_parent_by_contract_number_prefix(self): orphan = InternetContract.objects.create( provider=self.provider, location=self.location, contract_number='70805211', service_type=self.service, status=self.status, start_date=date(2024, 1, 1), monthly_cost=Decimal('400.00'), ) rows = _build_billing_rows( [self.master, orphan], payments_by_contract={}, ) contract_rows = [r for r in rows if not r.get('is_provider_header')] self.assertEqual(len(contract_rows), 2) self.assertEqual(contract_rows[1]['contract'], orphan) self.assertEqual(contract_rows[1]['indent'], 1) self.assertTrue(contract_rows[1]['is_inferred_child']) class InternetContractAdminFormTests(TestCase): def setUp(self): self.status = _ensure_contract_status() self.service = _ensure_service_type() self.provider = InternetProvider.objects.create(name='WESTELECOM') def test_framework_contract_form_without_location(self): form = InternetContractForm(data={ 'provider': self.provider.pk, 'location': '', 'contract_number': '708052', 'service_type': self.service.pk, 'status': self.status.pk, 'start_date': '2024-01-01', 'is_framework': True, }) self.assertTrue(form.is_valid(), form.errors) def test_framework_contract_form_requires_start_date(self): form = InternetContractForm(data={ 'provider': self.provider.pk, 'location': '', 'contract_number': '708052', 'service_type': self.service.pk, 'status': self.status.pk, 'is_framework': True, }) self.assertFalse(form.is_valid()) self.assertIn('start_date', form.errors) class InternetBillingPortalTests(TestCase): def setUp(self): self.status = _ensure_contract_status() self.service = _ensure_service_type() self.provider = InternetProvider.objects.create(name='Лекол') self.location = _make_location(location_type='pharmacy', address='Pharmacy addr') self.contract = InternetContract.objects.create( provider=self.provider, location=self.location, contract_number='70805205', service_type=self.service, status=self.status, start_date=date(2024, 1, 1), monthly_cost=Decimal('500.00'), ) self.user = grant_all_locations(make_employee()) self.user.groups.add(Group.objects.get(name='Офисный сотрудник')) for attr in ('_perm_cache', '_user_perm_cache', '_group_perm_cache'): if hasattr(self.user, attr): delattr(self.user, attr) def test_office_user_can_access_internet_billing_tab(self): self.assertTrue(user_can_access(self.user, 'internet_billing')) def test_accounting_internet_tab_renders(self): login_2fa(self.client, self.user) response = self.client.get('/accounting/?tab=internet', HTTP_HX_REQUEST='true') self.assertEqual(response.status_code, 200) self.assertEqual(response.context['accounting_active_tab'], 'internet') contract_rows = [ r for r in response.context['billing_rows'] if not r.get('is_provider_header') ] self.assertEqual(len(contract_rows), 1) def test_mark_payment_paid(self): login_2fa(self.client, self.user) response = self.client.post( '/accounting/internet-payment/', { 'month': '2026-09', 'contract_id': self.contract.pk, 'action': 'mark_paid', }, HTTP_HX_REQUEST='true', ) self.assertEqual(response.status_code, 200) payment = InternetContractPayment.objects.get(contract=self.contract, period=date(2026, 9, 1)) self.assertTrue(payment.is_paid) self.assertEqual(payment.amount, Decimal('500.00'))