# CLAUDE.md — Helpdesk Module Self-contained MintyPHP module. Namespace: `MintyPHP\Module\Helpdesk\*`. All changes stay inside `modules/helpdesk/` — never touch core templates or global layout. After any manifest change (new route, permission, slot): `docker compose exec php php bin/console module:sync` --- ## Architecture ``` lib/Module/Helpdesk/ HelpdeskAuthorizationPolicy.php — all ability/permission constants + authorize() HelpdeskContainerRegistrar.php — all DI bindings (add new services here) Handler/ — scheduled job handlers Providers/HelpdeskLayoutProvider.php — resolves can_* flags for sidebar nav Repository/ — SQL only, no HTTP Service/ — business logic, no HTML pages/helpdesk/ — actions (*.php) + views (*.phtml) templates/ aside-helpdesk-panel.phtml — sidebar navigation helpdesk-ticket-detail.phtml — shared ticket detail body i18n/default_de.json + default_en.json — 551 keys each, always add both migrations/ — 011 SQL files applied via module:migrate tests/Module/Helpdesk/Service/ — 20 test files, happy path + edge case required web/css/helpdesk.css web/js/ — 16 JS files, components + page scripts ``` --- ## Page File Naming | URL | Action file | View file | |-----|-------------|-----------| | `helpdesk/team` | `pages/helpdesk/team/index().php` | `team/index(default).phtml` | | `helpdesk/handovers/edit/{id}` | `pages/helpdesk/handovers/edit($id).php` | `handovers/edit(default).phtml` | | `helpdesk/dashboard` | `pages/helpdesk/dashboard/index().php` | `dashboard/index(default).phtml` | | data endpoint | `pages/helpdesk/foo-data().php` | — | | drawer fragment | `pages/helpdesk/foo-fragment($id).php` | `foo-fragment(none).phtml` | **View structure pattern** (always): ```php
t('Page title')]; require templatePath('partials/app-details-titlebar.phtml'); ?>
``` --- ## Permissions (10 total) All constants in `HelpdeskAuthorizationPolicy`: | Constant | Permission key | |----------|---------------| | `ABILITY_ACCESS` | `helpdesk.access` — base access, customer search + detail views | | `ABILITY_SETTINGS_MANAGE` | `helpdesk.settings.manage` | | `ABILITY_TEAM_WORKLOAD` | `helpdesk.team-workload.view` | | `ABILITY_RISK_RADAR` | `helpdesk.risk-radar.view` | | `ABILITY_SOFTWARE_PRODUCTS_MANAGE` | `helpdesk.software-products.manage` | | `ABILITY_HANDOVERS_VIEW` | `helpdesk.handovers.view` | | `ABILITY_HANDOVERS_CREATE` | `helpdesk.handovers.create` | | `ABILITY_HANDOVERS_MANAGE` | `helpdesk.handovers.manage` | | `ABILITY_UPDATES_VIEW` | `helpdesk.updates.view` | | `ABILITY_UPDATES_MANAGE` | `helpdesk.updates.manage` | New pages reuse existing permissions — no new permission keys without product decision. **Action file boilerplate:** ```php Guard::requireLogin(); Guard::requireAbilityOrForbidden(HelpdeskAuthorizationPolicy::ABILITY_ACCESS); $session = app(SessionStoreInterface::class)->all(); $tenantId = (int) ($session['current_tenant']['id'] ?? 0); ``` --- ## Services & Repositories **Gateways (external BC API):** - `BcODataGateway` — OData V4 queries (customers, domains, tickets, contacts) - `BcSoapGateway` — SOAP (ticket communication feed, file downloads) **Core services:** - `EffectiveHelpdeskSettingsService` — resolves BC config per tenant (global + override) - `HelpdeskOAuthTokenService` — OAuth2 token acquisition + cache - `DebitorSearchService` — debtor search by name/number - `DebitorDetailService` — debtor master data + contacts + tickets - `DomainDetailService` — domain/contract data + handover/update enrichment - `DomainListService` — domain list with security level enrichment - `HandoverService` — handover protocol CRUD + status transitions - `HandoverRevisionService` — revision snapshots for audit trail - `UpdateService` — software update tracking + assignment - `SoftwareProductService` — product catalog + handover schema - `SoftwareProductSyncService` — BC sync (scheduled daily 02:00 CET) - `RiskRadarService` — risk scoring engine (5 dimensions, 0–100 score) - `DomainSecurityLevelService` — security level get/set (niedrig/normal/hoch/kritisch) - `SystemRecommendationEngine` — rule-based recommendation engine (no constructor) **Repositories:** - `HandoverRepository` — insert, findById, listPaged, updateFieldValues, updateStatus, deleteByIds, findByDomainNo, transactions - `UpdateRepository` — insert, findByTicketNo, findByDomainNo, findAllByTenant, updateAssignment - `SoftwareProductRepository` — upsertByCode, listPaged, updateHandoverSchema, softDeleteNotInCodes - `HandoverRevisionRepository` — insert, getLatestRevisionNumber, listByHandover - `DomainSecurityLevelRepository` — findByTenantAndDomainNo, listLevelsByDomainNos, upsert - `HelpdeskTenantSettingsRepository` — findByTenantId, upsert, deleteByTenantId - `HelpdeskTokenRepository` — findValidToken, storeToken, deleteByTenantId (encrypted) New service → register in `HelpdeskContainerRegistrar::register()` + add `@api` annotation if called from pages. --- ## Sidebar Navigation **`HelpdeskLayoutProvider`** resolves these flags → passed as `$layoutNav['helpdesk.nav']`: `can_view_dashboard`, `can_manage_settings`, `can_view_team`, `can_view_risk_radar`, `can_manage_software_products`, `can_view_handovers`, `can_create_handovers`, `can_manage_handovers`, `can_view_updates` **`aside-helpdesk-panel.phtml`** — 5 collapsible groups: 1. **Overview** (`bi-speedometer2`) — Dashboard 2. **Lookup** (`bi-search`) — Customers, Domains 3. **Monitoring** (`bi-bar-chart-line`) — Team workload, Risk radar 4. **Operations** (`bi-clipboard-check`) — Handovers, Updates 5. **Administration** (`bi-sliders`) — Software products, Settings Adding a nav item = 3 steps: (1) add `can_*` flag to `HelpdeskLayoutProvider`, (2) add `$dashboardActive = navActive(...)` + item to group in `aside-helpdesk-panel.phtml`, (3) add condition to `$customersActive` override if needed. --- ## DB Schema (relevant tables) | Table | Key columns | |-------|-------------| | `helpdesk_handovers` | id, tenant_id, status (draft/in_progress/completed/archived), debitor_no, domain_no | | `helpdesk_handover_revisions` | handover_id, revision_number, snapshot_json | | `helpdesk_updates` | id, tenant_id, status (open/assigned/done/resolved/closed), ticket_no, domain_no | | `helpdesk_software_products` | code, name, handover_schema_json | | `helpdesk_domain_security_levels` | tenant_id, domain_no, level, note | | `helpdesk_tenant_settings` | tenant_id, encrypted BC connection overrides | | `helpdesk_oauth_token_cache` | tenant_id, token (encrypted), expires_at | Tenant scope enforced on every query — always pass `$tenantId` to repository methods. --- ## Tests Location: `tests/Module/Helpdesk/Service/` Pattern: `->method('foo')->willReturn([...])` — no `->with()` (PHPUnit 12 incompatible with PHPStan). Every new/changed Service or Gateway needs happy path + at least one edge case test. Run: `docker compose exec php vendor/bin/phpunit modules/helpdesk/tests/` --- ## JS Components Registered via `data-app-component=""` on the container div: - `helpdesk-detail` — customer search detail - `helpdesk-domain-detail` — domain detail - `helpdesk-ticket` — ticket view - `helpdesk-team` — team workload - `helpdesk-risk-radar` — risk radar - `helpdesk-settings` — settings config - `helpdesk-communication` — ticket communication feed - `helpdesk-handover-domain-select` — domain picker in handover wizard - `helpdesk-handover-schema-editor` — handover schema JSON editor CSS class prefix: `helpdesk-` for module-specific styles in `web/css/helpdesk.css`.