feat(helpdesk): add software updates tracking with BC ticket integration

New Updates page in the Helpdesk module that fetches UPDATE/UPDATE-HF tickets
from Business Central (PBI_LV_Tickets) and allows assigning a domain and Gitea
link via a dialog. Ticket status (from BC) and assignment status (local) are
shown as separate columns with filters for both plus type and free-text search.
Assigned updates also appear on the domain detail page. Includes session-cached
BC fetch with refresh button, admin permissions, migration, and 16 unit tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-16 13:21:40 +02:00
parent 693d33a0c8
commit da0abc824a
23 changed files with 1971 additions and 6 deletions

View File

@@ -16,6 +16,10 @@ final class HelpdeskAuthorizationPolicy implements AuthorizationPolicyInterface
public const ABILITY_HANDOVERS_VIEW = 'helpdesk.handovers.view';
public const ABILITY_HANDOVERS_CREATE = 'helpdesk.handovers.create';
public const ABILITY_HANDOVERS_MANAGE = 'helpdesk.handovers.manage';
/** @api Used in pages/helpdesk/updates action files */
public const ABILITY_UPDATES_VIEW = 'helpdesk.updates.view';
/** @api Used in pages/helpdesk/updates action files */
public const ABILITY_UPDATES_MANAGE = 'helpdesk.updates.manage';
public const PERMISSION_ACCESS = 'helpdesk.access';
public const PERMISSION_SETTINGS_MANAGE = 'helpdesk.settings.manage';
@@ -29,6 +33,10 @@ final class HelpdeskAuthorizationPolicy implements AuthorizationPolicyInterface
public const PERMISSION_HANDOVERS_CREATE = 'helpdesk.handovers.create';
/** @api Used in authorize() match for ability resolution */
public const PERMISSION_HANDOVERS_MANAGE = 'helpdesk.handovers.manage';
/** @api Used in authorize() match */
public const PERMISSION_UPDATES_VIEW = 'helpdesk.updates.view';
/** @api Used in authorize() match */
public const PERMISSION_UPDATES_MANAGE = 'helpdesk.updates.manage';
public function __construct(
private readonly PermissionService $permissionService
@@ -37,7 +45,7 @@ final class HelpdeskAuthorizationPolicy implements AuthorizationPolicyInterface
public function supports(string $ability): bool
{
return in_array($ability, [self::ABILITY_ACCESS, self::ABILITY_SETTINGS_MANAGE, self::ABILITY_TEAM_WORKLOAD, self::ABILITY_RISK_RADAR, self::ABILITY_SOFTWARE_PRODUCTS_MANAGE, self::ABILITY_HANDOVERS_VIEW, self::ABILITY_HANDOVERS_CREATE, self::ABILITY_HANDOVERS_MANAGE], true);
return in_array($ability, [self::ABILITY_ACCESS, self::ABILITY_SETTINGS_MANAGE, self::ABILITY_TEAM_WORKLOAD, self::ABILITY_RISK_RADAR, self::ABILITY_SOFTWARE_PRODUCTS_MANAGE, self::ABILITY_HANDOVERS_VIEW, self::ABILITY_HANDOVERS_CREATE, self::ABILITY_HANDOVERS_MANAGE, self::ABILITY_UPDATES_VIEW, self::ABILITY_UPDATES_MANAGE], true);
}
public function authorize(string $ability, array $context = []): AuthorizationDecision
@@ -56,6 +64,8 @@ final class HelpdeskAuthorizationPolicy implements AuthorizationPolicyInterface
self::ABILITY_HANDOVERS_VIEW => self::PERMISSION_HANDOVERS_VIEW,
self::ABILITY_HANDOVERS_CREATE => self::PERMISSION_HANDOVERS_CREATE,
self::ABILITY_HANDOVERS_MANAGE => self::PERMISSION_HANDOVERS_MANAGE,
self::ABILITY_UPDATES_VIEW => self::PERMISSION_UPDATES_VIEW,
self::ABILITY_UPDATES_MANAGE => self::PERMISSION_UPDATES_MANAGE,
default => null,
};