Files
shahondin1624 53b3fd945a
Database Portability Tests / Integration (mysql) (push) Has been skipped
Database Portability Tests / Integration (postgres) (push) Has been skipped
Database Portability Tests / Integration (sqlite) (push) Has been skipped
Database Portability Tests / Verify no MySQL-specific SQL (push) Successful in 5s
Database Portability Tests / Unit Tests (PlatformHelper) (push) Failing after 43s
Release v0.3.2: Inventory tracking, test infra, and bugfixes
Inventory:
- General material, stock items, and sales CRUD
- Category management with CategoryPicker component
- Inventory reports service
- Database migrations and mappers

Frontend:
- Inventory view with tabs (Allgemeinmaterial, Verkaufsmaterial, Verkäufe)
- Forms for general material, stock items, and sales
- Search bar fix: flexbox wrapper with inline icon replaces broken NcTextField icon slot

Tests:
- All 1,491 tests pass (zero errors, warnings, deprecations)
- BundleImportServiceTest: fixed ZipArchive empty-file deprecation
- BundleImportService: null-coalescing for targetFields
- PHPUnit test infra: make test target via container

CLAUDE.md:
- Added Testing section as PR gating criterion
2026-04-22 10:15:22 +02:00

220 lines
20 KiB
PHP

<?php
declare(strict_types=1);
return [
'routes' => [
// ── Page routes ──────────────────────────────────────────────
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
// ── Member search ───────────────────────────────────────────
['name' => 'member#search', 'url' => '/api/v1/members/search', 'verb' => 'GET'],
// ── Member archive (soft-deleted, admin-only) ───────────────
['name' => 'member#archive', 'url' => '/api/v1/members/archive', 'verb' => 'GET'],
// ── Admin: reveal encrypted Allergien (audited) ──────────────
['name' => 'member#revealAllergies', 'url' => '/api/v1/members/allergien/reveal', 'verb' => 'POST'],
// ── Member CRUD ──────────────────────────────────────────────
['name' => 'member#index', 'url' => '/api/v1/members', 'verb' => 'GET'],
['name' => 'member#show', 'url' => '/api/v1/members/{id}', 'verb' => 'GET'],
['name' => 'member#create', 'url' => '/api/v1/members', 'verb' => 'POST'],
['name' => 'member#update', 'url' => '/api/v1/members/{id}', 'verb' => 'PUT'],
['name' => 'member#destroy', 'url' => '/api/v1/members/{id}', 'verb' => 'DELETE'],
// ── DSGVO actions (admin-only) ───────────────────────────────
['name' => 'dsgvo#export', 'url' => '/api/v1/members/{id}/dsgvo-export', 'verb' => 'POST'],
['name' => 'dsgvo#hardDelete', 'url' => '/api/v1/members/{id}/dsgvo-delete', 'verb' => 'DELETE'],
// ── Member addresses ─────────────────────────────────────────
['name' => 'member#createAddress', 'url' => '/api/v1/members/{memberId}/addresses', 'verb' => 'POST'],
['name' => 'member#updateAddress', 'url' => '/api/v1/members/{memberId}/addresses/{addrId}', 'verb' => 'PUT'],
['name' => 'member#destroyAddress', 'url' => '/api/v1/members/{memberId}/addresses/{addrId}', 'verb' => 'DELETE'],
// ── Member phones ────────────────────────────────────────────
['name' => 'member#createPhone', 'url' => '/api/v1/members/{memberId}/phones', 'verb' => 'POST'],
['name' => 'member#updatePhone', 'url' => '/api/v1/members/{memberId}/phones/{phoneId}', 'verb' => 'PUT'],
['name' => 'member#destroyPhone', 'url' => '/api/v1/members/{memberId}/phones/{phoneId}', 'verb' => 'DELETE'],
// ── Member emails ────────────────────────────────────────────
['name' => 'member#createEmail', 'url' => '/api/v1/members/{memberId}/emails', 'verb' => 'POST'],
['name' => 'member#updateEmail', 'url' => '/api/v1/members/{memberId}/emails/{emailId}', 'verb' => 'PUT'],
['name' => 'member#destroyEmail', 'url' => '/api/v1/members/{memberId}/emails/{emailId}', 'verb' => 'DELETE'],
// ── Family CRUD ──────────────────────────────────────────────
['name' => 'family#index', 'url' => '/api/v1/families', 'verb' => 'GET'],
['name' => 'family#show', 'url' => '/api/v1/families/{id}', 'verb' => 'GET'],
['name' => 'family#create', 'url' => '/api/v1/families', 'verb' => 'POST'],
['name' => 'family#update', 'url' => '/api/v1/families/{id}', 'verb' => 'PUT'],
['name' => 'family#destroy', 'url' => '/api/v1/families/{id}', 'verb' => 'DELETE'],
// ── Family member linking ────────────────────────────────────
['name' => 'family#linkMember', 'url' => '/api/v1/families/{id}/members/{memberId}', 'verb' => 'POST'],
['name' => 'family#unlinkMember', 'url' => '/api/v1/families/{id}/members/{memberId}', 'verb' => 'DELETE'],
// ── Stufen CRUD ──────────────────────────────────────────────
['name' => 'stufe#index', 'url' => '/api/v1/stufen', 'verb' => 'GET'],
['name' => 'stufe#create', 'url' => '/api/v1/stufen', 'verb' => 'POST'],
['name' => 'stufe#update', 'url' => '/api/v1/stufen/{id}', 'verb' => 'PUT'],
['name' => 'stufe#destroy', 'url' => '/api/v1/stufen/{id}', 'verb' => 'DELETE'],
// ── Stufe suggestions + history ──────────────────────────────
['name' => 'stufe#suggestions', 'url' => '/api/v1/stufen/suggestions', 'verb' => 'GET'],
['name' => 'stufe#memberHistory', 'url' => '/api/v1/members/{memberId}/stufe-history', 'verb' => 'GET'],
// ── Fee rules ───────────────────────────────────────────────
['name' => 'fee#listRules', 'url' => '/api/v1/fees/rules', 'verb' => 'GET'],
['name' => 'fee#createRule', 'url' => '/api/v1/fees/rules', 'verb' => 'POST'],
['name' => 'fee#updateRule', 'url' => '/api/v1/fees/rules/{ruleId}', 'verb' => 'PUT'],
// ── Fee records ─────────────────────────────────────────────
['name' => 'fee#listRecords', 'url' => '/api/v1/fees/records', 'verb' => 'GET'],
['name' => 'fee#memberRecords', 'url' => '/api/v1/fees/members/{memberId}/records', 'verb' => 'GET'],
['name' => 'fee#batchCalculate', 'url' => '/api/v1/fees/batch-calculate', 'verb' => 'POST'],
['name' => 'fee#markPaid', 'url' => '/api/v1/fees/records/{recordId}/paid', 'verb' => 'PUT'],
['name' => 'fee#manualOverride', 'url' => '/api/v1/fees/records/{recordId}/override', 'verb' => 'PUT'],
// ── Permissions ─────────────────────────────────────────────
['name' => 'permission#index', 'url' => '/api/v1/permissions', 'verb' => 'GET'],
['name' => 'permission#listUsers', 'url' => '/api/v1/permissions/users', 'verb' => 'GET'],
['name' => 'permission#setPermission', 'url' => '/api/v1/permissions/{ncUserId}', 'verb' => 'PUT'],
['name' => 'permission#removePermission', 'url' => '/api/v1/permissions/{ncUserId}', 'verb' => 'DELETE'],
// ── CSV Export (plain) ──────────────────────────────────────
['name' => 'export#members', 'url' => '/api/v1/export/members', 'verb' => 'GET'],
['name' => 'export#fees', 'url' => '/api/v1/export/fees', 'verb' => 'GET'],
['name' => 'export#birthdays', 'url' => '/api/v1/export/birthdays', 'verb' => 'GET'],
// ── Full bundle export (Issue #149) ────────────────────────
['name' => 'export#bundle', 'url' => '/api/v1/export/bundle', 'verb' => 'GET'],
['name' => 'export#bundleSensitive', 'url' => '/api/v1/export/bundle/sensitive', 'verb' => 'POST'],
// ── Per-entity export (Issue #148) ─────────────────────────
['name' => 'export#entityTypes', 'url' => '/api/v1/export/entity-types', 'verb' => 'GET'],
['name' => 'export#entity', 'url' => '/api/v1/export/entity/{type}', 'verb' => 'GET'],
// ── Encrypted Export (password-protected ZIP) ───────────────
['name' => 'export#membersEncrypted', 'url' => '/api/v1/export/members/encrypted', 'verb' => 'POST'],
['name' => 'export#feesEncrypted', 'url' => '/api/v1/export/fees/encrypted', 'verb' => 'POST'],
['name' => 'export#birthdaysEncrypted', 'url' => '/api/v1/export/birthdays/encrypted', 'verb' => 'POST'],
['name' => 'export#bankingEncrypted', 'url' => '/api/v1/export/banking/encrypted', 'verb' => 'POST'],
// ── Lager (camps) CRUD ──────────────────────────────────────
['name' => 'lager#index', 'url' => '/api/v1/lager', 'verb' => 'GET'],
['name' => 'lager#show', 'url' => '/api/v1/lager/{id}', 'verb' => 'GET'],
['name' => 'lager#create', 'url' => '/api/v1/lager', 'verb' => 'POST'],
['name' => 'lager#update', 'url' => '/api/v1/lager/{id}', 'verb' => 'PUT'],
['name' => 'lager#destroy', 'url' => '/api/v1/lager/{id}', 'verb' => 'DELETE'],
['name' => 'lager#addTeilnehmer', 'url' => '/api/v1/lager/{id}/teilnehmer', 'verb' => 'POST'],
['name' => 'lager#removeTeilnehmer', 'url' => '/api/v1/lager/{id}/teilnehmer/{memberId}', 'verb' => 'DELETE'],
['name' => 'lager#addFile', 'url' => '/api/v1/lager/{id}/files', 'verb' => 'POST'],
['name' => 'lager#removeFile', 'url' => '/api/v1/lager/{id}/files/{fileId}', 'verb' => 'DELETE'],
['name' => 'lager#memberHistory', 'url' => '/api/v1/members/{memberId}/lager-history', 'verb' => 'GET'],
// ── Reports (preview + PDF + encrypted) ────────────────────
['name' => 'report#index', 'url' => '/api/v1/reports', 'verb' => 'GET'],
['name' => 'report#preview', 'url' => '/api/v1/reports/{type}/preview', 'verb' => 'GET'],
['name' => 'report#pdf', 'url' => '/api/v1/reports/{type}/pdf', 'verb' => 'GET'],
['name' => 'report#encrypted', 'url' => '/api/v1/reports/{type}/encrypted', 'verb' => 'POST'],
// ── Milestones (Jubilaeen) ──────────────────────────────────
['name' => 'milestone#index', 'url' => '/api/v1/milestones', 'verb' => 'GET'],
['name' => 'milestone#getSettings', 'url' => '/api/v1/milestones/settings', 'verb' => 'GET'],
['name' => 'milestone#updateSettings', 'url' => '/api/v1/milestones/settings', 'verb' => 'PUT'],
// ── Injury tracking ────────────────────────────────────────
['name' => 'injury#index', 'url' => '/api/v1/injuries', 'verb' => 'GET'],
['name' => 'injury#show', 'url' => '/api/v1/injuries/{id}', 'verb' => 'GET'],
['name' => 'injury#create', 'url' => '/api/v1/injuries', 'verb' => 'POST'],
['name' => 'injury#update', 'url' => '/api/v1/injuries/{id}', 'verb' => 'PUT'],
['name' => 'injury#destroy', 'url' => '/api/v1/injuries/{id}', 'verb' => 'DELETE'],
['name' => 'injury#addInvolved', 'url' => '/api/v1/injuries/{id}/involved', 'verb' => 'POST'],
['name' => 'injury#removeInvolved', 'url' => '/api/v1/injuries/{id}/involved/{memberId}', 'verb' => 'DELETE'],
// ── Audit log ───────────────────────────────────────────────
['name' => 'audit#index', 'url' => '/api/v1/audit-log', 'verb' => 'GET'],
// ── Import wizard (admin-only) ─────────────────────────────
['name' => 'import#upload', 'url' => '/api/v1/import/upload', 'verb' => 'POST'],
['name' => 'import#preview', 'url' => '/api/v1/import/preview', 'verb' => 'POST'],
['name' => 'import#execute', 'url' => '/api/v1/import/execute', 'verb' => 'POST'],
// ── Per-entity import (Issue #150) ────────────────────────
['name' => 'import#entityUpload', 'url' => '/api/v1/import/entity/upload', 'verb' => 'POST'],
['name' => 'import#entitySchema', 'url' => '/api/v1/import/entity/schema/{type}', 'verb' => 'GET'],
['name' => 'import#entityPreview', 'url' => '/api/v1/import/entity/preview', 'verb' => 'POST'],
['name' => 'import#entityExecute', 'url' => '/api/v1/import/entity/execute', 'verb' => 'POST'],
['name' => 'import#entityMerge', 'url' => '/api/v1/import/entity/merge', 'verb' => 'POST'],
// ── ZIP bundle import (Issue #151) ────────────────────────
['name' => 'import#bundleUpload', 'url' => '/api/v1/import/bundle/upload', 'verb' => 'POST'],
['name' => 'import#bundlePreview', 'url' => '/api/v1/import/bundle/preview', 'verb' => 'POST'],
['name' => 'import#bundleExecute', 'url' => '/api/v1/import/bundle/execute', 'verb' => 'POST'],
// ── Saved queries + query execution ────────────────────────
['name' => 'query#fields', 'url' => '/api/v1/queries/fields', 'verb' => 'GET'],
['name' => 'query#executeAdHoc', 'url' => '/api/v1/queries/execute', 'verb' => 'POST'],
['name' => 'query#index', 'url' => '/api/v1/queries', 'verb' => 'GET'],
['name' => 'query#create', 'url' => '/api/v1/queries', 'verb' => 'POST'],
['name' => 'query#update', 'url' => '/api/v1/queries/{id}', 'verb' => 'PUT'],
['name' => 'query#destroy', 'url' => '/api/v1/queries/{id}', 'verb' => 'DELETE'],
['name' => 'query#executeSaved', 'url' => '/api/v1/queries/{id}/execute', 'verb' => 'POST'],
// ── Backup & Restore (admin-only) ─────────────────────────
['name' => 'backup#getSettings', 'url' => '/api/v1/backups/settings', 'verb' => 'GET'],
['name' => 'backup#updateSettings', 'url' => '/api/v1/backups/settings', 'verb' => 'PUT'],
['name' => 'backup#index', 'url' => '/api/v1/backups', 'verb' => 'GET'],
['name' => 'backup#create', 'url' => '/api/v1/backups', 'verb' => 'POST'],
['name' => 'backup#show', 'url' => '/api/v1/backups/{filename}', 'verb' => 'GET'],
['name' => 'backup#download', 'url' => '/api/v1/backups/{filename}/download', 'verb' => 'GET'],
['name' => 'backup#restore', 'url' => '/api/v1/backups/{filename}/restore', 'verb' => 'POST'],
['name' => 'backup#destroy', 'url' => '/api/v1/backups/{filename}', 'verb' => 'DELETE'],
// ── Self-update (admin-only) ───────────────────────────────
['name' => 'backup#checkUpdate', 'url' => '/api/v1/update/check', 'verb' => 'GET'],
['name' => 'backup#installUpdate', 'url' => '/api/v1/update/install', 'verb' => 'POST'],
['name' => 'backup#installUpdateManual', 'url' => '/api/v1/update/install-manual', 'verb' => 'POST'],
// ── Calendar sync ──────────────────────────────────────────────
['name' => 'calendar#status', 'url' => '/api/v1/calendar/status', 'verb' => 'GET'],
['name' => 'calendar#sync', 'url' => '/api/v1/calendar/sync', 'verb' => 'POST'],
['name' => 'calendar#enableApp', 'url' => '/api/v1/calendar/enable-app', 'verb' => 'POST'],
// ── Files integration (NC Files browser) ────────────────────
['name' => 'file#getSettings', 'url' => '/api/v1/files/settings', 'verb' => 'GET'],
['name' => 'file#updateSettings', 'url' => '/api/v1/files/settings', 'verb' => 'PUT'],
['name' => 'file#memberFiles', 'url' => '/api/v1/members/{memberId}/files', 'verb' => 'GET'],
['name' => 'file#ensureFolder', 'url' => '/api/v1/members/{memberId}/files/ensure-folder', 'verb' => 'POST'],
['name' => 'file#lagerFiles', 'url' => '/api/v1/lager/{lagerId}/files/browse', 'verb' => 'GET'],
['name' => 'file#ensureLagerFolder', 'url' => '/api/v1/lager/{lagerId}/files/ensure-folder', 'verb' => 'POST'],
// ── Inventory Tracking ──────────────────────────────────────
['name' => 'inventory#listCategories', 'url' => '/api/v1/inventory/categories', 'verb' => 'GET'],
['name' => 'inventory#createCategory', 'url' => '/api/v1/inventory/categories', 'verb' => 'POST'],
['name' => 'inventory#updateCategory', 'url' => '/api/v1/inventory/categories/{id}', 'verb' => 'PUT'],
['name' => 'inventory#deleteCategory', 'url' => '/api/v1/inventory/categories/{id}', 'verb' => 'DELETE'],
['name' => 'inventory#listGeneral', 'url' => '/api/v1/inventory/general', 'verb' => 'GET'],
['name' => 'inventory#showGeneral', 'url' => '/api/v1/inventory/general/{id}', 'verb' => 'GET'],
['name' => 'inventory#createGeneral', 'url' => '/api/v1/inventory/general', 'verb' => 'POST'],
['name' => 'inventory#updateGeneral', 'url' => '/api/v1/inventory/general/{id}', 'verb' => 'PUT'],
['name' => 'inventory#deleteGeneral', 'url' => '/api/v1/inventory/general/{id}', 'verb' => 'DELETE'],
['name' => 'inventory#listStock', 'url' => '/api/v1/inventory/stock', 'verb' => 'GET'],
['name' => 'inventory#showStock', 'url' => '/api/v1/inventory/stock/{id}', 'verb' => 'GET'],
['name' => 'inventory#createStock', 'url' => '/api/v1/inventory/stock', 'verb' => 'POST'],
['name' => 'inventory#updateStock', 'url' => '/api/v1/inventory/stock/{id}', 'verb' => 'PUT'],
['name' => 'inventory#deleteStock', 'url' => '/api/v1/inventory/stock/{id}', 'verb' => 'DELETE'],
['name' => 'inventory#createVariant', 'url' => '/api/v1/inventory/variants', 'verb' => 'POST'],
['name' => 'inventory#updateVariant', 'url' => '/api/v1/inventory/variants/{id}', 'verb' => 'PUT'],
['name' => 'inventory#deleteVariant', 'url' => '/api/v1/inventory/variants/{id}', 'verb' => 'DELETE'],
['name' => 'inventory#listSales', 'url' => '/api/v1/inventory/sales', 'verb' => 'GET'],
['name' => 'inventory#showSale', 'url' => '/api/v1/inventory/sales/{id}', 'verb' => 'GET'],
['name' => 'inventory#createSale', 'url' => '/api/v1/inventory/sales', 'verb' => 'POST'],
['name' => 'inventory#deleteSale', 'url' => '/api/v1/inventory/sales/{id}', 'verb' => 'DELETE'],
// ── Inventory Reports ─────────────────────────────────────
['name' => 'inventoryReport#condition', 'url' => '/api/v1/inventory/reports/condition', 'verb' => 'GET'],
['name' => 'inventoryReport#sales', 'url' => '/api/v1/inventory/reports/sales', 'verb' => 'GET'],
],
];