208 lines
8.1 KiB
Python
208 lines
8.1 KiB
Python
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'))
|