50825ce505
Co-authored-by: Cursor <cursoragent@cursor.com>
90 lines
4.6 KiB
Python
90 lines
4.6 KiB
Python
"""Реестр разделов портала — единый источник для меню и проверки на view."""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class PortalSection:
|
|
key: str
|
|
path: str
|
|
label: str
|
|
nav: str # main | manage | support | it | none
|
|
perm: str | None = None # 'core.access_<key>' or None = any authenticated
|
|
|
|
|
|
# Ограниченные разделы — нужны Django permissions core.access_*
|
|
RESTRICTED_KEYS = (
|
|
'hr_portal',
|
|
'recruiting',
|
|
'structure',
|
|
'timetrack',
|
|
'databases',
|
|
'reports',
|
|
'purchases',
|
|
'email',
|
|
'pharmacies',
|
|
'rro',
|
|
'utilities',
|
|
'internet_billing',
|
|
'it_manager',
|
|
'ad_mapping',
|
|
)
|
|
|
|
# UI-заглушки (мок-страницы): в меню и по URL только для staff (доступ к /admin/).
|
|
STUB_SECTION_KEYS = frozenset({
|
|
'purchases',
|
|
})
|
|
|
|
PORTAL_SECTIONS: dict[str, PortalSection] = {
|
|
'dashboard': PortalSection('dashboard', '/dashboard/', 'Главная', 'main'),
|
|
'news': PortalSection('news', '/news/', 'Новости', 'main'),
|
|
'employees': PortalSection('employees', '/employees/', 'Сотрудники', 'main'),
|
|
'tasks': PortalSection('tasks', '/tasks/', 'Задачи', 'main'),
|
|
'dislocation': PortalSection('dislocation', '/dislocation/', 'Дислокация', 'main'),
|
|
'phonebook': PortalSection('phonebook', '/telephony/', 'Телефонная книга', 'none'),
|
|
'documents': PortalSection('documents', '/documents/', 'Документы', 'main'),
|
|
'email': PortalSection('email', '/email/', 'Электронная почта', 'main', 'core.access_email'),
|
|
'chat': PortalSection('chat', '/chat/', 'Чат', 'main'),
|
|
'calendar': PortalSection('calendar', '/calendar/', 'Календарь', 'main'),
|
|
'reports': PortalSection('reports', '/reports/', 'Отчёты', 'main', 'core.access_reports'),
|
|
'purchases': PortalSection('purchases', '/purchases/', 'Закупки', 'main', 'core.access_purchases'),
|
|
# Открыт всем; кадровые операции — по core.access_hr_portal (user_can_manage_hr_portal).
|
|
'hr_portal': PortalSection('hr_portal', '/hr-portal/', 'HR-портал', 'main'),
|
|
'accounting_portal': PortalSection('accounting_portal', '/accounting/', 'Операционный учёт', 'main'),
|
|
'profile': PortalSection('profile', '/profile/', 'Профиль', 'none'),
|
|
'structure': PortalSection('structure', '/structure/', 'Структура', 'manage', 'core.access_structure'),
|
|
'pharmacies': PortalSection('pharmacies', '/pharmacies/', 'Аптеки', 'manage', 'core.access_pharmacies'),
|
|
'pharmacy_detail': PortalSection('pharmacy_detail', '/pharmacies/', 'Карточка аптеки', 'none'),
|
|
'databases': PortalSection('databases', '/databases/', 'Системные справочники', 'manage', 'core.access_databases'),
|
|
'user_groups': PortalSection('user_groups', '/user-groups/', 'Группы пользователей', 'manage'),
|
|
'settings': PortalSection('settings', '/settings/', 'Настройки', 'manage'),
|
|
'rro': PortalSection('rro', '/rro/', 'РРО / ПРРО', 'none', 'core.access_rro'),
|
|
'utilities': PortalSection('utilities', '/utilities/', 'Коммунальные услуги', 'none', 'core.access_utilities'),
|
|
'internet_billing': PortalSection(
|
|
'internet_billing', '/accounting/?tab=internet', 'Оплата провайдеров', 'none',
|
|
'core.access_internet_billing',
|
|
),
|
|
'recruiting': PortalSection('recruiting', '/hr-portal/?tab=vacancies', 'Рекрутинг', 'none', 'core.access_recruiting'),
|
|
'timetrack': PortalSection('timetrack', '/timetrack/', 'Учёт рабочего времени', 'none', 'core.access_timetrack'),
|
|
'help': PortalSection('help', '/help/', 'Справка', 'support'),
|
|
'it_support': PortalSection('it_support', '/it-support/', 'Поддержка IT', 'support'),
|
|
'it_manager': PortalSection('it_manager', '/it-manager/', 'IT-отдел', 'it', 'core.access_it_manager'),
|
|
'ad_mapping': PortalSection(
|
|
'ad_mapping', '/acore-a-panel/ad_connector/adsynclog/match/', 'Active Directory', 'none',
|
|
'core.access_ad_mapping',
|
|
),
|
|
}
|
|
|
|
|
|
def get_section(key: str) -> PortalSection | None:
|
|
return PORTAL_SECTIONS.get(key)
|
|
|
|
|
|
def portal_permission_codenames() -> list[tuple[str, str]]:
|
|
"""Список (codename, name) для Meta.permissions модели PortalAccess."""
|
|
return [
|
|
(f'access_{key}', f'Доступ к разделу: {PORTAL_SECTIONS[key].label}')
|
|
for key in RESTRICTED_KEYS
|
|
]
|