эксплуатация: показания счётчиков, энергоактивы и РРО

Конфиг счётчиков и день/ночь-тарифы, учёт энергосистем (генераторы,
инверторы), доступ к разделу РРО/ПРРО.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-09 18:03:44 +03:00
parent 3e969e29dc
commit 16bf2cf1c7
26 changed files with 2044 additions and 88 deletions
+99 -1
View File
@@ -1,17 +1,41 @@
from datetime import date
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.core.exceptions import ValidationError
from django.db import IntegrityError, transaction
from django.test import TestCase
from core.models import Directory, DirectoryType
from core.models import Directory, DirectoryType, PortalAccess
from apps.employees.models import EmployeePermissionOverride
from apps.locations.models import Pharmacy
from apps.rro.models import CashRegister
Employee = get_user_model()
def grant_office_access(user):
user.groups.add(Group.objects.get(name='Офисный сотрудник'))
for attr in ('_perm_cache', '_user_perm_cache', '_group_perm_cache'):
if hasattr(user, attr):
delattr(user, attr)
def grant_rro_access(user):
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
ct = ContentType.objects.get_for_model(PortalAccess, for_concrete_model=False)
perm = Permission.objects.get(content_type=ct, codename='access_rro')
EmployeePermissionOverride.objects.get_or_create(
employee=user,
permission=perm,
defaults={'override_type': 'grant'},
)
for attr in ('_perm_cache', '_user_perm_cache', '_group_perm_cache'):
if hasattr(user, attr):
delattr(user, attr)
class CashRegisterModelTests(TestCase):
@classmethod
def setUpTestData(cls):
@@ -113,6 +137,7 @@ class CashRegisterPortalTests(TestCase):
ad_sync_enabled=False,
totp_enabled=True,
)
grant_rro_access(cls.user)
def _login(self):
self.client.force_login(self.user)
@@ -170,6 +195,76 @@ class CashRegisterPortalTests(TestCase):
self.assertNotContains(resp, 'ph-panel-utilities')
class ManagerRroScopeTests(TestCase):
@classmethod
def setUpTestData(cls):
org_type = DirectoryType.objects.create(code='organization', name='Организация')
region_type = DirectoryType.objects.create(code='region', name='Регион')
status_type, _ = DirectoryType.objects.get_or_create(
code='rro_status', defaults={'name': 'Статус РРО/ПРРО', 'hierarchy_type': 'strict'},
)
cls.status_active, _ = Directory.objects.get_or_create(
directory_type=status_type, code='active',
defaults={'name': 'Активный', 'is_active': True},
)
region = Directory.objects.create(directory_type=region_type, code='kv2', name='Киев')
org = Directory.objects.create(directory_type=org_type, code='orgm', name='ТестОрг')
cls.pharmacy_a = Pharmacy.objects.create(
organization=org, location_region=region,
pharmacy_number=1, location_type='pharmacy',
street='A', house_number='1',
)
cls.pharmacy_b = Pharmacy.objects.create(
organization=org, location_region=region,
pharmacy_number=2, location_type='pharmacy',
street='B', house_number='2',
)
CashRegister.objects.create(
pharmacy=cls.pharmacy_a, kind=CashRegister.Kind.RRO,
fiscal_number='FN-MINE', status=cls.status_active,
)
CashRegister.objects.create(
pharmacy=cls.pharmacy_b, kind=CashRegister.Kind.RRO,
fiscal_number='FN-OTHER', status=cls.status_active,
)
cls.manager = Employee.objects.create(
username='rro.manager',
email='rro.manager@company.local',
first_name='Зав',
last_name='Аптекой',
hire_date=date(2026, 1, 1),
user_location_type='pharmacy',
user_pharmacy=cls.pharmacy_a,
is_staff=False,
mattermost_sync_enabled=False,
ad_sync_enabled=False,
)
cls.manager.groups.add(Group.objects.get(name='Заведующий аптекой'))
def _login(self):
self.client.force_login(self.manager)
session = self.client.session
session['2fa_verified'] = True
session.save()
def test_manager_sees_only_own_pharmacy(self):
self._login()
resp = self.client.get('/rro/', HTTP_HX_REQUEST='true')
self.assertEqual(resp.status_code, 200)
self.assertContains(resp, 'FN-MINE')
self.assertNotContains(resp, 'FN-OTHER')
self.assertTrue(resp.context['rro_pharmacy_scoped'])
def test_manager_cannot_bypass_with_query_param(self):
self._login()
resp = self.client.get(
f'/rro/?pharmacy={self.pharmacy_b.pk}',
HTTP_HX_REQUEST='true',
)
self.assertEqual(resp.status_code, 200)
self.assertNotContains(resp, 'FN-OTHER')
class CashRegisterCrudTests(TestCase):
@classmethod
def setUpTestData(cls):
@@ -217,6 +312,7 @@ class CashRegisterCrudTests(TestCase):
mattermost_sync_enabled=False,
ad_sync_enabled=False,
)
grant_rro_access(cls.staff)
def _login(self, user):
self.client.force_login(user)
@@ -244,6 +340,8 @@ class CashRegisterCrudTests(TestCase):
self.assertContains(resp, 'Кассовый аппарат добавлен')
def test_non_staff_create_forbidden(self):
# Есть просмотр РРО, но без staff — create запрещён (не редирект раздела).
grant_rro_access(self.regular)
self._login(self.regular)
resp = self.client.post('/rro/create/', self._payload(fiscal_number='FN-FORBIDDEN'))
self.assertEqual(resp.status_code, 403)