"""Visibility scoping for HQ executives. - director / director_admin: all sites / city sites / GMs - buh / bu_admin: only GMs (and gm_admins) they manage, plus those GMs' sites - gm / gm_admin: only their own sites / city sites they registered Farm Site rows are only visible to non-directors when linked to an active CitySite in the viewer's Pengaturan registry. """ from __future__ import annotations from django.db.models import QuerySet from apps.accounts.models import User DIRECTOR_STATUSES = frozenset( { User.STATUS_DIRECTOR, User.STATUS_DIRECTOR_ADMIN, User.STATUS_SUPERUSER, } ) BUH_STATUSES = frozenset( { User.STATUS_BUH, User.STATUS_BU_ADMIN, } ) GM_STATUSES = frozenset( { User.STATUS_GM, User.STATUS_GM_ADMIN, } ) def is_director_scope(user: User) -> bool: return bool(user and user.status in DIRECTOR_STATUSES) def is_buh_scope(user: User) -> bool: return bool(user and user.status in BUH_STATUSES) def is_gm_scope(user: User) -> bool: return bool(user and user.status in GM_STATUSES) def managed_gm_ids(buh: User) -> list[int]: """User pks for GMs / GM Admins reporting to this BUH.""" return list( User.objects.filter( managed_by=buh, status__in=GM_STATUSES, ).values_list("pk", flat=True) ) def visible_owner_ids(user: User) -> list[int] | None: """ Site.user / CitySite.registered_by ids this viewer may see. Returns None when there is no restriction (director / superuser). """ if is_director_scope(user): return None if is_buh_scope(user): return managed_gm_ids(user) if is_gm_scope(user): return [user.pk] return [] def visible_active_city_site_ids(user: User) -> list[int] | None: """ Active CitySite pks in this viewer's Pengaturan registry. Returns None for directors (no restriction). Empty list means no farm sites. """ from apps.sync.models import CitySite owners = visible_owner_ids(user) if owners is None: return None if not owners: return [] return list( CitySite.objects.filter( registered_by_id__in=owners, is_active=True, ).values_list("pk", flat=True) ) def is_account_admin_scope(user: User) -> bool: return bool(user and user.status == User.STATUS_ACCOUNT_ADMIN) def filter_users_queryset(qs: QuerySet, viewer: User) -> QuerySet: """BUH sees managed GMs; director / account_admin see product users. Break-glass status=superuser is never listed in the user directory. """ qs = qs.exclude(status=User.STATUS_SUPERUSER) if is_director_scope(viewer) or is_account_admin_scope(viewer): return qs if is_buh_scope(viewer): return qs.filter(pk__in=managed_gm_ids(viewer)) if is_gm_scope(viewer): return qs.filter(pk=viewer.pk) return qs.none() def filter_sites_queryset(qs: QuerySet, viewer: User) -> QuerySet: owners = visible_owner_ids(viewer) if owners is None: return qs if not owners: return qs.none() city_ids = visible_active_city_site_ids(viewer) if not city_ids: return qs.none() return qs.filter(user_id__in=owners, city_site_id__in=city_ids) def filter_city_sites_queryset(qs: QuerySet, viewer: User) -> QuerySet: owners = visible_owner_ids(viewer) if owners is None: return qs if not owners: return qs.none() return qs.filter(registered_by_id__in=owners) def filter_kandangs_queryset(qs: QuerySet, viewer: User) -> QuerySet: owners = visible_owner_ids(viewer) if owners is None: return qs if not owners: return qs.none() city_ids = visible_active_city_site_ids(viewer) if not city_ids: return qs.none() return qs.filter(site__user_id__in=owners, site__city_site_id__in=city_ids) def filter_flocks_queryset(qs: QuerySet, viewer: User) -> QuerySet: owners = visible_owner_ids(viewer) if owners is None: return qs if not owners: return qs.none() city_ids = visible_active_city_site_ids(viewer) if not city_ids: return qs.none() return qs.filter( kandang__site__user_id__in=owners, kandang__site__city_site_id__in=city_ids, ) def filter_cycles_queryset(qs: QuerySet, viewer: User) -> QuerySet: owners = visible_owner_ids(viewer) if owners is None: return qs if not owners: return qs.none() city_ids = visible_active_city_site_ids(viewer) if not city_ids: return qs.none() return qs.filter( kandang__site__user_id__in=owners, kandang__site__city_site_id__in=city_ids, )