locations: право редактирования аптеки, формы и дислокация
Отдельное право на редактирование карточки аптеки, формы, пресеты колонок дислокации и правки видимости карточки. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+184
-1
@@ -260,6 +260,48 @@ class DislocationColumnsTests(TestCase):
|
||||
self.assertContains(resp, "disloSortBy('manager'")
|
||||
self.assertNotContains(resp, "disloSortBy('phone'")
|
||||
|
||||
def test_staff_can_create_preset(self):
|
||||
from apps.locations.models import DislocationColumnPreset
|
||||
from core.test_utils import login_2fa
|
||||
|
||||
self.user.is_staff = True
|
||||
self.user.save(update_fields=['is_staff'])
|
||||
login_2fa(self.client, self.user)
|
||||
|
||||
resp = self.client.post(
|
||||
'/dislocation/column-presets/',
|
||||
{
|
||||
'action': 'create',
|
||||
'name': 'IT набор',
|
||||
'code': 'it',
|
||||
'columns': ['pharmacy', 'mikrotik', 'provider', 'gpon'],
|
||||
'is_default': '',
|
||||
},
|
||||
HTTP_HX_REQUEST='true',
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
preset = DislocationColumnPreset.objects.get(code='it')
|
||||
self.assertEqual(preset.name, 'IT набор')
|
||||
self.assertEqual(preset.columns, ['pharmacy', 'mikrotik', 'provider', 'gpon'])
|
||||
|
||||
def test_non_staff_cannot_create_preset(self):
|
||||
self.client.force_login(self.user)
|
||||
session = self.client.session
|
||||
session['2fa_verified'] = True
|
||||
session.save()
|
||||
|
||||
resp = self.client.post(
|
||||
'/dislocation/column-presets/',
|
||||
{
|
||||
'action': 'create',
|
||||
'name': 'Hack',
|
||||
'code': 'hack',
|
||||
'columns': ['pharmacy', 'phone'],
|
||||
},
|
||||
HTTP_HX_REQUEST='true',
|
||||
)
|
||||
self.assertEqual(resp.status_code, 403)
|
||||
|
||||
|
||||
class PharmacyCardVisibilityTests(TestCase):
|
||||
"""Матрица видимости вкладок карточки аптеки (синглтон в админке)."""
|
||||
@@ -316,7 +358,8 @@ class PharmacyCardVisibilityTests(TestCase):
|
||||
from apps.locations.models import PharmacyCardVisibility
|
||||
|
||||
policy = PharmacyCardVisibility.load()
|
||||
setattr(policy, f'show_{section}', level)
|
||||
attr = f'show_{section}' if section != 'edit' else 'edit_pharmacy'
|
||||
setattr(policy, attr, level)
|
||||
policy.save()
|
||||
|
||||
def test_own_or_staff_default(self):
|
||||
@@ -329,6 +372,88 @@ class PharmacyCardVisibilityTests(TestCase):
|
||||
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_edit_pharmacy_default_staff_only(self):
|
||||
from apps.locations.models import pharmacy_card_flags, user_can_edit_pharmacy
|
||||
|
||||
self.assertFalse(user_can_edit_pharmacy(self.own, self.pharmacy))
|
||||
self.assertTrue(user_can_edit_pharmacy(self.staff, self.pharmacy))
|
||||
flags = pharmacy_card_flags(self.own, self.pharmacy)
|
||||
self.assertFalse(flags['can_edit'])
|
||||
flags_staff = pharmacy_card_flags(self.staff, self.pharmacy)
|
||||
self.assertTrue(flags_staff['can_edit'])
|
||||
|
||||
def test_edit_pharmacy_own_or_staff_allows_own(self):
|
||||
from apps.locations.models import PharmacyCardVisibility, user_can_edit_pharmacy
|
||||
|
||||
self._set_level('edit', PharmacyCardVisibility.Level.OWN_OR_STAFF)
|
||||
self.assertTrue(user_can_edit_pharmacy(self.own, self.pharmacy))
|
||||
self.assertFalse(user_can_edit_pharmacy(self.stranger, self.pharmacy))
|
||||
self.assertTrue(user_can_edit_pharmacy(self.staff, self.pharmacy))
|
||||
|
||||
def test_portal_edit_forbidden_without_permission(self):
|
||||
self.client.force_login(self.own)
|
||||
session = self.client.session
|
||||
session['2fa_verified'] = True
|
||||
session.save()
|
||||
resp = self.client.post(
|
||||
f'/pharmacies/{self.pharmacy.pk}/edit/',
|
||||
{'working_hours': '09:00-21:00'},
|
||||
HTTP_HX_REQUEST='true',
|
||||
)
|
||||
self.assertEqual(resp.status_code, 403)
|
||||
|
||||
def test_portal_edit_saves_when_allowed(self):
|
||||
from apps.locations.models import PharmacyCardVisibility
|
||||
|
||||
self._set_level('edit', PharmacyCardVisibility.Level.OWN_OR_STAFF)
|
||||
self.client.force_login(self.own)
|
||||
session = self.client.session
|
||||
session['2fa_verified'] = True
|
||||
session.save()
|
||||
resp = self.client.post(
|
||||
f'/pharmacies/{self.pharmacy.pk}/edit/',
|
||||
{
|
||||
'organization': self.pharmacy.organization_id,
|
||||
'pharmacy_number': self.pharmacy.pharmacy_number,
|
||||
'status': 'open',
|
||||
'category': 'A',
|
||||
'vat_category': '',
|
||||
'working_hours': '09:00-21:00',
|
||||
'location_region': self.pharmacy.location_region_id,
|
||||
'street': 'Нова',
|
||||
'house_number': '10',
|
||||
'building': '',
|
||||
'apartment': '',
|
||||
'location': '46.4825,30.7233',
|
||||
'phone': '',
|
||||
'license_number': '',
|
||||
'license_expiry_date': '',
|
||||
'head_pharmacist': '',
|
||||
'responsible_manager': '',
|
||||
'curator': '',
|
||||
'pharmacists_count': 2,
|
||||
'comments': 'Оновлено з порталу',
|
||||
},
|
||||
HTTP_HX_REQUEST='true',
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.pharmacy.refresh_from_db()
|
||||
self.assertEqual(self.pharmacy.working_hours, '09:00-21:00')
|
||||
self.assertEqual(self.pharmacy.street, 'Нова')
|
||||
self.assertEqual(self.pharmacy.comments, 'Оновлено з порталу')
|
||||
self.assertContains(resp, 'Изменения сохранены')
|
||||
|
||||
def test_detail_shows_edit_button_for_staff(self):
|
||||
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['can_edit'])
|
||||
self.assertContains(resp, 'edit=1')
|
||||
self.assertNotContains(resp, 'Редактировать в админке')
|
||||
|
||||
def test_everyone_and_hidden(self):
|
||||
from apps.locations.models import (
|
||||
PharmacyCardVisibility, user_can_view_pharmacy_section,
|
||||
@@ -401,3 +526,61 @@ class PharmacyCardVisibilityTests(TestCase):
|
||||
self.assertTrue(resp.context.get('can_view_rro'))
|
||||
self.assertContains(resp, 'FN-VIS-1')
|
||||
self.assertContains(resp, 'ph-panel-rro')
|
||||
|
||||
|
||||
class PharmacyHistoryTests(TestCase):
|
||||
"""PharmacyHistory заполняется при изменении аптеки, а не при создании."""
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
org_type = DirectoryType.objects.create(code='organization', name='Организация')
|
||||
region_type = DirectoryType.objects.create(code='region', name='Регион')
|
||||
cls.region = Directory.objects.create(directory_type=region_type, code='od', name='Одесса')
|
||||
cls.aks = Directory.objects.create(
|
||||
directory_type=org_type, code='akc', name='АКС', extra_data={'mail_prefix': 'a'})
|
||||
|
||||
def _pharmacy(self, number=1, **kw):
|
||||
return Pharmacy.objects.create(
|
||||
organization=self.aks, location_region=self.region,
|
||||
pharmacy_number=number, location_type='pharmacy', **kw)
|
||||
|
||||
def test_create_does_not_write_history(self):
|
||||
from apps.locations.models import PharmacyHistory
|
||||
p = self._pharmacy(30)
|
||||
self.assertEqual(PharmacyHistory.objects.filter(pharmacy=p).count(), 0)
|
||||
|
||||
def test_field_change_writes_history(self):
|
||||
from apps.locations.models import PharmacyHistory
|
||||
p = self._pharmacy(31, phone='+380501111111')
|
||||
p.phone = '+380502222222'
|
||||
p.save()
|
||||
rows = list(PharmacyHistory.objects.filter(pharmacy=p))
|
||||
self.assertEqual(len(rows), 1)
|
||||
self.assertEqual(rows[0].changed_field, 'phone')
|
||||
self.assertEqual(rows[0].old_value, '+380501111111')
|
||||
self.assertEqual(rows[0].new_value, '+380502222222')
|
||||
self.assertIsNone(rows[0].changed_by)
|
||||
|
||||
def test_unchanged_save_writes_nothing(self):
|
||||
from apps.locations.models import PharmacyHistory
|
||||
p = self._pharmacy(32)
|
||||
p.save()
|
||||
self.assertEqual(PharmacyHistory.objects.filter(pharmacy=p).count(), 0)
|
||||
|
||||
def test_history_user_is_recorded(self):
|
||||
from apps.locations.models import PharmacyHistory
|
||||
from apps.employees.tests import make_employee
|
||||
user = make_employee('hist-user')
|
||||
p = self._pharmacy(33, working_hours='08:00-20:00')
|
||||
p.working_hours = '09:00-21:00'
|
||||
p._history_user = user
|
||||
p.save()
|
||||
row = PharmacyHistory.objects.get(pharmacy=p, changed_field='working_hours')
|
||||
self.assertEqual(row.changed_by, user)
|
||||
|
||||
def test_untracked_update_fields_skip_history(self):
|
||||
from apps.locations.models import PharmacyHistory
|
||||
p = self._pharmacy(34, carbonio_sync_status='pending')
|
||||
p.carbonio_sync_status = 'synced'
|
||||
p.save(update_fields=['carbonio_sync_status'])
|
||||
self.assertEqual(PharmacyHistory.objects.filter(pharmacy=p).count(), 0)
|
||||
|
||||
Reference in New Issue
Block a user