Add configurable pharmacy card tab visibility matrix.

Let admins set per-section access levels so operational data on pharmacy detail is no longer all-or-nothing for every logged-in user.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-09 20:43:33 +03:00
parent ae5c1f1848
commit 56e2b1c8fe
8 changed files with 455 additions and 38 deletions
+142
View File
@@ -259,3 +259,145 @@ class DislocationColumnsTests(TestCase):
self.assertContains(resp, "disloSortBy('curator'")
self.assertContains(resp, "disloSortBy('manager'")
self.assertNotContains(resp, "disloSortBy('phone'")
class PharmacyCardVisibilityTests(TestCase):
"""Матрица видимости вкладок карточки аптеки (синглтон в админке)."""
@classmethod
def setUpTestData(cls):
from datetime import date
from django.contrib.auth.models import Group
from apps.employees.models import Employee
from apps.locations.models import PharmacyCardVisibility
org_type = DirectoryType.objects.create(code='organization', name='Организация')
region_type = DirectoryType.objects.create(code='region', name='Регион')
region = Directory.objects.create(directory_type=region_type, code='vis', name='Вид')
org = Directory.objects.create(directory_type=org_type, code='visorg', name='ВизОрг')
cls.pharmacy = Pharmacy.objects.create(
organization=org, location_region=region,
pharmacy_number=42, location_type='pharmacy',
street='Вид', house_number='1',
)
other = Pharmacy.objects.create(
organization=org, location_region=region,
pharmacy_number=43, location_type='pharmacy',
street='Інша', house_number='2',
)
common = dict(
hire_date=date(2024, 1, 1),
mattermost_sync_enabled=False,
ad_sync_enabled=False,
)
cls.own = Employee.objects.create_user(
username='vis.own', user_location_type='pharmacy',
user_pharmacy=cls.pharmacy, **common,
)
cls.stranger = Employee.objects.create_user(
username='vis.stranger', user_location_type='pharmacy',
user_pharmacy=other, **common,
)
cls.staff = Employee.objects.create_user(
username='vis.staff', is_staff=True, totp_enabled=True, **common,
)
cls.superuser = Employee.objects.create_user(
username='vis.super', is_staff=True, is_superuser=True,
totp_enabled=True, **common,
)
cls.it_user = Employee.objects.create_user(username='vis.it', **common)
it_group, _ = Group.objects.get_or_create(name='IT')
cls.it_user.groups.add(it_group)
PharmacyCardVisibility.load()
def _set_level(self, section, level):
from apps.locations.models import PharmacyCardVisibility
policy = PharmacyCardVisibility.load()
setattr(policy, f'show_{section}', level)
policy.save()
def test_own_or_staff_default(self):
from apps.locations.models import (
PharmacyCardVisibility, user_can_view_pharmacy_section,
)
level = PharmacyCardVisibility.Level.OWN_OR_STAFF
self.assertTrue(user_can_view_pharmacy_section(self.own, self.pharmacy, level))
self.assertFalse(user_can_view_pharmacy_section(self.stranger, self.pharmacy, level))
self.assertTrue(user_can_view_pharmacy_section(self.staff, self.pharmacy, level))
def test_everyone_and_hidden(self):
from apps.locations.models import (
PharmacyCardVisibility, user_can_view_pharmacy_section,
)
everyone = PharmacyCardVisibility.Level.EVERYONE
hidden = PharmacyCardVisibility.Level.HIDDEN
self.assertTrue(user_can_view_pharmacy_section(self.stranger, self.pharmacy, everyone))
self.assertFalse(user_can_view_pharmacy_section(self.staff, self.pharmacy, hidden))
self.assertTrue(user_can_view_pharmacy_section(self.superuser, self.pharmacy, hidden))
def test_staff_and_it_levels(self):
from apps.locations.models import (
PharmacyCardVisibility, user_can_view_pharmacy_section,
)
staff_lvl = PharmacyCardVisibility.Level.STAFF
it_lvl = PharmacyCardVisibility.Level.IT
self.assertFalse(user_can_view_pharmacy_section(self.own, self.pharmacy, staff_lvl))
self.assertTrue(user_can_view_pharmacy_section(self.staff, self.pharmacy, staff_lvl))
self.assertTrue(user_can_view_pharmacy_section(self.it_user, self.pharmacy, it_lvl))
self.assertFalse(user_can_view_pharmacy_section(self.own, self.pharmacy, it_lvl))
self.assertFalse(user_can_view_pharmacy_section(self.staff, self.pharmacy, it_lvl))
def test_pharmacy_card_flags_respect_per_section(self):
from apps.locations.models import (
PharmacyCardVisibility, pharmacy_card_flags,
)
self._set_level('rro', PharmacyCardVisibility.Level.EVERYONE)
self._set_level('equipment', PharmacyCardVisibility.Level.HIDDEN)
flags = pharmacy_card_flags(self.stranger, self.pharmacy)
self.assertTrue(flags['can_view_rro'])
self.assertFalse(flags['can_view_equipment'])
def test_detail_view_hides_tab_when_section_staff_only(self):
from apps.locations.models import PharmacyCardVisibility
from apps.rro.models import CashRegister
from core.models import Directory, DirectoryType
status_type, _ = DirectoryType.objects.get_or_create(
code='rro_status', defaults={'name': 'Статус РРО', 'hierarchy_type': 'strict'},
)
status, _ = Directory.objects.get_or_create(
directory_type=status_type, code='active',
defaults={'name': 'Активный', 'is_active': True},
)
CashRegister.objects.create(
pharmacy=self.pharmacy, kind='prro', fiscal_number='FN-VIS-1',
status=status, is_primary=True,
)
self._set_level('rro', PharmacyCardVisibility.Level.STAFF)
self.client.force_login(self.own)
session = self.client.session
session['2fa_verified'] = True
session.save()
resp = self.client.get(f'/pharmacies/{self.pharmacy.pk}/', HTTP_HX_REQUEST='true')
self.assertEqual(resp.status_code, 200)
self.assertFalse(resp.context['can_view_rro'])
self.assertNotContains(resp, 'FN-VIS-1')
self.assertNotContains(resp, 'ph-panel-rro')
self.client.force_login(self.staff)
session = self.client.session
session['2fa_verified'] = True
session.save()
resp = self.client.get(f'/pharmacies/{self.pharmacy.pk}/', HTTP_HX_REQUEST='true')
self.assertEqual(resp.status_code, 200)
self.assertTrue(resp.context.get('can_view_rro'))
self.assertContains(resp, 'FN-VIS-1')
self.assertContains(resp, 'ph-panel-rro')