2026-04-02 17:48:27 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helpdesk module manifest.
|
|
|
|
|
*
|
|
|
|
|
* Internal employee lookup for BC debtors, contacts and tickets
|
|
|
|
|
* via OData V4. Read-only V1 with Basic Auth (OAuth2 optional).
|
|
|
|
|
*/
|
|
|
|
|
return [
|
|
|
|
|
'id' => 'helpdesk',
|
|
|
|
|
'version' => '1.0.0',
|
|
|
|
|
'enabled_by_default' => false,
|
|
|
|
|
'load_order' => 50,
|
|
|
|
|
'requires' => [],
|
|
|
|
|
|
|
|
|
|
'routes' => [
|
|
|
|
|
['path' => 'helpdesk/search-data', 'target' => 'helpdesk/search-data'],
|
2026-04-14 21:34:32 +02:00
|
|
|
['path' => 'helpdesk/domains', 'target' => 'helpdesk/domains'],
|
|
|
|
|
['path' => 'helpdesk/domains-data', 'target' => 'helpdesk/domains-data'],
|
2026-04-21 13:43:16 +02:00
|
|
|
['path' => 'helpdesk/domains/security-level-data', 'target' => 'helpdesk/domains/security-level-data'],
|
feat(core): generic CSV export primitive + helpdesk domains consumer
Add a reusable CSV-export building block to the core so any Grid.js list
page can gain a filter/sort-aware download with two clicks of glue.
Core primitive:
- core/Service/Export/CsvExportService: flavor-aware writer (plain CSV
or Excel-compatible UTF-8+BOM+semicolon), with OWASP-aligned formula-
injection escape (=, @, +, -, TAB, CR) applied to both rows *and*
headers. Value objects for columns (ExportColumn) carry an extractor
closure and an allowSignedNumeric flag for phone-number-shaped cells.
- core/Support/helpers/export.php: thin HTTP layer (exportSendCsv,
exportCapLimit, exportResolveFlavor, exportRequireGetRequest) reusing
the requestInput() contract for GR-CORE-003 consistency. Filename is
sanitized and length-clamped; callers must still enforce auth.
- templates/partials/app-list-export-dropdown.phtml: zero-config
<details class="dropdown"> with CSV + Excel triggers.
- web/js/core/app-list-export.js: initListExport({ gridConfig, exportUrl })
mirrors the current grid filters + sort onto the export URL and
navigates, preserving session cookies for download.
First consumer — Helpdesk domains:
- DomainListService extracts the shared filter/enrich/sort logic from
domains-data so the grid endpoint and the new domains/export endpoint
cannot drift.
- domains/export endpoint delegates to DomainListService, declares
ExportColumns (with translated level labels), and exits via
exportSendCsv.
- domains-data now ~20 lines, delegating to DomainListService.
Tests:
- CsvExportServiceTest (10 cases): both flavors, BOM/no-BOM, formula
escapes incl. TAB/CR, header escape, signed-numeric allowlist,
quoting, multiline, empty columns, generator-compatible iterable.
- ExportHelpersTest (5 cases): exportCapLimit bounds.
- DomainListServiceTest (8 cases): filter, search, "all" sentinel,
sort, paging, enrichment, BC failure, tenant-scoped security filter.
Gates: PHPUnit green, PHPStan clean on touched files, module:sync ok.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 21:57:19 +02:00
|
|
|
['path' => 'helpdesk/domains/export', 'target' => 'helpdesk/domains/export'],
|
2026-04-16 13:21:40 +02:00
|
|
|
['path' => 'helpdesk/domain/{id}', 'target' => 'helpdesk/domain'],
|
|
|
|
|
['path' => 'helpdesk/domain-detail-data', 'target' => 'helpdesk/domain-detail-data'],
|
2026-04-02 17:48:27 +02:00
|
|
|
['path' => 'helpdesk/debitor/{id}', 'target' => 'helpdesk/debitor'],
|
|
|
|
|
['path' => 'helpdesk/ticket/{id}', 'target' => 'helpdesk/ticket'],
|
|
|
|
|
['path' => 'helpdesk/settings', 'target' => 'helpdesk/settings'],
|
|
|
|
|
['path' => 'helpdesk/settings/test-connection-data', 'target' => 'helpdesk/settings/test-connection-data'],
|
|
|
|
|
['path' => 'helpdesk/settings/diagnose-data', 'target' => 'helpdesk/settings/diagnose-data'],
|
2026-04-04 18:34:03 +02:00
|
|
|
['path' => 'helpdesk/debitor-support-dashboard-data', 'target' => 'helpdesk/debitor-support-dashboard-data'],
|
|
|
|
|
['path' => 'helpdesk/debitor-overview-data', 'target' => 'helpdesk/debitor-overview-data'],
|
2026-04-02 21:05:54 +02:00
|
|
|
['path' => 'helpdesk/debitor-summary-data', 'target' => 'helpdesk/debitor-summary-data'],
|
2026-04-02 17:48:27 +02:00
|
|
|
['path' => 'helpdesk/debitor-contacts-data', 'target' => 'helpdesk/debitor-contacts-data'],
|
|
|
|
|
['path' => 'helpdesk/debitor-tickets-data', 'target' => 'helpdesk/debitor-tickets-data'],
|
|
|
|
|
['path' => 'helpdesk/debitor-ticket-categories-data', 'target' => 'helpdesk/debitor-ticket-categories-data'],
|
|
|
|
|
['path' => 'helpdesk/ticket-log-data', 'target' => 'helpdesk/ticket-log-data'],
|
2026-04-03 16:45:41 +02:00
|
|
|
['path' => 'helpdesk/ticket-communication-data', 'target' => 'helpdesk/ticket-communication-data'],
|
2026-04-05 22:49:01 +02:00
|
|
|
['path' => 'helpdesk/ticket-file-data', 'target' => 'helpdesk/ticket-file-data'],
|
2026-04-03 16:45:41 +02:00
|
|
|
['path' => 'helpdesk/debitor-communication-data', 'target' => 'helpdesk/debitor-communication-data'],
|
2026-04-04 18:34:03 +02:00
|
|
|
['path' => 'helpdesk/debitor-sales-dashboard-data', 'target' => 'helpdesk/debitor-sales-dashboard-data'],
|
2026-04-13 14:13:29 +02:00
|
|
|
['path' => 'helpdesk/debitor-meetings-data', 'target' => 'helpdesk/debitor-meetings-data'],
|
2026-04-04 18:34:03 +02:00
|
|
|
['path' => 'helpdesk/debitor-controlling-dashboard-data', 'target' => 'helpdesk/debitor-controlling-dashboard-data'],
|
2026-04-05 18:46:54 +02:00
|
|
|
['path' => 'helpdesk/team', 'target' => 'helpdesk/team'],
|
|
|
|
|
['path' => 'helpdesk/team-workload-data', 'target' => 'helpdesk/team-workload-data'],
|
2026-04-06 00:24:48 +02:00
|
|
|
['path' => 'helpdesk/risk-radar', 'target' => 'helpdesk/risk-radar'],
|
|
|
|
|
['path' => 'helpdesk/risk-radar-data', 'target' => 'helpdesk/risk-radar-data'],
|
2026-04-14 22:16:21 +02:00
|
|
|
['path' => 'helpdesk/software-products', 'target' => 'helpdesk/software-products'],
|
|
|
|
|
['path' => 'helpdesk/software-products-data', 'target' => 'helpdesk/software-products-data'],
|
|
|
|
|
['path' => 'helpdesk/software-products/edit/{code}', 'target' => 'helpdesk/software-products/edit'],
|
2026-04-15 20:42:41 +02:00
|
|
|
['path' => 'helpdesk/handovers', 'target' => 'helpdesk/handovers'],
|
|
|
|
|
['path' => 'helpdesk/handovers-data', 'target' => 'helpdesk/handovers-data'],
|
|
|
|
|
['path' => 'helpdesk/handovers/create', 'target' => 'helpdesk/handovers/create'],
|
|
|
|
|
['path' => 'helpdesk/handovers/edit/{id}', 'target' => 'helpdesk/handovers/edit'],
|
2026-04-16 13:21:40 +02:00
|
|
|
['path' => 'helpdesk/handovers/bulk/{action}', 'target' => 'helpdesk/handovers/bulk'],
|
2026-04-15 20:42:41 +02:00
|
|
|
['path' => 'helpdesk/debitor-lookup-data', 'target' => 'helpdesk/debitor-lookup-data'],
|
2026-04-16 13:21:40 +02:00
|
|
|
['path' => 'helpdesk/handover-domains-data', 'target' => 'helpdesk/handover-domains-data'],
|
|
|
|
|
['path' => 'helpdesk/updates', 'target' => 'helpdesk/updates'],
|
|
|
|
|
['path' => 'helpdesk/updates-data', 'target' => 'helpdesk/updates-data'],
|
|
|
|
|
['path' => 'helpdesk/updates/assign', 'target' => 'helpdesk/updates/assign'],
|
|
|
|
|
['path' => 'helpdesk/update-domains-data', 'target' => 'helpdesk/update-domains-data'],
|
2026-04-02 17:48:27 +02:00
|
|
|
],
|
|
|
|
|
'public_paths' => [],
|
|
|
|
|
|
|
|
|
|
'container_registrars' => [
|
|
|
|
|
\MintyPHP\Module\Helpdesk\HelpdeskContainerRegistrar::class,
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
'ui_slots' => [
|
|
|
|
|
'aside.tab_panel' => [
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk',
|
|
|
|
|
'label' => 'Helpdesk',
|
|
|
|
|
'icon' => 'bi-headset',
|
2026-04-06 22:22:00 +02:00
|
|
|
'icon_fill' => 'bi-headset',
|
2026-04-05 21:49:24 +02:00
|
|
|
'href' => '',
|
2026-04-02 17:48:27 +02:00
|
|
|
'permission' => 'helpdesk.access',
|
|
|
|
|
'panel_template' => 'templates/aside-helpdesk-panel.phtml',
|
|
|
|
|
'details_storage' => 'aside-helpdesk-sections-v1',
|
|
|
|
|
'details_open_active' => true,
|
|
|
|
|
'order' => 800,
|
|
|
|
|
],
|
|
|
|
|
],
|
2026-04-20 22:31:13 +02:00
|
|
|
'runtime.component' => [
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk-detail',
|
|
|
|
|
'script' => 'modules/helpdesk/js/helpdesk-detail.js',
|
|
|
|
|
'export' => 'initHelpdeskDetail',
|
|
|
|
|
'selector' => '[data-app-component="helpdesk-detail"]',
|
|
|
|
|
'config_path' => 'components.helpdesk.detail',
|
|
|
|
|
'default_config' => [],
|
|
|
|
|
'phase' => 'default',
|
|
|
|
|
'permission' => 'helpdesk.access',
|
|
|
|
|
'order' => 801,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk-domain-detail',
|
|
|
|
|
'script' => 'modules/helpdesk/js/helpdesk-domain-detail.js',
|
|
|
|
|
'export' => 'initHelpdeskDomainDetail',
|
|
|
|
|
'selector' => '[data-app-component="helpdesk-domain-detail"]',
|
|
|
|
|
'config_path' => 'components.helpdesk.domainDetail',
|
|
|
|
|
'default_config' => [],
|
|
|
|
|
'phase' => 'default',
|
|
|
|
|
'permission' => 'helpdesk.access',
|
|
|
|
|
'order' => 802,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk-ticket',
|
|
|
|
|
'script' => 'modules/helpdesk/js/helpdesk-ticket.js',
|
|
|
|
|
'export' => 'initHelpdeskTicket',
|
|
|
|
|
'selector' => '[data-app-component="helpdesk-ticket"]',
|
|
|
|
|
'config_path' => 'components.helpdesk.ticket',
|
|
|
|
|
'default_config' => [],
|
|
|
|
|
'phase' => 'default',
|
|
|
|
|
'permission' => 'helpdesk.access',
|
|
|
|
|
'order' => 803,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk-team',
|
|
|
|
|
'script' => 'modules/helpdesk/js/helpdesk-team.js',
|
|
|
|
|
'export' => 'initHelpdeskTeam',
|
|
|
|
|
'selector' => '[data-app-component="helpdesk-team"]',
|
|
|
|
|
'config_path' => 'components.helpdesk.team',
|
|
|
|
|
'default_config' => [],
|
|
|
|
|
'phase' => 'default',
|
|
|
|
|
'permission' => 'helpdesk.team-workload.view',
|
|
|
|
|
'order' => 804,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk-risk-radar',
|
|
|
|
|
'script' => 'modules/helpdesk/js/helpdesk-risk-radar.js',
|
|
|
|
|
'export' => 'initHelpdeskRiskRadar',
|
|
|
|
|
'selector' => '[data-app-component="helpdesk-risk-radar"]',
|
|
|
|
|
'config_path' => 'components.helpdesk.riskRadar',
|
|
|
|
|
'default_config' => [],
|
|
|
|
|
'phase' => 'default',
|
|
|
|
|
'permission' => 'helpdesk.risk-radar.view',
|
|
|
|
|
'order' => 805,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk-settings',
|
|
|
|
|
'script' => 'modules/helpdesk/js/helpdesk-settings.js',
|
|
|
|
|
'export' => 'initHelpdeskSettings',
|
|
|
|
|
'selector' => '[data-app-component="helpdesk-settings"]',
|
|
|
|
|
'config_path' => 'components.helpdesk.settings',
|
|
|
|
|
'default_config' => [],
|
|
|
|
|
'phase' => 'default',
|
|
|
|
|
'permission' => 'helpdesk.settings.manage',
|
|
|
|
|
'order' => 806,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk-handover-domain-select',
|
|
|
|
|
'script' => 'modules/helpdesk/js/handover-domain-select.js',
|
|
|
|
|
'export' => 'initHandoverDomainSelect',
|
|
|
|
|
'selector' => '#wizard-domain-group[data-app-component="helpdesk-handover-domain-select"]',
|
|
|
|
|
'config_path' => 'components.helpdesk.handoverDomainSelect',
|
|
|
|
|
'default_config' => [],
|
|
|
|
|
'phase' => 'default',
|
|
|
|
|
'permission' => 'helpdesk.handovers.create',
|
|
|
|
|
'order' => 807,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk-handover-schema-editor',
|
|
|
|
|
'script' => 'modules/helpdesk/js/handover-schema-editor.js',
|
|
|
|
|
'export' => 'initHandoverSchemaEditor',
|
|
|
|
|
'selector' => '#handover-schema-editor[data-app-component="helpdesk-handover-schema-editor"]',
|
|
|
|
|
'config_path' => 'components.helpdesk.handoverSchemaEditor',
|
|
|
|
|
'default_config' => [],
|
|
|
|
|
'phase' => 'default',
|
|
|
|
|
'permission' => 'helpdesk.software-products.manage',
|
|
|
|
|
'order' => 808,
|
|
|
|
|
],
|
|
|
|
|
],
|
2026-04-02 17:48:27 +02:00
|
|
|
],
|
|
|
|
|
|
|
|
|
|
'authorization_policies' => [
|
|
|
|
|
\MintyPHP\Module\Helpdesk\HelpdeskAuthorizationPolicy::class,
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
'permissions' => [
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk.access',
|
|
|
|
|
'description' => 'Access helpdesk debtor search and detail views',
|
|
|
|
|
'active' => true,
|
|
|
|
|
'is_system' => true,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk.settings.manage',
|
|
|
|
|
'description' => 'Manage helpdesk BC connection settings',
|
|
|
|
|
'active' => true,
|
|
|
|
|
'is_system' => true,
|
|
|
|
|
],
|
2026-04-05 18:46:54 +02:00
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk.team-workload.view',
|
|
|
|
|
'description' => 'View helpdesk team workload dashboard',
|
|
|
|
|
'active' => true,
|
|
|
|
|
'is_system' => true,
|
|
|
|
|
],
|
2026-04-06 00:24:48 +02:00
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk.risk-radar.view',
|
|
|
|
|
'description' => 'View helpdesk risk radar dashboard',
|
|
|
|
|
'active' => true,
|
|
|
|
|
'is_system' => true,
|
|
|
|
|
],
|
2026-04-14 22:16:21 +02:00
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk.software-products.manage',
|
|
|
|
|
'description' => 'Manage helpdesk software products (view and edit product names)',
|
|
|
|
|
'active' => true,
|
|
|
|
|
'is_system' => true,
|
|
|
|
|
],
|
2026-04-15 20:42:41 +02:00
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk.handovers.view',
|
|
|
|
|
'description' => 'View helpdesk handover protocols',
|
|
|
|
|
'active' => true,
|
|
|
|
|
'is_system' => true,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk.handovers.create',
|
|
|
|
|
'description' => 'Create and edit own helpdesk handover protocols',
|
|
|
|
|
'active' => true,
|
|
|
|
|
'is_system' => true,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk.handovers.manage',
|
|
|
|
|
'description' => 'Manage all helpdesk handover protocols (edit any, archive)',
|
|
|
|
|
'active' => true,
|
|
|
|
|
'is_system' => true,
|
|
|
|
|
],
|
2026-04-16 13:21:40 +02:00
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk.updates.view',
|
|
|
|
|
'description' => 'View software updates list',
|
|
|
|
|
'active' => true,
|
|
|
|
|
'is_system' => true,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'key' => 'helpdesk.updates.manage',
|
|
|
|
|
'description' => 'Assign and manage software updates',
|
|
|
|
|
'active' => true,
|
|
|
|
|
'is_system' => true,
|
|
|
|
|
],
|
2026-04-02 17:48:27 +02:00
|
|
|
],
|
|
|
|
|
|
|
|
|
|
'search_resources' => [],
|
|
|
|
|
'asset_groups' => [
|
|
|
|
|
'helpdesk' => [
|
|
|
|
|
'modules/helpdesk/css/helpdesk.css',
|
|
|
|
|
],
|
|
|
|
|
],
|
2026-04-14 22:16:21 +02:00
|
|
|
'scheduler_jobs' => [
|
|
|
|
|
[
|
|
|
|
|
'job_key' => 'helpdesk_software_product_sync',
|
|
|
|
|
'handler' => \MintyPHP\Module\Helpdesk\Handler\SoftwareProductSyncJobHandler::class,
|
|
|
|
|
'label' => 'Software product sync',
|
|
|
|
|
'description' => 'Syncs contract types with Create_SaaS_License from Business Central to local database',
|
|
|
|
|
'default_enabled' => 1,
|
|
|
|
|
'default_timezone' => 'Europe/Berlin',
|
|
|
|
|
'default_schedule_type' => 'daily',
|
|
|
|
|
'default_schedule_interval' => 1,
|
|
|
|
|
'default_schedule_time' => '02:00',
|
|
|
|
|
'default_schedule_weekdays_csv' => null,
|
|
|
|
|
'default_catchup_once' => 1,
|
|
|
|
|
'allowed_schedule_types' => ['daily', 'weekly'],
|
|
|
|
|
],
|
|
|
|
|
],
|
2026-04-02 17:48:27 +02:00
|
|
|
'layout_context_providers' => [
|
|
|
|
|
\MintyPHP\Module\Helpdesk\Providers\HelpdeskLayoutProvider::class,
|
|
|
|
|
],
|
|
|
|
|
'session_providers' => [],
|
|
|
|
|
'event_listeners' => [],
|
|
|
|
|
'deactivation_handler' => null,
|
|
|
|
|
'migrations_path' => 'migrations',
|
|
|
|
|
'i18n_path' => 'i18n',
|
|
|
|
|
];
|