7.8 KiB
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):
<div class="app-details-container">
<section>
<?php
$titlebar = ['title' => t('Page title')];
require templatePath('partials/app-details-titlebar.phtml');
?>
<!-- content -->
</section>
</div>
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:
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 + cacheDebitorSearchService— debtor search by name/numberDebitorDetailService— debtor master data + contacts + ticketsDomainDetailService— domain/contract data + handover/update enrichmentDomainListService— domain list with security level enrichmentHandoverService— handover protocol CRUD + status transitionsHandoverRevisionService— revision snapshots for audit trailUpdateService— software update tracking + assignmentSoftwareProductService— product catalog + handover schemaSoftwareProductSyncService— 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, transactionsUpdateRepository— insert, findByTicketNo, findByDomainNo, findAllByTenant, updateAssignmentSoftwareProductRepository— upsertByCode, listPaged, updateHandoverSchema, softDeleteNotInCodesHandoverRevisionRepository— insert, getLatestRevisionNumber, listByHandoverDomainSecurityLevelRepository— findByTenantAndDomainNo, listLevelsByDomainNos, upsertHelpdeskTenantSettingsRepository— findByTenantId, upsert, deleteByTenantIdHelpdeskTokenRepository— 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:
- Overview (
bi-speedometer2) — Dashboard - Lookup (
bi-search) — Customers, Domains - Monitoring (
bi-bar-chart-line) — Team workload, Risk radar - Operations (
bi-clipboard-check) — Handovers, Updates - 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="<name>" on the container div:
helpdesk-detail— customer search detailhelpdesk-domain-detail— domain detailhelpdesk-ticket— ticket viewhelpdesk-team— team workloadhelpdesk-risk-radar— risk radarhelpdesk-settings— settings confighelpdesk-communication— ticket communication feedhelpdesk-handover-domain-select— domain picker in handover wizardhelpdesk-handover-schema-editor— handover schema JSON editor
CSS class prefix: helpdesk- for module-specific styles in web/css/helpdesk.css.