feat(helpdesk): add Software Products page with BC contract type sync

Add a new Software-Produkte feature to the helpdesk module that syncs
contract types (Create_SaaS_License=true) from BC OData into a local
database table with a nightly scheduler job, providing a Grid.js list
page and detail/edit page for managing custom product names.

- DB migration 003: helpdesk_software_products table (code UNIQUE key)
- BcODataGateway: FS_Contract_Types entity with SaaS filter + fallback
- SoftwareProductRepository: upsert, listPaged, softDelete, updateName
- SoftwareProductSyncService: fetch → upsert → soft-delete lifecycle
- SoftwareProductSyncJobHandler: daily at 02:00 via scheduler platform
- SoftwareProductService: web UI business logic with validation
- Permission: helpdesk.software-products.manage (nav-gated)
- List page: Grid.js with Code, BC Description, Name, Status columns
- Detail page: Code/BC Description read-only, Name editable, PRG pattern
- i18n: de + en translation keys

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-14 22:16:21 +02:00
parent d472026df4
commit ef4473de64
23 changed files with 1209 additions and 5 deletions

View File

@@ -0,0 +1,35 @@
<?php
namespace MintyPHP\Module\Helpdesk\Handler;
use MintyPHP\Module\Helpdesk\Service\SoftwareProductSyncService;
use MintyPHP\Service\Scheduler\Handler\ScheduledJobHandlerInterface;
class SoftwareProductSyncJobHandler implements ScheduledJobHandlerInterface
{
public function __construct(
private readonly SoftwareProductSyncService $syncService
) {
}
public function definition(): array
{
return [
'label' => 'Software product sync',
'description' => 'Syncs contract types with Create_SaaS_License from Business Central to local database',
'default_enabled' => 1,
'default_timezone' => defined('APP_TIMEZONE') ? (string) APP_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'],
];
}
public function execute(?int $actorUserId): array
{
return $this->syncService->sync();
}
}