""" Bedrijfslogica rond modules: status uitlezen en veilig aan/uit zetten, met respect voor onderlinge afhankelijkheden. """ from __future__ import annotations from .models import ModuleState from .registry import ModuleSpec, registry class ModuleError(Exception): """Een aan/uit-actie is niet toegestaan.""" def _state_map() -> dict[str, bool]: return {s.key: s.enabled for s in ModuleState.objects.all()} def is_enabled(key: str) -> bool: spec = registry.get(key) if spec is None: return False if spec.core: return True state = ModuleState.objects.filter(key=key).first() return bool(state and state.enabled) def sync_states() -> None: """Zorg dat elke geregistreerde module een rij in de database heeft.""" existing = set(ModuleState.objects.values_list("key", flat=True)) for spec in registry.all(): if spec.core: continue if spec.key not in existing: ModuleState.objects.create(key=spec.key, enabled=spec.default_enabled) def describe_all() -> list[dict]: """Alle modules met metadata + actuele status, voor de API/het beheerscherm.""" states = _state_map() result: list[dict] = [] for spec in registry.all(): enabled = True if spec.core else states.get(spec.key, spec.default_enabled) result.append( { "key": spec.key, "name": spec.name, "description": spec.description, "version": spec.version, "category": spec.category, "core": spec.core, "enabled": enabled, "depends_on": list(spec.depends_on), "menu_items": [ { "label": m.label, "path": m.path, "icon": m.icon, "order": m.order, } for m in spec.menu_items ] if enabled else [], } ) return result def set_enabled(key: str, enabled: bool) -> ModuleSpec: """Zet een module aan of uit, met controle op afhankelijkheden.""" spec = registry.get(key) if spec is None: raise ModuleError(f"Onbekende module: {key}") if spec.core: raise ModuleError(f"Kernmodule '{key}' kan niet worden uitgeschakeld.") if enabled: # Vereiste modules moeten ook aan staan. for dep in spec.depends_on: if not is_enabled(dep): dep_spec = registry.get(dep) dep_name = dep_spec.name if dep_spec else dep raise ModuleError( f"Module '{spec.name}' vereist eerst '{dep_name}'." ) else: # Modules die hiervan afhangen mogen niet meer aan staan. for dependent in registry.dependents_of(key): if is_enabled(dependent.key): raise ModuleError( f"'{dependent.name}' heeft '{spec.name}' nodig; " f"schakel die eerst uit." ) state, _ = ModuleState.objects.get_or_create(key=key) state.enabled = enabled state.save(update_fields=["enabled", "updated_at"]) return spec