Add RRO, portal section perms, dislocation presets, and hiring intern flow.

Ship cash-register registry, section-based portal access, configurable dislocation columns, and trial-hire intern accounts with credentials PDF.
This commit is contained in:
2026-08-09 14:58:17 +03:00
parent 237275abd0
commit ae5c1f1848
63 changed files with 5987 additions and 2217 deletions
+124
View File
@@ -135,3 +135,127 @@ class CarbonioPasswordSyncTests(TestCase):
self.assertEqual(p.carbonio_password, '') # на update пароль не трогаем
fake.create_account.assert_not_called()
fake.set_account_status.assert_called_once_with('acc-existing', 'active')
class DislocationColumnsTests(TestCase):
"""Whitelist, резолв пресета default и сохранение preference пользователя."""
@classmethod
def setUpTestData(cls):
from datetime import date
from django.contrib.auth import get_user_model
from apps.locations.dislocation_columns import HR_COLUMNS, OPS_COLUMNS, STANDARD_COLUMNS
from apps.locations.models import DislocationColumnPreset
Employee = get_user_model()
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='kv', name='Киев')
cls.org = Directory.objects.create(directory_type=org_type, code='org', name='ТестОрг')
cls.user = Employee.objects.create(
username='dislo.cols',
email='dislo.cols@company.local',
first_name='Коля',
last_name='Колонки',
hire_date=date(2026, 1, 1),
user_location_type='office',
mattermost_sync_enabled=False,
ad_sync_enabled=False,
)
DislocationColumnPreset.objects.update_or_create(
code='standard',
defaults={
'name': 'Стандарт',
'columns': list(STANDARD_COLUMNS),
'is_default': True,
},
)
DislocationColumnPreset.objects.update_or_create(
code='hr',
defaults={'name': 'HR', 'columns': list(HR_COLUMNS), 'is_default': False},
)
DislocationColumnPreset.objects.update_or_create(
code='ops',
defaults={'name': 'Ops', 'columns': list(OPS_COLUMNS), 'is_default': False},
)
Pharmacy.objects.create(
organization=cls.org, location_region=cls.region,
pharmacy_number=101, location_type='pharmacy', street='Тестовая', house_number='1',
)
def test_normalize_whitelist_and_locked_pharmacy(self):
from apps.locations.dislocation_columns import normalize_columns
self.assertEqual(
normalize_columns(['email', 'pharmacy', 'unknown', 'email', 'region']),
['email', 'pharmacy', 'region'],
)
self.assertEqual(
normalize_columns(['organization', 'pharmacy', 'phone']),
['organization', 'pharmacy', 'phone'],
)
self.assertEqual(normalize_columns([]), ['pharmacy'])
self.assertEqual(normalize_columns(['secret', 'password']), ['pharmacy'])
# pharmacy обязательна, но если её нет в списке — добавляется первой
self.assertEqual(
normalize_columns(['region', 'phone']),
['pharmacy', 'region', 'phone'],
)
def test_resolve_uses_default_preset(self):
from apps.locations.dislocation_columns import STANDARD_COLUMNS, resolve_columns_for_user
self.user.dislocation_columns = None
self.user.save(update_fields=['dislocation_columns'])
self.assertEqual(resolve_columns_for_user(self.user), list(STANDARD_COLUMNS))
def test_resolve_uses_custom_preference(self):
from apps.locations.dislocation_columns import resolve_columns_for_user
self.user.dislocation_columns = ['region', 'phone', 'bogus']
self.user.save(update_fields=['dislocation_columns'])
self.assertEqual(
resolve_columns_for_user(self.user),
['pharmacy', 'region', 'phone'],
)
def test_post_saves_columns_preference(self):
self.client.force_login(self.user)
session = self.client.session
session['2fa_verified'] = True
session.save()
resp = self.client.post(
'/dislocation/columns/',
{'columns': ['organization', 'pharmacy', 'staff', 'region', 'email', 'evil']},
HTTP_HX_REQUEST='true',
)
self.assertEqual(resp.status_code, 200)
self.user.refresh_from_db()
self.assertEqual(
self.user.dislocation_columns,
['organization', 'pharmacy', 'staff', 'region', 'email'],
)
self.assertContains(resp, "disloSortBy('organization'")
self.assertContains(resp, "disloSortBy('region'")
self.assertContains(resp, "disloSortBy('email'")
self.assertNotContains(resp, "disloSortBy('phone'")
def test_post_preset_code_applies_hr(self):
from apps.locations.dislocation_columns import HR_COLUMNS
self.client.force_login(self.user)
session = self.client.session
session['2fa_verified'] = True
session.save()
resp = self.client.post(
'/dislocation/columns/',
{'preset_code': 'hr'},
HTTP_HX_REQUEST='true',
)
self.assertEqual(resp.status_code, 200)
self.user.refresh_from_db()
self.assertEqual(self.user.dislocation_columns, list(HR_COLUMNS))
self.assertContains(resp, "disloSortBy('curator'")
self.assertContains(resp, "disloSortBy('manager'")
self.assertNotContains(resp, "disloSortBy('phone'")