Auto-generate field keys and option values from labels via slugify,
add live preview panel with hover-highlight, insert-between-fields
dividers, and various spacing/styling improvements.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a JSON-based schema editor as a new tab on the software product edit
page. Admins can define protocol fields (heading, paragraph, text,
textarea, number, date, checkbox, select) via a structured UI with
add/remove/reorder controls. The schema is stored as a versioned JSON
column on the product row.
Includes DB migration, server-side validation (type whitelist, key
uniqueness, key format, max 50 fields, select option checks), i18n
(DE+EN), CSS, and 10 PHPUnit tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Scheduler jobs run without an active session, causing SessionStore::all()
to fail. Wrap the call in try-catch to fall back to global config (tenant
ID 0) when no session is available.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code and BC description are already shown in the aside — no need to
duplicate them as readonly inputs in the form.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use badge-list wrapper, <p><small> pattern, <hr> separators, and dt()
for date formatting — matching the departments edit page convention.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use fully qualified \MintyPHP\Session in _form.phtml and remove
duplicate CSRF token call from edit template (already in form partial).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Jobs that have never run have last_run_status=NULL which caused a crash
when calling ->value and ->badgeVariant() on null. Use explicit null
checks instead of direct property access.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The new permission was missing from the admin role seed in init.sql and
needed an idempotent update script for existing installations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Add a new "Domains" page to the helpdesk module that fetches domain data
from the FS_Contract_Domains OData endpoint and enriches each domain with
its contract type (PI_Header_Type) by joining with FS_Contract_Lines_Test
where Type = 'Domain'.
New files:
- domains/index().php, index(default).phtml, filter-schema.php — list page
- domains-data().php — data endpoint with PHP-side filtering/sorting
- helpdesk-domains-index.js — Grid.js via initStandardListPage()
Gateway additions:
- listDomains() — fetch all contract domains
- listDomainContractLines() — fetch domain-type contract lines for type lookup
Sidebar restructured into three collapsible groups (Lookup, Monitoring,
Administration) with per-group icons and color coding, matching the core
admin panel pattern.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Upgrade phpstan/phpstan 1.12 → 2.1
- Add tomasvotruba/unused-public 2.2 for automatic dead code detection
- Generate baseline (486 existing findings) to keep CI green
- Remove stale @phpstan-ignore annotation in login page (ternary.alwaysFalse)
- New code is fully checked against PHPStan 2 + unused-public rules
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dead methods (9):
- Command::bootstrapModuleApp() — unused by all subclasses
- SearchConfig::providerCoverageGaps() — diagnostic method, never called
- RequestInput::queryArray() — no callers
- LocaleResolver::getAvailableLocales() / getDefaultLocale() — unused getters
- EmailVerificationService::getExpiryMinutes() — constant wrapper, never called
- AuthService::loginAndRedirect() — convenience wrapper, never called
- DepartmentService::listForUserAssignments() — no callers
- UserTenantContextService::getAvailableDepartmentsByTenant() — no callers
Dead functions (3):
- navActivePublicPages(), appNormalizeStringList(), appNormalizePositiveIntList()
Dead constant (1):
- PermissionService::API_DOCS_VIEW — duplicated via other paths
Orphaned file (1):
- bin/cli-bootstrap.php — never included anywhere
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CliAppBootstrap and ModuleCliRuntime still required from the old
lib/ path, which would break all CLI commands. Also fixes stale
lib/ references in docs, test assertion messages, and a PHPDoc
comment.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
reviewer-code.md was missing 4 of 17 assigned guards (LAYERS, META,
MODULE, TAXONOMY); reviewer-security.md was missing GR-SEC-010. README
did not list the Scheduled Jobs feature.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Audited all 1441 keys against 12 translation pipelines (direct t() calls,
flash messages, enum labelToken(), filter schemas, module manifests, theme
configs, etc.). Removed 180 keys unused by any pipeline from both language
files (1441 → 1261 keys, parity maintained).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update security guard references from 9 to 10 (GR-SEC-010 was missing),
code reviewer guards from 13 to 17 (new GR-CORE-LAYERS, GR-CORE-MODULE,
GR-CORE-META, GR-UI-TAXONOMY), and total guard count from 22 to 27.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover Settings gateways (Session, LoginPersistence, SystemAudit, Smtp,
FrontendTelemetry, Metadata, App), Auth gateways (ExternalIdentity,
Tenant), DirectorySettings, UserSettings, and HotkeyService.
Gateway test coverage rises from 25% to 52%, overall service/gateway
coverage from 58.6% to 70.7%.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move HelpdeskTenantSettingsRepository and HelpdeskTokenRepository from
Service/ to Repository/ directory (GR-SEC-003: SQL only in repositories)
- Make AccessControl constructor require IntendedUrlService explicitly
instead of falling back to `new IntendedUrlService()` (GR-TEST-002:
no service instantiation outside factories)
- Update all imports and tests accordingly
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Core code (lib/) no longer imports any MintyPHP\Module\Audit\* classes directly.
New ApiAuditServiceInterface and ApiSystemAuditReporterInterface follow the
existing pattern (interface + null implementation in core, concrete binding
from module registrar). DoctorRunner and helpers/app.php now resolve through
DI interfaces, ensuring the audit module remains fully optional.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add missing i18n key "LDAP attribute map could not be encoded" (de+en)
- Replace raw font-weight/line-height with design tokens in app-topbar.css
- Fix import ordering in SchedulerRunService.php (php-cs-fixer)
- Fix PHPStan mock chain errors in DebitorDetailServiceTest and TenantSsoServiceTest (PHPUnit 12 API)
- Simplify redundant comparison in NotificationService dedupe window
- Add PHPStan type hint for mock callback array in SchedulerJobFailedNotificationListenerTest
- Fix undefined variable and add flow-analysis suppression in login().php
- Increase dev PHP memory_limit to 1G for PHPStan (prod stays 256M)
All 6 quality gates now pass: QG-001 (1618 tests), QG-002 (0 errors),
QG-003 (6630 assertions), QG-006 (0 fixable), QG-008, QG-009.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds 11 tests for BcODataGateway covering getMeetingsForCustomer (empty,
whitespace, not-configured), OData injection rejection for all customerNo
methods (meetings, contract lines, escalation tickets, controlling), and
valid input acceptance. Also adds meetingsKey format and inclusion tests
for DebitorCacheControl.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces escapeODataTrustedLiteral with escapeODataStrictUserInput for all
customerNo parameters that originate from HTTP request input. The trusted
literal escape (quote-only) remains for BC-sourced values like customerName.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a meetings section to the debitor detail page, fetching meeting data from BC OData API with cache control support. Includes timeline UI for upcoming and past meetings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fetches displayName/jobTitle from Graph API and syncs them via SSO profile sync. Also fixes LDAP enabled state in tenant form, adds LDAP save on tenant create, and hardens LDAP port validation precedence.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds deleteOrphanedJobs() to clean up stale job_keys during ensureSystemJobs, and prevents editing jobs that are no longer registered in the runtime registry.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Users who exist in Microsoft Entra ID or LDAP but have no local account
were blocked at the email-first login step because the system required a
local user record before showing SSO options. This adds a domain-based
fallback: when the email is unknown locally, the system checks tenant SSO
configurations for matching allowed_domains and presents SSO-only login
options, enabling the existing JIT provisioning to trigger on callback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restructure helpdesk tenant settings into per-connection config with
improved token handling. Update risk radar data endpoint and UI.
Clean up ImageUploadTrait and update architecture contract tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace float-based header layout with flexbox (align-items: flex-start)
so the close button stays pinned top-right regardless of title length.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce data-icon-swap attribute with paired outline/fill <i> elements.
CSS toggles to filled icon on hover and active state. Module slots
auto-derive fill variant from icon name, with explicit icon_fill override
for icons without a fill counterpart (e.g. bi-headset). Add enforcement
script bin/icon-swap-check.sh.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use Stripe pattern: outline icons by default, filled for active state
(bookmarks bookmarked, notifications unread). Remove chevron arrows
from tenant/user dropdowns. Add max-width with ellipsis to breadcrumb
items to prevent overflow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move topbar outside the grid container as a standalone sticky header
(Shopify/Stripe pattern). Sidebar and icon bar use position:sticky
relative to topbar height. Mobile uses drawer pattern with backdrop,
focus trap, ESC close, and scroll lock. Tenant branding moved from
sidebar header into the topbar.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
createForTenantAdminUsersFromMessage relies on listActiveUserIdsByTenantId
which returns empty results in CLI context (scheduler). Instead, iterate
admin users directly and call createFromMessage per user per tenant.
Verified end-to-end: notification created, dedup blocks duplicates.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Change DEDUPE_TYPES from bool to int|true — int value overrides the
default 30-min window. scheduler.job_failed uses 1440 min (24h) so
admins receive at most one notification per failed job per day.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ?ModuleEventDispatcher to SchedulerRunService (fail-open, nullable).
Dispatch scheduler.job_failed event when a job fails with payload:
job_id, job_key, label, error_code, error_message, trigger_type.
New SchedulerJobFailedNotificationListener in notifications module
creates in-app admin notifications for users with JOBS_MANAGE
permission, scoped per tenant, with 30-min dedup per job_key.
Also fixes audit module manifest: system_audit_purge job_key was
mismatched (audit_system_purge vs system_audit_purge in DB).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add architecture test ViewLayerOutputEscapingContractTest that flags
raw <?php echo in .phtml files. Legitimate uses (pre-built ARIA attrs
from navActive(), hardcoded role values, pre-rendered HTML fragments)
must carry an inline // raw-html-ok: reason marker.
Annotated all 11 existing raw echo sites with justification markers.
Added guard to catalog, enforcement map (automated), CLAUDE.md security
section, and never-do-this list.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace bare <input type="file"> across all upload locations with a
card-based dropzone component via shared partial. Three visual states:
current server file (thumbnail + Replace/Delete), empty dropzone, and
pending file preview (local FileReader thumbnail + metadata). Delete
actions use data-confirm-message for confirmation dialog.
Centralized as templates/partials/app-file-upload.phtml to prevent
markup drift — documented as deliberate exception to plain-HTML inputs
convention in CLAUDE.md and developer checklist.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce per-tenant override for helpdesk BC connection config.
New table helpdesk_tenant_settings stores tenant-specific credentials
with encrypted secrets (AES-256-GCM). EffectiveHelpdeskSettingsService
resolves global vs tenant config per request. Settings UI extended with
tenant override tab, toggle, and dual editing mode.
All runtime consumers (OData, SOAP, OAuth) read through the new
resolver. Token cache flushed on any config change. Default behavior
unchanged — tenants use global config until override explicitly enabled.
Also includes risk radar UI refinements: Stripe-style card redesign
with accent borders and score pills, removal of redundant KPI tiles
and search, and filtering of zero-score entries.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- margin-bottom: 0 on cards to override article shell default
- Level thresholds stricter: high >= 55 (was 70), medium >= 25 (was 40)
so fewer customers appear as 'low risk' and issues surface earlier
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove background circle, just show the score as a large colored
number. Lighter, more Stripe-like.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stripe-style layout: large colored score circle on the left, customer
name + risk level label on the right. One-line key facts below
separated by middot (e.g. '5 open · 2 critical · 1 SLA · ↑3').
Removed border-left color accent, pill list, and metric row.
Three visual levels: score (eye-catching), name+level (context),
facts (detail). Clean scannable layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each dimension shows 'points / max' (e.g. '18 / 35') making the
score composition immediately understandable. Total row sums to
the final 'score / 100'. Replaces abstract percentage column.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolution time measures the past, not current risk. The remaining
4 dimensions (open pressure 35%, trend 30%, SLA overdue 25%,
inactivity 10%) are all current and actionable. Removes resolution
aggregation, median calculation, and weight redistribution logic.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>