From a264afdf646f4b71aa91ba39937e9f7641842161 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 09:54:22 +0200 Subject: [PATCH 001/108] small bugfixes and refactoring --- .../Controllers/Legacy/LegacyController.php | 4 +- composer.json | 1 + composer.lock | 125 +++++++++++++++++- legacy/lib/forms/projekte/ProjektHandler.php | 2 +- .../projekte/auslagen/AuslagenHandler2.php | 4 +- resources/views/components/headline.blade.php | 19 --- resources/views/components/intro.blade.php | 24 ++++ .../views/components/layouts/index.blade.php | 6 +- .../views/flux/table/row-headline.blade.php | 7 + .../views/livewire/bank/csv-import.blade.php | 8 +- routes/legacy.php | 19 ++- 11 files changed, 183 insertions(+), 36 deletions(-) delete mode 100644 resources/views/components/headline.blade.php create mode 100644 resources/views/components/intro.blade.php create mode 100644 resources/views/flux/table/row-headline.blade.php diff --git a/app/Http/Controllers/Legacy/LegacyController.php b/app/Http/Controllers/Legacy/LegacyController.php index 10fdcc1b..b5c1b550 100644 --- a/app/Http/Controllers/Legacy/LegacyController.php +++ b/app/Http/Controllers/Legacy/LegacyController.php @@ -73,7 +73,7 @@ public function belegePdf(int $project_id, int $auslagen_id, int $version, ?stri ]); $ah->generate_belege_pdf(); $path = route('legacy.belege-pdf', [ - 'project_id' => $project_id, + 'projekt_id' => $project_id, 'auslagen_id' => $auslagen_id, 'version' => $version, 'file_name' => "Belege-IP$project_id-A$auslagen_id.pdf", @@ -100,7 +100,7 @@ public function zahlungsanweisungPdf(int $project_id, int $auslagen_id, int $ver ]); $ah->generate_zahlungsanweisung_pdf(); $path = route('legacy.zahlungsanweisung-pdf', [ - 'project_id' => $project_id, + 'projekt_id' => $project_id, 'auslagen_id' => $auslagen_id, 'version' => $version, 'file_name' => "Zahlungsanweisung-IP$project_id-A$auslagen_id.pdf", diff --git a/composer.json b/composer.json index ba71703f..8cc5c7e0 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "archtechx/money": "^0.5.3", "blade-ui-kit/blade-heroicons": "^2.6", "defuse/php-encryption": "^2.4", + "diglactic/laravel-breadcrumbs": "^10.0", "globalcitizen/php-iban": "^4.2", "graham-campbell/markdown": "^16.0", "guzzlehttp/guzzle": "^7.9", diff --git a/composer.lock b/composer.lock index afba1d94..31c2b4dc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "edfdaac4a61eadf9d119934a3c98520e", + "content-hash": "b8c8188fb2ce80d217b128f8f35864f5", "packages": [ { "name": "archtechx/helpers", @@ -692,6 +692,76 @@ }, "time": "2024-07-08T12:26:09+00:00" }, + { + "name": "diglactic/laravel-breadcrumbs", + "version": "v10.0.0", + "source": { + "type": "git", + "url": "https://github.com/diglactic/laravel-breadcrumbs.git", + "reference": "1bfe5e29aacf88beeab64ae3e860e0f2d8ace36a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/diglactic/laravel-breadcrumbs/zipball/1bfe5e29aacf88beeab64ae3e860e0f2d8ace36a", + "reference": "1bfe5e29aacf88beeab64ae3e860e0f2d8ace36a", + "shasum": "" + }, + "require": { + "facade/ignition-contracts": "^1.0", + "laravel/framework": "^10.0 || ^11.0 || ^12.0", + "php": "^8.1" + }, + "conflict": { + "davejamesmiller/laravel-breadcrumbs": "*" + }, + "require-dev": { + "orchestra/testbench": "^8.0 || ^9.0 || ^10.0", + "phpunit/phpunit": "^10.5 || ^11.5", + "spatie/phpunit-snapshot-assertions": "^5.1" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Breadcrumbs": "Diglactic\\Breadcrumbs\\Breadcrumbs" + }, + "providers": [ + "Diglactic\\Breadcrumbs\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Diglactic\\Breadcrumbs\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sheng Slogar", + "email": "sheng@diglactic.com", + "role": "Maintainer" + }, + { + "name": "Dave James Miller", + "email": "dave@davejamesmiller.com", + "role": "Original Creator" + } + ], + "description": "A simple Laravel-style way to create breadcrumbs.", + "homepage": "https://github.com/diglactic/laravel-breadcrumbs", + "keywords": [ + "laravel" + ], + "support": { + "issues": "https://github.com/diglactic/laravel-breadcrumbs/issues", + "source": "https://github.com/diglactic/laravel-breadcrumbs/tree/v10.0.0" + }, + "time": "2025-02-25T05:44:07+00:00" + }, { "name": "doctrine/inflector", "version": "2.0.10", @@ -1053,6 +1123,59 @@ }, "time": "2024-11-01T03:51:45+00:00" }, + { + "name": "facade/ignition-contracts", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/facade/ignition-contracts.git", + "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/3c921a1cdba35b68a7f0ccffc6dffc1995b18267", + "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^v2.15.8", + "phpunit/phpunit": "^9.3.11", + "vimeo/psalm": "^3.17.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Facade\\IgnitionContracts\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://flareapp.io", + "role": "Developer" + } + ], + "description": "Solution contracts for Ignition", + "homepage": "https://github.com/facade/ignition-contracts", + "keywords": [ + "contracts", + "flare", + "ignition" + ], + "support": { + "issues": "https://github.com/facade/ignition-contracts/issues", + "source": "https://github.com/facade/ignition-contracts/tree/1.0.2" + }, + "time": "2020-10-16T08:27:54+00:00" + }, { "name": "firebase/php-jwt", "version": "v6.11.1", diff --git a/legacy/lib/forms/projekte/ProjektHandler.php b/legacy/lib/forms/projekte/ProjektHandler.php index 59dfae3b..8eb9fc5e 100644 --- a/legacy/lib/forms/projekte/ProjektHandler.php +++ b/legacy/lib/forms/projekte/ProjektHandler.php @@ -981,7 +981,7 @@ class="fa fa-fw fa-trash"> $permissionIcon = $hasPermission ? 'fa-check' : 'fa-ban'; $abrechnungIcon = $hasNoAbrechnungen ? 'fa-check' : 'fa-ban'; ?> -
+ auslagen_data['id'], $this->auslagen_data['belege']) && $this->routeInfo['action'] !== 'edit' && count($this->auslagen_data['belege']) > 0) { ?>
) { ?>
'h1', // TODO: different styles? - 'headline', - 'subText', -]) - - diff --git a/resources/views/components/intro.blade.php b/resources/views/components/intro.blade.php new file mode 100644 index 00000000..64331df2 --- /dev/null +++ b/resources/views/components/intro.blade.php @@ -0,0 +1,24 @@ +@props([ + 'headline' => $slot, // backwards compatible + 'subHeadline' => '', // used as attribute string + 'button', // used as string text or a slot + 'level' => 1 +]) + +
+
+ {{ $headline }} + @if($subHeadline) + {{ $subHeadline }} + @endif +
+ @isset($button) +
+ @if(is_string($button)) + {{ $button }} + @else + {{ $button }} + @endif +
+ @endisset +
diff --git a/resources/views/components/layouts/index.blade.php b/resources/views/components/layouts/index.blade.php index 2ce12c83..aa457bbe 100644 --- a/resources/views/components/layouts/index.blade.php +++ b/resources/views/components/layouts/index.blade.php @@ -216,15 +216,15 @@ class="absolute top-1 right-0 -mr-14 p-1"> -->
- - + +
diff --git a/resources/views/flux/table/row-headline.blade.php b/resources/views/flux/table/row-headline.blade.php new file mode 100644 index 00000000..8ab1429a --- /dev/null +++ b/resources/views/flux/table/row-headline.blade.php @@ -0,0 +1,7 @@ +@props([ + 'key' => null, +]) + +merge(['class' => 'bg-zinc-200']) }} data-flux-row> + {{ $slot }} + diff --git a/resources/views/livewire/bank/csv-import.blade.php b/resources/views/livewire/bank/csv-import.blade.php index cb1321e8..766d2774 100644 --- a/resources/views/livewire/bank/csv-import.blade.php +++ b/resources/views/livewire/bank/csv-import.blade.php @@ -1,5 +1,5 @@
- +
@@ -43,10 +43,10 @@
@if($account_id !== "")
- +

{{ __('konto.csv-draganddrop-fat-text') }}

-

{{ __('konto.csv-draganddrop-sub-text') }}

+

{{ __('konto.csv-draganddrop-sub-headline') }}

@endif @@ -54,7 +54,7 @@ @if(isset($csv) && !$errors->has('csv'))
- +
{{ __('konto.manual-button-reverse-csv-order') }} diff --git a/routes/legacy.php b/routes/legacy.php index 20005007..a450bb79 100644 --- a/routes/legacy.php +++ b/routes/legacy.php @@ -6,6 +6,7 @@ Route::middleware(['auth'])->name('legacy.')->group(function (): void { Route::get('menu/hv', [LegacyController::class, 'render'])->name('todo.hv'); Route::get('menu/kv', [LegacyController::class, 'render'])->name('todo.kv'); + Route::get('menu/kv/exportBank', [LegacyController::class, 'render'])->name('todo.kv.bank'); Route::get('menu/belege', [LegacyController::class, 'render'])->name('todo.belege'); Route::get('menu/stura', [LegacyController::class, 'render'])->name('sitzung'); Route::get('menu/{sub}', [LegacyController::class, 'render'])->name('dashboard'); @@ -14,13 +15,21 @@ Route::get('konto/{hhp_id?}/{konto_id?}', [LegacyController::class, 'render'])->name('konto'); Route::get('konto/credentials', [LegacyController::class, 'render'])->name('konto.credentials'); Route::get('konto/credentials/new', [LegacyController::class, 'render'])->name('konto.credentials.new'); + Route::get('konto/credentials/{credential_id}/login', [LegacyController::class, 'render'])->name('konto.credentials.login'); + Route::get('konto/credentials/{credential_id}/tan-mode', [LegacyController::class, 'render'])->name('konto.credentials.tan-mode'); + Route::get('konto/credentials/{credential_id}/sepa', [LegacyController::class, 'render'])->name('konto.credentials.sepa'); + Route::get('konto/credentials/{credential_id}/{short_iban}', [LegacyController::class, 'render'])->name('konto.credentials.import-transactions'); + Route::get('konto/credentials/{credential_id}/{short_iban}/import', [LegacyController::class, 'render'])->name('konto.credentials.import-konto'); Route::get('booking', [LegacyController::class, 'render'])->name('booking'); Route::get('booking/{hhp_id}/instruct', [LegacyController::class, 'render'])->name('booking.instruct'); Route::get('booking/{hhp_id}/text', [LegacyController::class, 'render'])->name('booking.text'); Route::get('booking/{hhp_id}/history', [LegacyController::class, 'render'])->name('booking.history'); - Route::get('hhp/{hhp_id?}', [LegacyController::class, 'render'])->name('hhp'); - Route::get('hhp/{hhp_id}/titel/{titel_id}', [LegacyController::class, 'render']); + Route::get('hhp', [LegacyController::class, 'render'])->name('hhp'); + Route::get('hhp/{hhp_id}', [LegacyController::class, 'render'])->name('hhp.view'); + Route::get('hhp/{hhp_id}/titel/{titel_id}', [LegacyController::class, 'render'])->name('hhp.titel.view'); Route::get('projekt/create', [LegacyController::class, 'render'])->name('new-project'); + Route::get('projekt/{projekt_id}', [LegacyController::class, 'render'])->name('projekt'); + Route::get('projekt/{projekt_id}/auslagen/{auslagen_id}', [LegacyController::class, 'render'])->name('expense-long'); Route::get('files/get/{auslagen_id}/{hash}', [LegacyController::class, 'renderFile'])->name('get-file'); Route::get('auslagen/{auslagen_id}/{fileHash}/{filename}.pdf', [LegacyController::class, 'deliverFile']); @@ -47,9 +56,11 @@ // "new" adapted stuff Route::get('download/hhp/{hhp_id}/{filetype}', [\App\Http\Controllers\Legacy\ExportController::class, 'budgetPlan']); - Route::post('project/{project_id}/delete', \App\Http\Controllers\Legacy\DeleteProject::class)->name('project.delete'); + Route::post('projekt/{projekt_id}/delete', \App\Http\Controllers\Legacy\DeleteProject::class)->name('projekt.delete'); Route::post('expenses/{expenses_id}/delete', \App\Http\Controllers\Legacy\DeleteExpenses::class)->name('expenses.delete'); // catch all - Route::any('{path}', [LegacyController::class, 'render'])->where('path', '.*'); + Route::any('{path}', [LegacyController::class, 'render']) + ->where('path', '.*') + ->name('catch-all'); }); From 0c6042f44c64aa2a220e95691fcc7bdae3c56ba5 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 11:19:19 +0200 Subject: [PATCH 002/108] renaming --- .../{Budgetplan => BudgetPlan}/BudgetPlanEdit.php | 4 ++-- app/Livewire/{Budgetplan => BudgetPlan}/ItemForm.php | 2 +- .../{budgetplan => budget-plan}/plan-edit.blade.php | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) rename app/Livewire/{Budgetplan => BudgetPlan}/BudgetPlanEdit.php (98%) rename app/Livewire/{Budgetplan => BudgetPlan}/ItemForm.php (95%) rename resources/views/livewire/{budgetplan => budget-plan}/plan-edit.blade.php (93%) diff --git a/app/Livewire/Budgetplan/BudgetPlanEdit.php b/app/Livewire/BudgetPlan/BudgetPlanEdit.php similarity index 98% rename from app/Livewire/Budgetplan/BudgetPlanEdit.php rename to app/Livewire/BudgetPlan/BudgetPlanEdit.php index baa494a8..48e77bdb 100644 --- a/app/Livewire/Budgetplan/BudgetPlanEdit.php +++ b/app/Livewire/BudgetPlan/BudgetPlanEdit.php @@ -1,6 +1,6 @@ query(1)->whereNull('parent_id')->pluck('id'); $out_ids = $this->query(-1)->whereNull('parent_id')->pluck('id'); - return view('livewire.budgetplan.plan-edit', [ + return view('livewire.budget-plan.plan-edit', [ 'fiscal_years' => $fiscal_years, 'all_items' => $item_models, 'root_items' => [ diff --git a/app/Livewire/Budgetplan/ItemForm.php b/app/Livewire/BudgetPlan/ItemForm.php similarity index 95% rename from app/Livewire/Budgetplan/ItemForm.php rename to app/Livewire/BudgetPlan/ItemForm.php index 30b8fa37..4630a9b1 100644 --- a/app/Livewire/Budgetplan/ItemForm.php +++ b/app/Livewire/BudgetPlan/ItemForm.php @@ -1,6 +1,6 @@
- + None @foreach($fiscal_years as $fiscal_year) {{ $fiscal_year->start_date->format('d.m.y') }} - {{ $fiscal_year->end_date->format('d.m.y') }} @@ -43,7 +43,7 @@
- {{ __('budget-plan.edit.table.headline.shortname') }} + {{ __('budget-plan.budget-shortname') }} @@ -52,7 +52,7 @@
- {{ __('budget-plan.edit.table.headline.name') }} + {{ __('budget-plan.budget-longname') }} @@ -61,7 +61,7 @@
- {{ __('budget-plan.edit.table.headline.value') }} + {{ __('budget-plan.budget-value') }} From 93d481b9ed09130f4ddc2962dd56770d0ad74008 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 11:19:33 +0200 Subject: [PATCH 003/108] lang --- lang/de/budget-plan.php | 19 ++++++++++++------- lang/de/general.php | 13 +++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/lang/de/budget-plan.php b/lang/de/budget-plan.php index 1f701c65..cfe92e40 100644 --- a/lang/de/budget-plan.php +++ b/lang/de/budget-plan.php @@ -1,27 +1,32 @@ 'Neu anlegen', - 'index.headline' => 'Haushaltspläne', - 'index.headline.sub' => '', + 'budget-plan' => 'Haushaltsplan', + 'budget-plans' => 'Haushaltspläne', + 'fiscal-year' => 'Haushaltsjahr', + 'budget' => 'Haushaltstitel', + 'budget-shortname' => 'Titelnummer', + 'budget-longname' => 'Titelname', + 'budget-value' => 'Wert', + 'budget-group' => 'Haushaltstitelgruppe', + 'index.button.new' => 'Neu anlegen', + 'index.headline' => 'Übersicht aller Haushaltspläne', 'index.no-plans' => 'keine Haushaltspläne vorhanden', 'index.orphaned-plans' => 'Nicht zugeordnete Haushaltspläne', 'index.no-entries' => 'Keine Einträge', + 'index.table.state' => 'Status', + 'index.table.actions' => 'Aktionen', 'edit.headline' => 'Haushaltsplan bearbeiten', 'edit.sub' => 'Bearbeite oder erstelle einen Haushaltsplan.', 'edit.organization' => 'Organisation', 'edit.organization-sub' => 'Wähle die Organisation aus, zu welcher der Haushaltsplan gehören soll.', - 'edit.fiscal-year' => 'Haushaltsjahr', 'edit.fiscal-year-sub' => 'Wähle das Haushaltsjahr aus, zu welchem der Haushaltsplan gehören soll.', 'edit.resolution-date' => 'Beschlussdatum', 'edit.approval-date' => 'Genehmigungsdatum', 'edit.tab-headline.in' => 'Einnahmen', 'edit.tab-headline.out' => 'Ausgaben', - 'edit.table.headline.shortname' => 'Titelnummer', 'edit.table.headline.shortname-hint' => 'Die Titelnummer dient der schnellen Identifikation von Haushaltstiteln und sollte eineindeutig sein. Sie darf aus Zahlen und Nummern bestehen. Sonderzeichen', - 'edit.table.headline.name' => 'Titelname', 'edit.table.headline.name-hint' => 'Der Titelname soll kurz aber beschreibend sein. Er sollte die Verwendung der Mittel widerspiegeln und beim Bearbeiten nicht zu weit abgewandelt werden, um eine Nachvollziehbarkeit über mehrere Haushaltsjahre hinweg gewährleisten zu können.', - 'edit.table.headline.value' => 'Wert', 'edit.table.headline.value-hint' => 'Der Wert eines Haushaltstitels sollte angemessen festgelegt werden. Beachte, ob du dich im "Einnahmen"- oder "Ausgaben"-Tab befindest. In der Regel sollen die Werte nach den Landeshaushaltsordnungen auf volle 10 EUR gerundet sein. Titelgruppen summieren sich immer automatisch aus den darunterliegenden Titeln und Titelgruppen. In Titelgruppen kann nicht direkt gebucht werden.', 'edit.save' => 'Speichern und zum nächsten Schritt', '' => '', diff --git a/lang/de/general.php b/lang/de/general.php index a3aadb47..e7d0a9d9 100644 --- a/lang/de/general.php +++ b/lang/de/general.php @@ -1,6 +1,19 @@ 'Neues Projekt', + 'breadcrumb.todo' => 'TODO', + 'breadcrumb.booking' => 'Buchungen anweisen', + 'breadcrumb.booking.text' => 'Buchungen durchführen', + 'breadcrumb.booking.history' => 'Buchungsliste / Historie', + 'breadcrumb.konto' => 'Konto/Kasse', + 'breadcrumb.konto.new' => 'Neues Konto oder neue Kasse anlegen', + 'breadcrumb.konto.import.csv' => 'CSV-Import', + 'breadcrumb.konto.credentials' => 'Bankimport', + 'breadcrumb.konto.credentials.new' => 'Neuen Zugang anlegen', + 'breadcrumb.sitzung' => 'Sitzung', + 'breadcrumb.budget-plan' => 'Haushaltsplan', + 'breadcrumb.hhp-title-details' => 'Haushaltstitel Details', 'changelog.headline' => 'Changelog', 'changelog.sub-headline' => 'Die wichtigsten Änderungen findet ihr hier in gekürzter Form auf Deutsch. Eine ausführlichere Liste alle Anpassungen an StuFiS findet ihr auf Github in Englisch.', 'version_notification.text' => 'StuFiS wurde erfolgreich upgedatet. Mehr Informationen zu den Änderungen findest du unten links, wenn du auf die Versionsnummer klickst.', From 3df430b3f7a6476333ce125f8a5e2298daad547a Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 11:28:23 +0200 Subject: [PATCH 004/108] added translation checker --- .github/workflows/translations.yml | 31 +++++++++++++++ composer.json | 1 + composer.lock | 64 +++++++++++++++++++++++++++++- 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/translations.yml diff --git a/.github/workflows/translations.yml b/.github/workflows/translations.yml new file mode 100644 index 00000000..ba3be7fe --- /dev/null +++ b/.github/workflows/translations.yml @@ -0,0 +1,31 @@ +name: Analysis +on: + - push +jobs: + translations: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + extensions: mbstring, intl + ini-values: post_max_size=256M, max_execution_time=180 + coverage: xdebug + tools: php-cs-fixer, phpunit + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - name: Setup composer cache + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + - name: Install composer dependencies + env: + COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }} + run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + - name: Run translations check + run: php artisan translations:check --excludedDirectories=vendor diff --git a/composer.json b/composer.json index 8cc5c7e0..8c3685c2 100644 --- a/composer.json +++ b/composer.json @@ -43,6 +43,7 @@ "laravel/pint": "^1.20", "laravel/sail": "^1.26", "laravel/tinker": "^2.9", + "larswiegers/laravel-translations-checker": "^0.9.3", "magentron/laravel-blade-lint": "^2.0", "mockery/mockery": "^1.6", "nunomaduro/collision": "^8.0", diff --git a/composer.lock b/composer.lock index 31c2b4dc..db98e711 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b8c8188fb2ce80d217b128f8f35864f5", + "content-hash": "07fa28e9410095126480521c45d434fb", "packages": [ { "name": "archtechx/helpers", @@ -9639,6 +9639,68 @@ }, "time": "2025-01-27T14:24:01+00:00" }, + { + "name": "larswiegers/laravel-translations-checker", + "version": "v0.9.3", + "source": { + "type": "git", + "url": "https://github.com/LarsWiegers/laravel-translations-checker.git", + "reference": "205cb106dd0d96be1a117ca14c75958eb6aaae86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/LarsWiegers/laravel-translations-checker/zipball/205cb106dd0d96be1a117ca14c75958eb6aaae86", + "reference": "205cb106dd0d96be1a117ca14c75958eb6aaae86", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^7.4|^8.0|^8.1|^8.2" + }, + "require-dev": { + "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "phpunit/phpunit": "^9.0|^10.0|^11.0" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "LaravelTranslationsChecker": "Larswiegers\\LaravelTranslationsChecker\\LaravelTranslationsCheckerFacade" + }, + "providers": [ + "Larswiegers\\LaravelTranslationsChecker\\LaravelTranslationsCheckerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Larswiegers\\LaravelTranslationsChecker\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Wiegers", + "email": "larswiegers@live.nl", + "role": "Developer" + } + ], + "description": "Make sure your laravel translations are checked and are included in all languages.", + "homepage": "https://github.com/larswiegers/laravel-translations-checker", + "keywords": [ + "laravel-translations-checker", + "larswiegers" + ], + "support": { + "issues": "https://github.com/LarsWiegers/laravel-translations-checker/issues", + "source": "https://github.com/LarsWiegers/laravel-translations-checker/tree/v0.9.3" + }, + "time": "2025-02-20T18:40:31+00:00" + }, { "name": "magentron/laravel-blade-lint", "version": "2.0.2", From 37b3b3022c08ebd0d28f601e23dd0178a37db30e Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 11:30:06 +0200 Subject: [PATCH 005/108] fixed workflow name --- .github/workflows/translations.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/translations.yml b/.github/workflows/translations.yml index ba3be7fe..a1f1d459 100644 --- a/.github/workflows/translations.yml +++ b/.github/workflows/translations.yml @@ -1,4 +1,4 @@ -name: Analysis +name: Translations on: - push jobs: From 5a07f06d5fe630902bca4e6dcc57f58315d0c97c Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 12:42:56 +0200 Subject: [PATCH 006/108] removed english translations --- lang/en/budget-plan.php | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 lang/en/budget-plan.php diff --git a/lang/en/budget-plan.php b/lang/en/budget-plan.php deleted file mode 100644 index 20cd752e..00000000 --- a/lang/en/budget-plan.php +++ /dev/null @@ -1,6 +0,0 @@ - 'Budget Plans', - 'index-headline-sub' => 'A list of all budget plans', -]; From b2ad5970738b226a1ce14354f3f1bdd2bb86f48f Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 13:39:19 +0200 Subject: [PATCH 007/108] feature: breadcrumbs --- config/breadcrumbs.php | 75 +++++++ lang/de/general.php | 16 +- .../views/components/layouts/index.blade.php | 10 +- .../livewire/budget-plan/plan-edit.blade.php | 9 - .../vendor/breadcrumbs/tailwind.blade.php | 16 ++ routes/breadcrumbs.php | 193 ++++++++++++++++++ routes/legacy.php | 3 +- 7 files changed, 304 insertions(+), 18 deletions(-) create mode 100644 config/breadcrumbs.php create mode 100644 resources/views/vendor/breadcrumbs/tailwind.blade.php create mode 100644 routes/breadcrumbs.php diff --git a/config/breadcrumbs.php b/config/breadcrumbs.php new file mode 100644 index 00000000..61574787 --- /dev/null +++ b/config/breadcrumbs.php @@ -0,0 +1,75 @@ + 'breadcrumbs::tailwind', + + /* + |-------------------------------------------------------------------------- + | Breadcrumbs File(s) + |-------------------------------------------------------------------------- + | + | The file(s) where breadcrumbs are defined. e.g. + | + | - base_path('routes/breadcrumbs.php') + | - glob(base_path('breadcrumbs/*.php')) + | + */ + + 'files' => base_path('routes/breadcrumbs.php'), + + /* + |-------------------------------------------------------------------------- + | Exceptions + |-------------------------------------------------------------------------- + | + | Determine when to throw an exception. + | + */ + + // When route-bound breadcrumbs are used but the current route doesn't have a name (UnnamedRouteException) + 'unnamed-route-exception' => true, + + // When route-bound breadcrumbs are used and the matching breadcrumb doesn't exist (InvalidBreadcrumbException) + 'missing-route-bound-breadcrumb-exception' => false, + + // When a named breadcrumb is used but doesn't exist (InvalidBreadcrumbException) + 'invalid-named-breadcrumb-exception' => true, + + /* + |-------------------------------------------------------------------------- + | Classes + |-------------------------------------------------------------------------- + | + | Subclass the default classes for more advanced customisations. + | + */ + + // Manager + 'manager-class' => Diglactic\Breadcrumbs\Manager::class, + + // Generator + 'generator-class' => Diglactic\Breadcrumbs\Generator::class, + +]; diff --git a/lang/de/general.php b/lang/de/general.php index e7d0a9d9..0730bfc2 100644 --- a/lang/de/general.php +++ b/lang/de/general.php @@ -5,15 +5,25 @@ 'breadcrumb.todo' => 'TODO', 'breadcrumb.booking' => 'Buchungen anweisen', 'breadcrumb.booking.text' => 'Buchungen durchführen', - 'breadcrumb.booking.history' => 'Buchungsliste / Historie', - 'breadcrumb.konto' => 'Konto/Kasse', - 'breadcrumb.konto.new' => 'Neues Konto oder neue Kasse anlegen', + 'breadcrumb.booking.history' => 'Buchungsliste bzw. Historie', + 'breadcrumb.konto' => 'Konto bzw. Kasse', + 'breadcrumb.konto.new' => 'Neu anlegen', 'breadcrumb.konto.import.csv' => 'CSV-Import', 'breadcrumb.konto.credentials' => 'Bankimport', 'breadcrumb.konto.credentials.new' => 'Neuen Zugang anlegen', + 'breadcrumb.konto.login' => 'Login', + 'breadcrumb.konto.tan-mode' => 'Tan-Modus', + 'breadcrumb.konto.sepa' => 'Konten', + 'breadcrumb.konto.import-konto' => 'Neu', 'breadcrumb.sitzung' => 'Sitzung', 'breadcrumb.budget-plan' => 'Haushaltsplan', + 'breadcrumb.budget-plan-edit' => 'Bearbeiten', + 'breadcrumb.budget-plan-import' => 'Importieren', 'breadcrumb.hhp-title-details' => 'Haushaltstitel Details', + 'breadcrumb.projekt' => 'Projekt', + 'breadcrumb.abrechnung' => 'Abrechnung', + 'breadcrumb.belege-pdf' => 'Belege-PDF', + 'breadcrumb.zahlungsanweisung-pdf' => 'Zahlungsanweisung-PDF', 'changelog.headline' => 'Changelog', 'changelog.sub-headline' => 'Die wichtigsten Änderungen findet ihr hier in gekürzter Form auf Deutsch. Eine ausführlichere Liste alle Anpassungen an StuFiS findet ihr auf Github in Englisch.', 'version_notification.text' => 'StuFiS wurde erfolgreich upgedatet. Mehr Informationen zu den Änderungen findest du unten links, wenn du auf die Versionsnummer klickst.', diff --git a/resources/views/components/layouts/index.blade.php b/resources/views/components/layouts/index.blade.php index aa457bbe..c6f28ca5 100644 --- a/resources/views/components/layouts/index.blade.php +++ b/resources/views/components/layouts/index.blade.php @@ -196,11 +196,8 @@ class="absolute top-1 right-0 -mr-14 p-1">
-
- - - Neues Projekt - +
+ {{ Breadcrumbs::render() }}
+
+ {{ __('general.new-project-button') }} +
diff --git a/resources/views/livewire/budget-plan/plan-edit.blade.php b/resources/views/livewire/budget-plan/plan-edit.blade.php index c9b47f7e..75367f88 100644 --- a/resources/views/livewire/budget-plan/plan-edit.blade.php +++ b/resources/views/livewire/budget-plan/plan-edit.blade.php @@ -1,13 +1,4 @@
- - - Haushaltspläne - 2025 - Edit - - - -
{{ __('budget-plan.edit.headline') }} {{ __('budget-plan.edit.sub') }} diff --git a/resources/views/vendor/breadcrumbs/tailwind.blade.php b/resources/views/vendor/breadcrumbs/tailwind.blade.php new file mode 100644 index 00000000..aa6aa45f --- /dev/null +++ b/resources/views/vendor/breadcrumbs/tailwind.blade.php @@ -0,0 +1,16 @@ +{{-- used by the breadcrumbs libary --}} +@unless ($breadcrumbs->isEmpty()) + + @foreach ($breadcrumbs as $breadcrumb) + @if($loop->first) + + {{ $breadcrumb->title }} + + @else + + {{ $breadcrumb->title }} + + @endif + @endforeach + +@endunless diff --git a/routes/breadcrumbs.php b/routes/breadcrumbs.php new file mode 100644 index 00000000..aa661165 --- /dev/null +++ b/routes/breadcrumbs.php @@ -0,0 +1,193 @@ +push('Home', route('legacy.dashboard', 'mygremium')); +}); + +// Home > TODOS +Breadcrumbs::for('legacy.todo.belege', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.todo'), route('legacy.todo.belege')); +}); + +Breadcrumbs::for('legacy.todo.hv', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.todo'), route('legacy.todo.belege')); +}); + +Breadcrumbs::for('legacy.todo.kv', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.todo'), route('legacy.todo.belege')); +}); + +Breadcrumbs::for('legacy.todo.kv.bank', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.todo'), route('legacy.todo.belege')); +}); + +// Home > Booking +Breadcrumbs::for('legacy.booking', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.booking'), route('legacy.booking')); +}); + +Breadcrumbs::for('legacy.booking.instruct', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.booking.text'), route('legacy.booking')); +}); + +Breadcrumbs::for('legacy.booking.text', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.booking.text'), route('legacy.booking')); +}); + +Breadcrumbs::for('legacy.booking.history', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.booking.history'), route('legacy.booking')); +}); + +// Home > Konto +Breadcrumbs::for('legacy.konto', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.konto'), route('legacy.konto')); +}); + +// Home > Konto > New +Breadcrumbs::for('bank-account.new', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.konto'); + $trail->push(__('general.breadcrumb.konto.new'), route('bank-account.new')); +}); + +// Home > Konto > Import +Breadcrumbs::for('bank-account.import.csv', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.konto'); + $trail->push(__('general.breadcrumb.konto.import.csv'), route('bank-account.import.csv')); +}); + +// Home > Konto > Credentials +Breadcrumbs::for('legacy.konto.credentials', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.konto'); + $trail->push(__('general.breadcrumb.konto.credentials'), route('legacy.konto.credentials')); +}); + +// Home > Konto > Credentials +Breadcrumbs::for('legacy.konto.credentials.new', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.konto.credentials'); + $trail->push(__('general.breadcrumb.konto.credentials.new'), route('legacy.konto.credentials.new')); +}); + +// Home > Konto > Credentials > Login +Breadcrumbs::for('legacy.konto.credentials.login', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.konto.credentials'); + $trail->push(__('general.breadcrumb.konto.login')); +}); + +// Home > Konto > Credentials > TAN Mode +Breadcrumbs::for('legacy.konto.credentials.tan-mode', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.konto.credentials'); + $trail->push(__('general.breadcrumb.konto.tan-mode')); +}); + +// Home > Konto > Credentials > Sepa +Breadcrumbs::for('legacy.konto.credentials.sepa', static function (BreadcrumbTrail $trail, $credential_id) { + $trail->parent('legacy.konto.credentials'); + $trail->push(__('general.breadcrumb.konto.sepa'), route('legacy.konto.credentials.sepa', $credential_id)); +}); + +// Home > Konto > Credentials > Sepa +Breadcrumbs::for('legacy.konto.credentials.import-konto', static function (BreadcrumbTrail $trail, $credential_id, $shortIban) { + $trail->parent('legacy.konto.credentials.sepa', $credential_id); + $trail->push(__('general.breadcrumb.konto.import-konto')); +}); + +// Home > Sitzung +Breadcrumbs::for('legacy.sitzung', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.sitzung'), route('legacy.sitzung')); +}); + +// Home > HHP +Breadcrumbs::for('legacy.hhp', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.budget-plan'), route('legacy.hhp')); +}); + +// Home > HHP > Import +Breadcrumbs::for('legacy.hhp.import', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.hhp'); + $trail->push(__('general.breadcrumb.budget-plan-import'), route('legacy.hhp.import')); +}); + +// Home > HHP > $hhp_id +Breadcrumbs::for('legacy.hhp.view', static function (BreadcrumbTrail $trail, $hhp_id) { + $trail->parent('legacy.hhp'); + $trail->push($hhp_id, route('legacy.hhp.view', $hhp_id)); +}); + +// Home > HHP > $hhp_id > Titel-Details +Breadcrumbs::for('legacy.hhp.titel.view', static function (BreadcrumbTrail $trail, int $hhp_id, int $title_id) { + $trail->parent('legacy.hhp.view', $hhp_id); + $trail->push(__('general.breadcrumb.hhp-title-details'), route('legacy.hhp.titel.view', [$hhp_id, $title_id])); +}); + +// Home > Projekt > PID +Breadcrumbs::for('legacy.projekt', static function (BreadcrumbTrail $trail, $projekt_id) { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.projekt')); + $trail->push($projekt_id, route('legacy.projekt', $projekt_id)); +}); + +// Home > Projekt > PID > Abrechnung > AID +Breadcrumbs::for('legacy.expense-long', static function (BreadcrumbTrail $trail, $projekt_id, $auslagen_id) { + $trail->parent('legacy.projekt', $projekt_id); + $trail->push(__('general.breadcrumb.abrechnung')); + $trail->push($auslagen_id, route('legacy.expense', $auslagen_id)); +}); + +// Home > Projekt > PID > Abrechnung > AID > BelegePDF +Breadcrumbs::for('legacy.belege-pdf', static function (BreadcrumbTrail $trail, $projekt_id, $auslagen_id, $version) { + $trail->parent('legacy.expense-long', $projekt_id, $auslagen_id); + $trail->push(__('general.breadcrumb.belege-pdf')); +}); + +// Home > Projekt > PID > Abrechnung > AID > Zahlungsanweisung +Breadcrumbs::for('legacy.zahlungsanweisung-pdf', static function (BreadcrumbTrail $trail, $projekt_id, $auslagen_id, $version) { + $trail->parent('legacy.expense-long', $projekt_id, $auslagen_id); + $trail->push(__('general.breadcrumb.zahlungsanweisung-pdf')); +}); + +/** + * not legacy + */ + +// Home > Budget-Plans +Breadcrumbs::for('budget-plan.index', static function (BreadcrumbTrail $trail) { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.budget-plan'), route('budget-plan.index')); +}); + +// Home > Budget-Plans > ID +Breadcrumbs::for('budget-plan.view', static function (BreadcrumbTrail $trail, $plan_id) { + $trail->parent('budget-plan.index'); + $trail->push($plan_id, route('budget-plan.view', $plan_id)); +}); + +// Home > Budget-Plans > ID +Breadcrumbs::for('budget-plan.edit', static function (BreadcrumbTrail $trail, $plan_id) { + $trail->parent('budget-plan.view', $plan_id); + $trail->push(__('general.breadcrumb.budget-plan-edit'), route('budget-plan.edit', $plan_id)); +}); diff --git a/routes/legacy.php b/routes/legacy.php index a450bb79..07c5010f 100644 --- a/routes/legacy.php +++ b/routes/legacy.php @@ -15,7 +15,7 @@ Route::get('konto/{hhp_id?}/{konto_id?}', [LegacyController::class, 'render'])->name('konto'); Route::get('konto/credentials', [LegacyController::class, 'render'])->name('konto.credentials'); Route::get('konto/credentials/new', [LegacyController::class, 'render'])->name('konto.credentials.new'); - Route::get('konto/credentials/{credential_id}/login', [LegacyController::class, 'render'])->name('konto.credentials.login'); + Route::any('konto/credentials/{credential_id}/login', [LegacyController::class, 'render'])->name('konto.credentials.login'); Route::get('konto/credentials/{credential_id}/tan-mode', [LegacyController::class, 'render'])->name('konto.credentials.tan-mode'); Route::get('konto/credentials/{credential_id}/sepa', [LegacyController::class, 'render'])->name('konto.credentials.sepa'); Route::get('konto/credentials/{credential_id}/{short_iban}', [LegacyController::class, 'render'])->name('konto.credentials.import-transactions'); @@ -25,6 +25,7 @@ Route::get('booking/{hhp_id}/text', [LegacyController::class, 'render'])->name('booking.text'); Route::get('booking/{hhp_id}/history', [LegacyController::class, 'render'])->name('booking.history'); Route::get('hhp', [LegacyController::class, 'render'])->name('hhp'); + Route::get('hhp/import', [LegacyController::class, 'render'])->name('hhp.import'); Route::get('hhp/{hhp_id}', [LegacyController::class, 'render'])->name('hhp.view'); Route::get('hhp/{hhp_id}/titel/{titel_id}', [LegacyController::class, 'render'])->name('hhp.titel.view'); Route::get('projekt/create', [LegacyController::class, 'render'])->name('new-project'); From 40d147eaf0e7edf8ecf42b7c3b90f35df35f988b Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 13:39:40 +0200 Subject: [PATCH 008/108] renamed route --- app/Http/Controllers/BudgetPlanController.php | 2 +- resources/views/budget-plan/show.blade.php | 4 ---- resources/views/budget-plan/view.blade.php | 6 ++++++ routes/web-dev.php | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) delete mode 100644 resources/views/budget-plan/show.blade.php create mode 100644 resources/views/budget-plan/view.blade.php diff --git a/app/Http/Controllers/BudgetPlanController.php b/app/Http/Controllers/BudgetPlanController.php index fb478893..d2a5f396 100644 --- a/app/Http/Controllers/BudgetPlanController.php +++ b/app/Http/Controllers/BudgetPlanController.php @@ -21,7 +21,7 @@ public function show(int $plan_id) { $plan = BudgetPlan::findOrFail($plan_id); - return view('budget-plan.show', ['plan' => $plan]); + return view('budget-plan.view', ['plan' => $plan]); } public function create() diff --git a/resources/views/budget-plan/show.blade.php b/resources/views/budget-plan/show.blade.php deleted file mode 100644 index 7805caa2..00000000 --- a/resources/views/budget-plan/show.blade.php +++ /dev/null @@ -1,4 +0,0 @@ - - BudgetPlan.show -> - Edit - diff --git a/resources/views/budget-plan/view.blade.php b/resources/views/budget-plan/view.blade.php new file mode 100644 index 00000000..1a00a689 --- /dev/null +++ b/resources/views/budget-plan/view.blade.php @@ -0,0 +1,6 @@ + + + a + b + + diff --git a/routes/web-dev.php b/routes/web-dev.php index 666d1cb5..4b8e1252 100644 --- a/routes/web-dev.php +++ b/routes/web-dev.php @@ -10,7 +10,7 @@ // Feature Budget Plans Route::get('plan', [\App\Http\Controllers\BudgetPlanController::class, 'index'])->name('budget-plan.index'); Route::get('plan/create', [\App\Http\Controllers\BudgetPlanController::class, 'create'])->name('budget-plan.create'); - Route::get('plan/{plan_id}', [\App\Http\Controllers\BudgetPlanController::class, 'show'])->name('budget-plan.show'); + Route::get('plan/{plan_id}', [\App\Http\Controllers\BudgetPlanController::class, 'show'])->name('budget-plan.view'); Route::get('plan/{plan_id}/edit', \App\Livewire\BudgetPlan\BudgetPlanEdit::class)->name('budget-plan.edit'); // Route::get('plan/{plan_id}/edit', \App\Http\Livewire\BudgetPlanLivewire::class)->name('budget-plan.edit'); From 164064adc8de44458b7ea75afbae8be805a5a066 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 13:40:00 +0200 Subject: [PATCH 009/108] code hardening --- app/Providers/AppServiceProvider.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index e9378124..87ad11e8 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -53,12 +53,13 @@ public function bootRoute() RateLimiter::for('api', fn (Request $request) => Limit::perMinute(60)->by($request->user()?->id ?: $request->ip())); // Make sure, this vars cannot be matched with strings - // preventing missrouting + // prevents wrong routing Route::pattern('hhp_id', '[0-9]+'); Route::pattern('konto_id', '[0-9]+'); Route::pattern('titel_id', '[0-9]+'); Route::pattern('projekt_id', '[0-9]+'); Route::pattern('auslagen_id', '[0-9]+'); + Route::pattern('credential_id', '[0-9]+'); } public function registerAuth(): void From 6909391fae5e123218deace4d7684405e64f3806 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 13:48:04 +0200 Subject: [PATCH 010/108] rector --- .../Commands/LegacyDeleteBudgetPlan.php | 2 +- .../Commands/LegacyMigrateEncryption.php | 4 +- app/Exports/LegacyBudgetExport.php | 3 + app/Http/Middleware/Authenticate.php | 2 + app/Livewire/BudgetPlan/BudgetPlanEdit.php | 2 +- app/Models/BudgetItem.php | 1 + app/Models/BudgetPlan.php | 1 + app/Models/Changelog.php | 1 + app/Models/FiscalYear.php | 1 + app/Models/Legacy/BankAccount.php | 1 + app/Models/Legacy/BankTransaction.php | 1 + app/Models/User.php | 1 + app/Providers/AppServiceProvider.php | 1 + app/Rules/BicRule.php | 1 + .../BalanceColumnRule.php | 1 + .../CsvTransactionImport/BicColumnRule.php | 1 + app/Rules/CsvTransactionImport/BicRule.php | 1 + .../CsvTransactionImport/DateColumnRule.php | 1 + .../CsvTransactionImport/IbanColumnRule.php | 1 + app/Rules/CsvTransactionImport/IbanRule.php | 1 + .../CsvTransactionImport/MoneyColumnRule.php | 1 + app/Rules/IbanRule.php | 1 + app/Services/Auth/LocalAuthService.php | 6 ++ app/Services/Auth/OidcAuthService.php | 7 +++ app/Services/Auth/StumvAuthService.php | 7 +++ app/View/Components/Layout.php | 1 + config/markdown.php | 18 +++--- routes/breadcrumbs.php | 60 +++++++++---------- 28 files changed, 85 insertions(+), 44 deletions(-) diff --git a/app/Console/Commands/LegacyDeleteBudgetPlan.php b/app/Console/Commands/LegacyDeleteBudgetPlan.php index 3d824ee3..0cb7a0ce 100644 --- a/app/Console/Commands/LegacyDeleteBudgetPlan.php +++ b/app/Console/Commands/LegacyDeleteBudgetPlan.php @@ -36,7 +36,7 @@ public function handle() return; } - \DB::transaction(function () use ($hhp, $groups, $title) { + \DB::transaction(function () use ($hhp, $groups, $title): void { \Schema::disableForeignKeyConstraints(); $title->delete(); $groups->delete(); diff --git a/app/Console/Commands/LegacyMigrateEncryption.php b/app/Console/Commands/LegacyMigrateEncryption.php index 4fa1e3f6..8ec8d1b0 100644 --- a/app/Console/Commands/LegacyMigrateEncryption.php +++ b/app/Console/Commands/LegacyMigrateEncryption.php @@ -61,7 +61,7 @@ public function handle(): int $count++; } } - } catch (WrongKeyOrModifiedCiphertextException $e) { + } catch (WrongKeyOrModifiedCiphertextException) { // do nothing } }); @@ -72,7 +72,7 @@ public function handle(): int $cryptIban = $expense->getAttribute('zahlung-iban'); try { \Crypt::decryptString($cryptIban); - } catch (DecryptException $d) { + } catch (DecryptException) { $iban = AuslagenHandler2::legacyDecryptStr($cryptIban); $expense->setAttribute('zahlung-iban', \Crypt::encryptString($iban)); $expense->etag = \Str::random(32); diff --git a/app/Exports/LegacyBudgetExport.php b/app/Exports/LegacyBudgetExport.php index 928e247a..cfebb9ad 100644 --- a/app/Exports/LegacyBudgetExport.php +++ b/app/Exports/LegacyBudgetExport.php @@ -18,6 +18,7 @@ class LegacyBudgetExport implements FromView, WithColumnFormatting, WithColumnWi public function __construct(public LegacyBudgetPlan $plan) {} + #[\Override] public function view(): View { @@ -29,6 +30,7 @@ public function view(): View ]); } + #[\Override] public function columnFormats(): array { return [ @@ -46,6 +48,7 @@ public function sum(string $column, array|Collection $rows) return '=SUM('.$fields.')'; } + #[\Override] public function columnWidths(): array { return [ diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index a053d2c1..2a7536f0 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -10,6 +10,7 @@ class Authenticate extends Middleware { + #[\Override] public function handle($request, Closure $next, ...$guards): Response { // do it like in the parent @@ -27,6 +28,7 @@ public function handle($request, Closure $next, ...$guards): Response /** * Get the path the user should be redirected to when they are not authenticated. */ + #[\Override] protected function redirectTo(Request $request) { if (! $request->expectsJson()) { diff --git a/app/Livewire/BudgetPlan/BudgetPlanEdit.php b/app/Livewire/BudgetPlan/BudgetPlanEdit.php index 48e77bdb..221b8d43 100644 --- a/app/Livewire/BudgetPlan/BudgetPlanEdit.php +++ b/app/Livewire/BudgetPlan/BudgetPlanEdit.php @@ -92,7 +92,7 @@ public function render() public function updatedItems($value, $property): void { - [$item_id, $item_prop] = explode('.', $property, 2); + [$item_id, $item_prop] = explode('.', (string) $property, 2); if (in_array($item_prop, ['short_name', 'name', 'value'])) { $item = BudgetItem::findOrFail($item_id); $item->update([$item_prop => $value]); diff --git a/app/Models/BudgetItem.php b/app/Models/BudgetItem.php index a1ae9e8c..75760f8b 100644 --- a/app/Models/BudgetItem.php +++ b/app/Models/BudgetItem.php @@ -66,6 +66,7 @@ public function orderedChildren(): HasMany return $this->hasMany(self::class, 'parent_id')->orderBy('position', 'asc'); } + #[\Override] protected function casts(): array { return [ diff --git a/app/Models/BudgetPlan.php b/app/Models/BudgetPlan.php index 3bbbd272..8fa3a0bd 100644 --- a/app/Models/BudgetPlan.php +++ b/app/Models/BudgetPlan.php @@ -49,6 +49,7 @@ class BudgetPlan extends Model */ protected $fillable = ['organization', 'fiscal_year_id', 'resolution_date', 'approval_date', 'state', 'parent_plan']; + #[\Override] protected function casts(): array { return [ diff --git a/app/Models/Changelog.php b/app/Models/Changelog.php index f513563d..19d29484 100644 --- a/app/Models/Changelog.php +++ b/app/Models/Changelog.php @@ -52,6 +52,7 @@ class Changelog extends Model * * @return array */ + #[\Override] protected function casts(): array { return [ diff --git a/app/Models/FiscalYear.php b/app/Models/FiscalYear.php index fd6d22a7..ca760024 100644 --- a/app/Models/FiscalYear.php +++ b/app/Models/FiscalYear.php @@ -23,6 +23,7 @@ class FiscalYear extends Model */ protected $fillable = ['start_date', 'end_date']; + #[\Override] protected function casts(): array { return [ diff --git a/app/Models/Legacy/BankAccount.php b/app/Models/Legacy/BankAccount.php index e1d9423c..2f383bc7 100644 --- a/app/Models/Legacy/BankAccount.php +++ b/app/Models/Legacy/BankAccount.php @@ -62,6 +62,7 @@ class BankAccount extends Model */ protected $fillable = ['name', 'short', 'sync_from', 'sync_until', 'iban', 'last_sync', 'csv_import_settings', 'manually_enterable']; + #[\Override] public function casts(): array { return [ diff --git a/app/Models/Legacy/BankTransaction.php b/app/Models/Legacy/BankTransaction.php index 80ee7a43..28ad22a3 100644 --- a/app/Models/Legacy/BankTransaction.php +++ b/app/Models/Legacy/BankTransaction.php @@ -71,6 +71,7 @@ class BankTransaction extends Model */ protected $fillable = ['date', 'valuta', 'type', 'empf_iban', 'empf_bic', 'empf_name', 'primanota', 'value', 'saldo', 'zweck', 'comment', 'customer_ref']; + #[\Override] protected function casts(): array { return [ diff --git a/app/Models/User.php b/app/Models/User.php index ec9ae23b..45ba263a 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -109,6 +109,7 @@ class User extends Authenticatable * * @return array */ + #[\Override] protected function casts(): array { return [ diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 87ad11e8..2b15b665 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -17,6 +17,7 @@ class AppServiceProvider extends ServiceProvider /** * Register any application services. */ + #[\Override] public function register(): void { // diff --git a/app/Rules/BicRule.php b/app/Rules/BicRule.php index 9bb78037..b11f08bd 100644 --- a/app/Rules/BicRule.php +++ b/app/Rules/BicRule.php @@ -7,5 +7,6 @@ class BicRule implements ValidationRule { + #[\Override] public function validate(string $attribute, mixed $value, Closure $fail): void {} } diff --git a/app/Rules/CsvTransactionImport/BalanceColumnRule.php b/app/Rules/CsvTransactionImport/BalanceColumnRule.php index ca35ce25..6ad3f8ab 100644 --- a/app/Rules/CsvTransactionImport/BalanceColumnRule.php +++ b/app/Rules/CsvTransactionImport/BalanceColumnRule.php @@ -27,6 +27,7 @@ public function __construct(Collection $differences, Collection $balances, ?stri * * @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail */ + #[\Override] public function validate(string $attribute, mixed $value, Closure $fail): void { try { diff --git a/app/Rules/CsvTransactionImport/BicColumnRule.php b/app/Rules/CsvTransactionImport/BicColumnRule.php index 5cc068c4..061dd6b7 100644 --- a/app/Rules/CsvTransactionImport/BicColumnRule.php +++ b/app/Rules/CsvTransactionImport/BicColumnRule.php @@ -18,6 +18,7 @@ public function __construct(public Collection $bics) {} * * @param Closure(string): PotentiallyTranslatedString $fail */ + #[\Override] public function validate(string $attribute, mixed $value, Closure $fail): void { foreach ($this->bics as $bic) { diff --git a/app/Rules/CsvTransactionImport/BicRule.php b/app/Rules/CsvTransactionImport/BicRule.php index 53ab5a60..ab8b6527 100644 --- a/app/Rules/CsvTransactionImport/BicRule.php +++ b/app/Rules/CsvTransactionImport/BicRule.php @@ -15,6 +15,7 @@ public function __construct(public Collection $bics) {} * * @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail */ + #[\Override] public function validate(string $attribute, mixed $value, Closure $fail): void { foreach ($this->bics as $bic) { diff --git a/app/Rules/CsvTransactionImport/DateColumnRule.php b/app/Rules/CsvTransactionImport/DateColumnRule.php index 1d12805e..6976e2b7 100644 --- a/app/Rules/CsvTransactionImport/DateColumnRule.php +++ b/app/Rules/CsvTransactionImport/DateColumnRule.php @@ -16,6 +16,7 @@ public function __construct(private readonly Collection $column) {} * * @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail */ + #[\Override] public function validate(string $attribute, mixed $value, Closure $fail): void { try { diff --git a/app/Rules/CsvTransactionImport/IbanColumnRule.php b/app/Rules/CsvTransactionImport/IbanColumnRule.php index 7a2a3e15..31ab7be7 100644 --- a/app/Rules/CsvTransactionImport/IbanColumnRule.php +++ b/app/Rules/CsvTransactionImport/IbanColumnRule.php @@ -18,6 +18,7 @@ public function __construct(public Collection $ibans) {} * * @param Closure(string): PotentiallyTranslatedString $fail */ + #[\Override] public function validate(string $attribute, mixed $value, Closure $fail): void { foreach ($this->ibans as $iban) { diff --git a/app/Rules/CsvTransactionImport/IbanRule.php b/app/Rules/CsvTransactionImport/IbanRule.php index 6f70f6b5..0f906532 100644 --- a/app/Rules/CsvTransactionImport/IbanRule.php +++ b/app/Rules/CsvTransactionImport/IbanRule.php @@ -15,6 +15,7 @@ public function __construct(public Collection $ibans) {} * * @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail */ + #[\Override] public function validate(string $attribute, mixed $value, Closure $fail): void { foreach ($this->ibans as $iban) { diff --git a/app/Rules/CsvTransactionImport/MoneyColumnRule.php b/app/Rules/CsvTransactionImport/MoneyColumnRule.php index 4f76b09a..984c9b62 100644 --- a/app/Rules/CsvTransactionImport/MoneyColumnRule.php +++ b/app/Rules/CsvTransactionImport/MoneyColumnRule.php @@ -15,6 +15,7 @@ public function __construct(private readonly Collection $column) {} * * @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail */ + #[\Override] public function validate(string $attribute, mixed $value, Closure $fail): void { diff --git a/app/Rules/IbanRule.php b/app/Rules/IbanRule.php index 8f879047..91b39138 100644 --- a/app/Rules/IbanRule.php +++ b/app/Rules/IbanRule.php @@ -7,6 +7,7 @@ class IbanRule implements ValidationRule { + #[\Override] public function validate(string $attribute, mixed $value, Closure $fail): void { if (! verify_iban($value)) { diff --git a/app/Services/Auth/LocalAuthService.php b/app/Services/Auth/LocalAuthService.php index ef7dd7e3..221a726e 100644 --- a/app/Services/Auth/LocalAuthService.php +++ b/app/Services/Auth/LocalAuthService.php @@ -9,11 +9,13 @@ class LocalAuthService extends AuthService { + #[\Override] public function prepareLogin(): Response|RedirectResponse { return redirect()->route('login.callback'); } + #[\Override] public function userFromCallback(Request $request): array { return [ @@ -22,6 +24,7 @@ public function userFromCallback(Request $request): array ]; } + #[\Override] public function userCommittees(): Collection { return match (\Auth::user()->username) { @@ -33,6 +36,7 @@ public function userCommittees(): Collection }; } + #[\Override] public function allCommittees(): Collection { return collect([ @@ -41,6 +45,7 @@ public function allCommittees(): Collection ); } + #[\Override] public function userGroupsRaw(): Collection { return match (\Auth::user()->username) { @@ -53,6 +58,7 @@ public function userGroupsRaw(): Collection }; } + #[\Override] public function afterLogout() { return redirect()->route('login.callback'); diff --git a/app/Services/Auth/OidcAuthService.php b/app/Services/Auth/OidcAuthService.php index af96eafa..79bb897e 100644 --- a/app/Services/Auth/OidcAuthService.php +++ b/app/Services/Auth/OidcAuthService.php @@ -29,6 +29,7 @@ public function __construct() } } + #[\Override] public function prepareLogin(): Response|RedirectResponse { // redirect to IdP if unauthenticated @@ -38,6 +39,7 @@ public function prepareLogin(): Response|RedirectResponse return redirect()->to('/auth/callback'); } + #[\Override] public function userFromCallback(Request $request): array { // check response @@ -71,21 +73,25 @@ public function userFromCallback(Request $request): array return [$identifiers, $userAttributes]; } + #[\Override] public function userCommittees(): Collection { return collect(session('oidc.committees')); } + #[\Override] public function userGroupsRaw(): Collection { return collect(session('oidc.groups-raw')); } + #[\Override] public function groupMapping(): Collection { return collect(config('services.oidc.group-mapping')); } + #[\Override] public function afterLogout(): RedirectResponse { \Session::flush(); @@ -93,6 +99,7 @@ public function afterLogout(): RedirectResponse return redirect()->to(config('services.oidc.logout_url')); } + #[\Override] public function allCommittees(): Collection { return collect(session('oidc.all-committees')); diff --git a/app/Services/Auth/StumvAuthService.php b/app/Services/Auth/StumvAuthService.php index 3c29c58d..e93e8201 100644 --- a/app/Services/Auth/StumvAuthService.php +++ b/app/Services/Auth/StumvAuthService.php @@ -19,6 +19,7 @@ private function api(): PendingRequest ->acceptJson(); } + #[\Override] public function prepareLogin(): Response|RedirectResponse { $driver = Socialite::driver('stumv') @@ -27,6 +28,7 @@ public function prepareLogin(): Response|RedirectResponse return $driver->redirect(); } + #[\Override] public function userFromCallback(Request $request): array { $driver = Socialite::driver('stumv'); @@ -65,21 +67,25 @@ public function userFromCallback(Request $request): array return [$identifiers, $userAttributes]; } + #[\Override] public function userCommittees(): Collection { return \Session::remember('stumv.comittees', fn () => $this->api()->get('/api/my/committees')->collect()); } + #[\Override] public function userGroupsRaw(): Collection { return \Session::remember('stumv.groups', fn () => $this->api()->get('/api/my/groups')->collect()); } + #[\Override] public function groupMapping(): Collection { return collect(config('services.stumv.mapping', [])); } + #[\Override] public function afterLogout() { \Session::flush(); @@ -89,6 +95,7 @@ public function afterLogout() ); } + #[\Override] public function allCommittees(): Collection { $community_uid = config('stufis.community_uid'); diff --git a/app/View/Components/Layout.php b/app/View/Components/Layout.php index f532ff65..ecb4cc11 100644 --- a/app/View/Components/Layout.php +++ b/app/View/Components/Layout.php @@ -11,6 +11,7 @@ class Layout extends Component * * @return \Illuminate\Contracts\View\View|\Closure|string */ + #[\Override] public function render() { return view('components.layouts.index'); diff --git a/config/markdown.php b/config/markdown.php index bde2ea9c..57e7a88f 100644 --- a/config/markdown.php +++ b/config/markdown.php @@ -172,16 +172,14 @@ 'default_attributes' => [ Heading::class => [ - 'class' => static function (Heading $node) { - return match ($node->getLevel()) { - 1 => 'text-xl font-semibold text-gray-900 dark:text-white mb-6 mt-8 first:mt-0', - 2 => 'text-lg font-semibold text-gray-900 dark:text-white mb-4 mt-6', - 3 => 'text-lg font-medium text-gray-900 dark:text-white mb-3 mt-5', - 4 => 'text-base font-medium text-gray-900 dark:text-white mb-2 mt-4', - 5 => 'text-base font-medium text-gray-900 dark:text-white mb-2 mt-3', - 6 => 'text-base font-medium text-gray-700 dark:text-gray-300 mb-2 mt-3', - default => 'font-medium text-gray-900 dark:text-white mb-2 mt-3', - }; + 'class' => static fn (Heading $node) => match ($node->getLevel()) { + 1 => 'text-xl font-semibold text-gray-900 dark:text-white mb-6 mt-8 first:mt-0', + 2 => 'text-lg font-semibold text-gray-900 dark:text-white mb-4 mt-6', + 3 => 'text-lg font-medium text-gray-900 dark:text-white mb-3 mt-5', + 4 => 'text-base font-medium text-gray-900 dark:text-white mb-2 mt-4', + 5 => 'text-base font-medium text-gray-900 dark:text-white mb-2 mt-3', + 6 => 'text-base font-medium text-gray-700 dark:text-gray-300 mb-2 mt-3', + default => 'font-medium text-gray-900 dark:text-white mb-2 mt-3', }, ], // Paragraphs with proper spacing diff --git a/routes/breadcrumbs.php b/routes/breadcrumbs.php index aa661165..4b3844ef 100644 --- a/routes/breadcrumbs.php +++ b/routes/breadcrumbs.php @@ -14,158 +14,158 @@ */ // Home -Breadcrumbs::for('legacy.dashboard', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.dashboard', static function (BreadcrumbTrail $trail): void { $trail->push('Home', route('legacy.dashboard', 'mygremium')); }); // Home > TODOS -Breadcrumbs::for('legacy.todo.belege', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.todo.belege', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.todo'), route('legacy.todo.belege')); }); -Breadcrumbs::for('legacy.todo.hv', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.todo.hv', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.todo'), route('legacy.todo.belege')); }); -Breadcrumbs::for('legacy.todo.kv', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.todo.kv', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.todo'), route('legacy.todo.belege')); }); -Breadcrumbs::for('legacy.todo.kv.bank', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.todo.kv.bank', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.todo'), route('legacy.todo.belege')); }); // Home > Booking -Breadcrumbs::for('legacy.booking', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.booking', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.booking'), route('legacy.booking')); }); -Breadcrumbs::for('legacy.booking.instruct', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.booking.instruct', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.booking.text'), route('legacy.booking')); }); -Breadcrumbs::for('legacy.booking.text', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.booking.text', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.booking.text'), route('legacy.booking')); }); -Breadcrumbs::for('legacy.booking.history', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.booking.history', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.booking.history'), route('legacy.booking')); }); // Home > Konto -Breadcrumbs::for('legacy.konto', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.konto', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.konto'), route('legacy.konto')); }); // Home > Konto > New -Breadcrumbs::for('bank-account.new', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('bank-account.new', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.konto'); $trail->push(__('general.breadcrumb.konto.new'), route('bank-account.new')); }); // Home > Konto > Import -Breadcrumbs::for('bank-account.import.csv', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('bank-account.import.csv', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.konto'); $trail->push(__('general.breadcrumb.konto.import.csv'), route('bank-account.import.csv')); }); // Home > Konto > Credentials -Breadcrumbs::for('legacy.konto.credentials', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.konto.credentials', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.konto'); $trail->push(__('general.breadcrumb.konto.credentials'), route('legacy.konto.credentials')); }); // Home > Konto > Credentials -Breadcrumbs::for('legacy.konto.credentials.new', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.konto.credentials.new', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.konto.credentials'); $trail->push(__('general.breadcrumb.konto.credentials.new'), route('legacy.konto.credentials.new')); }); // Home > Konto > Credentials > Login -Breadcrumbs::for('legacy.konto.credentials.login', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.konto.credentials.login', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.konto.credentials'); $trail->push(__('general.breadcrumb.konto.login')); }); // Home > Konto > Credentials > TAN Mode -Breadcrumbs::for('legacy.konto.credentials.tan-mode', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.konto.credentials.tan-mode', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.konto.credentials'); $trail->push(__('general.breadcrumb.konto.tan-mode')); }); // Home > Konto > Credentials > Sepa -Breadcrumbs::for('legacy.konto.credentials.sepa', static function (BreadcrumbTrail $trail, $credential_id) { +Breadcrumbs::for('legacy.konto.credentials.sepa', static function (BreadcrumbTrail $trail, $credential_id): void { $trail->parent('legacy.konto.credentials'); $trail->push(__('general.breadcrumb.konto.sepa'), route('legacy.konto.credentials.sepa', $credential_id)); }); // Home > Konto > Credentials > Sepa -Breadcrumbs::for('legacy.konto.credentials.import-konto', static function (BreadcrumbTrail $trail, $credential_id, $shortIban) { +Breadcrumbs::for('legacy.konto.credentials.import-konto', static function (BreadcrumbTrail $trail, $credential_id, $shortIban): void { $trail->parent('legacy.konto.credentials.sepa', $credential_id); $trail->push(__('general.breadcrumb.konto.import-konto')); }); // Home > Sitzung -Breadcrumbs::for('legacy.sitzung', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.sitzung', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.sitzung'), route('legacy.sitzung')); }); // Home > HHP -Breadcrumbs::for('legacy.hhp', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.hhp', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.budget-plan'), route('legacy.hhp')); }); // Home > HHP > Import -Breadcrumbs::for('legacy.hhp.import', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.hhp.import', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.hhp'); $trail->push(__('general.breadcrumb.budget-plan-import'), route('legacy.hhp.import')); }); // Home > HHP > $hhp_id -Breadcrumbs::for('legacy.hhp.view', static function (BreadcrumbTrail $trail, $hhp_id) { +Breadcrumbs::for('legacy.hhp.view', static function (BreadcrumbTrail $trail, $hhp_id): void { $trail->parent('legacy.hhp'); $trail->push($hhp_id, route('legacy.hhp.view', $hhp_id)); }); // Home > HHP > $hhp_id > Titel-Details -Breadcrumbs::for('legacy.hhp.titel.view', static function (BreadcrumbTrail $trail, int $hhp_id, int $title_id) { +Breadcrumbs::for('legacy.hhp.titel.view', static function (BreadcrumbTrail $trail, int $hhp_id, int $title_id): void { $trail->parent('legacy.hhp.view', $hhp_id); $trail->push(__('general.breadcrumb.hhp-title-details'), route('legacy.hhp.titel.view', [$hhp_id, $title_id])); }); // Home > Projekt > PID -Breadcrumbs::for('legacy.projekt', static function (BreadcrumbTrail $trail, $projekt_id) { +Breadcrumbs::for('legacy.projekt', static function (BreadcrumbTrail $trail, $projekt_id): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.projekt')); $trail->push($projekt_id, route('legacy.projekt', $projekt_id)); }); // Home > Projekt > PID > Abrechnung > AID -Breadcrumbs::for('legacy.expense-long', static function (BreadcrumbTrail $trail, $projekt_id, $auslagen_id) { +Breadcrumbs::for('legacy.expense-long', static function (BreadcrumbTrail $trail, $projekt_id, $auslagen_id): void { $trail->parent('legacy.projekt', $projekt_id); $trail->push(__('general.breadcrumb.abrechnung')); $trail->push($auslagen_id, route('legacy.expense', $auslagen_id)); }); // Home > Projekt > PID > Abrechnung > AID > BelegePDF -Breadcrumbs::for('legacy.belege-pdf', static function (BreadcrumbTrail $trail, $projekt_id, $auslagen_id, $version) { +Breadcrumbs::for('legacy.belege-pdf', static function (BreadcrumbTrail $trail, $projekt_id, $auslagen_id, $version): void { $trail->parent('legacy.expense-long', $projekt_id, $auslagen_id); $trail->push(__('general.breadcrumb.belege-pdf')); }); // Home > Projekt > PID > Abrechnung > AID > Zahlungsanweisung -Breadcrumbs::for('legacy.zahlungsanweisung-pdf', static function (BreadcrumbTrail $trail, $projekt_id, $auslagen_id, $version) { +Breadcrumbs::for('legacy.zahlungsanweisung-pdf', static function (BreadcrumbTrail $trail, $projekt_id, $auslagen_id, $version): void { $trail->parent('legacy.expense-long', $projekt_id, $auslagen_id); $trail->push(__('general.breadcrumb.zahlungsanweisung-pdf')); }); @@ -175,19 +175,19 @@ */ // Home > Budget-Plans -Breadcrumbs::for('budget-plan.index', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('budget-plan.index', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.budget-plan'), route('budget-plan.index')); }); // Home > Budget-Plans > ID -Breadcrumbs::for('budget-plan.view', static function (BreadcrumbTrail $trail, $plan_id) { +Breadcrumbs::for('budget-plan.view', static function (BreadcrumbTrail $trail, $plan_id): void { $trail->parent('budget-plan.index'); $trail->push($plan_id, route('budget-plan.view', $plan_id)); }); // Home > Budget-Plans > ID -Breadcrumbs::for('budget-plan.edit', static function (BreadcrumbTrail $trail, $plan_id) { +Breadcrumbs::for('budget-plan.edit', static function (BreadcrumbTrail $trail, $plan_id): void { $trail->parent('budget-plan.view', $plan_id); $trail->push(__('general.breadcrumb.budget-plan-edit'), route('budget-plan.edit', $plan_id)); }); From 84588086628b851e0ce3183bf600b956c66e9881 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 13:50:03 +0200 Subject: [PATCH 011/108] rector ci --- .github/workflows/rector.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/rector.yml diff --git a/.github/workflows/rector.yml b/.github/workflows/rector.yml new file mode 100644 index 00000000..607c4605 --- /dev/null +++ b/.github/workflows/rector.yml @@ -0,0 +1,31 @@ +name: Rector Code Style Check +on: + - push +jobs: + rector: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + extensions: mbstring, intl + ini-values: post_max_size=256M, max_execution_time=180 + coverage: xdebug + tools: php-cs-fixer, phpunit + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - name: Setup composer cache + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + - name: Install composer dependencies + env: + COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }} + run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + - name: Run rector check + run: php ./vendor/bin/rector --dry-run From 8f51f34791e13fc5cbd9913006d242230e98a4e5 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 13:56:05 +0200 Subject: [PATCH 012/108] cache rector --- .github/workflows/rector.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rector.yml b/.github/workflows/rector.yml index 607c4605..162dd200 100644 --- a/.github/workflows/rector.yml +++ b/.github/workflows/rector.yml @@ -27,5 +27,14 @@ jobs: env: COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }} run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist - - name: Run rector check - run: php ./vendor/bin/rector --dry-run + - name: Rector Cache + uses: actions/cache@v4 + with: + path: /tmp/rector + key: ${{ runner.os }}-rector-${{ github.run_id }} + restore-keys: ${{ runner.os }}-rector- + + - run: mkdir -p /tmp/rector + + - name: Rector Dry Run + run: php vendor/bin/rector process --dry-run --config=rector.php From fbfe3180edd9757e5c299ced7fd210ed7c57185a Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 13:56:12 +0200 Subject: [PATCH 013/108] test rector ci --- routes/breadcrumbs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/breadcrumbs.php b/routes/breadcrumbs.php index 4b3844ef..3992c96f 100644 --- a/routes/breadcrumbs.php +++ b/routes/breadcrumbs.php @@ -121,7 +121,7 @@ }); // Home > HHP -Breadcrumbs::for('legacy.hhp', static function (BreadcrumbTrail $trail): void { +Breadcrumbs::for('legacy.hhp', static function (BreadcrumbTrail $trail) { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.budget-plan'), route('legacy.hhp')); }); From 018eda1b3efba98fc86d4b0b84f4144c159e05a5 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 13:57:56 +0200 Subject: [PATCH 014/108] using rector cache --- rector.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rector.php b/rector.php index c7b5c1eb..84747424 100644 --- a/rector.php +++ b/rector.php @@ -2,11 +2,19 @@ declare(strict_types=1); +use Rector\Caching\ValueObject\Storage\FileCacheStorage; use Rector\Config\RectorConfig; use RectorLaravel\Set\LaravelLevelSetList; use RectorLaravel\Set\LaravelSetList; return RectorConfig::configure() + ->withCache( + // specify a path that works locally as well as on CI job runners + cacheDirectory: '/tmp/rector', + + // ensure file system caching is used instead of in-memory + cacheClass: FileCacheStorage::class + ) ->withSets([ LaravelLevelSetList::UP_TO_LARAVEL_110, LaravelSetList::LARAVEL_CODE_QUALITY, From 8da453acdfec89c8f7d506f11c7d386bce868b52 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 13:59:01 +0200 Subject: [PATCH 015/108] fix rector ci --- routes/breadcrumbs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/breadcrumbs.php b/routes/breadcrumbs.php index 3992c96f..4b3844ef 100644 --- a/routes/breadcrumbs.php +++ b/routes/breadcrumbs.php @@ -121,7 +121,7 @@ }); // Home > HHP -Breadcrumbs::for('legacy.hhp', static function (BreadcrumbTrail $trail) { +Breadcrumbs::for('legacy.hhp', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); $trail->push(__('general.breadcrumb.budget-plan'), route('legacy.hhp')); }); From 0d34f4387f5f87a83cfaa1e49a13d9a5fb5e7d86 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 14:03:18 +0200 Subject: [PATCH 016/108] fix new-project button link --- resources/views/components/layouts/index.blade.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/views/components/layouts/index.blade.php b/resources/views/components/layouts/index.blade.php index c6f28ca5..61f26236 100644 --- a/resources/views/components/layouts/index.blade.php +++ b/resources/views/components/layouts/index.blade.php @@ -213,7 +213,9 @@ class="absolute top-1 right-0 -mr-14 p-1"> -->
- {{ __('general.new-project-button') }} + + {{ __('general.new-project-button') }} +
From 38ae6b127c844bc811c5d556762a8ac2ea4d20fb Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 14:05:03 +0200 Subject: [PATCH 017/108] fix a breadcrumb --- routes/breadcrumbs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/breadcrumbs.php b/routes/breadcrumbs.php index 4b3844ef..4d95acde 100644 --- a/routes/breadcrumbs.php +++ b/routes/breadcrumbs.php @@ -47,7 +47,7 @@ Breadcrumbs::for('legacy.booking.instruct', static function (BreadcrumbTrail $trail): void { $trail->parent('legacy.dashboard'); - $trail->push(__('general.breadcrumb.booking.text'), route('legacy.booking')); + $trail->push(__('general.breadcrumb.booking'), route('legacy.booking')); }); Breadcrumbs::for('legacy.booking.text', static function (BreadcrumbTrail $trail): void { From 64c6dc1590813eca4d7a762e015a4c089e8e5d21 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 14:09:23 +0200 Subject: [PATCH 018/108] typo --- lang/de/general.php | 1 + resources/views/components/layouts/index.blade.php | 2 +- routes/breadcrumbs.php | 7 +++++++ routes/legacy.php | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lang/de/general.php b/lang/de/general.php index 0730bfc2..b673cfba 100644 --- a/lang/de/general.php +++ b/lang/de/general.php @@ -21,6 +21,7 @@ 'breadcrumb.budget-plan-import' => 'Importieren', 'breadcrumb.hhp-title-details' => 'Haushaltstitel Details', 'breadcrumb.projekt' => 'Projekt', + 'breadcrumb.projekt-new' => 'Neu', 'breadcrumb.abrechnung' => 'Abrechnung', 'breadcrumb.belege-pdf' => 'Belege-PDF', 'breadcrumb.zahlungsanweisung-pdf' => 'Zahlungsanweisung-PDF', diff --git a/resources/views/components/layouts/index.blade.php b/resources/views/components/layouts/index.blade.php index 61f26236..3ee3f14e 100644 --- a/resources/views/components/layouts/index.blade.php +++ b/resources/views/components/layouts/index.blade.php @@ -213,7 +213,7 @@ class="absolute top-1 right-0 -mr-14 p-1"> -->
- + {{ __('general.new-project-button') }}
diff --git a/routes/breadcrumbs.php b/routes/breadcrumbs.php index 4d95acde..6343fbad 100644 --- a/routes/breadcrumbs.php +++ b/routes/breadcrumbs.php @@ -144,6 +144,13 @@ $trail->push(__('general.breadcrumb.hhp-title-details'), route('legacy.hhp.titel.view', [$hhp_id, $title_id])); }); +// Home > Projekt > New +Breadcrumbs::for('legacy.new-projekt', static function (BreadcrumbTrail $trail): void { + $trail->parent('legacy.dashboard'); + $trail->push(__('general.breadcrumb.projekt')); + $trail->push(__('general.breadcrumb.projekt-new'), route('legacy.new-projekt')); +}); + // Home > Projekt > PID Breadcrumbs::for('legacy.projekt', static function (BreadcrumbTrail $trail, $projekt_id): void { $trail->parent('legacy.dashboard'); diff --git a/routes/legacy.php b/routes/legacy.php index 07c5010f..1969adca 100644 --- a/routes/legacy.php +++ b/routes/legacy.php @@ -28,7 +28,7 @@ Route::get('hhp/import', [LegacyController::class, 'render'])->name('hhp.import'); Route::get('hhp/{hhp_id}', [LegacyController::class, 'render'])->name('hhp.view'); Route::get('hhp/{hhp_id}/titel/{titel_id}', [LegacyController::class, 'render'])->name('hhp.titel.view'); - Route::get('projekt/create', [LegacyController::class, 'render'])->name('new-project'); + Route::get('projekt/create', [LegacyController::class, 'render'])->name('new-projekt'); Route::get('projekt/{projekt_id}', [LegacyController::class, 'render'])->name('projekt'); Route::get('projekt/{projekt_id}/auslagen/{auslagen_id}', [LegacyController::class, 'render'])->name('expense-long'); From ee16b0fa27a73b676757360f31418c9e1fd5995e Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 14:15:51 +0200 Subject: [PATCH 019/108] improved comment --- rector.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rector.php b/rector.php index 84747424..c8f395ef 100644 --- a/rector.php +++ b/rector.php @@ -31,8 +31,7 @@ __DIR__.'/routes', __DIR__.'/tests', ]) - // uncomment to reach your current PHP version - ->withPhpSets() + ->withPhpSets() // defaults to the php version from composer ->withTypeCoverageLevel(0) ->withDeadCodeLevel(0) ->withCodeQualityLevel(0); From 76c5e369eae89f508360c6a067d562aab1b3fe12 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 14:17:24 +0200 Subject: [PATCH 020/108] changelog --- docs/feature-changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index 8b0b3b71..1a5eee8f 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -1,3 +1,7 @@ +# v4.3.2 +* **Brotkrumen wurden hinzugefügt:** Es ist nun links oben ersichtlich, wo du dich innerhalb StuFiS befindest und kannst schneller innerhalb der Struktur die Seite wechseln. Der Projekt-Button ist nun nach rechts oben gewandert. +* **Testverbesserungen** +* **Verschiedene Fehlerbehebungen** # v4.3.1 * Profilbilder können aus dem SSO übernommen werden From b13889bb5c163b2455c813d185940f7ec7c35f5a Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 12 Aug 2025 14:19:36 +0200 Subject: [PATCH 021/108] version bump + dep updates --- composer.json | 2 +- composer.lock | 120 +++++++++++++++++++++++++--------------------- package-lock.json | 18 +++---- 3 files changed, 76 insertions(+), 64 deletions(-) diff --git a/composer.json b/composer.json index 8c3685c2..87f9566c 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "openadministration/stufis", "type": "project", "description": "Webinterface für das Management und Digitalisierung von Finanzanträgen und deren Buchung für Studierendenschaften nach Deutschem Recht", - "version": "4.3.1", + "version": "4.3.2", "license": "AGPL", "require": { "php": "^8.4", diff --git a/composer.lock b/composer.lock index db98e711..1e3e7181 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "07fa28e9410095126480521c45d434fb", + "content-hash": "efaa8ff1900c745113c95a9470a11bef", "packages": [ { "name": "archtechx/helpers", @@ -764,33 +764,32 @@ }, { "name": "doctrine/inflector", - "version": "2.0.10", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11.0", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25 || ^5.4" + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + "Doctrine\\Inflector\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -835,7 +834,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.10" + "source": "https://github.com/doctrine/inflector/tree/2.1.0" }, "funding": [ { @@ -851,7 +850,7 @@ "type": "tidelift" } ], - "time": "2024-02-18T20:23:39+00:00" + "time": "2025-08-10T19:31:58+00:00" }, { "name": "doctrine/lexer", @@ -3142,16 +3141,16 @@ }, { "name": "livewire/flux", - "version": "v2.2.3", + "version": "v2.2.4", "source": { "type": "git", "url": "https://github.com/livewire/flux.git", - "reference": "0fb4c0b78eac393ad3a19a387af193573c310371" + "reference": "af81b5fd34c6490d5b5e05ed0f8140c0250e5069" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/flux/zipball/0fb4c0b78eac393ad3a19a387af193573c310371", - "reference": "0fb4c0b78eac393ad3a19a387af193573c310371", + "url": "https://api.github.com/repos/livewire/flux/zipball/af81b5fd34c6490d5b5e05ed0f8140c0250e5069", + "reference": "af81b5fd34c6490d5b5e05ed0f8140c0250e5069", "shasum": "" }, "require": { @@ -3199,25 +3198,25 @@ ], "support": { "issues": "https://github.com/livewire/flux/issues", - "source": "https://github.com/livewire/flux/tree/v2.2.3" + "source": "https://github.com/livewire/flux/tree/v2.2.4" }, - "time": "2025-07-11T00:25:51+00:00" + "time": "2025-08-09T01:46:51+00:00" }, { "name": "livewire/flux-pro", - "version": "2.2.3", + "version": "2.2.4", "dist": { "type": "zip", - "url": "https://composer.fluxui.dev/download/9f5c71a4-3991-455e-a2aa-5f090e1ab06b/flux-pro-2.2.3.zip", - "reference": "c5d01f717049e92b6402f0866ff61a7ee0486bcf", - "shasum": "78c16099c98a93191886484f4c60752997f1c91f" + "url": "https://composer.fluxui.dev/download/9f96e524-b277-440e-96f6-6a8f1b41e048/flux-pro-2.2.4.zip", + "reference": "51bf2c15a33e6ca7eddef336ff18fa9b3d952ed3", + "shasum": "cc4207cb6cc365bc0c02c61c9b03797b528ba4fc" }, "require": { "illuminate/console": "^10.0|^11.0|^12.0", "illuminate/support": "^10.0|^11.0|^12.0", "illuminate/view": "^10.0|^11.0|^12.0", "laravel/prompts": "^0.1.24|^0.2|^0.3", - "livewire/flux": "2.2.3|dev-main", + "livewire/flux": "2.2.4|dev-main", "livewire/livewire": "^3.6.2", "php": "^8.1", "symfony/console": "^6.0|^7.0" @@ -3258,7 +3257,7 @@ "livewire", "ui" ], - "time": "2025-07-11T00:32:04+00:00" + "time": "2025-08-09T01:53:03+00:00" }, { "name": "livewire/livewire", @@ -3765,12 +3764,12 @@ "source": { "type": "git", "url": "https://github.com/nemiah/phpFinTS.git", - "reference": "ccdc258856ccda063120ecce8f0be88341e8d415" + "reference": "8719e941e1cb56314c2081745421fc443f649c68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nemiah/phpFinTS/zipball/ccdc258856ccda063120ecce8f0be88341e8d415", - "reference": "ccdc258856ccda063120ecce8f0be88341e8d415", + "url": "https://api.github.com/repos/nemiah/phpFinTS/zipball/8719e941e1cb56314c2081745421fc443f649c68", + "reference": "8719e941e1cb56314c2081745421fc443f649c68", "shasum": "" }, "require": { @@ -3807,7 +3806,7 @@ "issues": "https://github.com/nemiah/phpFinTS/issues", "source": "https://github.com/nemiah/phpFinTS/tree/master" }, - "time": "2025-07-03T15:01:01+00:00" + "time": "2025-08-09T19:37:10+00:00" }, { "name": "nesbot/carbon", @@ -4379,16 +4378,16 @@ }, { "name": "phpoffice/phpspreadsheet", - "version": "1.29.12", + "version": "1.30.0", "source": { "type": "git", "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "7c06eed662cce7ecab88f6f9f7626b443f5285df" + "reference": "2f39286e0136673778b7a142b3f0d141e43d1714" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/7c06eed662cce7ecab88f6f9f7626b443f5285df", - "reference": "7c06eed662cce7ecab88f6f9f7626b443f5285df", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/2f39286e0136673778b7a142b3f0d141e43d1714", + "reference": "2f39286e0136673778b7a142b3f0d141e43d1714", "shasum": "" }, "require": { @@ -4479,9 +4478,9 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.29.12" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.30.0" }, - "time": "2025-07-23T04:40:30+00:00" + "time": "2025-08-10T06:28:02+00:00" }, { "name": "phpoption/phpoption", @@ -9131,16 +9130,16 @@ }, { "name": "filp/whoops", - "version": "2.18.3", + "version": "2.18.4", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "59a123a3d459c5a23055802237cb317f609867e5" + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/59a123a3d459c5a23055802237cb317f609867e5", - "reference": "59a123a3d459c5a23055802237cb317f609867e5", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", "shasum": "" }, "require": { @@ -9190,7 +9189,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.3" + "source": "https://github.com/filp/whoops/tree/2.18.4" }, "funding": [ { @@ -9198,7 +9197,7 @@ "type": "github" } ], - "time": "2025-06-16T00:02:10+00:00" + "time": "2025-08-08T12:00:00+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -11490,12 +11489,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "90fce983a76dd90a53a5b8171a98421aa703d58d" + "reference": "fc5feda437d44655ee5df703bda813e00674b227" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/90fce983a76dd90a53a5b8171a98421aa703d58d", - "reference": "90fce983a76dd90a53a5b8171a98421aa703d58d", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/fc5feda437d44655ee5df703bda813e00674b227", + "reference": "fc5feda437d44655ee5df703bda813e00674b227", "shasum": "" }, "conflict": { @@ -11624,7 +11623,7 @@ "corveda/phpsandbox": "<1.3.5", "cosenary/instagram": "<=2.3", "couleurcitron/tarteaucitron-wp": "<0.3", - "craftcms/cms": "<4.15.3|>=5,<5.7.5", + "craftcms/cms": "<4.16.3|>=5,<5.8.4", "croogo/croogo": "<4", "cuyz/valinor": "<0.12", "czim/file-handling": "<1.5|>=2,<2.3", @@ -12444,7 +12443,7 @@ "type": "tidelift" } ], - "time": "2025-08-06T18:07:51+00:00" + "time": "2025-08-08T20:05:48+00:00" }, { "name": "sebastian/cli-parser", @@ -13192,23 +13191,23 @@ }, { "name": "sebastian/recursion-context", - "version": "5.0.0", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", "shasum": "" }, "require": { "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { @@ -13243,15 +13242,28 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T07:05:40+00:00" + "time": "2025-08-10T07:50:56+00:00" }, { "name": "sebastian/type", diff --git a/package-lock.json b/package-lock.json index 44cb1b2d..70db3d33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -519,9 +519,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", "dependencies": { @@ -540,16 +540,16 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "dev": true, "license": "MIT", "dependencies": { From 1f9ec144f70c2e88e906eea1934cde662c678e5a Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Fri, 5 Sep 2025 11:21:12 +0200 Subject: [PATCH 022/108] fiscal year create + edit --- app/Livewire/FiscalYear/EditFiscalYear.php | 60 ++++++ app/Models/BudgetItem.php | 2 - app/Models/BudgetPlan.php | 5 + app/Models/FiscalYear.php | 8 +- app/Providers/AppServiceProvider.php | 1 + .../dev/2024_04_02_125041_hhp_upgrade.php | 2 + docs/feature-changelog.md | 21 ++ resources/views/budget-plan/index.blade.php | 188 +++++++----------- .../views/flux/table/row-headline.blade.php | 6 +- .../fiscal-year/edit-fiscal-year.blade.php | 18 ++ routes/web-dev.php | 4 +- 11 files changed, 192 insertions(+), 123 deletions(-) create mode 100644 app/Livewire/FiscalYear/EditFiscalYear.php create mode 100644 resources/views/livewire/fiscal-year/edit-fiscal-year.blade.php diff --git a/app/Livewire/FiscalYear/EditFiscalYear.php b/app/Livewire/FiscalYear/EditFiscalYear.php new file mode 100644 index 00000000..edccee8d --- /dev/null +++ b/app/Livewire/FiscalYear/EditFiscalYear.php @@ -0,0 +1,60 @@ +id = $year_id; + if ($this->id) { + // edit + $fiscal_year = FiscalYear::find($this->id); + $this->start_date = $fiscal_year->start_date->format('Y-m-d'); + $this->end_date = $fiscal_year->end_date->format('Y-m-d'); + } else { + // create with suggestions + $lastYear = FiscalYear::orderBy('end_date', 'desc')->limit(1)->first(); + if ($lastYear) { + $this->start_date = $lastYear->end_date->addDay()->format('Y-m-d'); + $this->end_date = $lastYear->end_date->addYear()->format('Y-m-d'); + } + } + } + + public function rules(): array + { + return [ + 'start_date' => 'required|date', + 'end_date' => 'required|date|after_or_equal:start_date', + ]; + } + + public function save() + { + $this->validate(); + FiscalYear::updateOrCreate([ + 'id' => $this->id, + ], [ + 'start_date' => $this->start_date, + 'end_date' => $this->end_date, + ]); + $this->redirect(route('budget-plan.index')); + } + + public function render() + { + return view('livewire.fiscal-year.edit-fiscal-year'); + } +} diff --git a/app/Models/BudgetItem.php b/app/Models/BudgetItem.php index 75760f8b..63f5e798 100644 --- a/app/Models/BudgetItem.php +++ b/app/Models/BudgetItem.php @@ -27,8 +27,6 @@ class BudgetItem extends Model { use HasFactory; - public $timestamps = false; - /** * The table associated with the model. * diff --git a/app/Models/BudgetPlan.php b/app/Models/BudgetPlan.php index 8fa3a0bd..14a053d9 100644 --- a/app/Models/BudgetPlan.php +++ b/app/Models/BudgetPlan.php @@ -64,6 +64,11 @@ public function budgetItems(): \Illuminate\Database\Eloquent\Relations\HasMany return $this->hasMany(BudgetItem::class); } + public function rootBudgetItems() + { + return $this->hasMany(BudgetItem::class)->whereNull('parent_id'); + } + public function fiscalYear(): \Illuminate\Database\Eloquent\Relations\BelongsTo { return $this->belongsTo(FiscalYear::class); diff --git a/app/Models/FiscalYear.php b/app/Models/FiscalYear.php index ca760024..73a57c50 100644 --- a/app/Models/FiscalYear.php +++ b/app/Models/FiscalYear.php @@ -9,8 +9,6 @@ class FiscalYear extends Model { use HasFactory; - public $timestamps = false; - /** * The table associated with the model. * @@ -21,7 +19,11 @@ class FiscalYear extends Model /** * @var array */ - protected $fillable = ['start_date', 'end_date']; + protected $fillable = [ + 'id', + 'start_date', + 'end_date', + ]; #[\Override] protected function casts(): array diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 2b15b665..13cdca19 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -61,6 +61,7 @@ public function bootRoute() Route::pattern('projekt_id', '[0-9]+'); Route::pattern('auslagen_id', '[0-9]+'); Route::pattern('credential_id', '[0-9]+'); + Route::pattern('year_id', '[0-9]+'); } public function registerAuth(): void diff --git a/database/migrations/dev/2024_04_02_125041_hhp_upgrade.php b/database/migrations/dev/2024_04_02_125041_hhp_upgrade.php index fb59e16f..c771ee90 100644 --- a/database/migrations/dev/2024_04_02_125041_hhp_upgrade.php +++ b/database/migrations/dev/2024_04_02_125041_hhp_upgrade.php @@ -12,6 +12,7 @@ public function up(): void $table->id(); $table->date('start_date'); $table->date('end_date'); + $table->timestamps(); }); Schema::create('budget_plan', static function (Blueprint $table) { @@ -43,6 +44,7 @@ public function up(): void $table->foreign('budget_plan_id')->references('id')->on('budget_plan'); $table->foreign('parent_id')->references('id')->on('budget_item'); // $table->text('diff_description'); + $table->timestamps(); }); // TODO: nachtragshhp auch noch hier rein :) diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index 1a5eee8f..46affd00 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -3,11 +3,15 @@ * **Testverbesserungen** * **Verschiedene Fehlerbehebungen** +--- + # v4.3.1 * Profilbilder können aus dem SSO übernommen werden * Routen wurden repariert, die zuvor dazu geführt haben, dass der automatische Bankeinzug auf die falsche Seite weitergeleitet hat. * Ältere nicht genutzte Dienste wurden entfernt. +--- + # v4.3.0 ### Bankgeschäfte und Transaktionen @@ -27,6 +31,7 @@ * **PHP 8.4 Upgrade** --- + # v4.2.6 * **Verbesserungen beim CSV-Kontoupload:** @@ -40,17 +45,20 @@ * **Verschiedene Fehlerbehebungen** --- + # v4.2.5 Änderung der Konfiguration für den StuRa der FH Erfurt --- + # v4.2.4 * **Abrechnung:** Das Löschen von Abrechnungen ohne PDF-Anhang ist jetzt möglich. * **Haushaltsplan:** Grundlage für die komplette Überarbeitung gelegt. --- + # v4.2.3 * **PHP Framework Umstellung:** Aktualisierung auf Laravel 11.x. @@ -59,17 +67,20 @@ * **Verschiedene Fehlerbehebungen und Updates** --- + # v4.2.2 * **Konfiguration:** Anpassungen für den StuRa der FH Erfurt * **Testverbesserungen** --- + # v4.2.1 Behebung eines Fehlers, der das Erstellen von Abrechnungen verhindert hat. --- + # v4.2.0 * **Externe Projekte:** Grundlage für neue Antragsformulare geschaffen @@ -85,28 +96,33 @@ Behebung eines Fehlers, der das Erstellen von Abrechnungen verhindert hat. * **Verschiedene Fehlerbehebungen** --- + # v4.1.6 * **Konfiguration:** Anpassungen für den StuRa der FH Erfurt * **Fehlerbehebung im Haushaltsplan:** Einnahmen werden wieder berücksichtigt, auch wenn in der Abrechnung sowohl Einnahmen als auch Ausgaben vorhanden sind. --- + # v4.1.5 * **Konfiguration:** Anpassungen für die StuRa der FH Erfurt und der EAH Jena * Aktualisierung der Readme-Datei --- + # v4.1.4 Berechnung der Bargeld- und Transferkonten korrigiert. --- + # v4.1.3 Neue Validierung für die Datumssortierung im Konto-CSV-Import. --- + # v4.1.2 * **Angepasste Texte im CSV-Import:** Texte im CSV-Import wurden angepasst, um Verwirrung zu reduzieren. @@ -114,16 +130,19 @@ Neue Validierung für die Datumssortierung im Konto-CSV-Import. * **Verbesserte Datumsanzeige** --- + # v4.1.1 Fehlerbehebungen im Konto-CSV-Import. --- + # v4.1.0 Import von Kontoumsätzen durch CSV-Upload eingeführt. --- + # v4.0.2 * **Automatisches Update mit Backup:** Ein automatisches Update-System inklusive Backup-Funktion wurde implementiert. @@ -131,6 +150,7 @@ Import von Kontoumsätzen durch CSV-Upload eingeführt. * **Versionsnummer in der Benutzeroberfläche:** Die aktuelle Versionsnummer wird nun sichtbar links unten im Menü angezeigt. --- + # v4.0.1 * Bessere Fehlermeldung in Projekten bei fehlenden Haushaltstiteln @@ -138,6 +158,7 @@ Import von Kontoumsätzen durch CSV-Upload eingeführt. * **Verschiedene Fehlerbehebungen** --- + # v4.0.0 ### Verwaltung und neue Funktionen diff --git a/resources/views/budget-plan/index.blade.php b/resources/views/budget-plan/index.blade.php index 089e0a43..71c3260a 100644 --- a/resources/views/budget-plan/index.blade.php +++ b/resources/views/budget-plan/index.blade.php @@ -1,121 +1,77 @@ -
- - @if($years->isEmpty() && $orphaned_plans->isEmpty()) - - - - {{ __('budget-plan.index.no-plans') }} - - - @else -
-
    - @foreach($years as $year) -
    - {{ $year->start_date->format('F y') }} - {{ $year->end_date->format('F y') }} -
    - @forelse($year->budgetPlans as $plan) -
  • - -
    -
    -
    -

    - {{ $plan->organization }} -

    -
    -

    - {{ $plan->state }} -

    -
    -
    -
    -
    -

    - - {{ money_format(100000 + $plan->id * 3807.85) }} -

    - -
    -
    -
    -
    - -
    -
    -
    -
  • - @empty -
  • -
    -
    -
    -

    - {{ __('budget-plan.index.no-entries') }} -

    -
    -
    +
    + + {{ __('budget-plan.index.headline') }} + + + {{ __('budget-plan.index.button.new') }} + + {{ __('budget-plan.budget-plan') }} + {{ __('budget-plan.fiscal-year') }} + + + + + + + {{ __('budget-plan.budget-plans') }} + {{ __('budget-plan.index.table.state') }} + {{ __('budget-plan.index.table.actions') }} + -
    -
  • - @endforelse + + @foreach($years as $year) + + {{ __('budget-plan.fiscal-year') }} {{ $year->start_date->format('M y') }} to {{ $year->end_date->format('M y') }} + + + @foreach($year->budgetPlans as $plan) + + + {{ __('budget-plan.fiscal-year') }} {{ $plan->id }} + + + + {{ $plan->state }} + + + + + + + + + + + @endforeach -
-
- @endif - @if($orphaned_plans->isNotEmpty()) -
- -
- @endif + @endforeach + @if($orphaned_plans->isNotEmpty()) + + Pläne ohhneee HHHHJ + + @endif + @foreach($orphaned_plans as $plan) + + + {{ __('budget-plan.plan?') }} {{ $plan->id }} + + + + {{ $plan->state }} + + + + + + + + + + + + @endforeach + +
diff --git a/resources/views/flux/table/row-headline.blade.php b/resources/views/flux/table/row-headline.blade.php index 8ab1429a..a3efc1fe 100644 --- a/resources/views/flux/table/row-headline.blade.php +++ b/resources/views/flux/table/row-headline.blade.php @@ -3,5 +3,9 @@ ]) merge(['class' => 'bg-zinc-200']) }} data-flux-row> - {{ $slot }} + +
+ {{ $slot }} +
+ diff --git a/resources/views/livewire/fiscal-year/edit-fiscal-year.blade.php b/resources/views/livewire/fiscal-year/edit-fiscal-year.blade.php new file mode 100644 index 00000000..85e02bfd --- /dev/null +++ b/resources/views/livewire/fiscal-year/edit-fiscal-year.blade.php @@ -0,0 +1,18 @@ +
+ + Test + + Lorem ipsum + + +
+ + +
+
+ + Speichern + +
+ +
diff --git a/routes/web-dev.php b/routes/web-dev.php index 4b8e1252..950b2927 100644 --- a/routes/web-dev.php +++ b/routes/web-dev.php @@ -12,6 +12,8 @@ Route::get('plan/create', [\App\Http\Controllers\BudgetPlanController::class, 'create'])->name('budget-plan.create'); Route::get('plan/{plan_id}', [\App\Http\Controllers\BudgetPlanController::class, 'show'])->name('budget-plan.view'); Route::get('plan/{plan_id}/edit', \App\Livewire\BudgetPlan\BudgetPlanEdit::class)->name('budget-plan.edit'); - // Route::get('plan/{plan_id}/edit', \App\Http\Livewire\BudgetPlanLivewire::class)->name('budget-plan.edit'); + + Route::get('year/create', \App\Livewire\FiscalYear\EditFiscalYear::class)->name('fiscal-year.create'); + Route::get('year/{year_id}', \App\Livewire\FiscalYear\EditFiscalYear::class)->name('fiscal-year.edit'); }); From c69594c149ee955a308f131574ca4a046feca120 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Fri, 19 Sep 2025 15:21:12 +0200 Subject: [PATCH 023/108] pre money experiments commit --- app/Http/Controllers/BudgetPlanController.php | 9 +- app/Livewire/BudgetPlan/BudgetPlanEdit.php | 36 +- app/Models/BudgetPlan.php | 2 +- composer.lock | 629 ++++++++++-------- rebuild-stufis.sh | 5 +- resources/views/budget-plan/view.blade.php | 71 +- .../budgetplan/item-group-edit.blade.php | 95 +++ .../item-group-view.blade.php} | 0 8 files changed, 545 insertions(+), 302 deletions(-) create mode 100644 resources/views/components/budgetplan/item-group-edit.blade.php rename resources/views/components/{item-group.blade.php => budgetplan/item-group-view.blade.php} (100%) diff --git a/app/Http/Controllers/BudgetPlanController.php b/app/Http/Controllers/BudgetPlanController.php index d2a5f396..0ce4abaf 100644 --- a/app/Http/Controllers/BudgetPlanController.php +++ b/app/Http/Controllers/BudgetPlanController.php @@ -21,7 +21,14 @@ public function show(int $plan_id) { $plan = BudgetPlan::findOrFail($plan_id); - return view('budget-plan.view', ['plan' => $plan]); + $items = [ + BudgetType::EXPENSE->slug() => $plan->rootBudgetItems()->with('children') + ->where('budget_type', BudgetType::EXPENSE)->get(), + BudgetType::INCOME->slug() => $plan->rootBudgetItems()->with('children') + ->where('budget_type', BudgetType::INCOME)->get(), + ]; + + return view('budget-plan.view', ['plan' => $plan, 'items' => $items]); } public function create() diff --git a/app/Livewire/BudgetPlan/BudgetPlanEdit.php b/app/Livewire/BudgetPlan/BudgetPlanEdit.php index 221b8d43..345d502a 100644 --- a/app/Livewire/BudgetPlan/BudgetPlanEdit.php +++ b/app/Livewire/BudgetPlan/BudgetPlanEdit.php @@ -90,20 +90,36 @@ public function render() ]); } - public function updatedItems($value, $property): void + /** + * Handle the updated event for an item's property. + * This method processes changes to item properties and updates the corresponding record in the database. + * If the updated property is `value`, it triggers a recalculation of the item's and its parents values. + * After the update, the method refreshes the component state. + * + * @param mixed $value The new value for the item's property. + * @param string $property The property identifier in the format "item_id.property_name". + */ + public function updatedItems(mixed $value, string $property): void { - [$item_id, $item_prop] = explode('.', (string) $property, 2); + [$item_id, $item_prop] = explode('.', $property, 2); if (in_array($item_prop, ['short_name', 'name', 'value'])) { $item = BudgetItem::findOrFail($item_id); $item->update([$item_prop => $value]); if ($item_prop === 'value') { $this->reSumItemValues($item); } - Flux::toast('Your changes have been saved.', variant: 'success'); + Flux::toast('FIXME: Your changes have been saved.', variant: 'success'); $this->refresh(); } } + /** + * Recalculate and update the values of parent budget items by summing the values of their child items. + * This method propagates updates upwards through the hierarchy of budget items, starting from a given leaf item. + * Each parent's value is recalculated based on the sum of its direct children's values, and the changes are saved to the database. + * + * @param BudgetItem $leafItem The leaf budget item from which the upward recalculation begins. + */ public function reSumItemValues(BudgetItem $leafItem): void { $item = $leafItem; @@ -118,7 +134,15 @@ public function reSumItemValues(BudgetItem $leafItem): void } } - public function updated($property): void + /** + * Handle the updated event for the specified property. + * This method is called whenever a property is updated. + * It updates the corresponding property in the model and saves the changes. + * Only the meta-data directly in the BudgetPlan Model is updated here. + * + * @param string $property The property name that has been updated. + */ + public function updated(string $property): void { if (in_array($property, ['organization', 'fiscal_year_id', 'resolution_date', 'approval_date'])) { $value = $this->$property; @@ -126,7 +150,7 @@ public function updated($property): void $plan->update([ $property => $value, ]); - Flux::toast(text: "$property -> $value", heading: 'Your changes have been saved.', variant: 'success'); + Flux::toast(text: "$property -> $value", heading: 'FIXME: Your changes have been saved.', variant: 'success'); } } @@ -172,7 +196,7 @@ public function save() 'organization' => $this->organization, ]); - return $this->redirect(route('budget-plan.index')); + $this->redirect(route('budget-plan.view', $this->plan_id)); } public function addGroup(BudgetType $budget_type): void diff --git a/app/Models/BudgetPlan.php b/app/Models/BudgetPlan.php index 14a053d9..2f41e062 100644 --- a/app/Models/BudgetPlan.php +++ b/app/Models/BudgetPlan.php @@ -64,7 +64,7 @@ public function budgetItems(): \Illuminate\Database\Eloquent\Relations\HasMany return $this->hasMany(BudgetItem::class); } - public function rootBudgetItems() + public function rootBudgetItems(): Builder|\Illuminate\Database\Eloquent\Relations\HasMany|BudgetPlan { return $this->hasMany(BudgetItem::class)->whereNull('parent_id'); } diff --git a/composer.lock b/composer.lock index 1e3e7181..b30ce242 100644 --- a/composer.lock +++ b/composer.lock @@ -263,25 +263,25 @@ }, { "name": "brick/math", - "version": "0.12.3", + "version": "0.14.0", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", + "url": "https://api.github.com/repos/brick/math/zipball/113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.2" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.1", - "vimeo/psalm": "6.8.8" + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" }, "type": "library", "autoload": { @@ -311,7 +311,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.3" + "source": "https://github.com/brick/math/tree/0.14.0" }, "funding": [ { @@ -319,7 +319,7 @@ "type": "github" } ], - "time": "2025-02-28T13:11:00+00:00" + "time": "2025-08-29T12:40:03+00:00" }, { "name": "carbonphp/carbon-doctrine-types", @@ -471,16 +471,16 @@ }, { "name": "composer/semver", - "version": "3.4.3", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { @@ -532,7 +532,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.3" + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { @@ -542,13 +542,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2024-09-19T14:15:21+00:00" + "time": "2025-08-20T19:15:30+00:00" }, { "name": "defuse/php-encryption", @@ -1311,16 +1307,16 @@ }, { "name": "giggsey/libphonenumber-for-php-lite", - "version": "9.0.11", + "version": "9.0.14", "source": { "type": "git", "url": "https://github.com/giggsey/libphonenumber-for-php-lite.git", - "reference": "5c573a4a555050c887f4a3bedccbb9358b50f9eb" + "reference": "af794cc2ed18edeebadf5ffe08eb6add04275709" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/5c573a4a555050c887f4a3bedccbb9358b50f9eb", - "reference": "5c573a4a555050c887f4a3bedccbb9358b50f9eb", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/af794cc2ed18edeebadf5ffe08eb6add04275709", + "reference": "af794cc2ed18edeebadf5ffe08eb6add04275709", "shasum": "" }, "require": { @@ -1385,7 +1381,7 @@ "issues": "https://github.com/giggsey/libphonenumber-for-php-lite/issues", "source": "https://github.com/giggsey/libphonenumber-for-php-lite" }, - "time": "2025-08-04T08:43:07+00:00" + "time": "2025-09-16T07:09:25+00:00" }, { "name": "globalcitizen/php-iban", @@ -1566,22 +1562,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.9.3", + "version": "7.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.3", - "guzzlehttp/psr7": "^2.7.0", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -1672,7 +1668,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.3" + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" }, "funding": [ { @@ -1688,20 +1684,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:37:11+00:00" + "time": "2025-08-23T22:36:01+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" + "reference": "481557b130ef3790cf82b713667b43030dc9c957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", "shasum": "" }, "require": { @@ -1709,7 +1705,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "type": "library", "extra": { @@ -1755,7 +1751,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.2.0" + "source": "https://github.com/guzzle/promises/tree/2.3.0" }, "funding": [ { @@ -1771,20 +1767,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:27:01+00:00" + "time": "2025-08-22T14:34:08+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.1", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", "shasum": "" }, "require": { @@ -1800,7 +1796,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1871,7 +1867,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.1" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, "funding": [ { @@ -1887,20 +1883,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T12:30:47+00:00" + "time": "2025-08-23T21:21:41+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.4", + "version": "v1.0.5", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "30e286560c137526eccd4ce21b2de477ab0676d2" + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/30e286560c137526eccd4ce21b2de477ab0676d2", - "reference": "30e286560c137526eccd4ce21b2de477ab0676d2", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", "shasum": "" }, "require": { @@ -1909,7 +1905,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", "uri-template/tests": "1.0.0" }, "type": "library", @@ -1957,7 +1953,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.4" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" }, "funding": [ { @@ -1973,7 +1969,7 @@ "type": "tidelift" } ], - "time": "2025-02-03T10:55:03+00:00" + "time": "2025-08-22T14:27:06+00:00" }, { "name": "intervention/validation", @@ -2107,20 +2103,20 @@ }, { "name": "laravel/framework", - "version": "v11.45.1", + "version": "v11.46.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "b09ba32795b8e71df10856a2694706663984a239" + "reference": "2c6d85f22d08123ad45aa3a6726b16f06e68eecd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/b09ba32795b8e71df10856a2694706663984a239", - "reference": "b09ba32795b8e71df10856a2694706663984a239", + "url": "https://api.github.com/repos/laravel/framework/zipball/2c6d85f22d08123ad45aa3a6726b16f06e68eecd", + "reference": "2c6d85f22d08123ad45aa3a6726b16f06e68eecd", "shasum": "" }, "require": { - "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", + "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12|^0.13|^0.14", "composer-runtime-api": "^2.2", "doctrine/inflector": "^2.0.5", "dragonmantank/cron-expression": "^3.4", @@ -2224,7 +2220,7 @@ "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "orchestra/testbench-core": "^9.13.2", + "orchestra/testbench-core": "^9.16.1", "pda/pheanstalk": "^5.0.6", "php-http/discovery": "^1.15", "phpstan/phpstan": "^2.0", @@ -2318,7 +2314,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-06-03T14:01:40+00:00" + "time": "2025-09-08T21:54:34+00:00" }, { "name": "laravel/prompts", @@ -3141,16 +3137,16 @@ }, { "name": "livewire/flux", - "version": "v2.2.4", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/livewire/flux.git", - "reference": "af81b5fd34c6490d5b5e05ed0f8140c0250e5069" + "reference": "8d83f34d64ab0542463e8e3feab4d166e1830ed9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/flux/zipball/af81b5fd34c6490d5b5e05ed0f8140c0250e5069", - "reference": "af81b5fd34c6490d5b5e05ed0f8140c0250e5069", + "url": "https://api.github.com/repos/livewire/flux/zipball/8d83f34d64ab0542463e8e3feab4d166e1830ed9", + "reference": "8d83f34d64ab0542463e8e3feab4d166e1830ed9", "shasum": "" }, "require": { @@ -3162,6 +3158,9 @@ "php": "^8.1", "symfony/console": "^6.0|^7.0" }, + "conflict": { + "livewire/blaze": "<0.1.0" + }, "type": "library", "extra": { "laravel": { @@ -3198,25 +3197,25 @@ ], "support": { "issues": "https://github.com/livewire/flux/issues", - "source": "https://github.com/livewire/flux/tree/v2.2.4" + "source": "https://github.com/livewire/flux/tree/v2.4.0" }, - "time": "2025-08-09T01:46:51+00:00" + "time": "2025-09-16T00:20:10+00:00" }, { "name": "livewire/flux-pro", - "version": "2.2.4", + "version": "2.4.0", "dist": { "type": "zip", - "url": "https://composer.fluxui.dev/download/9f96e524-b277-440e-96f6-6a8f1b41e048/flux-pro-2.2.4.zip", - "reference": "51bf2c15a33e6ca7eddef336ff18fa9b3d952ed3", - "shasum": "cc4207cb6cc365bc0c02c61c9b03797b528ba4fc" + "url": "https://composer.fluxui.dev/download/9fe336ed-832c-4a6a-97df-a308413a5420/flux-pro-2.4.0.zip", + "reference": "0c9737eb5c623c17cf88dad298ab57e2befcfbe1", + "shasum": "e6b1308d21725cc99821f545f488a567eb652eb3" }, "require": { "illuminate/console": "^10.0|^11.0|^12.0", "illuminate/support": "^10.0|^11.0|^12.0", "illuminate/view": "^10.0|^11.0|^12.0", "laravel/prompts": "^0.1.24|^0.2|^0.3", - "livewire/flux": "2.2.4|dev-main", + "livewire/flux": "2.4.0|dev-main", "livewire/livewire": "^3.6.2", "php": "^8.1", "symfony/console": "^6.0|^7.0" @@ -3257,7 +3256,7 @@ "livewire", "ui" ], - "time": "2025-08-09T01:53:03+00:00" + "time": "2025-09-16T00:24:40+00:00" }, { "name": "livewire/livewire", @@ -3337,16 +3336,16 @@ }, { "name": "maatwebsite/excel", - "version": "3.1.66", + "version": "3.1.67", "source": { "type": "git", "url": "https://github.com/SpartnerNL/Laravel-Excel.git", - "reference": "3b29c2426a46674f444890c45f742452a396aae8" + "reference": "e508e34a502a3acc3329b464dad257378a7edb4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/3b29c2426a46674f444890c45f742452a396aae8", - "reference": "3b29c2426a46674f444890c45f742452a396aae8", + "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/e508e34a502a3acc3329b464dad257378a7edb4d", + "reference": "e508e34a502a3acc3329b464dad257378a7edb4d", "shasum": "" }, "require": { @@ -3354,7 +3353,7 @@ "ext-json": "*", "illuminate/support": "5.8.*||^6.0||^7.0||^8.0||^9.0||^10.0||^11.0||^12.0", "php": "^7.0||^8.0", - "phpoffice/phpspreadsheet": "^1.29.12", + "phpoffice/phpspreadsheet": "^1.30.0", "psr/simple-cache": "^1.0||^2.0||^3.0" }, "require-dev": { @@ -3402,7 +3401,7 @@ ], "support": { "issues": "https://github.com/SpartnerNL/Laravel-Excel/issues", - "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.66" + "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.67" }, "funding": [ { @@ -3414,7 +3413,7 @@ "type": "github" } ], - "time": "2025-08-07T08:31:22+00:00" + "time": "2025-08-26T09:13:16+00:00" }, { "name": "maennchen/zipstream-php", @@ -3764,12 +3763,12 @@ "source": { "type": "git", "url": "https://github.com/nemiah/phpFinTS.git", - "reference": "8719e941e1cb56314c2081745421fc443f649c68" + "reference": "297a9995109549969d0bdc2ed19ba9a2a27db04f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nemiah/phpFinTS/zipball/8719e941e1cb56314c2081745421fc443f649c68", - "reference": "8719e941e1cb56314c2081745421fc443f649c68", + "url": "https://api.github.com/repos/nemiah/phpFinTS/zipball/297a9995109549969d0bdc2ed19ba9a2a27db04f", + "reference": "297a9995109549969d0bdc2ed19ba9a2a27db04f", "shasum": "" }, "require": { @@ -3779,9 +3778,9 @@ "psr/log": "^1|^2|^3" }, "require-dev": { - "friendsofphp/php-cs-fixer": "3.*", - "php-mock/php-mock-phpunit": "2.6.*", - "phpunit/phpunit": "9.5.*" + "friendsofphp/php-cs-fixer": "^3.0", + "php-mock/php-mock-phpunit": "^2.6", + "phpunit/phpunit": "^9.5" }, "suggest": { "abcaeffchen/sephpa": "1.*", @@ -3806,20 +3805,20 @@ "issues": "https://github.com/nemiah/phpFinTS/issues", "source": "https://github.com/nemiah/phpFinTS/tree/master" }, - "time": "2025-08-09T19:37:10+00:00" + "time": "2025-09-10T19:02:30+00:00" }, { "name": "nesbot/carbon", - "version": "3.10.2", + "version": "3.10.3", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24" + "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24", - "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", + "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", "shasum": "" }, "require": { @@ -3837,13 +3836,13 @@ "require-dev": { "doctrine/dbal": "^3.6.3 || ^4.0", "doctrine/orm": "^2.15.2 || ^3.0", - "friendsofphp/php-cs-fixer": "^3.75.0", + "friendsofphp/php-cs-fixer": "^v3.87.1", "kylekatarnls/multi-tester": "^2.5.3", "phpmd/phpmd": "^2.15.0", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.17", - "phpunit/phpunit": "^10.5.46", - "squizlabs/php_codesniffer": "^3.13.0" + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^10.5.53", + "squizlabs/php_codesniffer": "^3.13.4" }, "bin": [ "bin/carbon" @@ -3911,7 +3910,7 @@ "type": "tidelift" } ], - "time": "2025-08-02T09:36:06+00:00" + "time": "2025-09-06T13:39:36+00:00" }, { "name": "nette/schema", @@ -4484,16 +4483,16 @@ }, { "name": "phpoption/phpoption", - "version": "1.9.3", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", "shasum": "" }, "require": { @@ -4501,7 +4500,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" }, "type": "library", "extra": { @@ -4543,7 +4542,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" }, "funding": [ { @@ -4555,7 +4554,7 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:41:07+00:00" + "time": "2025-08-21T11:53:16+00:00" }, { "name": "phpseclib/phpseclib", @@ -5272,20 +5271,20 @@ }, { "name": "ramsey/uuid", - "version": "4.9.0", + "version": "4.9.1", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -5344,9 +5343,9 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.0" + "source": "https://github.com/ramsey/uuid/tree/4.9.1" }, - "time": "2025-06-25T14:20:11+00:00" + "time": "2025-09-04T20:59:21+00:00" }, { "name": "socialiteproviders/laravelpassport", @@ -5970,16 +5969,16 @@ }, { "name": "symfony/console", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", + "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", "shasum": "" }, "require": { @@ -6044,7 +6043,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.2" + "source": "https://github.com/symfony/console/tree/v7.3.3" }, "funding": [ { @@ -6064,7 +6063,7 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:13:41+00:00" + "time": "2025-08-25T06:35:40+00:00" }, { "name": "symfony/css-selector", @@ -6281,16 +6280,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v7.3.0", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d" + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", "shasum": "" }, "require": { @@ -6341,7 +6340,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" }, "funding": [ { @@ -6352,12 +6351,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-22T09:11:45+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -6505,16 +6508,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6" + "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6877c122b3a6cc3695849622720054f6e6fa5fa6", - "reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7475561ec27020196c49bb7c4f178d33d7d3dc00", + "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00", "shasum": "" }, "require": { @@ -6564,7 +6567,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.2" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.3" }, "funding": [ { @@ -6584,20 +6587,20 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-08-20T08:04:18+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c" + "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6ecc895559ec0097e221ed2fd5eb44d5fede083c", - "reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/72c304de37e1a1cec6d5d12b81187ebd4850a17b", + "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b", "shasum": "" }, "require": { @@ -6682,7 +6685,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.3.2" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.3" }, "funding": [ { @@ -6702,20 +6705,20 @@ "type": "tidelift" } ], - "time": "2025-07-31T10:45:04+00:00" + "time": "2025-08-29T08:23:45+00:00" }, { "name": "symfony/mailer", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b" + "reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/d43e84d9522345f96ad6283d5dfccc8c1cfc299b", - "reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b", + "url": "https://api.github.com/repos/symfony/mailer/zipball/a32f3f45f1990db8c4341d5122a7d3a381c7e575", + "reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575", "shasum": "" }, "require": { @@ -6766,7 +6769,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.3.2" + "source": "https://github.com/symfony/mailer/tree/v7.3.3" }, "funding": [ { @@ -6786,7 +6789,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "symfony/mime", @@ -6878,7 +6881,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -6937,7 +6940,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -6948,6 +6951,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -6957,16 +6964,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -7015,7 +7022,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -7026,16 +7033,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", @@ -7098,7 +7109,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" }, "funding": [ { @@ -7109,6 +7120,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -7118,7 +7133,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -7179,7 +7194,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -7190,6 +7205,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -7199,7 +7218,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -7260,7 +7279,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -7271,6 +7290,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -7280,7 +7303,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -7340,7 +7363,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -7351,6 +7374,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -7360,16 +7387,16 @@ }, { "name": "symfony/polyfill-php83", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { @@ -7416,7 +7443,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, "funding": [ { @@ -7427,16 +7454,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-07-08T02:45:35+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", @@ -7495,7 +7526,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" }, "funding": [ { @@ -7506,6 +7537,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -7515,16 +7550,16 @@ }, { "name": "symfony/process", - "version": "v7.3.0", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", "shasum": "" }, "require": { @@ -7556,7 +7591,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.0" + "source": "https://github.com/symfony/process/tree/v7.3.3" }, "funding": [ { @@ -7567,12 +7602,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-17T09:11:12+00:00" + "time": "2025-08-18T09:42:54+00:00" }, { "name": "symfony/routing", @@ -7744,16 +7783,16 @@ }, { "name": "symfony/string", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", + "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", "shasum": "" }, "require": { @@ -7811,7 +7850,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.2" + "source": "https://github.com/symfony/string/tree/v7.3.3" }, "funding": [ { @@ -7831,20 +7870,20 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-08-25T06:35:40+00:00" }, { "name": "symfony/translation", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "81b48f4daa96272efcce9c7a6c4b58e629df3c90" + "reference": "e0837b4cbcef63c754d89a4806575cada743a38d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/81b48f4daa96272efcce9c7a6c4b58e629df3c90", - "reference": "81b48f4daa96272efcce9c7a6c4b58e629df3c90", + "url": "https://api.github.com/repos/symfony/translation/zipball/e0837b4cbcef63c754d89a4806575cada743a38d", + "reference": "e0837b4cbcef63c754d89a4806575cada743a38d", "shasum": "" }, "require": { @@ -7911,7 +7950,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.3.2" + "source": "https://github.com/symfony/translation/tree/v7.3.3" }, "funding": [ { @@ -7931,7 +7970,7 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:31:46+00:00" + "time": "2025-08-01T21:02:37+00:00" }, { "name": "symfony/translation-contracts", @@ -8087,16 +8126,16 @@ }, { "name": "symfony/var-dumper", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "53205bea27450dc5c65377518b3275e126d45e75" + "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/53205bea27450dc5c65377518b3275e126d45e75", - "reference": "53205bea27450dc5c65377518b3275e126d45e75", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", + "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", "shasum": "" }, "require": { @@ -8150,7 +8189,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.2" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.3" }, "funding": [ { @@ -8170,7 +8209,7 @@ "type": "tidelift" } ], - "time": "2025-07-29T20:02:46+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -8850,16 +8889,16 @@ }, { "name": "composer/class-map-generator", - "version": "1.6.1", + "version": "1.6.2", "source": { "type": "git", "url": "https://github.com/composer/class-map-generator.git", - "reference": "134b705ddb0025d397d8318a75825fe3c9d1da34" + "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/class-map-generator/zipball/134b705ddb0025d397d8318a75825fe3c9d1da34", - "reference": "134b705ddb0025d397d8318a75825fe3c9d1da34", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/ba9f089655d4cdd64e762a6044f411ccdaec0076", + "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076", "shasum": "" }, "require": { @@ -8903,7 +8942,7 @@ ], "support": { "issues": "https://github.com/composer/class-map-generator/issues", - "source": "https://github.com/composer/class-map-generator/tree/1.6.1" + "source": "https://github.com/composer/class-map-generator/tree/1.6.2" }, "funding": [ { @@ -8913,13 +8952,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2025-03-24T13:50:44+00:00" + "time": "2025-08-20T18:52:43+00:00" }, { "name": "doctrine/deprecations", @@ -9069,16 +9104,16 @@ }, { "name": "fidry/cpu-core-counter", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "8520451a140d3f46ac33042715115e290cf5785f" + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", - "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { @@ -9088,10 +9123,10 @@ "fidry/makefile": "^0.2.0", "fidry/php-cs-fixer-config": "^1.1.2", "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^1.9.2", - "phpstan/phpstan-deprecation-rules": "^1.0.0", - "phpstan/phpstan-phpunit": "^1.2.2", - "phpstan/phpstan-strict-rules": "^1.4.4", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^8.5.31 || ^9.5.26", "webmozarts/strict-phpunit": "^7.5" }, @@ -9118,7 +9153,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" }, "funding": [ { @@ -9126,7 +9161,7 @@ "type": "github" } ], - "time": "2024-08-06T10:04:20+00:00" + "time": "2025-08-14T07:29:31+00:00" }, { "name": "filp/whoops", @@ -9442,16 +9477,16 @@ }, { "name": "laravel/pint", - "version": "v1.24.0", + "version": "v1.25.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a" + "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a", - "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a", + "url": "https://api.github.com/repos/laravel/pint/zipball/5016e263f95d97670d71b9a987bd8996ade6d8d9", + "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9", "shasum": "" }, "require": { @@ -9462,9 +9497,9 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.82.2", - "illuminate/view": "^11.45.1", - "larastan/larastan": "^3.5.0", + "friendsofphp/php-cs-fixer": "^3.87.2", + "illuminate/view": "^11.46.0", + "larastan/larastan": "^3.7.1", "laravel-zero/framework": "^11.45.0", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^2.3.1", @@ -9475,9 +9510,6 @@ ], "type": "project", "autoload": { - "files": [ - "overrides/Runner/Parallel/ProcessFactory.php" - ], "psr-4": { "App\\": "app/", "Database\\Seeders\\": "database/seeders/", @@ -9507,20 +9539,20 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-07-10T18:09:32+00:00" + "time": "2025-09-19T02:57:12+00:00" }, { "name": "laravel/sail", - "version": "v1.44.0", + "version": "v1.45.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "a09097bd2a8a38e23ac472fa6a6cf5b0d1c1d3fe" + "reference": "019a2933ff4a9199f098d4259713f9bc266a874e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/a09097bd2a8a38e23ac472fa6a6cf5b0d1c1d3fe", - "reference": "a09097bd2a8a38e23ac472fa6a6cf5b0d1c1d3fe", + "url": "https://api.github.com/repos/laravel/sail/zipball/019a2933ff4a9199f098d4259713f9bc266a874e", + "reference": "019a2933ff4a9199f098d4259713f9bc266a874e", "shasum": "" }, "require": { @@ -9570,7 +9602,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2025-07-04T16:17:06+00:00" + "time": "2025-08-25T19:28:31+00:00" }, { "name": "laravel/tinker", @@ -9910,16 +9942,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.0", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56" + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56", - "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", "shasum": "" }, "require": { @@ -9938,7 +9970,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -9962,9 +9994,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" }, - "time": "2025-07-27T20:03:57+00:00" + "time": "2025-08-13T20:13:15+00:00" }, { "name": "nunomaduro/collision", @@ -10699,16 +10731,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.6.2", + "version": "5.6.3", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62" + "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62", - "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94f8051919d1b0369a6bcc7931d679a511c03fe9", + "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9", "shasum": "" }, "require": { @@ -10757,9 +10789,9 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.3" }, - "time": "2025-04-13T19:20:35+00:00" + "time": "2025-08-01T19:43:32+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -10821,16 +10853,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8" + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8", - "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", "shasum": "" }, "require": { @@ -10862,22 +10894,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.2.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" }, - "time": "2025-07-13T07:04:09+00:00" + "time": "2025-08-30T15:50:23+00:00" }, { "name": "phpstan/phpstan", - "version": "1.12.28", + "version": "1.12.29", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "fcf8b71aeab4e1a1131d1783cef97b23a51b87a9" + "reference": "0835c625a38ac6484f050077116b6668bc3ab57d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fcf8b71aeab4e1a1131d1783cef97b23a51b87a9", - "reference": "fcf8b71aeab4e1a1131d1783cef97b23a51b87a9", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/0835c625a38ac6484f050077116b6668bc3ab57d", + "reference": "0835c625a38ac6484f050077116b6668bc3ab57d", "shasum": "" }, "require": { @@ -10922,7 +10954,7 @@ "type": "github" } ], - "time": "2025-07-17T17:15:39+00:00" + "time": "2025-09-16T08:46:57+00:00" }, { "name": "phpunit/php-code-coverage", @@ -11489,12 +11521,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "fc5feda437d44655ee5df703bda813e00674b227" + "reference": "662d5ecfaab913d8ce0de4521fc40ce92aa14e75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/fc5feda437d44655ee5df703bda813e00674b227", - "reference": "fc5feda437d44655ee5df703bda813e00674b227", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/662d5ecfaab913d8ce0de4521fc40ce92aa14e75", + "reference": "662d5ecfaab913d8ce0de4521fc40ce92aa14e75", "shasum": "" }, "conflict": { @@ -11512,7 +11544,7 @@ "airesvsg/acf-to-rest-api": "<=3.1", "akaunting/akaunting": "<2.1.13", "akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53", - "alextselegidis/easyappointments": "<=1.5.1", + "alextselegidis/easyappointments": "<1.5.2.0-beta1", "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", "amazing/media2click": ">=1,<1.3.3", "ameos/ameos_tarteaucitron": "<1.2.23", @@ -11525,8 +11557,8 @@ "aoe/restler": "<1.7.1", "apache-solr-for-typo3/solr": "<2.8.3", "apereo/phpcas": "<1.6", - "api-platform/core": "<3.4.17|>=4.0.0.0-alpha1,<4.0.22", - "api-platform/graphql": "<3.4.17|>=4.0.0.0-alpha1,<4.0.22", + "api-platform/core": "<3.4.17|>=4,<4.0.22|>=4.1,<4.1.5", + "api-platform/graphql": "<3.4.17|>=4,<4.0.22|>=4.1,<4.1.5", "appwrite/server-ce": "<=1.2.1", "arc/web": "<3", "area17/twill": "<1.2.5|>=2,<2.5.3", @@ -11550,7 +11582,7 @@ "backpack/crud": "<3.4.9", "backpack/filemanager": "<2.0.2|>=3,<3.0.9", "bacula-web/bacula-web": "<9.7.1", - "badaso/core": "<2.7", + "badaso/core": "<=2.9.11", "bagisto/bagisto": "<2.1", "barrelstrength/sprout-base-email": "<1.2.7", "barrelstrength/sprout-forms": "<3.9", @@ -11615,15 +11647,15 @@ "concrete5/core": "<8.5.8|>=9,<9.1", "contao-components/mediaelement": ">=2.14.2,<2.21.1", "contao/comments-bundle": ">=2,<4.13.40|>=5.0.0.0-RC1-dev,<5.3.4", - "contao/contao": ">=3,<3.5.37|>=4,<4.4.56|>=4.5,<4.9.40|>=4.10,<4.11.7|>=4.13,<4.13.21|>=5.1,<5.1.4", + "contao/contao": ">=3,<3.5.37|>=4,<4.4.56|>=4.5,<4.13.56|>=5,<5.3.38|>=5.4.0.0-RC1-dev,<5.6.1", "contao/core": "<3.5.39", - "contao/core-bundle": "<4.13.54|>=5,<5.3.30|>=5.4,<5.5.6", + "contao/core-bundle": "<4.13.56|>=5,<5.3.38|>=5.4,<5.6.1", "contao/listing-bundle": ">=3,<=3.5.30|>=4,<4.4.8", "contao/managed-edition": "<=1.5", "corveda/phpsandbox": "<1.3.5", "cosenary/instagram": "<=2.3", "couleurcitron/tarteaucitron-wp": "<0.3", - "craftcms/cms": "<4.16.3|>=5,<5.8.4", + "craftcms/cms": "<=4.16.5|>=5,<=5.8.6", "croogo/croogo": "<4", "cuyz/valinor": "<0.12", "czim/file-handling": "<1.5|>=2,<2.3", @@ -11632,6 +11664,7 @@ "dapphp/securimage": "<3.6.6", "darylldoyle/safe-svg": "<1.9.10", "datadog/dd-trace": ">=0.30,<0.30.2", + "datahihi1/tiny-env": "<1.0.3|>=1.0.9,<1.0.11", "datatables/datatables": "<1.10.10", "david-garcia/phpwhois": "<=4.3.1", "dbrisinajumi/d2files": "<1", @@ -11695,7 +11728,7 @@ "encore/laravel-admin": "<=1.8.19", "endroid/qr-code-bundle": "<3.4.2", "enhavo/enhavo-app": "<=0.13.1", - "enshrined/svg-sanitize": "<0.15", + "enshrined/svg-sanitize": "<0.22", "erusev/parsedown": "<1.7.2", "ether/logs": "<3.0.4", "evolutioncms/evolution": "<=3.2.3", @@ -11779,7 +11812,7 @@ "globalpayments/php-sdk": "<2", "goalgorilla/open_social": "<12.3.11|>=12.4,<12.4.10|>=13.0.0.0-alpha1,<13.0.0.0-alpha11", "gogentooss/samlbase": "<1.2.7", - "google/protobuf": "<3.15", + "google/protobuf": "<3.4", "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", "gree/jose": "<2.2.1", "gregwar/rst": "<1.0.3", @@ -11838,7 +11871,7 @@ "jbartels/wec-map": "<3.0.3", "jcbrand/converse.js": "<3.3.3", "joelbutcher/socialstream": "<5.6|>=6,<6.2", - "johnbillion/wp-crontrol": "<1.16.2", + "johnbillion/wp-crontrol": "<1.16.2|>=1.17,<1.19.2", "joomla/application": "<1.0.13", "joomla/archive": "<1.1.12|>=2,<2.0.1", "joomla/database": ">=1,<2.2|>=3,<3.4", @@ -11882,6 +11915,7 @@ "laravel/socialite": ">=1,<2.0.10", "latte/latte": "<2.10.8", "lavalite/cms": "<=9|==10.1", + "lavitto/typo3-form-to-database": "<2.2.5|>=3,<3.2.2|>=4,<4.2.3|>=5,<5.0.2", "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", "league/commonmark": "<2.7", "league/flysystem": "<1.1.4|>=2,<2.1.1", @@ -11903,13 +11937,14 @@ "luyadev/yii-helpers": "<1.2.1", "macropay-solutions/laravel-crud-wizard-free": "<3.4.17", "maestroerror/php-heic-to-jpg": "<1.0.5", - "magento/community-edition": "<2.4.5.0-patch13|==2.4.6|>=2.4.6.0-patch1,<2.4.6.0-patch11|>=2.4.7.0-beta1,<2.4.7.0-patch6|>=2.4.8.0-beta1,<2.4.8.0-patch1", + "magento/community-edition": "<=2.4.5.0-patch14|==2.4.6|>=2.4.6.0-patch1,<=2.4.6.0-patch12|>=2.4.7.0-beta1,<=2.4.7.0-patch7|>=2.4.8.0-beta1,<=2.4.8.0-patch2|>=2.4.9.0-alpha1,<=2.4.9.0-alpha2|==2.4.9", "magento/core": "<=1.9.4.5", "magento/magento1ce": "<1.9.4.3-dev", "magento/magento1ee": ">=1,<1.14.4.3-dev", "magento/product-community-edition": "<2.4.4.0-patch9|>=2.4.5,<2.4.5.0-patch8|>=2.4.6,<2.4.6.0-patch6|>=2.4.7,<2.4.7.0-patch1", "magento/project-community-edition": "<=2.0.2", "magneto/core": "<1.9.4.4-dev", + "mahocommerce/maho": "<25.9", "maikuolan/phpmussel": ">=1,<1.6", "mainwp/mainwp": "<=4.4.3.3", "manogi/nova-tiptap": "<=3.2.6", @@ -11918,7 +11953,7 @@ "marshmallow/nova-tiptap": "<5.7", "matomo/matomo": "<1.11", "matyhtf/framework": "<3.0.6", - "mautic/core": "<5.2.6|>=6.0.0.0-alpha,<6.0.2", + "mautic/core": "<5.2.8|>=6.0.0.0-alpha,<6.0.5", "mautic/core-lib": ">=1.0.0.0-beta,<4.4.13|>=5.0.0.0-alpha,<5.1.1", "maximebf/debugbar": "<1.19", "mdanter/ecc": "<2", @@ -11947,6 +11982,7 @@ "mongodb/mongodb": ">=1,<1.9.2", "monolog/monolog": ">=1.8,<1.12", "moodle/moodle": "<4.3.12|>=4.4,<4.4.8|>=4.5.0.0-beta,<4.5.4", + "moonshine/moonshine": "<=3.12.5", "mos/cimage": "<0.7.19", "movim/moxl": ">=0.8,<=0.10", "movingbytes/social-network": "<=1.2.1", @@ -12042,7 +12078,7 @@ "phpoffice/common": "<0.2.9", "phpoffice/math": "<=0.2", "phpoffice/phpexcel": "<=1.8.2", - "phpoffice/phpspreadsheet": "<1.29.9|>=2,<2.1.8|>=2.2,<2.3.7|>=3,<3.9", + "phpoffice/phpspreadsheet": "<1.30|>=2,<2.1.12|>=2.2,<2.4|>=3,<3.10|>=4,<5", "phpseclib/phpseclib": "<2.0.47|>=3,<3.0.36", "phpservermon/phpservermon": "<3.6", "phpsysinfo/phpsysinfo": "<3.4.3", @@ -12063,7 +12099,7 @@ "pixelfed/pixelfed": "<0.12.5", "plotly/plotly.js": "<2.25.2", "pocketmine/bedrock-protocol": "<8.0.2", - "pocketmine/pocketmine-mp": "<5.25.2", + "pocketmine/pocketmine-mp": "<5.32.1", "pocketmine/raklib": ">=0.14,<0.14.6|>=0.15,<0.15.1", "pressbooks/pressbooks": "<5.18", "prestashop/autoupgrade": ">=4,<4.10.1", @@ -12071,7 +12107,7 @@ "prestashop/blockwishlist": ">=2,<2.1.1", "prestashop/contactform": ">=1.0.1,<4.3", "prestashop/gamification": "<2.3.2", - "prestashop/prestashop": "<8.1.6", + "prestashop/prestashop": "<8.2.3", "prestashop/productcomments": "<5.0.2", "prestashop/ps_contactinfo": "<=3.3.2", "prestashop/ps_emailsubscription": "<2.6.1", @@ -12121,10 +12157,10 @@ "setasign/fpdi": "<2.6.4", "sfroemken/url_redirect": "<=1.2.1", "sheng/yiicms": "<1.2.1", - "shopware/core": "<6.5.8.18-dev|>=6.6,<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev", + "shopware/core": "<6.5.8.18-dev|>=6.6,<6.6.10.3-dev|>=6.7,<6.7.2.1-dev", "shopware/platform": "<=6.6.10.4|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev", "shopware/production": "<=6.3.5.2", - "shopware/shopware": "<=5.7.17", + "shopware/shopware": "<=5.7.17|>=6.7,<6.7.2.1-dev", "shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev", "shopxo/shopxo": "<=6.4", "showdoc/showdoc": "<2.10.4", @@ -12169,6 +12205,8 @@ "snipe/snipe-it": "<8.1", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", + "solspace/craft-freeform": ">=5,<5.10.16", + "soosyze/soosyze": "<=2", "spatie/browsershot": "<5.0.5", "spatie/image-optimizer": "<1.7.3", "spencer14420/sp-php-email-handler": "<1", @@ -12270,14 +12308,14 @@ "tribalsystems/zenario": "<=9.7.61188", "truckersmp/phpwhois": "<=4.3.1", "ttskch/pagination-service-provider": "<1", - "twbs/bootstrap": "<=3.4.1|>=4,<=4.6.2", + "twbs/bootstrap": "<3.4.1|>=4,<=4.6.2", "twig/twig": "<3.11.2|>=3.12,<3.14.1|>=3.16,<3.19", "typo3/cms": "<9.5.29|>=10,<10.4.35|>=11,<11.5.23|>=12,<12.2", - "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<10.4.46|>=11,<11.5.40|>=12,<=12.4.30|>=13,<=13.4.11", + "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/cms-belog": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", - "typo3/cms-beuser": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", - "typo3/cms-core": "<=8.7.56|>=9,<=9.5.50|>=10,<=10.4.49|>=11,<=11.5.43|>=12,<=12.4.30|>=13,<=13.4.11", - "typo3/cms-dashboard": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-beuser": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", + "typo3/cms-core": "<=8.7.56|>=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", + "typo3/cms-dashboard": ">=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/cms-extbase": "<6.2.24|>=7,<7.6.8|==8.1.1", "typo3/cms-extensionmanager": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", "typo3/cms-felogin": ">=4.2,<4.2.3", @@ -12287,10 +12325,13 @@ "typo3/cms-indexed-search": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", "typo3/cms-install": "<4.1.14|>=4.2,<4.2.16|>=4.3,<4.3.9|>=4.4,<4.4.5|>=12.2,<12.4.8|==13.4.2", "typo3/cms-lowlevel": ">=11,<=11.5.41", + "typo3/cms-recordlist": ">=11,<11.5.48", + "typo3/cms-recycler": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/cms-rte-ckeditor": ">=9.5,<9.5.42|>=10,<10.4.39|>=11,<11.5.30", "typo3/cms-scheduler": ">=11,<=11.5.41", "typo3/cms-setup": ">=9,<=9.5.50|>=10,<=10.4.49|>=11,<=11.5.43|>=12,<=12.4.30|>=13,<=13.4.11", "typo3/cms-webhooks": ">=12,<=12.4.30|>=13,<=13.4.11", + "typo3/cms-workspaces": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", "typo3/html-sanitizer": ">=1,<=1.5.2|>=2,<=2.1.3", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.3.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", @@ -12301,7 +12342,7 @@ "uasoft-indonesia/badaso": "<=2.9.7", "unisharp/laravel-filemanager": "<2.9.1", "universal-omega/dynamic-page-list3": "<3.6.4", - "unopim/unopim": "<0.1.5", + "unopim/unopim": "<=0.3", "userfrosting/userfrosting": ">=0.3.1,<4.6.3", "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", "uvdesk/community-skeleton": "<=1.1.1", @@ -12315,7 +12356,7 @@ "vertexvaar/falsftp": "<0.2.6", "villagedefrance/opencart-overclocked": "<=1.11.1", "vova07/yii2-fileapi-widget": "<0.1.9", - "vrana/adminer": "<4.8.1", + "vrana/adminer": "<=4.8.1", "vufind/vufind": ">=2,<9.1.1", "waldhacker/hcaptcha": "<2.1.2", "wallabag/tcpdf": "<6.2.22", @@ -12351,7 +12392,7 @@ "xataface/xataface": "<3", "xpressengine/xpressengine": "<3.0.15", "yab/quarx": "<2.4.5", - "yeswiki/yeswiki": "<4.5.4", + "yeswiki/yeswiki": "<=4.5.4", "yetiforce/yetiforce-crm": "<6.5", "yidashi/yii2cmf": "<=2", "yii2mod/yii2-cms": "<1.9.2", @@ -12443,7 +12484,7 @@ "type": "tidelift" } ], - "time": "2025-08-08T20:05:48+00:00" + "time": "2025-09-16T20:05:44+00:00" }, { "name": "sebastian/cli-parser", @@ -12615,16 +12656,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.3", + "version": "5.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" + "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e8e53097718d2b53cfb2aa859b06a41abf58c62e", + "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e", "shasum": "" }, "require": { @@ -12680,15 +12721,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2024-10-18T14:56:07+00:00" + "time": "2025-09-07T05:25:07+00:00" }, { "name": "sebastian/complexity", @@ -13376,16 +13429,16 @@ }, { "name": "symfony/yaml", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30" + "reference": "d4f4a66866fe2451f61296924767280ab5732d9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/b8d7d868da9eb0919e99c8830431ea087d6aae30", - "reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30", + "url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d", + "reference": "d4f4a66866fe2451f61296924767280ab5732d9d", "shasum": "" }, "require": { @@ -13428,7 +13481,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.2" + "source": "https://github.com/symfony/yaml/tree/v7.3.3" }, "funding": [ { @@ -13448,7 +13501,7 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-08-27T11:34:33+00:00" }, { "name": "ta-tikoma/phpunit-architecture-test", diff --git a/rebuild-stufis.sh b/rebuild-stufis.sh index cd06a7ea..3b5ba5ab 100644 --- a/rebuild-stufis.sh +++ b/rebuild-stufis.sh @@ -14,13 +14,16 @@ php artisan down --with-secret php artisan clear-compiled php artisan optimize:clear +php artisan view:clear +php artisan route:clear +php artisan config:clear # install dependencies composer install --no-dev --optimize-autoloader npm ci # performance optimization -php artisan config:cache +#php artisan config:cache php artisan view:cache php artisan route:cache diff --git a/resources/views/budget-plan/view.blade.php b/resources/views/budget-plan/view.blade.php index 1a00a689..770443a3 100644 --- a/resources/views/budget-plan/view.blade.php +++ b/resources/views/budget-plan/view.blade.php @@ -1,6 +1,67 @@ - - - a - b - + +
+ + head + sub + + + Button + + edit + + + + + + + + + {{ __('budget-plan.edit.tab-headline.in') }} + 100.000,01€ + + + {{ __('budget-plan.edit.tab-headline.out') }} + 100.400,01€ + + + @foreach(\App\Models\Enums\BudgetType::cases() as $budgetType) + + + + shortname + longname + value + booked + reserved + + + + @foreach($items[$budgetType->slug()] as $budgetItem) + $budgetItem->is_group])> + + {{ $budgetItem->short_name }} + + + {{ $budgetItem->name }} + + + {{ $budgetItem->value }} + + + tbd + + + tbd + + + @foreach($budgetItem->children as $child) + + @endforeach + @endforeach + + + + @endforeach + +
diff --git a/resources/views/components/budgetplan/item-group-edit.blade.php b/resources/views/components/budgetplan/item-group-edit.blade.php new file mode 100644 index 00000000..72ba6b01 --- /dev/null +++ b/resources/views/components/budgetplan/item-group-edit.blade.php @@ -0,0 +1,95 @@ +@props([ + 'level' => 0, + 'item', +]) + +
$level === 1, + //'col-start-3' => $level === 2, + //'col-start-4' => $level === 3, + //'ml-5' => $level === 1, + //'ml-10' => $level === 2, + //'ml-16' => $level === 3, + //"border-zinc-300 ", + //"border-l-1" => $level === 0 && $item->is_group, + //"border-l-2" => $level === 1 && $item->is_group, + //"border-l-3" => $level === 2 && $item->is_group, + //"border-l-4" => $level === 3 && $item->is_group, + + ]) x-sort:item="{{ $item->id }}"> +
$item->is_group, + "rounded" => $item->is_group, + "bg-zinc-300 my-2" => $item->is_group, + ])> +
+ + @if($item->is_group) + + @else + + @endif +
+
+ +
+
+ +
+
+ $level === 3, + //'pl-5' => $level === 2, + ])> + @if($item->is_group) + + Σ + + @endif + + + +
+
{{-- Action Buttons --}} + @if($item->is_group) + {{-- subtle or ghost --}} + + @endif + + + + Debug: L{{ $level }} id{{$item->id}} P{{$item->position}} + @if($item->is_group) + to budget + @else + to group + @endif + item up + item down + copy + + copy zur anderen seite + + Delete + + +
+
+ @if($item->is_group) +
$level === 0, + "border-l-16" => $level === 1, + "border-l-24" => $level === 2, + "border-l-28" => $level === 3, + ]) x-sort="$wire.sort($item,$position)"> + @foreach($item->orderedChildren as $child) + + @endforeach +
+ @endif +
diff --git a/resources/views/components/item-group.blade.php b/resources/views/components/budgetplan/item-group-view.blade.php similarity index 100% rename from resources/views/components/item-group.blade.php rename to resources/views/components/budgetplan/item-group-view.blade.php From e52edea0ca9bfead324a79e008a70282f3438a41 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 20 Sep 2025 18:23:43 +0200 Subject: [PATCH 024/108] money lib included and integrated --- app/Livewire/BudgetPlan/BudgetPlanEdit.php | 14 +- app/Livewire/BudgetPlan/ItemForm.php | 10 +- app/Models/BudgetItem.php | 2 + app/Providers/AppServiceProvider.php | 9 + app/Support/Money/DefaultMoneyFormater.php | 40 +++ app/Support/Money/MoneySynth.php | 32 ++ composer.json | 2 +- composer.lock | 274 +++++++++++------- config/money.php | 25 ++ .../dev/2024_04_02_125041_hhp_upgrade.php | 2 +- .../budgetplan/item-group-edit.blade.php | 70 +++-- .../views/components/money-input.blade.php | 19 +- .../livewire/budget-plan/plan-edit.blade.php | 6 +- 13 files changed, 344 insertions(+), 161 deletions(-) create mode 100644 app/Support/Money/DefaultMoneyFormater.php create mode 100644 app/Support/Money/MoneySynth.php create mode 100644 config/money.php diff --git a/app/Livewire/BudgetPlan/BudgetPlanEdit.php b/app/Livewire/BudgetPlan/BudgetPlanEdit.php index 345d502a..649bfb3d 100644 --- a/app/Livewire/BudgetPlan/BudgetPlanEdit.php +++ b/app/Livewire/BudgetPlan/BudgetPlanEdit.php @@ -6,6 +6,7 @@ use App\Models\BudgetPlan; use App\Models\Enums\BudgetType; use App\Models\FiscalYear; +use Cknow\Money\Money; use DB; use Flux\Flux; use Illuminate\Database\Eloquent\Builder; @@ -50,7 +51,7 @@ public function mount(int $plan_id): void ->get()->keyBy('id'); foreach ($all_items as $item) { - // registers new Livewire ItemForms, there is not yet a native way + // registers new Livewire ItemForms; there is not yet a native way // to generate a dynamic amount of ItemForms, or even multiple $form = new ItemForm($this, 'items.'.$item->id); $form->setItem($item); @@ -125,12 +126,13 @@ public function reSumItemValues(BudgetItem $leafItem): void $item = $leafItem; // iterate upwards until there is no parent left while (($item = $item->parent) !== null) { - $value = $item->children()->sum('value'); + $amount = $item->children()->sum('value'); + $money = Money::EUR($amount, true); // update db model - $item->value = $value; + $item->value = $money; $item->save(); // update frontend - $this->items[$item->id]->value = $value; + $this->items[$item->id]->value = $money; } } @@ -181,7 +183,7 @@ public function sort($item_id, $new_position): void $item->update(['position' => $new_position]); }); - Flux::toast('Dragging and dropping', variant: 'success'); + Flux::toast('FIXME: Dragging and dropping', variant: 'success'); } public function save() @@ -208,6 +210,7 @@ public function addGroup(BudgetType $budget_type): void 'budget_type' => $budget_type, 'is_group' => true, 'position' => $newPos, + 'value' => Money::EUR(100), ]); $form = new ItemForm($this, 'items.'.$new_item->budget_type->slug().'.'.$new_item->id); $form->setItem($new_item); @@ -240,6 +243,7 @@ private function addItem(int $parent_id, bool $is_group): void 'budget_type' => $parent->budget_type, 'is_group' => $is_group, 'position' => $pos + 1, + 'value' => Money::EUR(0), ]); $form = new ItemForm($this, 'items.'.$new_item->budget_type->slug().'.'.$new_item->id); $form->setItem($new_item); diff --git a/app/Livewire/BudgetPlan/ItemForm.php b/app/Livewire/BudgetPlan/ItemForm.php index 4630a9b1..4c6d173f 100644 --- a/app/Livewire/BudgetPlan/ItemForm.php +++ b/app/Livewire/BudgetPlan/ItemForm.php @@ -3,6 +3,7 @@ namespace App\Livewire\BudgetPlan; use App\Models\BudgetItem; +use Cknow\Money\Money; use Livewire\Component; use Livewire\Form; @@ -14,9 +15,9 @@ class ItemForm extends Form public $name = ''; - public int $value = 0; + public Money $value; - public $postion = 0; + public $position = 0; public $is_group = false; @@ -31,10 +32,11 @@ public function __construct(Component $component, $propertyName, ?BudgetItem $it public function setItem(BudgetItem $item): void { $this->id = $item->id; - $this->postion = $item->postion; + $this->position = $item->position; $this->is_group = $item->is_group; $this->name = $item->name; $this->short_name = $item->short_name; - $this->value = $item->value ?? 0; + // $this->value = ($item->value?->getAmount() ?? 0) / 100 ; + $this->value = $item->value; } } diff --git a/app/Models/BudgetItem.php b/app/Models/BudgetItem.php index 63f5e798..94e45181 100644 --- a/app/Models/BudgetItem.php +++ b/app/Models/BudgetItem.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Models\Enums\BudgetType; +use Cknow\Money\Casts\MoneyDecimalCast; use Database\Factories\BudgetItemFactory; use Eloquent; use Illuminate\Database\Eloquent\Builder; @@ -70,6 +71,7 @@ protected function casts(): array return [ 'is_group' => 'boolean', 'budget_type' => BudgetType::class, + 'value' => MoneyDecimalCast::class, ]; } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 13cdca19..8c455550 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use App\Services\Auth\AuthService; +use App\Support\Money\MoneySynth; use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Contracts\Foundation\Application; use Illuminate\Http\Request; @@ -10,6 +11,7 @@ use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; +use Livewire\Livewire; use SocialiteProviders\Manager\SocialiteWasCalled; class AppServiceProvider extends ServiceProvider @@ -46,6 +48,8 @@ public function boot(): void $this->bootRoute(); + $this->bootLivewire(); + // Carbon::setLocale(config('app.locale')); } @@ -77,4 +81,9 @@ public function registerAuth(): void abort(500, 'Config Error. Wrong Auth provider given in Environment. Fitting AuthService Class not found'); }); } + + private function bootLivewire(): void + { + Livewire::propertySynthesizer(MoneySynth::class); + } } diff --git a/app/Support/Money/DefaultMoneyFormater.php b/app/Support/Money/DefaultMoneyFormater.php new file mode 100644 index 00000000..b03b961a --- /dev/null +++ b/app/Support/Money/DefaultMoneyFormater.php @@ -0,0 +1,40 @@ +getAmount() / 100; + $currency = '€'; // fixed currency symbol for now + + // assemble the formatted string with 2 decimal places and a space after the comma + return number_format($amount, 2, ',', '.').' '.$currency; + } + + /** + * Converts a formatted money string into a Money object. + * + * This method is used by Livewire Synthesizer to reverse the formatting process. + * + * @param string $formatted The input money string with currency symbol and formatting. + * @return Money The resulting Money object with amount in cents and EUR currency. + */ + public function inverse(string $formatted): Money + { + // Remove currency symbol and trim + $formatted = str_replace('€', '', trim($formatted)); + + // Convert comma to decimal point and remove spaces + $amount = (float) str_replace([',', ' '], ['.', ''], $formatted); + + // Convert to cents + $cents = (int) round($amount * 100); + + // Create new Money object with EUR currency + return new Money($cents, new \Money\Currency('EUR')); + } +} diff --git a/app/Support/Money/MoneySynth.php b/app/Support/Money/MoneySynth.php new file mode 100644 index 00000000..63224325 --- /dev/null +++ b/app/Support/Money/MoneySynth.php @@ -0,0 +1,32 @@ +format(), []]; + } + + public function hydrate($value): Money + { + return Money::fromMoney(new DefaultMoneyFormater()->inverse($value)); + } + +} diff --git a/composer.json b/composer.json index 87f9566c..90705e5a 100644 --- a/composer.json +++ b/composer.json @@ -11,8 +11,8 @@ "ext-iconv": "*", "ext-pdo": "*", "ext-zip": "*", - "archtechx/money": "^0.5.3", "blade-ui-kit/blade-heroicons": "^2.6", + "cknow/laravel-money": "^8.4", "defuse/php-encryption": "^2.4", "diglactic/laravel-breadcrumbs": "^10.0", "globalcitizen/php-iban": "^4.2", diff --git a/composer.lock b/composer.lock index b30ce242..acc3811f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,113 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "efaa8ff1900c745113c95a9470a11bef", + "content-hash": "0d4054155ef2b048f6f9f814997b8cef", "packages": [ - { - "name": "archtechx/helpers", - "version": "v0.3.3", - "source": { - "type": "git", - "url": "https://github.com/archtechx/helpers.git", - "reference": "26253e1cbb6530a79a16dbca517b87db4660bf07" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/archtechx/helpers/zipball/26253e1cbb6530a79a16dbca517b87db4660bf07", - "reference": "26253e1cbb6530a79a16dbca517b87db4660bf07", - "shasum": "" - }, - "require": { - "illuminate/support": "^10.0|^11.0|^12.0", - "php": "^8.2" - }, - "require-dev": { - "orchestra/testbench": "^8.0|^9.0|^10.0", - "pestphp/pest": "^2.0|^3.0", - "pestphp/pest-plugin-laravel": "^2.0|^3.1" - }, - "type": "library", - "autoload": { - "files": [ - "src/helpers.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Samuel Štancl", - "email": "samuel@archte.ch" - } - ], - "description": "A collection of helpers we commonly use.", - "support": { - "source": "https://github.com/archtechx/helpers/tree/v0.3.3" - }, - "time": "2025-03-11T12:12:07+00:00" - }, - { - "name": "archtechx/money", - "version": "v0.5.3", - "source": { - "type": "git", - "url": "https://github.com/archtechx/money.git", - "reference": "f5011bce685fe9f5a7a10a211af24cdc1e093f86" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/archtechx/money/zipball/f5011bce685fe9f5a7a10a211af24cdc1e093f86", - "reference": "f5011bce685fe9f5a7a10a211af24cdc1e093f86", - "shasum": "" - }, - "require": { - "archtechx/helpers": "^0.3.2", - "illuminate/support": "^10.0|^11.0|^12.0", - "php": "^8.2" - }, - "require-dev": { - "larastan/larastan": "^2.4|^3.0", - "orchestra/testbench": "^8.0|^9.0|^10.0", - "pestphp/pest": "^2.0|^3.7", - "pestphp/pest-plugin-laravel": "^2.0|^3.1", - "phpstan/phpstan": "^1.9.8|^2.1" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "ArchTech\\Money\\MoneyServiceProvider" - ] - } - }, - "autoload": { - "files": [ - "src/helpers.php", - "src/Wireable.php" - ], - "psr-4": { - "ArchTech\\Money\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Samuel Štancl", - "email": "samuel@archte.ch" - } - ], - "description": "A lightweight package for handling money math in PHP.", - "support": { - "issues": "https://github.com/archtechx/money/issues", - "source": "https://github.com/archtechx/money/tree/v0.5.3" - }, - "time": "2025-03-11T16:38:43+00:00" - }, { "name": "blade-ui-kit/blade-heroicons", "version": "2.6.0", @@ -390,6 +285,73 @@ ], "time": "2024-02-09T16:56:22+00:00" }, + { + "name": "cknow/laravel-money", + "version": "v8.4.0", + "source": { + "type": "git", + "url": "https://github.com/cknow/laravel-money.git", + "reference": "07f1ddc82bb5c80a4e44120bc0bff1b3442cfd7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cknow/laravel-money/zipball/07f1ddc82bb5c80a4e44120bc0bff1b3442cfd7c", + "reference": "07f1ddc82bb5c80a4e44120bc0bff1b3442cfd7c", + "shasum": "" + }, + "require": { + "ext-intl": "*", + "ext-json": "*", + "illuminate/support": "^9.0|^10.0|^11.0|^12.0", + "illuminate/view": "^9.0|^10.0|^11.0|^12.0", + "moneyphp/money": "^4.6", + "php": "^8.1" + }, + "require-dev": { + "graham-campbell/testbench": "^6.1", + "illuminate/filesystem": "^9.0|^10.0|^11.0|^12.0", + "mockery/mockery": "^1.6", + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Cknow\\Money\\MoneyServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Cknow\\Money\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ricardo Gobbo de Souza", + "email": "ricardogobbosouza@yahoo.com.br" + } + ], + "description": "Laravel Money", + "homepage": "https://github.com/cknow/laravel-money", + "keywords": [ + "currency", + "laravel", + "money" + ], + "support": { + "issues": "https://github.com/cknow/laravel-money/issues", + "source": "https://github.com/cknow/laravel-money/tree/v8.4.0" + }, + "time": "2025-03-08T12:27:37+00:00" + }, { "name": "composer/pcre", "version": "3.3.2", @@ -3654,6 +3616,96 @@ }, "time": "2025-03-10T22:28:37+00:00" }, + { + "name": "moneyphp/money", + "version": "v4.7.1", + "source": { + "type": "git", + "url": "https://github.com/moneyphp/money.git", + "reference": "1a23f0e1b22e2c59ed5ed70cfbe4cbe696be9348" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/moneyphp/money/zipball/1a23f0e1b22e2c59ed5ed70cfbe4cbe696be9348", + "reference": "1a23f0e1b22e2c59ed5ed70cfbe4cbe696be9348", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "ext-filter": "*", + "ext-json": "*", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + }, + "require-dev": { + "cache/taggable-cache": "^1.1.0", + "doctrine/coding-standard": "^12.0", + "doctrine/instantiator": "^1.5.0 || ^2.0", + "ext-gmp": "*", + "ext-intl": "*", + "florianv/exchanger": "^2.8.1", + "florianv/swap": "^4.3.0", + "moneyphp/crypto-currencies": "^1.1.0", + "moneyphp/iso-currencies": "^3.4", + "php-http/message": "^1.16.0", + "php-http/mock-client": "^1.6.0", + "phpbench/phpbench": "^1.2.5", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1.9", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5.9", + "psr/cache": "^1.0.1 || ^2.0 || ^3.0", + "ticketswap/phpstan-error-formatter": "^1.1" + }, + "suggest": { + "ext-gmp": "Calculate without integer limits", + "ext-intl": "Format Money objects with intl", + "florianv/exchanger": "Exchange rates library for PHP", + "florianv/swap": "Exchange rates library for PHP", + "psr/cache-implementation": "Used for Currency caching" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Money\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mathias Verraes", + "email": "mathias@verraes.net", + "homepage": "http://verraes.net" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + }, + { + "name": "Frederik Bosch", + "email": "f.bosch@genkgo.nl" + } + ], + "description": "PHP implementation of Fowler's Money pattern", + "homepage": "http://moneyphp.org", + "keywords": [ + "Value Object", + "money", + "vo" + ], + "support": { + "issues": "https://github.com/moneyphp/money/issues", + "source": "https://github.com/moneyphp/money/tree/v4.7.1" + }, + "time": "2025-06-06T07:12:38+00:00" + }, { "name": "monolog/monolog", "version": "3.9.0", @@ -11521,12 +11573,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "662d5ecfaab913d8ce0de4521fc40ce92aa14e75" + "reference": "f48b3e601515b060334744b4b495f0d6b3cc2e6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/662d5ecfaab913d8ce0de4521fc40ce92aa14e75", - "reference": "662d5ecfaab913d8ce0de4521fc40ce92aa14e75", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/f48b3e601515b060334744b4b495f0d6b3cc2e6b", + "reference": "f48b3e601515b060334744b4b495f0d6b3cc2e6b", "shasum": "" }, "conflict": { @@ -12202,7 +12254,7 @@ "slim/slim": "<2.6", "slub/slub-events": "<3.0.3", "smarty/smarty": "<4.5.3|>=5,<5.1.1", - "snipe/snipe-it": "<8.1", + "snipe/snipe-it": "<8.1.18", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", "solspace/craft-freeform": ">=5,<5.10.16", @@ -12484,7 +12536,7 @@ "type": "tidelift" } ], - "time": "2025-09-16T20:05:44+00:00" + "time": "2025-09-19T18:07:33+00:00" }, { "name": "sebastian/cli-parser", diff --git a/config/money.php b/config/money.php new file mode 100644 index 00000000..2201a50b --- /dev/null +++ b/config/money.php @@ -0,0 +1,25 @@ + 'de_DE', + 'defaultCurrency' => 'EUR', + 'defaultFormatter' => \App\Support\Money\DefaultMoneyFormater::class, + // 'defaultSerializer' => \App\Support\Money\DefaultMoneySerializer::class, + 'defaultSerializer' => null, + 'isoCurrenciesPath' => is_dir(__DIR__.'/../vendor') + ? __DIR__.'/../vendor/moneyphp/money/resources/currency.php' + : __DIR__.'/../../../moneyphp/money/resources/currency.php', + 'currencies' => [ + 'iso' => 'all', + 'bitcoin' => '', + 'custom' => [ + // 'EUR' => 2, + // 'MY2' => 3 + ], + ], +]; diff --git a/database/migrations/dev/2024_04_02_125041_hhp_upgrade.php b/database/migrations/dev/2024_04_02_125041_hhp_upgrade.php index c771ee90..442033a4 100644 --- a/database/migrations/dev/2024_04_02_125041_hhp_upgrade.php +++ b/database/migrations/dev/2024_04_02_125041_hhp_upgrade.php @@ -34,7 +34,7 @@ public function up(): void $table->unsignedBigInteger('budget_plan_id'); $table->string('short_name', 16)->nullable(); $table->string('name')->nullable(); - $table->decimal('value', 10, 2)->nullable(); + $table->decimal('value', 10, 2)->default(0); $table->integer('budget_type'); $table->boolean('is_group'); $table->text('description'); diff --git a/resources/views/components/budgetplan/item-group-edit.blade.php b/resources/views/components/budgetplan/item-group-edit.blade.php index 72ba6b01..e0622ef9 100644 --- a/resources/views/components/budgetplan/item-group-edit.blade.php +++ b/resources/views/components/budgetplan/item-group-edit.blade.php @@ -1,10 +1,11 @@ @props([ 'level' => 0, 'item', + 'lastItem' => [], ])
$level === 1, //'col-start-3' => $level === 2, //'col-start-4' => $level === 3, @@ -19,41 +20,58 @@ ]) x-sort:item="{{ $item->id }}">
$item->is_group, - "rounded" => $item->is_group, - "bg-zinc-300 my-2" => $item->is_group, + //"py-2" => $item->is_group, + //"rounded" => $item->is_group, + //"bg-zinc-300 my-2" => $item->is_group, ])>
@if($item->is_group) - + @else - + @endif
-
+
-
+
-
+
+ @if($level > 0) +
+ @for($i = 1; $i <= $level; $i++) + +
$i < $level, + "h-full border-l-2 border-gray-300" => !($lastItem[$i-1]), + "h-1/2 border-l-2 border-gray-300" => ($lastItem[$i-1]) && $i === $level, + ])>
+ @endfor + +
+
+ @endif $level === 3, - //'pl-5' => $level === 2, + "my-2" + //'pl-16 border-l-8 border-zinc-300' => $level === 3, + //'pl-10 border-l-6 border-zinc-300' => $level === 2, + //'pl-5 border-l-4 border-zinc-300' => $level === 1, ])> @if($item->is_group) - + Σ @endif - - +
-
{{-- Action Buttons --}} +
{{-- Action Buttons --}} @if($item->is_group) {{-- subtle or ghost --}} @@ -62,6 +80,7 @@ Debug: L{{ $level }} id{{$item->id}} P{{$item->position}} + @dump($lastItem) @if($item->is_group) to budget @else @@ -80,15 +99,20 @@
@if($item->is_group)
$level === 0, - "border-l-16" => $level === 1, - "border-l-24" => $level === 2, - "border-l-28" => $level === 3, + "col-span-8 grid grid-cols-subgrid", + //"border-zinc-300 ", + //"border-l-12" => $level === 0, + //"border-l-16" => $level === 1, + //"border-l-24" => $level === 2, + //"border-l-28" => $level === 3, ]) x-sort="$wire.sort($item,$position)"> @foreach($item->orderedChildren as $child) - + @endforeach
@endif diff --git a/resources/views/components/money-input.blade.php b/resources/views/components/money-input.blade.php index 710832f6..79281487 100644 --- a/resources/views/components/money-input.blade.php +++ b/resources/views/components/money-input.blade.php @@ -1,15 +1,8 @@ @props([ - 'model', - 'value' => 0, + 'disabled' => false, ]) - -whereDoesntStartWith('wire:model') }} -/> - - - - - +@if(!$disabled) + merge(['class:input' => 'text-right']) }} /> +@else + merge(['class:input' => 'text-right text-black!']) }}/> +@endif diff --git a/resources/views/livewire/budget-plan/plan-edit.blade.php b/resources/views/livewire/budget-plan/plan-edit.blade.php index 75367f88..89da80e8 100644 --- a/resources/views/livewire/budget-plan/plan-edit.blade.php +++ b/resources/views/livewire/budget-plan/plan-edit.blade.php @@ -51,10 +51,10 @@
-
+
{{ __('budget-plan.budget-value') }} - + {{ __('budget-plan.edit.table.headline.value-hint') }} @@ -64,7 +64,7 @@
@foreach($root_items[$budgetType->slug()] as $id) - + @endforeach
From 2295e00b6594737d420aea0f1aae47df48ec5c4b Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 8 Nov 2025 14:18:23 +0100 Subject: [PATCH 025/108] WIP: Legacy Budgetplan Converter --- .../Commands/ConvertLegacyBudgetPlans.php | 342 ++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 app/Console/Commands/ConvertLegacyBudgetPlans.php diff --git a/app/Console/Commands/ConvertLegacyBudgetPlans.php b/app/Console/Commands/ConvertLegacyBudgetPlans.php new file mode 100644 index 00000000..d9610ec1 --- /dev/null +++ b/app/Console/Commands/ConvertLegacyBudgetPlans.php @@ -0,0 +1,342 @@ +option('dry-run'); + $planId = $this->option('plan-id'); + $organization = $this->option('organization'); + + if ($dryRun) { + $this->warn('🔍 Running in DRY-RUN mode - no changes will be made'); + } + + // Get legacy plans to convert + $legacyPlans = $planId + ? LegacyBudgetPlan::where('id', $planId)->get() + : LegacyBudgetPlan::all(); + + if ($legacyPlans->isEmpty()) { + $this->error('No legacy budget plans found to convert.'); + + return self::FAILURE; + } + + $this->info("Found {$legacyPlans->count()} legacy budget plan(s) to convert."); + + // CRITICAL: Find global maximum item ID across ALL plans before starting + $this->nextGroupId = $this->findGlobalMaxItemId($legacyPlans) + 1; + $this->line('🔢 Global max legacy item ID: '.($this->nextGroupId - 1)); + $this->line("🔢 Group IDs will start from: {$this->nextGroupId}"); + + DB::beginTransaction(); + + try { + foreach ($legacyPlans as $legacyPlan) { + $this->info("\n📋 Converting Legacy Plan ID: {$legacyPlan->id}"); + + $this->convertPlan($legacyPlan, $organization, $dryRun); + } + + if ($dryRun) { + DB::rollBack(); + $this->warn("\n✅ Dry run completed - no changes were made"); + } else { + DB::commit(); + $this->info("\n✅ Conversion completed successfully!"); + } + + return self::SUCCESS; + + } catch (\Exception $e) { + DB::rollBack(); + $this->error("\n❌ Error during conversion: ".$e->getMessage()); + $this->error($e->getTraceAsString()); + + return self::FAILURE; + } + } + + /** + * Find the global maximum legacy item ID across ALL plans to be converted + */ + protected function findGlobalMaxItemId($legacyPlans): int + { + $maxId = 0; + + foreach ($legacyPlans as $plan) { + $planMaxId = LegacyBudgetItem::whereHas('budgetGroup', function ($query) use ($plan) { + $query->where('hhp_id', $plan->id); + })->max('id'); + + if ($planMaxId > $maxId) { + $maxId = $planMaxId; + } + } + + // Also check existing budget items in case we're adding to existing data + $existingMaxId = BudgetItem::max('id') ?? 0; + + return max($maxId, $existingMaxId); + } + + /** + * Convert a single legacy budget plan + */ + protected function convertPlan(LegacyBudgetPlan $legacyPlan, string $organization, bool $dryRun): void + { + // Check if plan already exists in new structure + if (BudgetPlan::find($legacyPlan->id)) { + $this->warn(" ⚠️ Budget Plan ID {$legacyPlan->id} already exists in new structure. Skipping."); + + return; + } + + // Create or find fiscal year + $fiscalYear = $this->getOrCreateFiscalYear($legacyPlan, $dryRun); + + // Convert state + $state = $this->convertState($legacyPlan->state); + + // Create new budget plan with the same ID + $newPlan = new BudgetPlan([ + 'organization' => $organization, + 'fiscal_year_id' => $fiscalYear->id, + 'state' => $state, + 'resolution_date' => $legacyPlan->von, + 'approval_date' => null, // Legacy doesn't have this + 'parent_plan' => null, + ]); + + // Force the ID to match the legacy plan + $newPlan->id = $legacyPlan->id; + + if (! $dryRun) { + $newPlan->save(); + } + + $this->line(" ✓ Created Budget Plan (ID: {$newPlan->id}, State: {$state->value})"); + + // Get all legacy groups and items + $legacyGroups = $legacyPlan->budgetGroups()->with('budgetItems')->get(); + $this->line(" 📁 Processing {$legacyGroups->count()} budget groups..."); + + // PASS 1: Create all budget items with preserved IDs + $groupPosition = 0; + foreach ($legacyGroups as $legacyGroup) { + $this->convertItemsFirstPass($legacyGroup, $newPlan, $groupPosition, $dryRun); + $groupPosition++; + } + + // PASS 2: Create group items and update parent_id references + $this->createGroupItems($legacyGroups, $newPlan, $dryRun); + } + + /** + * First pass: Create budget items with their original IDs, temporarily with no parent + */ + protected function convertItemsFirstPass( + LegacyBudgetGroup $legacyGroup, + BudgetPlan $newPlan, + int $groupPosition, + bool $dryRun + ): void { + // Determine budget type from legacy type field (0 = income, 1 = expense) + $budgetType = $legacyGroup->type == 0 ? BudgetType::INCOME : BudgetType::EXPENSE; + + // Assign the next available group ID and increment for next group + $futureGroupId = $this->nextGroupId; + $this->nextGroupId++; + + // Store the mapping for later + $this->groupIdMapping[$legacyGroup->id] = [ + 'new_id' => $futureGroupId, + 'name' => $legacyGroup->gruppen_name, + 'type' => $budgetType, + 'position' => $groupPosition, + 'items' => [], + ]; + + $itemPosition = 0; + foreach ($legacyGroup->budgetItems as $legacyItem) { + if (BudgetItem::find($legacyItem->id)) { + $this->warn(" ⚠️ Budget Item ID {$legacyItem->id} already exists. Skipping."); + + continue; + } + + $newItem = new BudgetItem([ + 'budget_plan_id' => $newPlan->id, + 'short_name' => $legacyItem->titel_nr ?? $this->generateShortName($legacyItem->titel_name), + 'name' => $legacyItem->titel_name, + 'value' => $legacyItem->value, + 'budget_type' => $budgetType, + 'description' => null, + 'parent_id' => null, // Will be updated in pass 2 + 'is_group' => false, + 'position' => $itemPosition, + ]); + + // Force the ID to match the legacy item + $newItem->id = $legacyItem->id; + + if (! $dryRun) { + $newItem->save(); + } + + $this->line(" ✓ Item {$legacyItem->id}: {$legacyItem->titel_name}"); + + // Store the legacy group ID this item belongs to + $this->groupIdMapping[$legacyGroup->id]['items'][] = $legacyItem->id; + + $itemPosition++; + } + + $this->line(" ✓ Group items created: {$legacyGroup->gruppen_name} (will be ID: {$futureGroupId})"); + } + + /** + * Second pass: Create group items and update parent references + */ + protected function createGroupItems($legacyGroups, BudgetPlan $newPlan, bool $dryRun): void + { + $this->line("\n 📦 Creating group items and updating parent references..."); + + foreach ($legacyGroups as $legacyGroup) { + $groupInfo = $this->groupIdMapping[$legacyGroup->id]; + $groupId = $groupInfo['new_id']; + + // Calculate total value for the group + $totalValue = $legacyGroup->budgetItems()->sum('value'); + + // Create the group item with the predetermined ID + $groupItem = new BudgetItem([ + 'budget_plan_id' => $newPlan->id, + 'short_name' => $this->generateShortName($groupInfo['name']), + 'name' => $groupInfo['name'], + 'value' => $totalValue, + 'budget_type' => $groupInfo['type'], + 'description' => null, + 'parent_id' => null, + 'is_group' => true, + 'position' => $groupInfo['position'], + ]); + + $groupItem->id = $groupId; + + if (! $dryRun) { + $groupItem->save(); + } + + $this->line(" ✓ Created Group Item ID {$groupId}: {$groupInfo['name']}"); + + // Update all child items to point to this group + if (! empty($groupInfo['items']) && ! $dryRun) { + $itemCount = BudgetItem::whereIn('id', $groupInfo['items']) + ->update(['parent_id' => $groupId]); + + $this->line(" ↳ Updated {$itemCount} child items to parent ID {$groupId}"); + } elseif (! empty($groupInfo['items'])) { + $this->line(' ↳ Would update '.count($groupInfo['items'])." child items to parent ID {$groupId}"); + } + } + } + + /** + * Get or create fiscal year based on legacy plan dates + */ + protected function getOrCreateFiscalYear(LegacyBudgetPlan $legacyPlan, bool $dryRun): FiscalYear + { + // Try to find existing fiscal year that matches the dates + $fiscalYear = FiscalYear::where('start_date', $legacyPlan->von) + ->where('end_date', $legacyPlan->bis) + ->first(); + + if (! $fiscalYear) { + // Create a new fiscal year + $fiscalYear = new FiscalYear([ + 'start_date' => $legacyPlan->von, + 'end_date' => $legacyPlan->bis, + ]); + + if (! $dryRun) { + $fiscalYear->save(); + } else { + // In dry-run, we need a mock ID + $fiscalYear->id = 9999; + } + + $this->line(" ✓ Created Fiscal Year: {$legacyPlan->von} to {$legacyPlan->bis}"); + } + + return $fiscalYear; + } + + /** + * Convert legacy state to new BudgetPlanState enum + */ + protected function convertState(?string $state): BudgetPlanState + { + // Adjust this mapping based on your legacy state values + return match ($state) { + 'final', 'approved', '1' => BudgetPlanState::FINAL, + default => BudgetPlanState::DRAFT, + }; + } + + /** + * Generate a short name from a full name + */ + protected function generateShortName(string $fullName): string + { + // Take first 3 words or first 20 characters + $words = explode(' ', $fullName); + $shortName = implode(' ', array_slice($words, 0, 3)); + + return substr($shortName, 0, 20); + } +} From f745a8b8197b02114d050be68511ee40927454e3 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 8 Nov 2025 14:18:44 +0100 Subject: [PATCH 026/108] minor changes to moneyformating --- app/Support/Money/DefaultMoneyFormater.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Support/Money/DefaultMoneyFormater.php b/app/Support/Money/DefaultMoneyFormater.php index b03b961a..4a24bcd1 100644 --- a/app/Support/Money/DefaultMoneyFormater.php +++ b/app/Support/Money/DefaultMoneyFormater.php @@ -28,8 +28,8 @@ public function inverse(string $formatted): Money // Remove currency symbol and trim $formatted = str_replace('€', '', trim($formatted)); - // Convert comma to decimal point and remove spaces - $amount = (float) str_replace([',', ' '], ['.', ''], $formatted); + // Convert remove the thousand separators, comma to decimal point and remove spaces + $amount = (float) str_replace(['.', ',', ' '], ['', '.', ''], $formatted); // Convert to cents $cents = (int) round($amount * 100); From a6ef079952ddf1acae16714cd91285f124db75a2 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 8 Nov 2025 14:19:32 +0100 Subject: [PATCH 027/108] update budget editing: streamlined layout, added value support for new items, and enhanced enums --- app/Livewire/BudgetPlan/BudgetPlanEdit.php | 10 +++++---- app/Models/Enums/BudgetPlanState.php | 16 ++++++++++++++ .../budgetplan/item-group-edit.blade.php | 22 +++++-------------- .../views/components/money-input.blade.php | 1 + 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/app/Livewire/BudgetPlan/BudgetPlanEdit.php b/app/Livewire/BudgetPlan/BudgetPlanEdit.php index 649bfb3d..c2b63a64 100644 --- a/app/Livewire/BudgetPlan/BudgetPlanEdit.php +++ b/app/Livewire/BudgetPlan/BudgetPlanEdit.php @@ -219,9 +219,9 @@ public function addGroup(BudgetType $budget_type): void $this->addBudget($new_item->id); } - public function addBudget(int $parent_id): void + public function addBudget(int $parent_id, float $value = 0.0): void { - $this->addItem($parent_id, false); + $this->addItem($parent_id, false, $value); } public function addSubGroup(int $parent_id): void @@ -229,7 +229,7 @@ public function addSubGroup(int $parent_id): void $this->addItem($parent_id, true); } - private function addItem(int $parent_id, bool $is_group): void + private function addItem(int $parent_id, bool $is_group, $value = 0.0): void { $parent = BudgetItem::findOrFail($parent_id); if ($parent->is_group === 0) { @@ -243,7 +243,7 @@ private function addItem(int $parent_id, bool $is_group): void 'budget_type' => $parent->budget_type, 'is_group' => $is_group, 'position' => $pos + 1, - 'value' => Money::EUR(0), + 'value' => Money::EUR($value, true), ]); $form = new ItemForm($this, 'items.'.$new_item->budget_type->slug().'.'.$new_item->id); $form->setItem($new_item); @@ -258,6 +258,7 @@ public function convertToGroup(int $item_id): void return; } $item->update(['is_group' => true]); + $this->addBudget($item->id, $item->value->getAmount() / 100); } public function convertToBudget(int $item_id): void @@ -306,6 +307,7 @@ public function delete(int $item_id): void return; } $item->delete(); + $this->resumItemValues($item); } public function refresh(): void diff --git a/app/Models/Enums/BudgetPlanState.php b/app/Models/Enums/BudgetPlanState.php index 78aedfbf..5796f168 100644 --- a/app/Models/Enums/BudgetPlanState.php +++ b/app/Models/Enums/BudgetPlanState.php @@ -6,4 +6,20 @@ enum BudgetPlanState: string { case FINAL = 'final'; case DRAFT = 'draft'; + + public function slug(): string + { + return match ($this) { + self::FINAL => 'final', + self::DRAFT => 'draft', + }; + } + + public function label(): string + { + return match ($this) { + self::FINAL => __('Final'), + self::DRAFT => __('Draft'), + }; + } } diff --git a/resources/views/components/budgetplan/item-group-edit.blade.php b/resources/views/components/budgetplan/item-group-edit.blade.php index e0622ef9..8be71322 100644 --- a/resources/views/components/budgetplan/item-group-edit.blade.php +++ b/resources/views/components/budgetplan/item-group-edit.blade.php @@ -1,23 +1,12 @@ @props([ 'level' => 0, 'item', + /* @var bool array of booleans, one for each level, indicating if the item is the last one on that level */ 'lastItem' => [], ])
$level === 1, - //'col-start-3' => $level === 2, - //'col-start-4' => $level === 3, - //'ml-5' => $level === 1, - //'ml-10' => $level === 2, - //'ml-16' => $level === 3, - //"border-zinc-300 ", - //"border-l-1" => $level === 0 && $item->is_group, - //"border-l-2" => $level === 1 && $item->is_group, - //"border-l-3" => $level === 2 && $item->is_group, - //"border-l-4" => $level === 3 && $item->is_group, - ]) x-sort:item="{{ $item->id }}">
$item->is_group, @@ -35,17 +24,17 @@ @endif
-
+
-
+
@if($level > 0)
@for($i = 1; $i <= $level; $i++) - +
$i < $level, @@ -68,7 +57,7 @@ Σ @endif - +
{{-- Action Buttons --}} @@ -80,7 +69,6 @@ Debug: L{{ $level }} id{{$item->id}} P{{$item->position}} - @dump($lastItem) @if($item->is_group) to budget @else diff --git a/resources/views/components/money-input.blade.php b/resources/views/components/money-input.blade.php index 79281487..fe01257a 100644 --- a/resources/views/components/money-input.blade.php +++ b/resources/views/components/money-input.blade.php @@ -1,6 +1,7 @@ @props([ 'disabled' => false, ]) + @if(!$disabled) merge(['class:input' => 'text-right']) }} /> @else From 927fb13319a3dbeabcdc03e569fe754ebff8bc0c Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 29 Nov 2025 15:02:53 +0100 Subject: [PATCH 028/108] made booking unclickable --- legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php b/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php index 36cbc370..1970adef 100644 --- a/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php +++ b/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php @@ -1989,6 +1989,10 @@ public function getDiagramStatelistFiltered(): array if ($k === 'revocation') { continue; } + // Skip 'booked' state - make it unclickable + if ($k === 'booked') { + continue; + } if (! $this->state_change_possible($k)) { continue; } From 2968fa34ba9bf9e451dcd1e744990e6fb697b58f Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 29 Nov 2025 15:03:35 +0100 Subject: [PATCH 029/108] fix: chrome: no double dropdowns with new projektposten in auslagen.js --- public/js/auslagen.js | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/public/js/auslagen.js b/public/js/auslagen.js index 3e77a679..cb69cd65 100644 --- a/public/js/auslagen.js +++ b/public/js/auslagen.js @@ -1,17 +1,17 @@ /** - * + * */ (function(){ var animate_delete = function($elm, callback){ - $elm.animate({ height: 0, opacity: 0 }, 500,function(){ + $elm.animate({ height: 0, opacity: 0 }, 500,function(){ $(this).remove(); if (typeof(callback) == 'function'){ callback(); } }); }; - + var get_projektdata = function ($elm){ var $dlist = $elm.closest('.beleg-table').children('.datalists').children('.datalist-projekt').children('option'); var map = {}; @@ -20,7 +20,7 @@ }); return map; } - + var add_beleg_counter = 0; var add_beleg = function(){ add_beleg_counter++; @@ -63,7 +63,7 @@ $select.siblings('.posten-out').hide(); }); }; - + var remove_beleg = function(ev){ ev.preventDefault(); ev.stopPropagation(); @@ -78,17 +78,17 @@ var $container = $e.closest('.beleg-container'); animate_delete($container.parent()); }; - + var delete_posten = function (){ var $e = $(this); var list = $e.closest('.posten-inner-list'); - animate_delete($e.closest('.posten-entry'), + animate_delete($e.closest('.posten-entry'), function() { update_posten_counter(list); } ); } - + var update_posten_counter = function ($list){ //update 'keine Angaben' var $l = $list.children('.posten-entry'); @@ -123,7 +123,7 @@ $sumline.children('.posten-sum-in').find('span')[1].innerHTML = sum_in.toFixed(2); $sumline.children('.posten-sum-out').find('span')[1].innerHTML = sum_out.toFixed(2); } - + var add_posten_counter = 0; var add_posten = function(ev, a,b,c,eee) { ev.stopPropagation(); @@ -137,9 +137,14 @@ //clone line var $old = $e.closest('.posten-entry-new'); var $new = $old.clone(); - //old -> remove values + + // FIX: Remove Bootstrap Select markup from cloned element - chrome + $new.find('.projekt-posten-select .bootstrap-select').remove(); + $new.find('.projekt-posten-select select').remove(); + + //old -> remove values $e.val(0); - //new class -> posten-entry-new -> posten-entry + //new class -> posten-entry-new -> posten-entry $new.removeClass('posten-entry-new').addClass('posten-entry'); //new -> remove this callback function on inputs $new.find('input').off('input', add_posten); @@ -168,7 +173,7 @@ update_posten_counter($new.closest('.posten-inner-list')); return true; }; - + var posten_handler = function ($posten_inner_list) { // remove button $posten_inner_list.find('.posten-counter .fa-trash').on('click', delete_posten); @@ -183,7 +188,7 @@ }); }; - + // append editable select list for posten name var posten_project_list = function ($target, trigger_addposten){ var data = get_projektdata($target); @@ -231,7 +236,7 @@ }); }, 100); }; - + var auslagen_einnahmen_disable = function($t){ var $select = $t.find('.projekt-posten-select'); var $inp = $t.find('.posten-in input'); @@ -260,7 +265,7 @@ $outa.show(); } }; - + var update_fileinput = function($target){ var $t = $target.find('input'); var cfg = { @@ -275,7 +280,7 @@ }; $t.fileinput(cfg); }; - + $(document).ready(function(){ $('.beleg-table .beleg-container .beleg-file input').each(function(i, e){ //update name @@ -385,4 +390,4 @@ auslagen_einnahmen_disable($e); }); }); -})(); \ No newline at end of file +})(); From 34c44bf529587790cd2d41908beb10534278e52f Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 29 Nov 2025 16:03:30 +0100 Subject: [PATCH 030/108] fix: linewrap links --- docs/feature-changelog.md | 5 +++++ public/css/main.css | 1 + 2 files changed, 6 insertions(+) diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index 46affd00..37612152 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -1,3 +1,8 @@ +# unreleased +* fix: booking button not clickable +* fix: chrome: no double button in expense form +* fix: linewrap links in project + # v4.3.2 * **Brotkrumen wurden hinzugefügt:** Es ist nun links oben ersichtlich, wo du dich innerhalb StuFiS befindest und kannst schneller innerhalb der Struktur die Seite wechseln. Der Projekt-Button ist nun nach rechts oben gewandert. * **Testverbesserungen** diff --git a/public/css/main.css b/public/css/main.css index 5b404c0d..3e39e6a8 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -687,6 +687,7 @@ button.btn.btn-arrow-left { .textarea_readonly { white-space: pre-wrap; + word-break: break-word; } .bt-dark { From 746550fa10a3d911ce1be1fdbfa19b68688c0b5d Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 29 Nov 2025 17:33:57 +0100 Subject: [PATCH 031/108] fix: remember changed budget year between tabs --- app/Models/Legacy/LegacyBudgetPlan.php | 5 +++++ docs/feature-changelog.md | 1 + legacy/config/config.routing.php | 16 +++++----------- legacy/lib/framework/render/MenuRenderer.php | 13 +++++-------- .../views/components/layouts/index.blade.php | 10 +++++----- routes/breadcrumbs.php | 2 +- routes/legacy.php | 3 ++- routes/web.php | 8 +++++++- 8 files changed, 31 insertions(+), 27 deletions(-) diff --git a/app/Models/Legacy/LegacyBudgetPlan.php b/app/Models/Legacy/LegacyBudgetPlan.php index 9929fa76..6f3213cc 100644 --- a/app/Models/Legacy/LegacyBudgetPlan.php +++ b/app/Models/Legacy/LegacyBudgetPlan.php @@ -3,6 +3,7 @@ namespace App\Models\Legacy; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\HasOne; /** * App\Models\Legacy\LegacyBudgetPlan @@ -51,4 +52,8 @@ public function budgetItems(): \Illuminate\Database\Eloquent\Relations\HasManyTh { return $this->hasManyThrough(LegacyBudgetItem::class, LegacyBudgetGroup::class); } + + public static function latest() : LegacyBudgetPlan { + return self::orderBy('id', 'desc')->first(); + } } diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index 37612152..05954180 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -2,6 +2,7 @@ * fix: booking button not clickable * fix: chrome: no double button in expense form * fix: linewrap links in project +* fix: remember changed budget year between tabs # v4.3.2 * **Brotkrumen wurden hinzugefügt:** Es ist nun links oben ersichtlich, wo du dich innerhalb StuFiS befindest und kannst schneller innerhalb der Struktur die Seite wechseln. Der Projekt-Button ist nun nach rechts oben gewandert. diff --git a/legacy/config/config.routing.php b/legacy/config/config.routing.php index 74407f79..9e407ed9 100644 --- a/legacy/config/config.routing.php +++ b/legacy/config/config.routing.php @@ -185,28 +185,22 @@ 'navigation' => 'mykonto', ], [ - 'path' => '(mygremium|allgremium|open-projects)', + 'path' => '\d+', 'type' => 'pattern', - 'param' => 'action', + 'param' => 'hhp-id', 'navigation' => 'overview', 'load' => [ LoadGroups::SELECTPICKER, ], 'children' => [ [ - 'path' => '\d+', + 'path' => '(mygremium|allgremium|open-projects)', 'type' => 'pattern', - 'param' => 'hhp-id', + 'param' => 'action', ], ], ], - [ - 'path' => 'extern', - 'type' => 'path', - 'action' => 'extern', - 'navigation' => 'overview', - 'groups' => 'ref-finanzen', - ], + [ 'path' => 'hv', 'type' => 'path', diff --git a/legacy/lib/framework/render/MenuRenderer.php b/legacy/lib/framework/render/MenuRenderer.php index 3c3e6f8d..4edc4f50 100644 --- a/legacy/lib/framework/render/MenuRenderer.php +++ b/legacy/lib/framework/render/MenuRenderer.php @@ -60,7 +60,7 @@ public function render(): void public function renderProjekte($active): void { - [$hhps, $hhp_id] = $this->renderHHPSelector($this->pathinfo, URIBASE."menu/$active/"); + [$hhps, $hhp_id] = $this->renderHHPSelector($this->pathinfo, URIBASE."menu/", "/$active" ); echo "
"; $hhp_von = $hhps[$hhp_id]['von']; $hhp_bis = $hhps[$hhp_id]['bis']; @@ -81,7 +81,7 @@ public function renderProjekte($active): void break; case 'mygremium': if (empty($userGremien)) { - $this->setOverviewTabs($active); + $this->setOverviewTabs($active, $hhp_id); $this->renderAlert( 'Schade!', $this->makeClickableMails( @@ -175,7 +175,7 @@ static function ($res, $key) use (&$pids) { } // var_dump(end(end($projekte))); - $this->setOverviewTabs($active); ?> + $this->setOverviewTabs($active, $hhp_id); ?>
renderTable($header,[$extern],array_keys($header)); } - public function setOverviewTabs($active): void + public function setOverviewTabs($active, $hhp_id): void { - $linkbase = URIBASE.'menu/'; + $linkbase = URIBASE. "menu/$hhp_id/"; $tabs = [ 'mygremium' => " Meine Gremien", 'allgremium' => " Alle Gremien", 'open-projects' => " Offene Projekte", ]; - if (AuthHandler::getInstance()->hasGroup('ref-finanzen')) { - // $tabs["extern"] = " Externe Anträge"; - } // $tabs["search"] = " Suche"; HTMLPageRenderer::setTabs($tabs, $linkbase, $active); } diff --git a/resources/views/components/layouts/index.blade.php b/resources/views/components/layouts/index.blade.php index 3ee3f14e..92b0f2c1 100644 --- a/resources/views/components/layouts/index.blade.php +++ b/resources/views/components/layouts/index.blade.php @@ -35,7 +35,7 @@
{{ config('app.name') }}
- @@ -195,8 +195,8 @@ class="absolute top-1 right-0 -mr-14 p-1"> -
-
+
+
@@ -217,8 +217,8 @@ class="absolute top-1 right-0 -mr-14 p-1"> {{ __('general.new-project-button') }}
-
- +
+ diff --git a/routes/breadcrumbs.php b/routes/breadcrumbs.php index 6343fbad..1f12898a 100644 --- a/routes/breadcrumbs.php +++ b/routes/breadcrumbs.php @@ -15,7 +15,7 @@ // Home Breadcrumbs::for('legacy.dashboard', static function (BreadcrumbTrail $trail): void { - $trail->push('Home', route('legacy.dashboard', 'mygremium')); + $trail->push('Home', route('legacy.dashboard', ['sub' => 'mygremium'])); }); // Home > TODOS diff --git a/routes/legacy.php b/routes/legacy.php index 1969adca..26a9e734 100644 --- a/routes/legacy.php +++ b/routes/legacy.php @@ -4,12 +4,13 @@ use Illuminate\Support\Facades\Route; Route::middleware(['auth'])->name('legacy.')->group(function (): void { + Route::get('menu/hv', [LegacyController::class, 'render'])->name('todo.hv'); Route::get('menu/kv', [LegacyController::class, 'render'])->name('todo.kv'); Route::get('menu/kv/exportBank', [LegacyController::class, 'render'])->name('todo.kv.bank'); Route::get('menu/belege', [LegacyController::class, 'render'])->name('todo.belege'); Route::get('menu/stura', [LegacyController::class, 'render'])->name('sitzung'); - Route::get('menu/{sub}', [LegacyController::class, 'render'])->name('dashboard'); + Route::get('menu/{hhp_id?}/{sub}', [LegacyController::class, 'render'])->name('dashboard'); // legacy hhp-picker needs that url schema as a easy forward - route names are here not usable :( Route::redirect('konto/{hhp_id}/new', '/bank-account/new'); Route::get('konto/{hhp_id?}/{konto_id?}', [LegacyController::class, 'render'])->name('konto'); diff --git a/routes/web.php b/routes/web.php index 522cfd02..0853fb56 100644 --- a/routes/web.php +++ b/routes/web.php @@ -12,12 +12,18 @@ */ use App\Http\Controllers\ViewChangelog; +use App\Models\Legacy\LegacyBudgetPlan; use Illuminate\Support\Facades\Route; Route::middleware(['auth'])->group(function (): void { // legacy is register later, so we cannot route(legacy.dashboard) there - Route::redirect('/', 'menu/mygremium')->name('home'); + Route::get('/', function (){ + $latestPlan = LegacyBudgetPlan::latest(); + return redirect()->route( + 'legacy.dashboard', ['sub' => 'mygremium', 'hhp_id' => $latestPlan->id] + ); + })->name('home'); Route::get('bank-account/new', \App\Livewire\NewBankingAccount::class)->name('bank-account.new'); Route::get('bank-account/import/manual', \App\Livewire\TransactionImportWire::class)->name('bank-account.import.csv'); From fe484314ba41898cd7e040baa4f3bfee95bf37e3 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 29 Nov 2025 17:37:47 +0100 Subject: [PATCH 032/108] fix: removed not working storno button --- docs/feature-changelog.md | 1 + legacy/lib/booking/BookingHandler.php | 20 -------------------- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index 05954180..fbfa6510 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -3,6 +3,7 @@ * fix: chrome: no double button in expense form * fix: linewrap links in project * fix: remember changed budget year between tabs +* fix: removed not working storno button # v4.3.2 * **Brotkrumen wurden hinzugefügt:** Es ist nun links oben ersichtlich, wo du dich innerhalb StuFiS befindest und kannst schneller innerhalb der Struktur die Seite wechseln. Der Projekt-Button ist nun nach rechts oben gewandert. diff --git a/legacy/lib/booking/BookingHandler.php b/legacy/lib/booking/BookingHandler.php index 1128485e..9e301ab6 100644 --- a/legacy/lib/booking/BookingHandler.php +++ b/legacy/lib/booking/BookingHandler.php @@ -274,7 +274,6 @@ private function renderBookingHistory($active): void Beleg Buchungs-Datum Zahlung - Stornieren Buchungstext @@ -342,25 +341,6 @@ class="fa fa-fw fa-question-circle" aria-hidden="true"> TextStyle::BLACK ); ?> - - -
- - renderNonce(); ?> - - - - -  Stornieren - -
- - - Durch '>B-Nr: - From c9a603941d40a2d41397f308fd16683d15f3fee8 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 29 Nov 2025 17:49:55 +0100 Subject: [PATCH 033/108] fix: fix: last projekt posten is undeletable --- docs/feature-changelog.md | 1 + public/css/main.css | 5 +++++ public/js/main.js | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index fbfa6510..87bec48e 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -4,6 +4,7 @@ * fix: linewrap links in project * fix: remember changed budget year between tabs * fix: removed not working storno button +* fix: last projekt posten is undeletable # v4.3.2 * **Brotkrumen wurden hinzugefügt:** Es ist nun links oben ersichtlich, wo du dich innerhalb StuFiS befindest und kannst schneller innerhalb der Struktur die Seite wechseln. Der Projekt-Button ist nun nach rechts oben gewandert. diff --git a/public/css/main.css b/public/css/main.css index 3e39e6a8..ed53deea 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -42,6 +42,11 @@ pre { display: none; } +.dynamic-table > tbody > tr:only-child > td.delete-row a.delete-row, +.dynamic-table > tbody > tr:nth-child(1):nth-last-child(2) > td.delete-row a.delete-row { + display: none; +} + .dynamic-table-readonly.dynamic-table > tbody > tr > td.delete-row a.delete-row { display: none; } diff --git a/public/js/main.js b/public/js/main.js index 0b9d82dd..84892dec 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -523,6 +523,11 @@ $(document).ready(function () { var $table = $tbody.closest("table"); if ($table.is(".dynamic-table-readonly")) return; + // Prevent deletion if it's the last non-template row + if ($tbody.children("tr:not(.new-table-row)").length <= 1) { + return false; + } + $tr.triggerHandler("pre-row-delete"); $tr.remove(); $tbody.children("tr").each(function (rowNumber, tr) { From 23d59a6a36e2acb355226e5dea2330b5346fa438 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 29 Nov 2025 17:53:13 +0100 Subject: [PATCH 034/108] lang: changed Auslagen to Abrechnung --- docs/feature-changelog.md | 1 + legacy/template/tex/belege-pdf.tex.twig | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index 87bec48e..0ed4574a 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -5,6 +5,7 @@ * fix: remember changed budget year between tabs * fix: removed not working storno button * fix: last projekt posten is undeletable +* lang: changed Auslagen to Abrechnung # v4.3.2 * **Brotkrumen wurden hinzugefügt:** Es ist nun links oben ersichtlich, wo du dich innerhalb StuFiS befindest und kannst schneller innerhalb der Struktur die Seite wechseln. Der Projekt-Button ist nun nach rechts oben gewandert. diff --git a/legacy/template/tex/belege-pdf.tex.twig b/legacy/template/tex/belege-pdf.tex.twig index 2e3cbade..fe51d55c 100644 --- a/legacy/template/tex/belege-pdf.tex.twig +++ b/legacy/template/tex/belege-pdf.tex.twig @@ -7,7 +7,7 @@ (% block main %) (( helper.hintbox('Die Belege für diese Abrechnung müssen an dieses Dokument angehangen, alles zusammen getackert, und im Stura Büro bei Referat Finanzen eingereicht werden. Dazu legt man das Dokument in das Postfach des Referats Finanzen.') )) (( helper.listing(projektMeta, 'Projekt Informationen') )) - (( helper.listing(auslagenMeta, 'Auslagen Informationen', true) )) + (( helper.listing(auslagenMeta, 'Abrechnungs Informationen', true) )) (( helper.headline('Versicherung') )) (( helper.smalltext('Ich versichere, dass alle Angaben von mir, zum jetzigen Zeitpunkt, korrekt eingereicht wurden. Weiterhin habe und werde ich die Belege bei keiner anderen Stelle angeben bzw. abrechnen. From 82bc4a502869685cab2893aec2018cd4153fe8a5 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 29 Nov 2025 22:42:05 +0100 Subject: [PATCH 035/108] fix: projektposten keep their internal id when deleted. so you should be able to delete other unused posten. Deleting used posten is still possible and will result in an error. --- app/Models/Legacy/ProjectPost.php | 4 +- docs/feature-changelog.md | 1 + legacy/lib/forms/projekte/ProjektHandler.php | 126 +++++++------------ 3 files changed, 52 insertions(+), 79 deletions(-) diff --git a/app/Models/Legacy/ProjectPost.php b/app/Models/Legacy/ProjectPost.php index 9694d644..ac3cbf3e 100644 --- a/app/Models/Legacy/ProjectPost.php +++ b/app/Models/Legacy/ProjectPost.php @@ -45,7 +45,9 @@ class ProjectPost extends Model /** * @var array */ - protected $fillable = ['titel_id', 'einnahmen', 'ausgaben', 'name', 'bemerkung']; + protected $fillable = ['titel_id', 'einnahmen', 'ausgaben', 'name', 'bemerkung', + 'id' + ]; public function project(): BelongsTo { diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index 0ed4574a..e77166e0 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -6,6 +6,7 @@ * fix: removed not working storno button * fix: last projekt posten is undeletable * lang: changed Auslagen to Abrechnung +* fix: projektposten keep their internal id when deleted. so you should be able to delete other unused posten. Deleting used posten is still possible and will result in an error. # v4.3.2 * **Brotkrumen wurden hinzugefügt:** Es ist nun links oben ersichtlich, wo du dich innerhalb StuFiS befindest und kannst schneller innerhalb der Struktur die Seite wechseln. Der Projekt-Button ist nun nach rechts oben gewandert. diff --git a/legacy/lib/forms/projekte/ProjektHandler.php b/legacy/lib/forms/projekte/ProjektHandler.php index 8eb9fc5e..d36ec1f9 100644 --- a/legacy/lib/forms/projekte/ProjektHandler.php +++ b/legacy/lib/forms/projekte/ProjektHandler.php @@ -3,6 +3,7 @@ namespace forms\projekte; use App\Exceptions\LegacyDieException; +use App\Models\Legacy\ProjectPost; use App\Models\User; use forms\chat\ChatHandler; use forms\FormHandlerInterface; @@ -14,6 +15,7 @@ use framework\auth\AuthHandler; use framework\DBConnector; use framework\render\HTMLPageRenderer; +use Illuminate\Support\Facades\DB; use PDOException; class ProjektHandler extends FormHandlerInterface @@ -81,13 +83,13 @@ public function __construct($pathInfo) [], ['projekt_id' => $this->id] ); - foreach ($tmp as $row) { - $idx = $row['id']; - $this->data['posten-name'][$idx] = $row['name']; - $this->data['posten-bemerkung'][$idx] = $row['bemerkung']; - $this->data['posten-einnahmen'][$idx] = $row['einnahmen']; - $this->data['posten-ausgaben'][$idx] = $row['ausgaben']; - $this->data['posten-titel'][$idx] = $row['titel_id']; + foreach ($tmp as $idx => $row) { + $this->data['posten-id'][$idx+1] = $row['id']; + $this->data['posten-name'][$idx+1] = $row['name']; + $this->data['posten-bemerkung'][$idx+1] = $row['bemerkung']; + $this->data['posten-einnahmen'][$idx+1] = $row['einnahmen']; + $this->data['posten-ausgaben'][$idx+1] = $row['ausgaben']; + $this->data['posten-titel'][$idx+1] = $row['titel_id']; } $stateNow = $this->data['state']; } @@ -193,6 +195,7 @@ public static function initStaticVars(): bool 'beschreibung' => '', 'recht' => '', 'recht-additional' => '', + 'posten-id' => [1 => ''], 'posten-name' => [1 => ''], 'posten-bemerkung' => [1 => ''], 'posten-titel' => [1 => ''], @@ -336,6 +339,7 @@ public static function getStateStringFromName($statename) */ public function updateSavedData($data): bool { + DB::beginTransaction(); $data = array_intersect_key($data, self::$emptyData); $version = (int) $data['version']; @@ -350,13 +354,15 @@ public function updateSavedData($data): bool count($data['posten-name']), count($data['posten-bemerkung']), count($data['posten-einnahmen']), - count($data['posten-ausgaben']) + count($data['posten-ausgaben']), + count($data['posten-id']) ); $minRows = min( count($data['posten-name']), count($data['posten-bemerkung']), count($data['posten-einnahmen']), - count($data['posten-ausgaben']) + count($data['posten-ausgaben']), + count($data['posten-id']) ); } // wenn posten-titel nicht mit übertragen setze dummy an seine stelle @@ -374,10 +380,13 @@ public function updateSavedData($data): bool 'lastupdated' => date('Y-m-d H:i:s'), 'version' => ($this->data['version'] + 1), ]; - // extract some fields for other db destination - $extractFields = ['posten-name', 'posten-bemerkung', 'posten-einnahmen', 'posten-ausgaben', 'posten-titel']; + // extract the posten fields, which go to a different table + $extractFields = ['posten-name', 'posten-bemerkung', 'posten-einnahmen', 'posten-ausgaben', 'posten-titel', 'posten-id']; $extractFields = array_intersect_key($data, array_flip($extractFields)); + + // remove the generated and extracted fields from the data array $data = array_diff_key($data, $generatedFields, $extractFields); + $recht_unset = false; if (isset($data['recht-additional'])) { if (! isset($data['recht']) && isset($this->data['recht'])) { @@ -421,72 +430,31 @@ public function updateSavedData($data): bool 'and' )) { // update old posten, create new, delete old - $oldRows = count($this->data['posten-name']); // update old posten (last minrow is empty all the time - $retUpdate = true; - for ($i = 0; $i < $minRows - 1 && $i < $oldRows; $i++) { - // would throw exception if not working - $rowsUpdated = DBConnector::getInstance()->dbUpdate( - 'projektposten', - [ - 'id' => $i + 1, - 'projekt_id' => $this->id, - ], - [ - 'titel_id' => $extractFields['posten-titel'][$i] === '' ? null : $extractFields['posten-titel'][$i], - 'einnahmen' => DBConnector::getInstance()->convertUserValueToDBValue( - $extractFields['posten-einnahmen'][$i], - 'money' - ), - 'ausgaben' => DBConnector::getInstance()->convertUserValueToDBValue( - $extractFields['posten-ausgaben'][$i], - 'money' - ), - 'name' => $extractFields['posten-name'][$i], - 'bemerkung' => $extractFields['posten-bemerkung'][$i], - ], - ); - // could return row count = 0 (if no changes happened) - $retUpdate = $retUpdate && ($rowsUpdated <= 1); - } + $nextFreeId = max($extractFields['posten-id']); - // add new posten - $retInsert = true; - for ($i = $oldRows; $i < $minRows - 1; $i++) { - $retInsert = $retInsert && (DBConnector::getInstance()->dbInsert( - 'projektposten', - [ - 'id' => $i + 1, - 'projekt_id' => $this->id, - 'titel_id' => $extractFields['posten-titel'][$i] === '' ? null : $extractFields['posten-titel'][$i], - 'einnahmen' => DBConnector::getInstance()->convertUserValueToDBValue( - $extractFields['posten-einnahmen'][$i], - 'money' - ), - 'ausgaben' => DBConnector::getInstance()->convertUserValueToDBValue( - $extractFields['posten-ausgaben'][$i], - 'money' - ), - 'name' => $extractFields['posten-name'][$i], - 'bemerkung' => $extractFields['posten-bemerkung'][$i], - ] - )) === '0'; // lastInsertedId returns "0" due to auto increment is not used (multikey) - } - // delete old ones - $retDelete = true; - if ($minRows - 1 < $oldRows) { - $retDelete = DBConnector::getInstance()->dbDelete( - 'projektposten', - [ - 'id' => ['>', $minRows - 1], - 'projekt_id' => $this->id, - ] - ); - $retDelete = $retDelete > 0; + // protocol which ids got used, so we can delete everything else afterwards + $used_ids = []; + + for ($i = 0; $i < $minRows - 1; $i++) { + $id = ((int)$extractFields['posten-id'][$i]); + if ($id === 0) { + $id = ++$nextFreeId; + } + $used_ids[] = $id; + ProjectPost::updateOrInsert(['id' => $id , 'projekt_id' => $this->id], [ + 'name' => $extractFields['posten-name'][$i], + 'bemerkung' => $extractFields['posten-bemerkung'][$i], + 'einnahmen' => DBConnector::getInstance()->convertUserValueToDBValue($extractFields['posten-einnahmen'][$i], 'money'), + 'ausgaben' => DBConnector::getInstance()->convertUserValueToDBValue($extractFields['posten-ausgaben'][$i], 'money'), + 'titel_id' => $extractFields['posten-titel'][$i], + ]); } + ProjectPost::whereNotIn('id', $used_ids)->delete(); - return $retMetaUpdate && $retDelete && $retInsert && $retUpdate; + DB::commit(); + return true; } return $retMetaUpdate; @@ -727,6 +695,7 @@ private function renderProjekt($title): void + @@ -736,11 +705,8 @@ private function renderProjekt($title): void - - data['posten-name'][] = ''; - + data['posten-name'][] = ''; foreach ($this->data['posten-name'] as $row_nr => $null) { $new_row = ($row_nr) === count($this->data['posten-name']); if ($new_row && ! $tablePartialEditable) { @@ -751,7 +717,10 @@ private function renderProjekt($title): void $sel_titel['values'] = $this->data['posten-titel'][$row_nr]; } ?> - + + @@ -807,6 +776,7 @@ class='fa fa-fw fa-trash'> + From 3054d0120d194733fc3f7d68dec88183b6f0c339 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 29 Nov 2025 22:53:07 +0100 Subject: [PATCH 036/108] fix: added a default "sonstige" organisation --- docs/feature-changelog.md | 1 + legacy/lib/forms/FormTemplater.php | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index e77166e0..0dfab1ba 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -7,6 +7,7 @@ * fix: last projekt posten is undeletable * lang: changed Auslagen to Abrechnung * fix: projektposten keep their internal id when deleted. so you should be able to delete other unused posten. Deleting used posten is still possible and will result in an error. +* fix: added a default "sonstige" organisation # v4.3.2 * **Brotkrumen wurden hinzugefügt:** Es ist nun links oben ersichtlich, wo du dich innerhalb StuFiS befindest und kannst schneller innerhalb der Struktur die Seite wechseln. Der Projekt-Button ist nun nach rechts oben gewandert. diff --git a/legacy/lib/forms/FormTemplater.php b/legacy/lib/forms/FormTemplater.php index 8123ab7e..aa3773cd 100644 --- a/legacy/lib/forms/FormTemplater.php +++ b/legacy/lib/forms/FormTemplater.php @@ -64,7 +64,6 @@ public static function generateGremienSelectable(): array $userGremien = AuthHandler::getInstance()->getUserGremien(); $showAll = AuthHandler::getInstance()->hasGroup('ref-finanzen'); - $selectable = []; foreach (GREMIEN as $groupName => $gremien) { $group = []; $group['label'] = $groupName; @@ -80,6 +79,8 @@ public static function generateGremienSelectable(): array $selectable['groups'][] = $group; } + $selectable['groups'][] = ['label' => 'Sonstige / Keine', 'options' => [['label' => 'Sonstige / Keine Organisation']]]; + return $selectable; } From e789c496e88650335462a165c36445f131a560cc Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 29 Nov 2025 23:03:24 +0100 Subject: [PATCH 037/108] fix: csv imports now set expenses to payed if they are mentioned in the verwendungszweck field --- app/Livewire/TransactionImportWire.php | 3 ++- docs/feature-changelog.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/Livewire/TransactionImportWire.php b/app/Livewire/TransactionImportWire.php index b53de1fd..ed006f82 100644 --- a/app/Livewire/TransactionImportWire.php +++ b/app/Livewire/TransactionImportWire.php @@ -10,6 +10,7 @@ use App\Rules\CsvTransactionImport\IbanColumnRule; use App\Rules\CsvTransactionImport\MoneyColumnRule; use Flux\Flux; +use forms\projekte\auslagen\AuslagenHandler2; use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Collection; @@ -214,7 +215,7 @@ public function save() $transaction->$db_col_name = $this->formatDataDb($currentBalance, $db_col_name); } } - + AuslagenHandler2::hookZahlung($transaction->zweck); $transaction->save(); } try { diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index 0dfab1ba..4d1b54dd 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -1,5 +1,5 @@ # unreleased -* fix: booking button not clickable +* fix: expenses booking state button not clickable anymore * fix: chrome: no double button in expense form * fix: linewrap links in project * fix: remember changed budget year between tabs @@ -8,6 +8,7 @@ * lang: changed Auslagen to Abrechnung * fix: projektposten keep their internal id when deleted. so you should be able to delete other unused posten. Deleting used posten is still possible and will result in an error. * fix: added a default "sonstige" organisation +* fix: csv imports now set expenses to payed if they are mentioned in the verwendungszweck field # v4.3.2 * **Brotkrumen wurden hinzugefügt:** Es ist nun links oben ersichtlich, wo du dich innerhalb StuFiS befindest und kannst schneller innerhalb der Struktur die Seite wechseln. Der Projekt-Button ist nun nach rechts oben gewandert. From 3997ca37d25f2ed41718d5775260675199cb4544 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 29 Nov 2025 23:34:18 +0100 Subject: [PATCH 038/108] * fix bug OP#406 & ok-belege resets now as well --- docs/feature-changelog.md | 1 + legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index 4d1b54dd..d33c7feb 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -9,6 +9,7 @@ * fix: projektposten keep their internal id when deleted. so you should be able to delete other unused posten. Deleting used posten is still possible and will result in an error. * fix: added a default "sonstige" organisation * fix: csv imports now set expenses to payed if they are mentioned in the verwendungszweck field +* fix bug OP#406 & ok-belege resets now as well # v4.3.2 * **Brotkrumen wurden hinzugefügt:** Es ist nun links oben ersichtlich, wo du dich innerhalb StuFiS befindest und kannst schneller innerhalb der Struktur die Seite wechseln. Der Projekt-Button ist nun nach rechts oben gewandert. diff --git a/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php b/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php index 1970adef..3318ef6b 100644 --- a/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php +++ b/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php @@ -1476,6 +1476,8 @@ public function state_change(string $newState, string $etag): bool case 'wip': $set['ok-hv'] = ''; $set['ok-kv'] = ''; + $set['ok-belege'] = ''; + $set['payed'] = ''; break; case 'draft': From 910df0dde8188f152a98c29d68b38d7291996ed4 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sun, 30 Nov 2025 00:05:17 +0100 Subject: [PATCH 039/108] fix: projectposten are not deletable if there is an expense using them --- app/Models/Legacy/ExpensesReceipt.php | 2 +- app/Models/Legacy/ExpensesReceiptPost.php | 2 +- docs/feature-changelog.md | 1 + legacy/lib/forms/projekte/ProjektHandler.php | 13 +++++++++++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/Models/Legacy/ExpensesReceipt.php b/app/Models/Legacy/ExpensesReceipt.php index 23e87e8e..ff1f11eb 100644 --- a/app/Models/Legacy/ExpensesReceipt.php +++ b/app/Models/Legacy/ExpensesReceipt.php @@ -60,6 +60,6 @@ public function posts(): HasMany public function expense(): BelongsTo { - return $this->belongsTo(Expenses::class); + return $this->belongsTo(Expenses::class, 'auslagen_id'); } } diff --git a/app/Models/Legacy/ExpensesReceiptPost.php b/app/Models/Legacy/ExpensesReceiptPost.php index 944870cc..e4f1b4e2 100644 --- a/app/Models/Legacy/ExpensesReceiptPost.php +++ b/app/Models/Legacy/ExpensesReceiptPost.php @@ -50,7 +50,7 @@ class ExpensesReceiptPost extends Model public function expensesReceipt(): BelongsTo { - return $this->belongsTo(ExpensesReceipt::class, 'beleg_id'); + return $this->belongsTo(ExpensesReceipt::class, 'beleg_id', 'id'); } public function bookings(): HasMany diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index d33c7feb..0ffb28ab 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -10,6 +10,7 @@ * fix: added a default "sonstige" organisation * fix: csv imports now set expenses to payed if they are mentioned in the verwendungszweck field * fix bug OP#406 & ok-belege resets now as well +* fix: projectposten are not deletable if there is an expense using them # v4.3.2 * **Brotkrumen wurden hinzugefügt:** Es ist nun links oben ersichtlich, wo du dich innerhalb StuFiS befindest und kannst schneller innerhalb der Struktur die Seite wechseln. Der Projekt-Button ist nun nach rechts oben gewandert. diff --git a/legacy/lib/forms/projekte/ProjektHandler.php b/legacy/lib/forms/projekte/ProjektHandler.php index d36ec1f9..76e7c22c 100644 --- a/legacy/lib/forms/projekte/ProjektHandler.php +++ b/legacy/lib/forms/projekte/ProjektHandler.php @@ -3,6 +3,9 @@ namespace forms\projekte; use App\Exceptions\LegacyDieException; +use App\Models\Legacy\Expenses; +use App\Models\Legacy\ExpensesReceipt; +use App\Models\Legacy\ExpensesReceiptPost; use App\Models\Legacy\ProjectPost; use App\Models\User; use forms\chat\ChatHandler; @@ -451,6 +454,16 @@ public function updateSavedData($data): bool 'titel_id' => $extractFields['posten-titel'][$i], ]); } + $project_id = $this->id; + $used_posten_deleted = ExpensesReceiptPost::whereNotIn('projekt_posten_id', $used_ids) + ->whereHas('expensesReceipt.expense', function ($query) use ($project_id) { + $query->where('projekt_id', $project_id); + })->exists(); + + if ($used_posten_deleted) { + throw new InvalidDataException('Posten mit denen noch eine Abrechnung existiert dürfen nicht gelöscht werden'); + } + ProjectPost::whereNotIn('id', $used_ids)->delete(); DB::commit(); From 85bdc4196c2246e39f759657ab205bd1279e674c Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sun, 30 Nov 2025 00:23:17 +0100 Subject: [PATCH 040/108] lang: OP#273 removed stura Branding --- docs/feature-changelog.md | 1 + legacy/lib/forms/projekte/ProjektHandler.php | 4 ++-- legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php | 2 +- legacy/lib/framework/render/HTMLPageRenderer.php | 2 +- legacy/lib/framework/render/MenuRenderer.php | 6 +++--- legacy/template/tex/belege-pdf.tex.twig | 2 +- resources/views/components/layouts/index.blade.php | 2 +- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index 0ffb28ab..77b110dd 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -11,6 +11,7 @@ * fix: csv imports now set expenses to payed if they are mentioned in the verwendungszweck field * fix bug OP#406 & ok-belege resets now as well * fix: projectposten are not deletable if there is an expense using them +* lang: OP#273 removed stura Branding # v4.3.2 * **Brotkrumen wurden hinzugefügt:** Es ist nun links oben ersichtlich, wo du dich innerhalb StuFiS befindest und kannst schneller innerhalb der Struktur die Seite wechseln. Der Projekt-Button ist nun nach rechts oben gewandert. diff --git a/legacy/lib/forms/projekte/ProjektHandler.php b/legacy/lib/forms/projekte/ProjektHandler.php index 76e7c22c..8e51f828 100644 --- a/legacy/lib/forms/projekte/ProjektHandler.php +++ b/legacy/lib/forms/projekte/ProjektHandler.php @@ -123,8 +123,8 @@ public static function initStaticVars(): bool 'draft' => ['Entwurf'], 'wip' => ['Beantragt', 'beantragen'], 'ok-by-hv' => ['Genehmigt durch HV (nicht verkündet)'], - 'need-stura' => ['Warte auf StuRa-Beschluss'], - 'ok-by-stura' => ['Genehmigt durch StuRa-Beschluss'], + 'need-stura' => ['Warte auf Gremien-Beschluss'], + 'ok-by-stura' => ['Genehmigt durch Gremien-Beschluss'], 'done-hv' => ['verkündet durch HV'], 'done-other' => ['Genehmigt'], 'revoked' => [ diff --git a/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php b/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php index 3318ef6b..8cfc3217 100644 --- a/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php +++ b/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php @@ -1892,7 +1892,7 @@ private function renderAuslagenerstattung(): void
routeInfo['action'] === 'edit' || $this->routeInfo['action'] === 'create') ? - 'Anschrift Empfangsberechtigter/ZahlungspflichtigerDer StuRa ist nach §12(2)-3 ThürStudFVO verpflichtet, diese Angaben abzufragen und aufzubewahren. Nach §18 ThürStudFVO beträgt die Dauer mindestens 6 Jahre nach Genehmigung der Entlastung.' : + 'Anschrift Empfangsberechtigter/ZahlungspflichtigerDie Studierendenschaften in Thüringen sind nach §12(2)-3 ThürStudFVO verpflichtet, diese Angaben abzufragen und aufzubewahren. Nach §18 ThürStudFVO beträgt die Dauer mindestens 6 Jahre nach Genehmigung der Entlastung.' : 'Anschrift Empfangsberechtigter/Zahlungspflichtiger'; $tmpvalue = ($this->checkPermissionByMap(self::$groups['stateless']['finanzen']) || $this->checkPermissionByMap(self::$groups['stateless']['owner']) diff --git a/legacy/lib/framework/render/HTMLPageRenderer.php b/legacy/lib/framework/render/HTMLPageRenderer.php index 54910468..bbc53c4b 100644 --- a/legacy/lib/framework/render/HTMLPageRenderer.php +++ b/legacy/lib/framework/render/HTMLPageRenderer.php @@ -25,7 +25,7 @@ public function __construct($routeInfo, $bodyContent = '') if (isset($this->routeInfo['titel'])) { $this->titel = $this->routeInfo['titel']; } else { - $this->titel = 'StuRa Finanzen'; + $this->titel = 'UNUSED'; } $this->bodyContent = $bodyContent; } diff --git a/legacy/lib/framework/render/MenuRenderer.php b/legacy/lib/framework/render/MenuRenderer.php index 4edc4f50..bcbe1a24 100644 --- a/legacy/lib/framework/render/MenuRenderer.php +++ b/legacy/lib/framework/render/MenuRenderer.php @@ -550,10 +550,10 @@ private function renderStuRaView(): void [$header, $internContent, $escapeFunctions] = $this->fetchProjectsWithState('need-stura'); [, $internContentHV] = $this->fetchProjectsWithState('ok-by-hv'); $groups = [ - 'Vom StuRa abzustimmen' => $internContent, - 'zur Verkündung (genehmigt von HV)' => $internContentHV, + 'Vom Gremium abzustimmen' => $internContent, + 'zur Verkündung' => $internContentHV, ]; - $this->renderHeadline('Projekte für die nächste StuRa Sitzung'); + $this->renderHeadline('Projekte für die nächste Sitzung'); $this->renderTable($header, $groups, [], $escapeFunctions); } diff --git a/legacy/template/tex/belege-pdf.tex.twig b/legacy/template/tex/belege-pdf.tex.twig index fe51d55c..f6a2bfc9 100644 --- a/legacy/template/tex/belege-pdf.tex.twig +++ b/legacy/template/tex/belege-pdf.tex.twig @@ -5,7 +5,7 @@ (% block author %)Open Administration(% endblock %) (% block footer %)(( footer ))(% endblock %) (% block main %) - (( helper.hintbox('Die Belege für diese Abrechnung müssen an dieses Dokument angehangen, alles zusammen getackert, und im Stura Büro bei Referat Finanzen eingereicht werden. Dazu legt man das Dokument in das Postfach des Referats Finanzen.') )) + (( helper.hintbox('Die Belege für diese Abrechnung müssen an dieses Dokument angehangen, alles zusammen getackert, und bei den Finanzverantwortlichen eingereicht werden. Dazu legt man das Dokument in das Postfach des Referats Finanzen.') )) (( helper.listing(projektMeta, 'Projekt Informationen') )) (( helper.listing(auslagenMeta, 'Abrechnungs Informationen', true) )) (( helper.headline('Versicherung') )) diff --git a/resources/views/components/layouts/index.blade.php b/resources/views/components/layouts/index.blade.php index 92b0f2c1..8bf8b90d 100644 --- a/resources/views/components/layouts/index.blade.php +++ b/resources/views/components/layouts/index.blade.php @@ -18,7 +18,7 @@ - {{ $title ?? 'StuRa Finanzen' }} + {{ $title ?? 'StuFiS Finanzen' }} From dbebefe30288158e8fc82f71b2b81c4973b10380 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sun, 30 Nov 2025 00:43:43 +0100 Subject: [PATCH 041/108] fix: OP#204 fwd to all gremien if user has no gremium --- docs/feature-changelog.md | 1 + routes/web.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/feature-changelog.md b/docs/feature-changelog.md index 77b110dd..0ee41931 100644 --- a/docs/feature-changelog.md +++ b/docs/feature-changelog.md @@ -12,6 +12,7 @@ * fix bug OP#406 & ok-belege resets now as well * fix: projectposten are not deletable if there is an expense using them * lang: OP#273 removed stura Branding +* fix: OP#204 fwd to all gremien if user has no gremium # v4.3.2 * **Brotkrumen wurden hinzugefügt:** Es ist nun links oben ersichtlich, wo du dich innerhalb StuFiS befindest und kannst schneller innerhalb der Struktur die Seite wechseln. Der Projekt-Button ist nun nach rechts oben gewandert. diff --git a/routes/web.php b/routes/web.php index 0853fb56..414073ba 100644 --- a/routes/web.php +++ b/routes/web.php @@ -17,11 +17,11 @@ Route::middleware(['auth'])->group(function (): void { - // legacy is register later, so we cannot route(legacy.dashboard) there Route::get('/', function (){ + $sub = Auth::user()->getCommittees()->isEmpty() ? 'allgremium' : 'mygremium'; $latestPlan = LegacyBudgetPlan::latest(); return redirect()->route( - 'legacy.dashboard', ['sub' => 'mygremium', 'hhp_id' => $latestPlan->id] + 'legacy.dashboard', ['sub' => $sub, 'hhp_id' => $latestPlan->id] ); })->name('home'); From b37a2816b017baff53456684c38ee56887e8625e Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sun, 30 Nov 2025 15:49:17 +0100 Subject: [PATCH 042/108] renamed Model from Expenses to Expense --- .../Commands/LegacyMigrateEncryption.php | 4 +- .../Commands/LegacyMigrateFilesToStorage.php | 4 +- .../Controllers/Legacy/DeleteExpenses.php | 8 +- app/Models/Legacy/Booking.php | 4 +- app/Models/Legacy/Expense.php | 89 +++++++++++++++++++ app/Models/Legacy/ExpenseReceipt.php | 65 ++++++++++++++ app/Models/Legacy/ExpenseReceiptPost.php | 60 +++++++++++++ app/Models/Legacy/Expenses.php | 89 ------------------- app/Models/Legacy/ExpensesReceipt.php | 65 -------------- app/Models/Legacy/ExpensesReceiptPost.php | 60 ------------- app/Models/Legacy/Project.php | 4 +- app/Models/Project.php | 70 --------------- ...ExpensesFactory.php => ExpenseFactory.php} | 6 +- ...tFactory.php => ExpenseReceiptFactory.php} | 6 +- ...tory.php => ExpenseReceiptPostFactory.php} | 6 +- legacy/lib/forms/projekte/ProjektHandler.php | 8 +- routes/legacy.php | 2 +- tests/Pest/Legacy/BookingTest.php | 4 +- 18 files changed, 242 insertions(+), 312 deletions(-) create mode 100644 app/Models/Legacy/Expense.php create mode 100644 app/Models/Legacy/ExpenseReceipt.php create mode 100644 app/Models/Legacy/ExpenseReceiptPost.php delete mode 100644 app/Models/Legacy/Expenses.php delete mode 100644 app/Models/Legacy/ExpensesReceipt.php delete mode 100644 app/Models/Legacy/ExpensesReceiptPost.php delete mode 100644 app/Models/Project.php rename database/factories/Legacy/{ExpensesFactory.php => ExpenseFactory.php} (81%) rename database/factories/Legacy/{ExpensesReceiptFactory.php => ExpenseReceiptFactory.php} (71%) rename database/factories/Legacy/{ExpensesReceiptPostFactory.php => ExpenseReceiptPostFactory.php} (73%) diff --git a/app/Console/Commands/LegacyMigrateEncryption.php b/app/Console/Commands/LegacyMigrateEncryption.php index 8ec8d1b0..f0724330 100644 --- a/app/Console/Commands/LegacyMigrateEncryption.php +++ b/app/Console/Commands/LegacyMigrateEncryption.php @@ -3,7 +3,7 @@ namespace App\Console\Commands; use App\Models\Legacy\ChatMessage; -use App\Models\Legacy\Expenses; +use App\Models\Legacy\Expense; use Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException; use forms\chat\ChatHandler; use forms\projekte\auslagen\AuslagenHandler2; @@ -68,7 +68,7 @@ public function handle(): int $this->info("Migrated $count chat messages from legacy encryption to laravel integrated"); $count = 0; - Expenses::all()->each(function ($expense) use (&$count): void { + Expense::all()->each(function ($expense) use (&$count): void { $cryptIban = $expense->getAttribute('zahlung-iban'); try { \Crypt::decryptString($cryptIban); diff --git a/app/Console/Commands/LegacyMigrateFilesToStorage.php b/app/Console/Commands/LegacyMigrateFilesToStorage.php index 578f885d..c8c28bd2 100644 --- a/app/Console/Commands/LegacyMigrateFilesToStorage.php +++ b/app/Console/Commands/LegacyMigrateFilesToStorage.php @@ -2,7 +2,7 @@ namespace App\Console\Commands; -use App\Models\Legacy\ExpensesReceipt; +use App\Models\Legacy\ExpenseReceipt; use App\Models\Legacy\FileInfo; use Illuminate\Console\Command; @@ -30,7 +30,7 @@ public function handle(): void FileInfo::lazy(20)->each(function (FileInfo $fileInfo): void { $data = $fileInfo->fileData; $link = $fileInfo->link; - $beleg = ExpensesReceipt::find($link); + $beleg = ExpenseReceipt::find($link); $expenses_id = $beleg?->auslagen_id; $pdfData = $data?->data; $hash = $fileInfo->hashname; diff --git a/app/Http/Controllers/Legacy/DeleteExpenses.php b/app/Http/Controllers/Legacy/DeleteExpenses.php index 7b729d0a..1f912064 100644 --- a/app/Http/Controllers/Legacy/DeleteExpenses.php +++ b/app/Http/Controllers/Legacy/DeleteExpenses.php @@ -3,8 +3,8 @@ namespace App\Http\Controllers\Legacy; use App\Http\Controllers\Controller; -use App\Models\Legacy\Expenses; -use App\Models\Legacy\ExpensesReceipt; +use App\Models\Legacy\Expense; +use App\Models\Legacy\ExpenseReceipt; use App\Models\Legacy\FileInfo; use framework\auth\AuthHandler; use Illuminate\Support\Facades\DB; @@ -13,7 +13,7 @@ class DeleteExpenses extends Controller { public function __invoke(int $expense_id) { - $expense = Expenses::findOrFail($expense_id); + $expense = Expense::findOrFail($expense_id); $project = $expense->project; // authorize user @@ -30,7 +30,7 @@ public function __invoke(int $expense_id) // to make sure to delete everything and not only parts \DB::beginTransaction(); $reciepts = $expense->receipts; - $reciepts->each(function (ExpensesReceipt $receipt): void { + $reciepts->each(function (ExpenseReceipt $receipt): void { // delete all posts $receipt->posts()->delete(); // delete all files db entries (storage later) diff --git a/app/Models/Legacy/Booking.php b/app/Models/Legacy/Booking.php index 2ba2e62c..9a3300eb 100644 --- a/app/Models/Legacy/Booking.php +++ b/app/Models/Legacy/Booking.php @@ -73,12 +73,12 @@ public function user(): BelongsTo public function expensesReceiptPost(): BelongsTo { - return $this->belongsTo(ExpensesReceiptPost::class, 'beleg_id'); + return $this->belongsTo(ExpenseReceiptPost::class, 'beleg_id'); } public function expenseReceipt(): HasOneThrough { - return $this->hasOneThrough(ExpensesReceipt::class, ExpensesReceiptPost::class, 'beleg_id', 'id'); + return $this->hasOneThrough(ExpenseReceipt::class, ExpenseReceiptPost::class, 'beleg_id', 'id'); } public function expense(): BelongsTo diff --git a/app/Models/Legacy/Expense.php b/app/Models/Legacy/Expense.php new file mode 100644 index 00000000..e75fd619 --- /dev/null +++ b/app/Models/Legacy/Expense.php @@ -0,0 +1,89 @@ + $comments + * @property-read int|null $comments_count + * @property-read \Illuminate\Database\Eloquent\Collection $receipts + * @property-read int|null $receipts_count + * + * @method static \Illuminate\Database\Eloquent\Builder|Expense newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Expense newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Expense query() + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereAddress($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereCreated($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereEtag($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereLastChange($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereLastChangeBy($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereNameSuffix($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereOkBelege($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereOkHv($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereOkKv($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense wherePayed($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereProjektId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereRejected($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereState($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereVersion($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereZahlungIban($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereZahlungName($value) + * @method static \Illuminate\Database\Eloquent\Builder|Expense whereZahlungVwzk($value) + * + * @mixin \Eloquent + */ +class Expense extends Model +{ + use HasFactory; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'auslagen'; + + public $timestamps = false; + + /** + * @var array + */ + protected $fillable = ['projekt_id', 'name_suffix', 'state', 'ok-belege', 'ok-hv', 'ok-kv', 'payed', 'rejected', 'zahlung-iban', 'zahlung-name', 'zahlung-vwzk', 'address', 'last_change', 'last_change_by', 'etag', 'version', 'created']; + + public function project(): BelongsTo + { + return $this->belongsTo(Project::class, 'projekt_id'); + } + + public function receipts(): HasMany + { + return $this->hasMany(ExpenseReceipt::class, 'auslagen_id'); + } +} diff --git a/app/Models/Legacy/ExpenseReceipt.php b/app/Models/Legacy/ExpenseReceipt.php new file mode 100644 index 00000000..f98aaf6d --- /dev/null +++ b/app/Models/Legacy/ExpenseReceipt.php @@ -0,0 +1,65 @@ +hasMany(\App\Models\Legacy\ExpenseReceiptPost::class, 'beleg_id'); + } + + public function expense(): BelongsTo + { + return $this->belongsTo(Expense::class, 'auslagen_id'); + } +} diff --git a/app/Models/Legacy/ExpenseReceiptPost.php b/app/Models/Legacy/ExpenseReceiptPost.php new file mode 100644 index 00000000..2021cc15 --- /dev/null +++ b/app/Models/Legacy/ExpenseReceiptPost.php @@ -0,0 +1,60 @@ +belongsTo(ExpenseReceipt::class, 'beleg_id', 'id'); + } + + public function bookings(): HasMany + { + return $this->hasMany(Booking::class, 'beleg_id')->where('beleg_type', 'belegposten'); + } +} diff --git a/app/Models/Legacy/Expenses.php b/app/Models/Legacy/Expenses.php deleted file mode 100644 index 4fd4bc6b..00000000 --- a/app/Models/Legacy/Expenses.php +++ /dev/null @@ -1,89 +0,0 @@ - $comments - * @property-read int|null $comments_count - * @property-read \Illuminate\Database\Eloquent\Collection $receipts - * @property-read int|null $receipts_count - * - * @method static \Illuminate\Database\Eloquent\Builder|Expenses newModelQuery() - * @method static \Illuminate\Database\Eloquent\Builder|Expenses newQuery() - * @method static \Illuminate\Database\Eloquent\Builder|Expenses query() - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereAddress($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereCreated($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereEtag($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereLastChange($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereLastChangeBy($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereNameSuffix($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereOkBelege($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereOkHv($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereOkKv($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses wherePayed($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereProjektId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereRejected($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereState($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereVersion($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereZahlungIban($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereZahlungName($value) - * @method static \Illuminate\Database\Eloquent\Builder|Expenses whereZahlungVwzk($value) - * - * @mixin \Eloquent - */ -class Expenses extends Model -{ - use HasFactory; - - /** - * The table associated with the model. - * - * @var string - */ - protected $table = 'auslagen'; - - public $timestamps = false; - - /** - * @var array - */ - protected $fillable = ['projekt_id', 'name_suffix', 'state', 'ok-belege', 'ok-hv', 'ok-kv', 'payed', 'rejected', 'zahlung-iban', 'zahlung-name', 'zahlung-vwzk', 'address', 'last_change', 'last_change_by', 'etag', 'version', 'created']; - - public function project(): BelongsTo - { - return $this->belongsTo(Project::class, 'projekt_id'); - } - - public function receipts(): HasMany - { - return $this->hasMany(ExpensesReceipt::class, 'auslagen_id'); - } -} diff --git a/app/Models/Legacy/ExpensesReceipt.php b/app/Models/Legacy/ExpensesReceipt.php deleted file mode 100644 index ff1f11eb..00000000 --- a/app/Models/Legacy/ExpensesReceipt.php +++ /dev/null @@ -1,65 +0,0 @@ -hasMany(\App\Models\Legacy\ExpensesReceiptPost::class, 'beleg_id'); - } - - public function expense(): BelongsTo - { - return $this->belongsTo(Expenses::class, 'auslagen_id'); - } -} diff --git a/app/Models/Legacy/ExpensesReceiptPost.php b/app/Models/Legacy/ExpensesReceiptPost.php deleted file mode 100644 index e4f1b4e2..00000000 --- a/app/Models/Legacy/ExpensesReceiptPost.php +++ /dev/null @@ -1,60 +0,0 @@ -belongsTo(ExpensesReceipt::class, 'beleg_id', 'id'); - } - - public function bookings(): HasMany - { - return $this->hasMany(Booking::class, 'beleg_id')->where('beleg_type', 'belegposten'); - } -} diff --git a/app/Models/Legacy/Project.php b/app/Models/Legacy/Project.php index f0a7c1d5..1c447bfa 100644 --- a/app/Models/Legacy/Project.php +++ b/app/Models/Legacy/Project.php @@ -30,7 +30,7 @@ * @property string $date_start * @property string $date_end * @property string $beschreibung - * @property Expenses[] $expenses + * @property Expense[] $expenses * @property User $user * @property ProjectPost[] $posts * @property-read User $creator @@ -86,7 +86,7 @@ class Project extends Model public function expenses(): HasMany { - return $this->hasMany(\App\Models\Legacy\Expenses::class, 'projekt_id'); + return $this->hasMany(\App\Models\Legacy\Expense::class, 'projekt_id'); } public function creator(): BelongsTo diff --git a/app/Models/Project.php b/app/Models/Project.php deleted file mode 100644 index 77156a4e..00000000 --- a/app/Models/Project.php +++ /dev/null @@ -1,70 +0,0 @@ -hasMany(Application::class); - } - - public function attachments(): HasMany - { - return $this->hasMany(ProjectAttachment::class); - } - - public function studentBodyDuties(): HasMany - { - return $this->hasMany(StudentBodyDuty::class, 'projects_to_student_body_duties'); - } - - public function formDefinition(): HasOne - { - return $this->hasOne(FormDefinition::class); - } -} diff --git a/database/factories/Legacy/ExpensesFactory.php b/database/factories/Legacy/ExpenseFactory.php similarity index 81% rename from database/factories/Legacy/ExpensesFactory.php rename to database/factories/Legacy/ExpenseFactory.php index 54cec9e6..7f242035 100644 --- a/database/factories/Legacy/ExpensesFactory.php +++ b/database/factories/Legacy/ExpenseFactory.php @@ -2,12 +2,12 @@ namespace Database\Factories\Legacy; -use App\Models\Legacy\Expenses; +use App\Models\Legacy\Expense; use Illuminate\Database\Eloquent\Factories\Factory; -class ExpensesFactory extends Factory +class ExpenseFactory extends Factory { - protected $model = Expenses::class; + protected $model = Expense::class; public function definition(): array { diff --git a/database/factories/Legacy/ExpensesReceiptFactory.php b/database/factories/Legacy/ExpenseReceiptFactory.php similarity index 71% rename from database/factories/Legacy/ExpensesReceiptFactory.php rename to database/factories/Legacy/ExpenseReceiptFactory.php index d093ac11..8c3dd183 100644 --- a/database/factories/Legacy/ExpensesReceiptFactory.php +++ b/database/factories/Legacy/ExpenseReceiptFactory.php @@ -2,12 +2,12 @@ namespace Database\Factories\Legacy; -use App\Models\Legacy\ExpensesReceipt; +use App\Models\Legacy\ExpenseReceipt; use Illuminate\Database\Eloquent\Factories\Factory; -class ExpensesReceiptFactory extends Factory +class ExpenseReceiptFactory extends Factory { - protected $model = ExpensesReceipt::class; + protected $model = ExpenseReceipt::class; public function definition(): array { diff --git a/database/factories/Legacy/ExpensesReceiptPostFactory.php b/database/factories/Legacy/ExpenseReceiptPostFactory.php similarity index 73% rename from database/factories/Legacy/ExpensesReceiptPostFactory.php rename to database/factories/Legacy/ExpenseReceiptPostFactory.php index 9ab56714..64791a81 100644 --- a/database/factories/Legacy/ExpensesReceiptPostFactory.php +++ b/database/factories/Legacy/ExpenseReceiptPostFactory.php @@ -2,12 +2,12 @@ namespace Database\Factories\Legacy; -use App\Models\Legacy\ExpensesReceiptPost; +use App\Models\Legacy\ExpenseReceiptPost; use Illuminate\Database\Eloquent\Factories\Factory; -class ExpensesReceiptPostFactory extends Factory +class ExpenseReceiptPostFactory extends Factory { - protected $model = ExpensesReceiptPost::class; + protected $model = ExpenseReceiptPost::class; public function definition(): array { diff --git a/legacy/lib/forms/projekte/ProjektHandler.php b/legacy/lib/forms/projekte/ProjektHandler.php index 8e51f828..b31167ca 100644 --- a/legacy/lib/forms/projekte/ProjektHandler.php +++ b/legacy/lib/forms/projekte/ProjektHandler.php @@ -3,9 +3,9 @@ namespace forms\projekte; use App\Exceptions\LegacyDieException; -use App\Models\Legacy\Expenses; -use App\Models\Legacy\ExpensesReceipt; -use App\Models\Legacy\ExpensesReceiptPost; +use App\Models\Legacy\Expense; +use App\Models\Legacy\ExpenseReceipt; +use App\Models\Legacy\ExpenseReceiptPost; use App\Models\Legacy\ProjectPost; use App\Models\User; use forms\chat\ChatHandler; @@ -455,7 +455,7 @@ public function updateSavedData($data): bool ]); } $project_id = $this->id; - $used_posten_deleted = ExpensesReceiptPost::whereNotIn('projekt_posten_id', $used_ids) + $used_posten_deleted = ExpenseReceiptPost::whereNotIn('projekt_posten_id', $used_ids) ->whereHas('expensesReceipt.expense', function ($query) use ($project_id) { $query->where('projekt_id', $project_id); })->exists(); diff --git a/routes/legacy.php b/routes/legacy.php index 26a9e734..9c60041e 100644 --- a/routes/legacy.php +++ b/routes/legacy.php @@ -45,7 +45,7 @@ Route::redirect('p/{projekt_id}', '/projekt/{projekt_id}'); Route::redirect('a/{auslagen_id}', '/auslagen/{auslagen_id}'); Route::get('auslagen/{auslagen_id}', static function ($auslage_id) { - $auslage = \App\Models\Legacy\Expenses::findOrFail($auslage_id); + $auslage = \App\Models\Legacy\Expense::findOrFail($auslage_id); return redirect()->to("projekt/$auslage->projekt_id/auslagen/$auslage->id"); })->name('expense'); diff --git a/tests/Pest/Legacy/BookingTest.php b/tests/Pest/Legacy/BookingTest.php index 87c2a4b3..e7f42ce0 100644 --- a/tests/Pest/Legacy/BookingTest.php +++ b/tests/Pest/Legacy/BookingTest.php @@ -1,10 +1,10 @@ state('payed') ->make(); // create matching payment From 97ec1758f86be3c179289dfc613e17d86f813b18 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 2 Dec 2025 13:35:07 +0100 Subject: [PATCH 043/108] WIP new BudgetPlan --- app/Livewire/BudgetPlan/BudgetPlanEdit.php | 10 +- app/Models/BudgetItem.php | 40 +++- app/Models/BudgetPlan.php | 31 ++- composer.json | 3 +- composer.lock | 176 ++++++++++++++++- package-lock.json | 13 +- resources/views/budget-plan/view.blade.php | 179 +++++++++++++----- .../budgetplan/item-group-view.blade.php | 174 +++++++++-------- .../components/budgetplan/view-row.blade.php | 125 ++++++++++++ .../livewire/budget-plan/plan-edit.blade.php | 3 + 10 files changed, 611 insertions(+), 143 deletions(-) create mode 100644 resources/views/components/budgetplan/view-row.blade.php diff --git a/app/Livewire/BudgetPlan/BudgetPlanEdit.php b/app/Livewire/BudgetPlan/BudgetPlanEdit.php index c2b63a64..d9b96d71 100644 --- a/app/Livewire/BudgetPlan/BudgetPlanEdit.php +++ b/app/Livewire/BudgetPlan/BudgetPlanEdit.php @@ -167,7 +167,7 @@ public function sort($item_id, $new_position): void } // pickup all items between old and new position - $block = BudgetItem::whereBetween('position', [ + $block = $item->siblings()->whereBetween('position', [ min($current_position, $new_position), max($current_position, $new_position), ]); @@ -181,6 +181,7 @@ public function sort($item_id, $new_position): void } $item->update(['position' => $new_position]); + }); Flux::toast('FIXME: Dragging and dropping', variant: 'success'); @@ -310,6 +311,13 @@ public function delete(int $item_id): void $this->resumItemValues($item); } + public function resetPositions(): void + { + $plan = BudgetPlan::findOrFail($this->plan_id); + $plan->normalizePositions(); + $this->refresh(); + } + public function refresh(): void { $this->refresh = ! $this->refresh; diff --git a/app/Models/BudgetItem.php b/app/Models/BudgetItem.php index 94e45181..c1ca402b 100644 --- a/app/Models/BudgetItem.php +++ b/app/Models/BudgetItem.php @@ -11,6 +11,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships; /** * App\Models\BudgetItem @@ -27,6 +28,7 @@ class BudgetItem extends Model { use HasFactory; + use HasRecursiveRelationships; /** * The table associated with the model. @@ -40,6 +42,7 @@ class BudgetItem extends Model */ protected $fillable = ['budget_plan_id', 'short_name', 'name', 'value', 'budget_type', 'description', 'parent_id', 'is_group', 'position']; + public function bookings(): HasMany { return $this->hasMany('tbd', 'titel_id'); @@ -50,16 +53,6 @@ public function budgetPlan(): BelongsTo return $this->belongsTo(BudgetPlan::class, 'budget_plan_id'); } - public function parent(): BelongsTo - { - return $this->belongsTo(self::class, 'parent_id'); - } - - public function children(): HasMany - { - return $this->hasMany(self::class, 'parent_id'); - } - public function orderedChildren(): HasMany { return $this->hasMany(self::class, 'parent_id')->orderBy('position', 'asc'); @@ -74,4 +67,31 @@ protected function casts(): array 'value' => MoneyDecimalCast::class, ]; } + + /** + * Get the custom paths for the model. + * @see https://github.com/staudenmeir/laravel-adjacency-list#custom-paths + * Usable to sort the whole tree by position + */ + public function getCustomPaths(): array + { + return [ + [ + 'name' => 'position_path', + 'column' => 'position', + 'separator' => '.', + ], + ]; + } + + public function normalizeChildPositionValues(): void + { + $idx = 0; + $this->orderedChildren() + ->each(function($child) use (&$idx) { + $child->update(['position' => $idx++]); + }); + } + + } diff --git a/app/Models/BudgetPlan.php b/app/Models/BudgetPlan.php index 2f41e062..4c39a768 100644 --- a/app/Models/BudgetPlan.php +++ b/app/Models/BudgetPlan.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Models\Enums\BudgetPlanState; +use App\Models\Enums\BudgetType; use Carbon\Carbon; use Database\Factories\BudgetPlanFactory; use Eloquent; @@ -59,10 +60,21 @@ protected function casts(): array ]; } - public function budgetItems(): \Illuminate\Database\Eloquent\Relations\HasMany - { + public function budgetItems(): \Illuminate\Database\Eloquent\Relations\HasMany{ return $this->hasMany(BudgetItem::class); } + public function budgetItemsTree(BudgetType $budgetType) + { + // this is not accessible from the closure scope + $plan_id = $this->id; + + $constraint = static fn($query) => + $query->whereNull('parent_id') + ->where('budget_plan_id', $plan_id) + ->where('budget_type', $budgetType); + // the full tree flattened out, the position path is a custom-built path + return BudgetItem::treeOf($constraint)->orderBy('position_path')->get(); + } public function rootBudgetItems(): Builder|\Illuminate\Database\Eloquent\Relations\HasMany|BudgetPlan { @@ -73,4 +85,19 @@ public function fiscalYear(): \Illuminate\Database\Eloquent\Relations\BelongsTo { return $this->belongsTo(FiscalYear::class); } + + /** + * Resets the position values of all children to be sequential starting from 0 + * Use in case of buggyness in the position values + * @return void + */ + public function normalizePositions() : void + { + $items = $this->rootBudgetItems()->get(); + while ($items->isNotEmpty()) { + $item = $items->pop(); + $item->normalizeChildPositionValues(); + $items = $items->merge($item->children); + } + } } diff --git a/composer.json b/composer.json index 90705e5a..24ca21e2 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,8 @@ "propaganistas/laravel-phone": "^5.3", "socialiteproviders/laravelpassport": "^4.3", "spatie/laravel-backup": "^8.6", - "spatie/regex": "*" + "spatie/regex": "*", + "staudenmeir/laravel-adjacency-list": "^1.0" }, "require-dev": { "barryvdh/laravel-debugbar": "^3.15", diff --git a/composer.lock b/composer.lock index acc3811f..44c26a69 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0d4054155ef2b048f6f9f814997b8cef", + "content-hash": "29cbdbd28224048cd882ffc367191864", "packages": [ { "name": "blade-ui-kit/blade-heroicons", @@ -5945,6 +5945,180 @@ ], "time": "2025-01-13T13:04:43+00:00" }, + { + "name": "staudenmeir/eloquent-has-many-deep-contracts", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts.git", + "reference": "3ad76c6eeda60042f262d113bf471dcce584d88b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staudenmeir/eloquent-has-many-deep-contracts/zipball/3ad76c6eeda60042f262d113bf471dcce584d88b", + "reference": "3ad76c6eeda60042f262d113bf471dcce584d88b", + "shasum": "" + }, + "require": { + "illuminate/database": "^11.0", + "php": "^8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Staudenmeir\\EloquentHasManyDeepContracts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonas Staudenmeir", + "email": "mail@jonas-staudenmeir.de" + } + ], + "description": "Contracts for staudenmeir/eloquent-has-many-deep", + "support": { + "issues": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts/issues", + "source": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts/tree/v1.2.1" + }, + "time": "2024-09-25T18:24:22+00:00" + }, + { + "name": "staudenmeir/laravel-adjacency-list", + "version": "v1.23.5", + "source": { + "type": "git", + "url": "https://github.com/staudenmeir/laravel-adjacency-list.git", + "reference": "297e175c5625564b006059800d59fe8c783ef186" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staudenmeir/laravel-adjacency-list/zipball/297e175c5625564b006059800d59fe8c783ef186", + "reference": "297e175c5625564b006059800d59fe8c783ef186", + "shasum": "" + }, + "require": { + "illuminate/database": "^11.0", + "php": "^8.2", + "staudenmeir/eloquent-has-many-deep-contracts": "^1.2", + "staudenmeir/laravel-cte": "^1.11" + }, + "require-dev": { + "barryvdh/laravel-ide-helper": "^3.0", + "harrygulliford/laravel-firebird": "^3.3", + "larastan/larastan": "^3.0", + "laravel/framework": "^11.0", + "mockery/mockery": "^1.5.1", + "orchestra/testbench-core": "^9.5", + "phpunit/phpunit": "^11.0", + "singlestoredb/singlestoredb-laravel": "^1.5.4", + "staudenmeir/eloquent-has-many-deep": "^1.20" + }, + "suggest": { + "barryvdh/laravel-ide-helper": "Provide type hints for attributes and relations." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Staudenmeir\\LaravelAdjacencyList\\IdeHelperServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Staudenmeir\\LaravelAdjacencyList\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonas Staudenmeir", + "email": "mail@jonas-staudenmeir.de" + } + ], + "description": "Recursive Laravel Eloquent relationships with CTEs", + "support": { + "issues": "https://github.com/staudenmeir/laravel-adjacency-list/issues", + "source": "https://github.com/staudenmeir/laravel-adjacency-list/tree/v1.23.5" + }, + "funding": [ + { + "url": "https://paypal.me/JonasStaudenmeir", + "type": "custom" + } + ], + "time": "2025-07-27T18:09:07+00:00" + }, + { + "name": "staudenmeir/laravel-cte", + "version": "v1.11.2", + "source": { + "type": "git", + "url": "https://github.com/staudenmeir/laravel-cte.git", + "reference": "81a172596e8e222170e371fd680e153425a9500c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staudenmeir/laravel-cte/zipball/81a172596e8e222170e371fd680e153425a9500c", + "reference": "81a172596e8e222170e371fd680e153425a9500c", + "shasum": "" + }, + "require": { + "illuminate/database": "^11.0", + "php": "^8.2" + }, + "require-dev": { + "harrygulliford/laravel-firebird": "^3.3", + "laravel/framework": "^11.0", + "orchestra/testbench-core": "^9.5", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^11.0", + "singlestoredb/singlestoredb-laravel": "^1.5.4", + "yajra/laravel-oci8": "^11.2.4" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Staudenmeir\\LaravelCte\\DatabaseServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Staudenmeir\\LaravelCte\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonas Staudenmeir", + "email": "mail@jonas-staudenmeir.de" + } + ], + "description": "Laravel queries with common table expressions", + "support": { + "issues": "https://github.com/staudenmeir/laravel-cte/issues", + "source": "https://github.com/staudenmeir/laravel-cte/tree/v1.11.2" + }, + "funding": [ + { + "url": "https://paypal.me/JonasStaudenmeir", + "type": "custom" + } + ], + "time": "2024-11-24T13:36:31+00:00" + }, { "name": "symfony/clock", "version": "v7.3.0", diff --git a/package-lock.json b/package-lock.json index 70db3d33..65863e6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,7 @@ "requires": true, "packages": { "": { + "name": "StuFis", "dependencies": { "@alpinejs/sort": "^3.14.9", "@fontsource-variable/inter": "^5.2.6", @@ -1165,9 +1166,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "dev": true, "license": "MIT", "dependencies": { @@ -2071,9 +2072,9 @@ } }, "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", "dependencies": { diff --git a/resources/views/budget-plan/view.blade.php b/resources/views/budget-plan/view.blade.php index 770443a3..80ea9909 100644 --- a/resources/views/budget-plan/view.blade.php +++ b/resources/views/budget-plan/view.blade.php @@ -1,67 +1,160 @@ +@php + use App\Models\Enums\BudgetType; + use Cknow\Money\Money; +@endphp + -
+
- head - sub + {{ __('budget-plan.view.headline') }} + + {{ $plan->organization ?? __('budget-plan.view.no-organization') }} + @if($plan->fiscalYear) + · {{ __('budget-plan.fiscal-year') }}: {{ $plan->fiscalYear->start_date->format('d.m.Y') }} + - {{ $plan->fiscalYear->end_date->format('d.m.Y') }} + @endif + - Button + {{ __('budget-plan.view.actions') }} - edit + {{ __('budget-plan.view.edit') }} + {{ __('budget-plan.view.duplicate') }} + {{ __('budget-plan.view.print') }} + {{ __('budget-plan.view.export') }} - - +
+
+
+
Status
+
+
+ 71,897 + from 70,946 +
+ +
+ + Increased by + 12% +
+
+
+
+
Avg. Open Rate
+
+
+ 58.16% + from 56.14% +
+ +
+ + Increased by + 2.02% +
+
+
+
+
Avg. Click Rate
+
+
+ 24.57% + from 28.62% +
+ +
+ + Decreased by + 4.05% +
+
+
+
+
+ + {{-- Budgetplan table --}} + + {{ __('budget-plan.edit.tab-headline.in') }} - 100.000,01€ {{ __('budget-plan.edit.tab-headline.out') }} - 100.400,01€ - @foreach(\App\Models\Enums\BudgetType::cases() as $budgetType) - - - - shortname - longname - value - booked - reserved - - - @foreach($items[$budgetType->slug()] as $budgetItem) - $budgetItem->is_group])> - - {{ $budgetItem->short_name }} - - - {{ $budgetItem->name }} - - - {{ $budgetItem->value }} - - - tbd - - - tbd - - - @foreach($budgetItem->children as $child) + @foreach(BudgetType::cases() as $budgetType) + +
+
+
+
+
+
Ein/AusgabengruppeAusgaben
. + . +
+ + + + + + + + + + + + @foreach($items[$budgetType->slug()] as $item) + + @endforeach + +
+ Title + + Name + + {{-- Sigma column --}} + + Soll + + Ist + + B +
+
+
+
+
+
+ {{-- +
+
{{ __('budget-plan.budget-shortname') }}
+
{{ __('budget-plan.budget-longname') }}
+
{{ __('budget-plan.budget-value') }}
+
{{ __('budget-plan.view.booked') }}
+
{{ __('budget-plan.view.available') }}
+ + @foreach($items[$budgetType->slug()] as $budgetItem) + + @endforeach - @endforeach - @endforeach - - +
+ --}} @endforeach +
diff --git a/resources/views/components/budgetplan/item-group-view.blade.php b/resources/views/components/budgetplan/item-group-view.blade.php index e87b7c4a..b7a15651 100644 --- a/resources/views/components/budgetplan/item-group-view.blade.php +++ b/resources/views/components/budgetplan/item-group-view.blade.php @@ -1,96 +1,112 @@ @props([ 'level' => 0, 'item', + 'lastItem' => [], ]) -
$level === 1, - //'col-start-3' => $level === 2, - //'col-start-4' => $level === 3, - //'ml-5' => $level === 1, - //'ml-10' => $level === 2, - //'ml-16' => $level === 3, - //"border-zinc-300 ", - //"border-l-1" => $level === 0 && $item->is_group, - //"border-l-2" => $level === 1 && $item->is_group, - //"border-l-3" => $level === 2 && $item->is_group, - //"border-l-4" => $level === 3 && $item->is_group, +{{-- children inside --}} +
$level === 0, + //"bg-gray-300" => $level === 1, + //"bg-gray-200" => $level === 2, + //"bg-gray-100" => $level === 3, +])> + {{-- only this row --}} +
+ {{-- Hieracy column --}} +
+
+ @for($i = 1; $i <= $level; $i++) + +
$i < $level, + 'h-full border-l-2 border-zinc-300 dark:border-zinc-600' => !($lastItem[$i-1] ?? false), + 'h-1/2 border-l-2 border-zinc-300 dark:border-zinc-600' => ($lastItem[$i-1] ?? false) && $i === $level, + ])>
+ @endfor + + @if($level > 0) +
+ @endif + + @if($item->is_group) + $level === 0, + 'fill-zinc-500 dark:fill-zinc-500' => $level > 0, + ])/> + @else + + @endif +
+
+ + {{-- Icon and short title column --}} +
- ]) x-sort:item="{{ $item->id }}"> -
$item->is_group, - "rounded" => $item->is_group, - "bg-zinc-300 my-2" => $item->is_group, - ])> -
$item->is_group, + 'text-zinc-700 dark:text-zinc-300' => !$item->is_group, ])> - + {{ $item->short_name }} + +
+ + {{-- Long Name Column --}} +
+ $item->is_group, + 'text-zinc-700 dark:text-zinc-300' => !$item->is_group, + ])> + {{ $item->name }} + +
+ + + + {{-- Value Column --}} +
@if($item->is_group) - + + {{ $item->value->format() }} + @else - + + {{ $item->value->format() }} + @endif
-
- -
-
- -
-
- $level === 3, - //'pl-5' => $level === 2, - ])> - @if($item->is_group) - - Σ - - @endif - - - + + {{-- Booked Column --}} +
+ + {{-- TODO: Implement booked amount calculation --}} + - +
-
{{-- Action Buttons --}} - @if($item->is_group) - {{-- subtle or ghost --}} - - @endif - - - - Debug: L{{ $level }} id{{$item->id}} P{{$item->position}} - @if($item->is_group) - to budget - @else - to group - @endif - item up - item down - copy - - copy zur anderen seite - - Delete - - + + {{-- Available Column --}} +
+ + {{-- TODO: Implement available amount calculation --}} + - +
+ + {{-- Empty column for alignment --}} +
- @if($item->is_group) -
$level === 0 && $item->is_group, - "border-l-16" => $level === 1 && $item->is_group, - "border-l-24" => $level === 2 && $item->is_group, - "border-l-28" => $level === 3 && $item->is_group, - ]) x-sort="$wire.sort($item,$position)"> - @foreach($item->orderedChildren as $child) - - @endforeach -
+ {{-- Recursively render children with hierarchy tracking --}} + @if($item->is_group && $item->orderedChildren->isNotEmpty()) + @foreach($item->orderedChildren as $child) + + @endforeach @endif
diff --git a/resources/views/components/budgetplan/view-row.blade.php b/resources/views/components/budgetplan/view-row.blade.php new file mode 100644 index 00000000..46c11ef0 --- /dev/null +++ b/resources/views/components/budgetplan/view-row.blade.php @@ -0,0 +1,125 @@ +@php use App\Models\BudgetItem; @endphp +@props([ + 'item', + 'level' => $item->depth, +]) + +@php /** @var $item BudgetItem */ @endphp + +class([ + "odd:bg-gray-50", + "border-t border-gray-200", + "border-x-4 border-x-indigo-600" => $level === 0, + "border-x-4 border-x-indigo-400" => $level === 1, + "border-x-4 border-x-indigo-200" => $level === 2, + "border-x-4 border-x-indigo-50 " => $level === 3, + "text-sm font-medium text-gray-900" => $item->is_group, + "text-sm whitespace-nowrap text-gray-700" => !$item->is_group, +])}}> + @if($item->is_group) + {{-- Is Group ; th needed to make sticky work --}} + $level === 0, + "px-3 sm:pl-8" => $level === 1, + "px-3 sm:pl-13" => $level === 2, + "px-3 sm:pl-18" => $level === 3, + ])> + + {{ $item->short_name }} + + {{ $item->name }} + + @if($item->is_group) Σ @endif + + {{ $item->value }} + {{ $item->value }} + {{ $item->value }} + + @else + {{-- No Group --}} + $level === 0, + "px-3 sm:pl-8" => $level === 1, + "px-3 sm:pl-13" => $level === 2, + "px-3 sm:pl-18" => $level === 3, + ])> + + {{ $item->short_name }} + + {{ $item->name }} + + {{ $item->value }} + {{ $item->value }} + {{ $item->value }} + @endif + + + + + diff --git a/resources/views/livewire/budget-plan/plan-edit.blade.php b/resources/views/livewire/budget-plan/plan-edit.blade.php index 89da80e8..e9cd3746 100644 --- a/resources/views/livewire/budget-plan/plan-edit.blade.php +++ b/resources/views/livewire/budget-plan/plan-edit.blade.php @@ -77,6 +77,9 @@
{{ __('budget-plan.edit.save') }} + + DEV: Reset Positions + Last saved yesterday From 178cb4e6c570d5b6b2944aa37c42da001ef23a2f Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 2 Dec 2025 13:35:28 +0100 Subject: [PATCH 044/108] small rector fix, dont rector cache --- rector.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rector.php b/rector.php index c8f395ef..b5919491 100644 --- a/rector.php +++ b/rector.php @@ -22,7 +22,8 @@ ]) ->withPaths([ __DIR__.'/app', - __DIR__.'/bootstrap', + __DIR__.'/bootstrap/app.php', + __DIR__.'/bootstrap/providers.php', __DIR__.'/config', __DIR__.'/lang', // __DIR__.'/legacy', From dacedc2a9ae18da139214cdeaf2129eeb197f982 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 2 Dec 2025 22:10:03 +0100 Subject: [PATCH 045/108] rector --- app/Console/Commands/ConvertLegacyBudgetPlans.php | 2 +- app/Models/BudgetItem.php | 2 +- app/Support/Money/DefaultMoneyFormater.php | 1 + app/Support/Money/MoneySynth.php | 3 ++- legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/Console/Commands/ConvertLegacyBudgetPlans.php b/app/Console/Commands/ConvertLegacyBudgetPlans.php index d9610ec1..3c831285 100644 --- a/app/Console/Commands/ConvertLegacyBudgetPlans.php +++ b/app/Console/Commands/ConvertLegacyBudgetPlans.php @@ -109,7 +109,7 @@ protected function findGlobalMaxItemId($legacyPlans): int $maxId = 0; foreach ($legacyPlans as $plan) { - $planMaxId = LegacyBudgetItem::whereHas('budgetGroup', function ($query) use ($plan) { + $planMaxId = LegacyBudgetItem::whereHas('budgetGroup', function ($query) use ($plan): void { $query->where('hhp_id', $plan->id); })->max('id'); diff --git a/app/Models/BudgetItem.php b/app/Models/BudgetItem.php index c1ca402b..dbb979b7 100644 --- a/app/Models/BudgetItem.php +++ b/app/Models/BudgetItem.php @@ -88,7 +88,7 @@ public function normalizeChildPositionValues(): void { $idx = 0; $this->orderedChildren() - ->each(function($child) use (&$idx) { + ->each(function($child) use (&$idx): void { $child->update(['position' => $idx++]); }); } diff --git a/app/Support/Money/DefaultMoneyFormater.php b/app/Support/Money/DefaultMoneyFormater.php index 4a24bcd1..8718e6c5 100644 --- a/app/Support/Money/DefaultMoneyFormater.php +++ b/app/Support/Money/DefaultMoneyFormater.php @@ -6,6 +6,7 @@ class DefaultMoneyFormater implements \Money\MoneyFormatter { + #[\Override] public function format(Money $money): string { $amount = $money->getAmount() / 100; diff --git a/app/Support/Money/MoneySynth.php b/app/Support/Money/MoneySynth.php index 63224325..6ec0fd44 100644 --- a/app/Support/Money/MoneySynth.php +++ b/app/Support/Money/MoneySynth.php @@ -14,6 +14,7 @@ class MoneySynth extends Synth { public static $key = 'money'; + #[\Override] static function match($target): bool { return $target instanceof Money || $target instanceof \Money\Money; @@ -26,7 +27,7 @@ public function dehydrate(Money $target): array public function hydrate($value): Money { - return Money::fromMoney(new DefaultMoneyFormater()->inverse($value)); + return Money::fromMoney((new DefaultMoneyFormater())->inverse($value)); } } diff --git a/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php b/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php index 8cfc3217..5fab822a 100644 --- a/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php +++ b/legacy/lib/forms/projekte/auslagen/AuslagenHandler2.php @@ -137,7 +137,7 @@ class AuslagenHandler2 extends FormHandlerInterface 'revocation' => true, ], 'instructed' => [ - 'booked' => ['groups' => ['ref-finanzen-kv']], + 'booked' => ['groups' => ['ref-finanzen-belege']], 'revocation' => ['groups' => ['ref-finanzen-belege']], ], 'booked' => [ From e214eeffa314ea79c25ced1953552315743b80b4 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Thu, 4 Dec 2025 00:44:19 +0100 Subject: [PATCH 046/108] cleanup --- app/Models/FinancePlanItem.php | 59 ----- app/Models/FinancePlanTopic.php | 56 ----- legacy/lib/forms/ExternVorgangHandler.php | 272 ---------------------- legacy/lib/forms/FormHandlerInterface.php | 20 -- 4 files changed, 407 deletions(-) delete mode 100644 app/Models/FinancePlanItem.php delete mode 100644 app/Models/FinancePlanTopic.php delete mode 100644 legacy/lib/forms/ExternVorgangHandler.php delete mode 100644 legacy/lib/forms/FormHandlerInterface.php diff --git a/app/Models/FinancePlanItem.php b/app/Models/FinancePlanItem.php deleted file mode 100644 index 6c7901d9..00000000 --- a/app/Models/FinancePlanItem.php +++ /dev/null @@ -1,59 +0,0 @@ -belongsTo(Application::class); - } - - public function financePlanTopic(): BelongsTo - { - return $this->belongsTo(FinancePlanTopic::class); - } -} diff --git a/app/Models/FinancePlanTopic.php b/app/Models/FinancePlanTopic.php deleted file mode 100644 index 3a30f0e6..00000000 --- a/app/Models/FinancePlanTopic.php +++ /dev/null @@ -1,56 +0,0 @@ - $financePlanItems - * @property-read int|null $finance_plan_items_count - * - * @method static Builder|FinancePlanTopic whereCreatedAt($value) - * @method static Builder|FinancePlanTopic whereUpdatedAt($value) - * - * @mixin Eloquent - */ -class FinancePlanTopic extends Model -{ - use HasFactory; - - public function application(): BelongsTo - { - return $this->belongsTo(Application::class); - } - - public function financePlanItems(): HasMany - { - return $this->hasMany(FinancePlanItem::class); - } -} diff --git a/legacy/lib/forms/ExternVorgangHandler.php b/legacy/lib/forms/ExternVorgangHandler.php deleted file mode 100644 index a80502da..00000000 --- a/legacy/lib/forms/ExternVorgangHandler.php +++ /dev/null @@ -1,272 +0,0 @@ - $vId, 'extern_id' => $eId]; - } else { - throw new LegacyDieException(400, 'non valid array. vid or eid is not set'); - } - $this->routeInfo = $routeInfoOrId; - } else { - $where = ['extern_data.id' => $routeInfoOrId]; - } - $this->data = DBConnector::getInstance()->dbFetchAll( - 'extern_data', - [DBConnector::FETCH_ASSOC], - ['extern_data.*', 'titel_nr', 'titel_name'], - $where, - [ - ['table' => 'haushaltstitel', 'type' => 'left', 'on' => ['haushaltstitel.id', 'extern_data.titel_id']], - ] - ); - if (! is_array($this->data) || count($this->data) !== 1) { - throw new LegacyDieException(400, 'Datensatz konnte nicht gefunden werden'); - } - $this->data = $this->data[0]; - $this->meta_data = DBConnector::getInstance()->dbFetchAll( - 'extern_meta', - [DBConnector::FETCH_ASSOC], - [], - [ - 'id' => $this->data['extern_id'], - ] - ); - if (! is_array($this->meta_data) || count($this->meta_data) !== 1) { - throw new LegacyDieException(400, 'Datensatz konnte nicht gefunden werden'); - } - $this->meta_data = $this->meta_data[0]; - } - - public static function initStaticVars() - { - // TODO: Implement initStaticVars() method. - } - - public static function getStateStringFromName($statename) - { - // TODO: Implement getStateStringFromName() method. - } - - public function updateSavedData($data) - { - // TODO: Implement updateSavedData() method. - } - - public function state_change($stateName, $etag): void - { - // TODO: Implement method and use etag :/ - switch ($stateName) { - case 'instructed': - case 'payed': - case 'booked': - $colName = "state_$stateName"; - break; - default: - throw new LegacyDieException(400, "Wrong State $stateName in External"); - break; - } - $newEtag = Str::random(32); - // TODO: also Version number tracking? - DBConnector::getInstance()->dbUpdate( - 'extern_data', - ['id' => $this->id, 'etag' => $etag], - [ - $colName => DBConnector::getInstance()->getUser()['fullname'].';'.date_create()->format(DateTime), - 'etag' => $newEtag, - ] - ); - } - - public function setState($stateName) - { - // TODO: Implement setState() method. - } - - public function state_change_possible($nextState) - { - // FIXME - return true; - } - - public function getStateString() - { - // FIXME - return 'I have no fucking state'; - } - - public function getNextPossibleStates() - { - // TODO: Implement getNextPossibleStates() method. - } - - public function getID(): ?int - { - // TODO: Implement getID() method. - } - - public function render(): void - { - // TODO: Implement render() method. - } - - public function handlePost(): void - { - if (isset($this->routeInfo['mfunction'])) { - switch ($this->routeInfo['mfunction']) { - case 'zahlungsanweisung': - $this->post_pdf_zahlungsanweisung($_POST['d'] === '0'); - break; - default: - throw new LegacyDieException(400, 'mfunction '.$this->routeInfo['mfunction'].' not known'); - break; - } - } - } - - private function post_pdf_zahlungsanweisung($modal = false): void - { - $details = []; - // var_dump($this->auslagen_data["belege"]); - $einnahme = 0; - $ausgabe = 0; - switch ('1') { - case $this->data['flag_vorkasse']: - $ausgabe = $this->data['value']; - $name = 'Auszahlung Vorkasse'; - break; - case $this->data['flag_pruefbescheid']: - $ausgabe = $this->data['value']; - $name = 'Auszahlung Prüfbescheid'; - break; - case $this->data['flag_rueckforderung']: - $einnahme = $this->data['value']; - $name = 'Rückforderungsbescheid'; - break; - } - $details[] = [ - 'beleg-id' => 'V'.$this->data['vorgang_id'], - 'projektposten' => '', - 'titel' => $this->data['titel_nr'], - 'einnahmen' => $einnahme, - 'ausgaben' => $ausgabe, - ]; - $recht = 'StuRa-Beschluss: '.$this->meta_data['beschluss_nr']; - - $out = [ - 'APIKEY' => FUI2PDF_APIKEY, - 'action' => 'zahlungsanweisung', - - 'short-type-projekt' => 'EP', - 'projekt-id' => $this->data['extern_id'], - 'projekt-name' => $this->meta_data['projekt_name'], - 'projekt-org' => $this->meta_data['org_name'], - 'projekt-recht' => $recht, - 'projekt-create' => $this->data['date'], - - 'short-type-auslage' => 'V', - 'auslage-id' => $this->data['vorgang_id'], - 'auslage-name' => $name, - - 'zahlung-name' => $this->meta_data['zahlung_empf'], - 'zahlung-iban' => $this->meta_data['zahlung_iban'], // TODO: de- and encryprion - 'zahlung-value' => ($einnahme - $ausgabe), - 'zahlung-adresse' => $this->meta_data['org_address'], - 'angewiesen-date' => $this->data['date'], - - 'details' => $details, - ]; - $result = Helper::do_post_request2(FUI2PDF_URL.'/pdfbuilder', $out, FUI2PDF_AUTH); - // return result to - if ($result['success'] && $modal) { - if (isset($result['data']['success']) && $result['data']['success']) { - JsonController::print_json( - [ - 'success' => true, - 'type' => 'modal', - 'subtype' => 'file', - 'container' => 'object', - 'headline' => - // direct link - '
'. - 'Zahlungsanweisung-E'. - str_pad($this->data['extern_id'], 3, '0', STR_PAD_LEFT). - '-V'. - str_pad($this->data['vorgang_id'], 3, '0', STR_PAD_LEFT). - '.pdf'. - ''. - ''. - ''. - ''.'
', - 'attr' => [ - 'type' => 'application/pdf', - 'download' => 'Zahlungsanweisung-E'. - str_pad($this->data['extern_id'], 3, '0', STR_PAD_LEFT). - '-V'. - str_pad($this->data['vorgang_id'], 3, '0', STR_PAD_LEFT). - '.pdf', - ], - 'fallback' => '
Die Datei kann leider nicht angezeigt werden, kann aber unter diesem Link heruntergeladen werden.'. - ''. - ''. - ''. - '
', - 'datapre' => 'data:application/pdf;base64,', - 'data' => $result['data']['data'], - ] - ); - } else { - JsonController::print_json( - [ - 'success' => false, - 'type' => 'modal', - 'subtype' => 'server-error', - 'status' => '200', - 'msg' => '
'.print_r( - $result['data']['error'] ?? $result['data'], - true - ).'
', - ] - ); - } - } elseif ($result['success'] && ! $modal) { - header('Content-Type: application/pdf'); - header( - 'Content-Disposition: attachment; filename="'.'Belegvorlage_P'. - str_pad($this->data['extern_id'], 3, '0', STR_PAD_LEFT). - '-A'. - str_pad($this->data['vorgang_id'], 3, '0', STR_PAD_LEFT). - '.pdf' - .'"' - ); - echo base64_decode($result['data']['data']); - exit(); - } else { - throw new LegacyDieException(400, print_r($result, true), '['.get_class($this).'][PDF-Creation]'); - $this->error = 'Error during PDF creation.'; - } - } -} diff --git a/legacy/lib/forms/FormHandlerInterface.php b/legacy/lib/forms/FormHandlerInterface.php deleted file mode 100644 index e44d9d52..00000000 --- a/legacy/lib/forms/FormHandlerInterface.php +++ /dev/null @@ -1,20 +0,0 @@ - Date: Thu, 4 Dec 2025 00:44:46 +0100 Subject: [PATCH 047/108] changed default timezone to Europe/Berlin --- config/app.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/app.php b/config/app.php index 58209c7b..8998c047 100644 --- a/config/app.php +++ b/config/app.php @@ -13,7 +13,7 @@ | */ - 'timezone' => env('APP_TIMEZONE', 'UTC'), + 'timezone' => env('APP_TIMEZONE', 'Europe/Berlin'), /* |-------------------------------------------------------------------------- From a321f665e75d0497352d250e066c5be12c502a40 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Thu, 4 Dec 2025 00:46:47 +0100 Subject: [PATCH 048/108] introduced spatie/states and refactored ProjectHandler --- app/Models/Legacy/Project.php | 16 +- app/Policies/ProjectPolicy.php | 127 ++ app/States/Project/Applied.php | 11 + app/States/Project/ApprovedByFinance.php | 17 + app/States/Project/ApprovedByOrg.php | 16 + app/States/Project/ApprovedByOther.php | 17 + app/States/Project/Draft.php | 12 + app/States/Project/NeedFinanceApproval.php | 10 + app/States/Project/NeedOrgApproval.php | 10 + app/States/Project/ProjectState.php | 96 + app/States/Project/Revoked.php | 10 + app/States/Project/Terminated.php | 10 + composer.json | 1 + composer.lock | 1542 ++++++++++------- config/model-states.php | 10 + legacy/lib/forms/FormTemplater.php | 38 - legacy/lib/forms/FormTemplaterProject.php | 591 +++++++ legacy/lib/forms/projekte/ProjektHandler.php | 559 +++--- .../projekte/auslagen/AuslagenHandler2.php | 3 +- routes/legacy.php | 1 + 20 files changed, 2111 insertions(+), 986 deletions(-) create mode 100644 app/Policies/ProjectPolicy.php create mode 100644 app/States/Project/Applied.php create mode 100644 app/States/Project/ApprovedByFinance.php create mode 100644 app/States/Project/ApprovedByOrg.php create mode 100644 app/States/Project/ApprovedByOther.php create mode 100644 app/States/Project/Draft.php create mode 100644 app/States/Project/NeedFinanceApproval.php create mode 100644 app/States/Project/NeedOrgApproval.php create mode 100644 app/States/Project/ProjectState.php create mode 100644 app/States/Project/Revoked.php create mode 100644 app/States/Project/Terminated.php create mode 100644 config/model-states.php create mode 100644 legacy/lib/forms/FormTemplaterProject.php diff --git a/app/Models/Legacy/Project.php b/app/Models/Legacy/Project.php index 1c447bfa..8d77427a 100644 --- a/app/Models/Legacy/Project.php +++ b/app/Models/Legacy/Project.php @@ -4,11 +4,13 @@ use App\Events\UpdatingModel; use App\Models\User; +use App\States\Project\ProjectState; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Spatie\ModelStates\HasStates; /** * App\Models\Legacy\Project @@ -18,7 +20,7 @@ * @property string $createdat * @property string $lastupdated * @property int $version - * @property string $state + * @property ProjectState $state * @property int $stateCreator_id * @property string $name * @property string $responsible @@ -65,6 +67,7 @@ class Project extends Model { use HasFactory; + use HasStates; /** * The table associated with the model. @@ -75,6 +78,14 @@ class Project extends Model public $timestamps = false; + public $casts = [ + 'state' => ProjectState::class, + 'createdat' => 'datetime', + 'lastupdated' => 'datetime', + 'date_start' => 'date', + 'date_end' => 'date', + ]; + /** * @var array */ @@ -86,7 +97,7 @@ class Project extends Model public function expenses(): HasMany { - return $this->hasMany(\App\Models\Legacy\Expense::class, 'projekt_id'); + return $this->hasMany(\App\Models\Legacy\Expense::class, 'projekt_id', 'id'); } public function creator(): BelongsTo @@ -103,4 +114,5 @@ public function posts(): HasMany { return $this->hasMany(ProjectPost::class, 'projekt_id'); } + } diff --git a/app/Policies/ProjectPolicy.php b/app/Policies/ProjectPolicy.php new file mode 100644 index 00000000..04aeab26 --- /dev/null +++ b/app/Policies/ProjectPolicy.php @@ -0,0 +1,127 @@ +getGroups(); + $financeAll = $userGroups->contains('ref-finanzen-belege'); + $approveFinance = $userGroups->contains('ref-finanzen-hv'); + $approveOrg = $userGroups->contains('ref-finanzen-hv'); + $approveOther = $userGroups->contains('ref-finanzen-hv'); + + return match($project->state::class) { + Draft::class => true, + Applied::class => $financeAll, + NeedOrgApproval::class => $approveOrg, + ApprovedByOrg::class => $approveOrg, + NeedFinanceApproval::class => $approveFinance, + ApprovedByFinance::class => $approveFinance, + ApprovedByOther::class => $approveOther, + Revoked::class => false, + Terminated::class => false, + + default => false, + }; + } + + public function delete(User $user, Project $Projects): bool + { + // depends on the state + return false; + } + + public function createExpense(User $user, Project $project): bool + { + return $project->state->expensable(); + } + + public function transitionTo(User $user, Project $project, ProjectState $newState): bool + { + $currentState = $project->state; + + // check if transition is possible + if(!$currentState->canTransitionTo($newState)) { + return false; + } + + $isOwner = $user->id === $project->creator->id; + $isOrg = $user->getCommittees()->contains($project->org); + $userGroups = $user->getGroups(); + + $financeAll = $userGroups->contains('ref-finanzen-belege'); + $approveFinance = $userGroups->contains('ref-finanzen-hv'); + $approveOrg = $userGroups->contains('ref-finanzen-hv'); + $approveOther = $userGroups->contains('ref-finanzen-hv'); + $terminator = $userGroups->contains('ref-finanzen-hv'); + + // there are some minor exceptions for certain states, but most of the time the needed permission is only + // defined by the new state, not the current one + return match($newState::class) { + Draft::class => $isOwner || $isOrg || $financeAll, + Applied::class => $isOwner || $isOrg || $financeAll, + NeedOrgApproval::class => $financeAll, + ApprovedByOrg::class => $approveOrg, + NeedFinanceApproval::class => $financeAll, + ApprovedByFinance::class => $approveFinance, + ApprovedByOther::class => $approveOther, + Revoked::class => $isOwner || $isOrg || $financeAll, + Terminated::class => $isOwner || $isOrg || $terminator, + + default => false, + }; + } + + public function updateField(User $user, Project $project, string $field) + { + if($this->update($user, $project) === false){ + return false; + } + + if($field === 'recht' || $field === 'recht-additional' || $field === 'posten-titel') { + return match ($project->state::class) { + Draft::class => false, + default => true + }; + } + return true; + } + + + +} diff --git a/app/States/Project/Applied.php b/app/States/Project/Applied.php new file mode 100644 index 00000000..9834e1cd --- /dev/null +++ b/app/States/Project/Applied.php @@ -0,0 +1,11 @@ +default(Draft::class) + ->allowTransition(Draft::class, Applied::class) + ->allowTransition([ApprovedByOrg::class, ApprovedByFinance::class, ApprovedByOther::class], Terminated::class) + ->allowTransition([NeedOrgApproval::class, NeedFinanceApproval::class], Revoked::class) + ->allowTransition([Revoked::class], Draft::class) + ; + + // here would be some dynamic logic from config possible + + $config = $config->allowTransition([ + Applied::class, + NeedFinanceApproval::class, + ApprovedByFinance::class, + //NeedOrgApproval::class, + ApprovedByOrg::class, + ApprovedByOther::class, + Terminated::class + ], NeedOrgApproval::class); + + $config = $config->allowTransition([ + Applied::class, + NeedFinanceApproval::class, + ApprovedByFinance::class, + NeedOrgApproval::class, + //ApprovedByOrg::class, + ApprovedByOther::class, + Terminated::class + ], ApprovedByOrg::class); + + + $config = $config->allowTransition([ + Applied::class, + //NeedFinanceApproval::class, + ApprovedByFinance::class, + NeedOrgApproval::class, + ApprovedByOrg::class, + ApprovedByOther::class, + Terminated::class + ], NeedFinanceApproval::class); + + $config = $config->allowTransition([ + Applied::class, + NeedFinanceApproval::class, + //ApprovedByFinance::class, + NeedOrgApproval::class, + ApprovedByOrg::class, + ApprovedByOther::class, + Terminated::class + ], ApprovedByOrg::class); + + $config = $config->allowTransition([ + Applied::class, + NeedFinanceApproval::class, + ApprovedByFinance::class, + NeedOrgApproval::class, + ApprovedByOrg::class, + //ApprovedByOther::class, + Terminated::class + ], ApprovedByOrg::class); + + return $config; + } + +} diff --git a/app/States/Project/Revoked.php b/app/States/Project/Revoked.php new file mode 100644 index 00000000..b3e04a2d --- /dev/null +++ b/app/States/Project/Revoked.php @@ -0,0 +1,10 @@ +=7.4.0 <8.5.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0", "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" @@ -4529,9 +4541,9 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.30.0" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.30.1" }, - "time": "2025-08-10T06:28:02+00:00" + "time": "2025-10-26T16:01:04+00:00" }, { "name": "phpoption/phpoption", @@ -4610,16 +4622,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "3.0.46", + "version": "3.0.47", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6" + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/9d6ca36a6c2dd434765b1071b2644a1c683b385d", + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d", "shasum": "" }, "require": { @@ -4700,7 +4712,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.46" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.47" }, "funding": [ { @@ -4716,7 +4728,7 @@ "type": "tidelift" } ], - "time": "2025-06-26T16:29:55+00:00" + "time": "2025-10-06T01:07:24+00:00" }, { "name": "propaganistas/laravel-phone", @@ -5525,21 +5537,21 @@ }, { "name": "spatie/db-dumper", - "version": "3.8.0", + "version": "3.8.1", "source": { "type": "git", "url": "https://github.com/spatie/db-dumper.git", - "reference": "91e1fd4dc000aefc9753cda2da37069fc996baee" + "reference": "e974cc7862b8de1bd3b7ea7d4839ba7167acb546" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/db-dumper/zipball/91e1fd4dc000aefc9753cda2da37069fc996baee", - "reference": "91e1fd4dc000aefc9753cda2da37069fc996baee", + "url": "https://api.github.com/repos/spatie/db-dumper/zipball/e974cc7862b8de1bd3b7ea7d4839ba7167acb546", + "reference": "e974cc7862b8de1bd3b7ea7d4839ba7167acb546", "shasum": "" }, "require": { "php": "^8.0", - "symfony/process": "^5.0|^6.0|^7.0" + "symfony/process": "^5.0|^6.0|^7.0|^8.0" }, "require-dev": { "pestphp/pest": "^1.22" @@ -5572,7 +5584,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/db-dumper/tree/3.8.0" + "source": "https://github.com/spatie/db-dumper/tree/3.8.1" }, "funding": [ { @@ -5584,7 +5596,7 @@ "type": "github" } ], - "time": "2025-02-14T15:04:22+00:00" + "time": "2025-11-26T09:51:23+00:00" }, { "name": "spatie/laravel-backup", @@ -5685,6 +5697,82 @@ ], "time": "2024-08-07T11:07:52+00:00" }, + { + "name": "spatie/laravel-model-states", + "version": "2.12.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-model-states.git", + "reference": "13cccd3ecb4396c9a3360fb710d9dd40c0f1a1fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-model-states/zipball/13cccd3ecb4396c9a3360fb710d9dd40c0f1a1fe", + "reference": "13cccd3ecb4396c9a3360fb710d9dd40c0f1a1fe", + "shasum": "" + }, + "require": { + "ext-json": "*", + "facade/ignition-contracts": "^1.0", + "illuminate/contracts": "^10.0 | ^11.0 | ^12.0", + "illuminate/database": "^10.0 | ^11.0 | ^12.0", + "illuminate/support": "^10.0 | ^11.0 | ^12.0", + "php": "^7.4|^8.0", + "spatie/laravel-package-tools": "^1.9", + "spatie/php-structure-discoverer": "^2.2" + }, + "require-dev": { + "orchestra/testbench": "^8.0 | ^9.0 | ^10.0", + "pestphp/pest": "^2.0|^3.0", + "phpunit/phpunit": "^10.0|^11.0|^12.0", + "symfony/var-dumper": "^6.0 | ^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\ModelStates\\ModelStatesServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\ModelStates\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brent Roose", + "email": "brent@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "State support for Eloquent models", + "homepage": "https://github.com/spatie/laravel-model-states", + "keywords": [ + "spatie", + "state" + ], + "support": { + "source": "https://github.com/spatie/laravel-model-states/tree/2.12.1" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-08-01T09:20:29+00:00" + }, { "name": "spatie/laravel-package-tools", "version": "1.92.7", @@ -5748,23 +5836,23 @@ }, { "name": "spatie/laravel-signal-aware-command", - "version": "2.1.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-signal-aware-command.git", - "reference": "8e8a226ed7fb45302294878ef339e75ffa9a878d" + "reference": "70207ba2702a9ee8f523af0cd4711d1104c5ca53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-signal-aware-command/zipball/8e8a226ed7fb45302294878ef339e75ffa9a878d", - "reference": "8e8a226ed7fb45302294878ef339e75ffa9a878d", + "url": "https://api.github.com/repos/spatie/laravel-signal-aware-command/zipball/70207ba2702a9ee8f523af0cd4711d1104c5ca53", + "reference": "70207ba2702a9ee8f523af0cd4711d1104c5ca53", "shasum": "" }, "require": { "illuminate/contracts": "^11.0|^12.0", "php": "^8.2", "spatie/laravel-package-tools": "^1.4.3", - "symfony/console": "^7.0" + "symfony/console": "^7.0|^8.0" }, "require-dev": { "brianium/paratest": "^6.2|^7.0", @@ -5811,7 +5899,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-signal-aware-command/issues", - "source": "https://github.com/spatie/laravel-signal-aware-command/tree/2.1.0" + "source": "https://github.com/spatie/laravel-signal-aware-command/tree/2.1.1" }, "funding": [ { @@ -5819,7 +5907,86 @@ "type": "github" } ], - "time": "2025-02-14T09:55:51+00:00" + "time": "2025-11-27T09:17:52+00:00" + }, + { + "name": "spatie/php-structure-discoverer", + "version": "2.3.3", + "source": { + "type": "git", + "url": "https://github.com/spatie/php-structure-discoverer.git", + "reference": "552a5b974a9853a32e5677a66e85ae615a96a90b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/php-structure-discoverer/zipball/552a5b974a9853a32e5677a66e85ae615a96a90b", + "reference": "552a5b974a9853a32e5677a66e85ae615a96a90b", + "shasum": "" + }, + "require": { + "illuminate/collections": "^11.0|^12.0", + "php": "^8.3", + "spatie/laravel-package-tools": "^1.92.7", + "symfony/finder": "^6.0|^7.3.5|^8.0" + }, + "require-dev": { + "amphp/parallel": "^2.3.2", + "illuminate/console": "^11.0|^12.0", + "nunomaduro/collision": "^7.0|^8.8.3", + "orchestra/testbench": "^9.5|^10.8", + "pestphp/pest": "^3.8|^4.0", + "pestphp/pest-plugin-laravel": "^3.2|^4.0", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan-deprecation-rules": "^1.2.1", + "phpstan/phpstan-phpunit": "^1.4.2", + "spatie/laravel-ray": "^1.43.1" + }, + "suggest": { + "amphp/parallel": "When you want to use the Parallel discover worker" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\StructureDiscoverer\\StructureDiscovererServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\StructureDiscoverer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ruben Van Assche", + "email": "ruben@spatie.be", + "role": "Developer" + } + ], + "description": "Automatically discover structures within your PHP application", + "homepage": "https://github.com/spatie/php-structure-discoverer", + "keywords": [ + "discover", + "laravel", + "php", + "php-structure-discoverer" + ], + "support": { + "issues": "https://github.com/spatie/php-structure-discoverer/issues", + "source": "https://github.com/spatie/php-structure-discoverer/tree/2.3.3" + }, + "funding": [ + { + "url": "https://github.com/LaravelAutoDiscoverer", + "type": "github" + } + ], + "time": "2025-11-24T16:41:01+00:00" }, { "name": "spatie/regex", @@ -6121,22 +6288,21 @@ }, { "name": "symfony/clock", - "version": "v7.3.0", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" + "reference": "832119f9b8dbc6c8e6f65f30c5969eca1e88764f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", - "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "url": "https://api.github.com/repos/symfony/clock/zipball/832119f9b8dbc6c8e6f65f30c5969eca1e88764f", + "reference": "832119f9b8dbc6c8e6f65f30c5969eca1e88764f", "shasum": "" }, "require": { - "php": ">=8.2", - "psr/clock": "^1.0", - "symfony/polyfill-php83": "^1.28" + "php": ">=8.4", + "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -6175,7 +6341,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.3.0" + "source": "https://github.com/symfony/clock/tree/v8.0.0" }, "funding": [ { @@ -6186,25 +6352,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2025-11-12T15:46:48+00:00" }, { "name": "symfony/console", - "version": "v7.3.3", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" + "reference": "0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "url": "https://api.github.com/repos/symfony/console/zipball/0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8", + "reference": "0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8", "shasum": "" }, "require": { @@ -6212,7 +6382,7 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2" + "symfony/string": "^7.2|^8.0" }, "conflict": { "symfony/dependency-injection": "<6.4", @@ -6226,16 +6396,16 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6269,7 +6439,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.3" + "source": "https://github.com/symfony/console/tree/v7.4.0" }, "funding": [ { @@ -6289,20 +6459,20 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-11-27T13:27:24+00:00" }, { "name": "symfony/css-selector", - "version": "v7.3.0", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" + "reference": "ab862f478513e7ca2fe9ec117a6f01a8da6e1135" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/ab862f478513e7ca2fe9ec117a6f01a8da6e1135", + "reference": "ab862f478513e7ca2fe9ec117a6f01a8da6e1135", "shasum": "" }, "require": { @@ -6338,7 +6508,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.3.0" + "source": "https://github.com/symfony/css-selector/tree/v7.4.0" }, "funding": [ { @@ -6349,12 +6519,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2025-10-30T13:39:42+00:00" }, { "name": "symfony/deprecation-contracts", @@ -6425,32 +6599,33 @@ }, { "name": "symfony/error-handler", - "version": "v7.3.2", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3" + "reference": "48be2b0653594eea32dcef130cca1c811dcf25c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/0b31a944fcd8759ae294da4d2808cbc53aebd0c3", - "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/48be2b0653594eea32dcef130cca1c811dcf25c2", + "reference": "48be2b0653594eea32dcef130cca1c811dcf25c2", "shasum": "" }, "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/polyfill-php85": "^1.32", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/deprecation-contracts": "<2.5", "symfony/http-kernel": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0|^8.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", "symfony/webpack-encore-bundle": "^1.0|^2.0" }, "bin": [ @@ -6482,7 +6657,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.3.2" + "source": "https://github.com/symfony/error-handler/tree/v7.4.0" }, "funding": [ { @@ -6502,28 +6677,28 @@ "type": "tidelift" } ], - "time": "2025-07-07T08:17:57+00:00" + "time": "2025-11-05T14:29:59+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.3.3", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" + "reference": "573f95783a2ec6e38752979db139f09fec033f03" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", - "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/573f95783a2ec6e38752979db139f09fec033f03", + "reference": "573f95783a2ec6e38752979db139f09fec033f03", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<6.4", + "symfony/security-http": "<7.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -6532,13 +6707,14 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/error-handler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/stopwatch": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -6566,7 +6742,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.0" }, "funding": [ { @@ -6586,7 +6762,7 @@ "type": "tidelift" } ], - "time": "2025-08-13T11:49:31+00:00" + "time": "2025-10-30T14:17:19+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -6666,23 +6842,23 @@ }, { "name": "symfony/finder", - "version": "v7.3.2", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" + "reference": "340b9ed7320570f319028a2cbec46d40535e94bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", - "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", + "url": "https://api.github.com/repos/symfony/finder/zipball/340b9ed7320570f319028a2cbec46d40535e94bd", + "reference": "340b9ed7320570f319028a2cbec46d40535e94bd", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0" + "symfony/filesystem": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6710,7 +6886,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.2" + "source": "https://github.com/symfony/finder/tree/v7.4.0" }, "funding": [ { @@ -6730,27 +6906,26 @@ "type": "tidelift" } ], - "time": "2025-07-15T13:41:35+00:00" + "time": "2025-11-05T05:42:40+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.3.3", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00" + "reference": "769c1720b68e964b13b58529c17d4a385c62167b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7475561ec27020196c49bb7c4f178d33d7d3dc00", - "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/769c1720b68e964b13b58529c17d4a385c62167b", + "reference": "769c1720b68e964b13b58529c17d4a385c62167b", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php83": "^1.27" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "^1.1" }, "conflict": { "doctrine/dbal": "<3.6", @@ -6759,13 +6934,13 @@ "require-dev": { "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5", - "symfony/clock": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0" + "symfony/cache": "^6.4.12|^7.1.5|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6793,7 +6968,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.3" + "source": "https://github.com/symfony/http-foundation/tree/v7.4.0" }, "funding": [ { @@ -6813,29 +6988,29 @@ "type": "tidelift" } ], - "time": "2025-08-20T08:04:18+00:00" + "time": "2025-11-13T08:49:24+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.3", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b" + "reference": "7348193cd384495a755554382e4526f27c456085" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/72c304de37e1a1cec6d5d12b81187ebd4850a17b", - "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/7348193cd384495a755554382e4526f27c456085", + "reference": "7348193cd384495a755554382e4526f27c456085", "shasum": "" }, "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.4|^7.0", - "symfony/event-dispatcher": "^7.3", - "symfony/http-foundation": "^7.3", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^7.3|^8.0", + "symfony/http-foundation": "^7.4|^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -6845,6 +7020,7 @@ "symfony/console": "<6.4", "symfony/dependency-injection": "<6.4", "symfony/doctrine-bridge": "<6.4", + "symfony/flex": "<2.10", "symfony/form": "<6.4", "symfony/http-client": "<6.4", "symfony/http-client-contracts": "<2.5", @@ -6862,27 +7038,27 @@ }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/clock": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/css-selector": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/dom-crawler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/dom-crawler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^7.1", - "symfony/routing": "^6.4|^7.0", - "symfony/serializer": "^7.1", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^7.1|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/serializer": "^7.1|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0", - "symfony/var-exporter": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0", "twig/twig": "^3.12" }, "type": "library", @@ -6911,7 +7087,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.3.3" + "source": "https://github.com/symfony/http-kernel/tree/v7.4.0" }, "funding": [ { @@ -6931,20 +7107,20 @@ "type": "tidelift" } ], - "time": "2025-08-29T08:23:45+00:00" + "time": "2025-11-27T13:38:24+00:00" }, { "name": "symfony/mailer", - "version": "v7.3.3", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575" + "reference": "a3d9eea8cfa467ece41f0f54ba28185d74bd53fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/a32f3f45f1990db8c4341d5122a7d3a381c7e575", - "reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575", + "url": "https://api.github.com/repos/symfony/mailer/zipball/a3d9eea8cfa467ece41f0f54ba28185d74bd53fd", + "reference": "a3d9eea8cfa467ece41f0f54ba28185d74bd53fd", "shasum": "" }, "require": { @@ -6952,8 +7128,8 @@ "php": ">=8.2", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/mime": "^7.2", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/mime": "^7.2|^8.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -6964,10 +7140,10 @@ "symfony/twig-bridge": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6995,7 +7171,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.3.3" + "source": "https://github.com/symfony/mailer/tree/v7.4.0" }, "funding": [ { @@ -7015,24 +7191,25 @@ "type": "tidelift" } ], - "time": "2025-08-13T11:49:31+00:00" + "time": "2025-11-21T15:26:00+00:00" }, { "name": "symfony/mime", - "version": "v7.3.2", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1" + "reference": "bdb02729471be5d047a3ac4a69068748f1a6be7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/e0a0f859148daf1edf6c60b398eb40bfc96697d1", - "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1", + "url": "https://api.github.com/repos/symfony/mime/zipball/bdb02729471be5d047a3ac4a69068748f1a6be7a", + "reference": "bdb02729471be5d047a3ac4a69068748f1a6be7a", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, @@ -7047,11 +7224,11 @@ "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/serializer": "^6.4.3|^7.0.3" + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4.3|^7.0.3|^8.0" }, "type": "library", "autoload": { @@ -7083,7 +7260,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.3.2" + "source": "https://github.com/symfony/mime/tree/v7.4.0" }, "funding": [ { @@ -7103,7 +7280,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T13:41:35+00:00" + "time": "2025-11-16T10:14:42+00:00" }, { "name": "symfony/polyfill-ctype", @@ -7691,6 +7868,86 @@ ], "time": "2025-07-08T02:45:35+00:00" }, + { + "name": "symfony/polyfill-php85", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-23T16:12:55+00:00" + }, { "name": "symfony/polyfill-uuid", "version": "v1.33.0", @@ -7776,16 +8033,16 @@ }, { "name": "symfony/process", - "version": "v7.3.3", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" + "reference": "7ca8dc2d0dcf4882658313aba8be5d9fd01026c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", + "url": "https://api.github.com/repos/symfony/process/zipball/7ca8dc2d0dcf4882658313aba8be5d9fd01026c8", + "reference": "7ca8dc2d0dcf4882658313aba8be5d9fd01026c8", "shasum": "" }, "require": { @@ -7817,7 +8074,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.3" + "source": "https://github.com/symfony/process/tree/v7.4.0" }, "funding": [ { @@ -7837,20 +8094,20 @@ "type": "tidelift" } ], - "time": "2025-08-18T09:42:54+00:00" + "time": "2025-10-16T11:21:06+00:00" }, { "name": "symfony/routing", - "version": "v7.3.2", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4" + "reference": "4720254cb2644a0b876233d258a32bf017330db7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/7614b8ca5fa89b9cd233e21b627bfc5774f586e4", - "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4", + "url": "https://api.github.com/repos/symfony/routing/zipball/4720254cb2644a0b876233d258a32bf017330db7", + "reference": "4720254cb2644a0b876233d258a32bf017330db7", "shasum": "" }, "require": { @@ -7864,11 +8121,11 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -7902,7 +8159,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.3.2" + "source": "https://github.com/symfony/routing/tree/v7.4.0" }, "funding": [ { @@ -7922,20 +8179,20 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-11-27T13:27:24+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -7989,7 +8246,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -8000,44 +8257,47 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-25T09:37:31+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/string", - "version": "v7.3.3", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" + "reference": "f929eccf09531078c243df72398560e32fa4cf4f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "url": "https://api.github.com/repos/symfony/string/zipball/f929eccf09531078c243df72398560e32fa4cf4f", + "reference": "f929eccf09531078c243df72398560e32fa4cf4f", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8076,7 +8336,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.3" + "source": "https://github.com/symfony/string/tree/v8.0.0" }, "funding": [ { @@ -8096,38 +8356,31 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-11T14:37:55+00:00" }, { "name": "symfony/translation", - "version": "v7.3.3", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "e0837b4cbcef63c754d89a4806575cada743a38d" + "reference": "82ab368a6fca6358d995b6dd5c41590fb42c03e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/e0837b4cbcef63c754d89a4806575cada743a38d", - "reference": "e0837b4cbcef63c754d89a4806575cada743a38d", + "url": "https://api.github.com/repos/symfony/translation/zipball/82ab368a6fca6358d995b6dd5c41590fb42c03e6", + "reference": "82ab368a6fca6358d995b6dd5c41590fb42c03e6", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.5|^3.0" + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation-contracts": "^3.6.1" }, "conflict": { "nikic/php-parser": "<5.0", - "symfony/config": "<6.4", - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<6.4", - "symfony/service-contracts": "<2.5", - "symfony/twig-bundle": "<6.4", - "symfony/yaml": "<6.4" + "symfony/service-contracts": "<2.5" }, "provide": { "symfony/translation-implementation": "2.3|3.0" @@ -8135,17 +8388,17 @@ "require-dev": { "nikic/php-parser": "^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^6.4|^7.0", + "symfony/routing": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0" + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8176,7 +8429,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.3.3" + "source": "https://github.com/symfony/translation/tree/v8.0.0" }, "funding": [ { @@ -8196,20 +8449,20 @@ "type": "tidelift" } ], - "time": "2025-08-01T21:02:37+00:00" + "time": "2025-11-27T08:09:45+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" + "reference": "65a8bc82080447fae78373aa10f8d13b38338977" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977", "shasum": "" }, "require": { @@ -8258,7 +8511,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" }, "funding": [ { @@ -8269,25 +8522,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-27T08:32:26+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/uid", - "version": "v7.3.1", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb" + "reference": "2498e9f81b7baa206f44de583f2f48350b90142c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/a69f69f3159b852651a6bf45a9fdd149520525bb", - "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb", + "url": "https://api.github.com/repos/symfony/uid/zipball/2498e9f81b7baa206f44de583f2f48350b90142c", + "reference": "2498e9f81b7baa206f44de583f2f48350b90142c", "shasum": "" }, "require": { @@ -8295,7 +8552,7 @@ "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -8332,7 +8589,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.3.1" + "source": "https://github.com/symfony/uid/tree/v7.4.0" }, "funding": [ { @@ -8343,25 +8600,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T19:55:54+00:00" + "time": "2025-09-25T11:02:55+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.3.3", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f" + "reference": "41fd6c4ae28c38b294b42af6db61446594a0dece" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", - "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/41fd6c4ae28c38b294b42af6db61446594a0dece", + "reference": "41fd6c4ae28c38b294b42af6db61446594a0dece", "shasum": "" }, "require": { @@ -8373,10 +8634,10 @@ "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/uid": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", "twig/twig": "^3.12" }, "bin": [ @@ -8415,7 +8676,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.3" + "source": "https://github.com/symfony/var-dumper/tree/v7.4.0" }, "funding": [ { @@ -8435,7 +8696,7 @@ "type": "tidelift" } ], - "time": "2025-08-13T11:49:31+00:00" + "time": "2025-10-27T20:36:44+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -8494,16 +8755,16 @@ }, { "name": "twig/twig", - "version": "v3.21.1", + "version": "v3.22.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d" + "reference": "1de2ec1fc43ab58a4b7e80b214b96bfc895750f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/285123877d4dd97dd7c11842ac5fb7e86e60d81d", - "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/1de2ec1fc43ab58a4b7e80b214b96bfc895750f3", + "reference": "1de2ec1fc43ab58a4b7e80b214b96bfc895750f3", "shasum": "" }, "require": { @@ -8557,7 +8818,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.21.1" + "source": "https://github.com/twigphp/Twig/tree/v3.22.1" }, "funding": [ { @@ -8569,7 +8830,7 @@ "type": "tidelift" } ], - "time": "2025-05-03T07:21:55+00:00" + "time": "2025-11-16T16:01:12+00:00" }, { "name": "vlucas/phpdotenv", @@ -8728,87 +8989,29 @@ } ], "time": "2024-11-21T01:49:47+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" } ], "packages-dev": [ { "name": "barryvdh/laravel-debugbar", - "version": "v3.16.0", + "version": "v3.16.1", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "f265cf5e38577d42311f1a90d619bcd3740bea23" + "reference": "21b2c6fce05453efd4bceb34f9fddaa1cdb44090" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/f265cf5e38577d42311f1a90d619bcd3740bea23", - "reference": "f265cf5e38577d42311f1a90d619bcd3740bea23", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/21b2c6fce05453efd4bceb34f9fddaa1cdb44090", + "reference": "21b2c6fce05453efd4bceb34f9fddaa1cdb44090", "shasum": "" }, "require": { - "illuminate/routing": "^9|^10|^11|^12", - "illuminate/session": "^9|^10|^11|^12", - "illuminate/support": "^9|^10|^11|^12", + "illuminate/routing": "^10|^11|^12", + "illuminate/session": "^10|^11|^12", + "illuminate/support": "^10|^11|^12", "php": "^8.1", - "php-debugbar/php-debugbar": "~2.2.0", + "php-debugbar/php-debugbar": "^2.2.4", "symfony/finder": "^6|^7" }, "require-dev": { @@ -8860,7 +9063,7 @@ ], "support": { "issues": "https://github.com/barryvdh/laravel-debugbar/issues", - "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.16.0" + "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.16.1" }, "funding": [ { @@ -8872,7 +9075,7 @@ "type": "github" } ], - "time": "2025-07-14T11:56:43+00:00" + "time": "2025-11-19T08:31:25+00:00" }, { "name": "barryvdh/laravel-ide-helper", @@ -9115,22 +9318,22 @@ }, { "name": "composer/class-map-generator", - "version": "1.6.2", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/composer/class-map-generator.git", - "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076" + "reference": "2373419b7709815ed323ebf18c3c72d03ff4a8a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/class-map-generator/zipball/ba9f089655d4cdd64e762a6044f411ccdaec0076", - "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/2373419b7709815ed323ebf18c3c72d03ff4a8a6", + "reference": "2373419b7709815ed323ebf18c3c72d03ff4a8a6", "shasum": "" }, "require": { "composer/pcre": "^2.1 || ^3.1", "php": "^7.2 || ^8.0", - "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7 || ^8" }, "require-dev": { "phpstan/phpstan": "^1.12 || ^2", @@ -9138,7 +9341,7 @@ "phpstan/phpstan-phpunit": "^1 || ^2", "phpstan/phpstan-strict-rules": "^1.1 || ^2", "phpunit/phpunit": "^8", - "symfony/filesystem": "^5.4 || ^6" + "symfony/filesystem": "^5.4 || ^6 || ^7 || ^8" }, "type": "library", "extra": { @@ -9168,7 +9371,7 @@ ], "support": { "issues": "https://github.com/composer/class-map-generator/issues", - "source": "https://github.com/composer/class-map-generator/tree/1.6.2" + "source": "https://github.com/composer/class-map-generator/tree/1.7.0" }, "funding": [ { @@ -9180,7 +9383,7 @@ "type": "github" } ], - "time": "2025-08-20T18:52:43+00:00" + "time": "2025-11-19T10:41:15+00:00" }, { "name": "doctrine/deprecations", @@ -9703,16 +9906,16 @@ }, { "name": "laravel/pint", - "version": "v1.25.1", + "version": "v1.26.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9" + "reference": "69dcca060ecb15e4b564af63d1f642c81a241d6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/5016e263f95d97670d71b9a987bd8996ade6d8d9", - "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9", + "url": "https://api.github.com/repos/laravel/pint/zipball/69dcca060ecb15e4b564af63d1f642c81a241d6f", + "reference": "69dcca060ecb15e4b564af63d1f642c81a241d6f", "shasum": "" }, "require": { @@ -9723,13 +9926,13 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.87.2", - "illuminate/view": "^11.46.0", - "larastan/larastan": "^3.7.1", - "laravel-zero/framework": "^11.45.0", + "friendsofphp/php-cs-fixer": "^3.90.0", + "illuminate/view": "^12.40.1", + "larastan/larastan": "^3.8.0", + "laravel-zero/framework": "^12.0.4", "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^2.3.1", - "pestphp/pest": "^2.36.0" + "nunomaduro/termwind": "^2.3.3", + "pestphp/pest": "^3.8.4" }, "bin": [ "builds/pint" @@ -9755,6 +9958,7 @@ "description": "An opinionated code formatter for PHP.", "homepage": "https://laravel.com", "keywords": [ + "dev", "format", "formatter", "lint", @@ -9765,20 +9969,20 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-09-19T02:57:12+00:00" + "time": "2025-11-25T21:15:52+00:00" }, { "name": "laravel/sail", - "version": "v1.45.0", + "version": "v1.49.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "019a2933ff4a9199f098d4259713f9bc266a874e" + "reference": "070c7f34ca8dbece4350fbfe0bab580047dfacc7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/019a2933ff4a9199f098d4259713f9bc266a874e", - "reference": "019a2933ff4a9199f098d4259713f9bc266a874e", + "url": "https://api.github.com/repos/laravel/sail/zipball/070c7f34ca8dbece4350fbfe0bab580047dfacc7", + "reference": "070c7f34ca8dbece4350fbfe0bab580047dfacc7", "shasum": "" }, "require": { @@ -9791,7 +9995,7 @@ }, "require-dev": { "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", - "phpstan/phpstan": "^1.10" + "phpstan/phpstan": "^2.0" }, "bin": [ "bin/sail" @@ -9828,20 +10032,20 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2025-08-25T19:28:31+00:00" + "time": "2025-11-25T21:15:57+00:00" }, { "name": "laravel/tinker", - "version": "v2.10.1", + "version": "v2.10.2", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3" + "reference": "3bcb5f62d6f837e0f093a601e26badafb127bd4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/22177cc71807d38f2810c6204d8f7183d88a57d3", - "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3", + "url": "https://api.github.com/repos/laravel/tinker/zipball/3bcb5f62d6f837e0f093a601e26badafb127bd4c", + "reference": "3bcb5f62d6f837e0f093a601e26badafb127bd4c", "shasum": "" }, "require": { @@ -9892,9 +10096,9 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.10.1" + "source": "https://github.com/laravel/tinker/tree/v2.10.2" }, - "time": "2025-01-27T14:24:01+00:00" + "time": "2025-11-20T16:29:12+00:00" }, { "name": "larswiegers/laravel-translations-checker", @@ -10168,16 +10372,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.1", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", "shasum": "" }, "require": { @@ -10220,9 +10424,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" }, - "time": "2025-08-13T20:13:15+00:00" + "time": "2025-10-21T19:32:17+00:00" }, { "name": "nunomaduro/collision", @@ -10957,16 +11161,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.6.3", + "version": "5.6.5", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9" + "reference": "90614c73d3800e187615e2dd236ad0e2a01bf761" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94f8051919d1b0369a6bcc7931d679a511c03fe9", - "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/90614c73d3800e187615e2dd236ad0e2a01bf761", + "reference": "90614c73d3800e187615e2dd236ad0e2a01bf761", "shasum": "" }, "require": { @@ -11015,22 +11219,22 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.3" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.5" }, - "time": "2025-08-01T19:43:32+00:00" + "time": "2025-11-27T19:50:05+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.10.0", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195", "shasum": "" }, "require": { @@ -11073,9 +11277,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0" }, - "time": "2024-11-09T15:12:26+00:00" + "time": "2025-11-21T15:09:14+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -11126,16 +11330,11 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.29", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "0835c625a38ac6484f050077116b6668bc3ab57d" - }, + "version": "1.12.32", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/0835c625a38ac6484f050077116b6668bc3ab57d", - "reference": "0835c625a38ac6484f050077116b6668bc3ab57d", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/2770dcdf5078d0b0d53f94317e06affe88419aa8", + "reference": "2770dcdf5078d0b0d53f94317e06affe88419aa8", "shasum": "" }, "require": { @@ -11180,7 +11379,7 @@ "type": "github" } ], - "time": "2025-09-16T08:46:57+00:00" + "time": "2025-09-30T10:16:31+00:00" }, { "name": "phpunit/php-code-coverage", @@ -11606,16 +11805,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.10", + "version": "v0.12.15", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22" + "reference": "38953bc71491c838fcb6ebcbdc41ab7483cd549c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/6e80abe6f2257121f1eb9a4c55bf29d921025b22", - "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/38953bc71491c838fcb6ebcbdc41ab7483cd549c", + "reference": "38953bc71491c838fcb6ebcbdc41ab7483cd549c", "shasum": "" }, "require": { @@ -11630,11 +11829,12 @@ "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.2" + "bamarni/composer-bin-plugin": "^1.2", + "composer/class-map-generator": "^1.6" }, "suggest": { + "composer/class-map-generator": "Improved tab completion performance with better class discovery.", "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", - "ext-pdo-sqlite": "The doc command requires SQLite to work.", "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." }, "bin": [ @@ -11678,9 +11878,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.10" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.15" }, - "time": "2025-08-04T12:39:37+00:00" + "time": "2025-11-28T00:00:14+00:00" }, { "name": "rector/rector", @@ -11747,18 +11947,18 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "f48b3e601515b060334744b4b495f0d6b3cc2e6b" + "reference": "d4ee198ff9c57b012547e180eb99ffb5c4911be8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/f48b3e601515b060334744b4b495f0d6b3cc2e6b", - "reference": "f48b3e601515b060334744b4b495f0d6b3cc2e6b", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/d4ee198ff9c57b012547e180eb99ffb5c4911be8", + "reference": "d4ee198ff9c57b012547e180eb99ffb5c4911be8", "shasum": "" }, "conflict": { "3f/pygmentize": "<1.2", "adaptcms/adaptcms": "<=1.3", - "admidio/admidio": "<4.3.12", + "admidio/admidio": "<=4.3.16", "adodb/adodb-php": "<=5.22.9", "aheinze/cockpit": "<2.2", "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", @@ -11771,6 +11971,7 @@ "akaunting/akaunting": "<2.1.13", "akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53", "alextselegidis/easyappointments": "<1.5.2.0-beta1", + "alt-design/alt-redirect": "<1.6.4", "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", "amazing/media2click": ">=1,<1.3.3", "ameos/ameos_tarteaucitron": "<1.2.23", @@ -11794,22 +11995,22 @@ "athlon1600/php-proxy-app": "<=3", "athlon1600/youtube-downloader": "<=4", "austintoddj/canvas": "<=3.4.2", - "auth0/auth0-php": ">=8.0.0.0-beta1,<8.14", - "auth0/login": "<7.17", - "auth0/symfony": "<5.4", - "auth0/wordpress": "<5.3", + "auth0/auth0-php": ">=3.3,<=8.16", + "auth0/login": "<=7.18", + "auth0/symfony": "<=5.4.1", + "auth0/wordpress": "<=5.3", "automad/automad": "<2.0.0.0-alpha5", "automattic/jetpack": "<9.8", "awesome-support/awesome-support": "<=6.0.7", "aws/aws-sdk-php": "<3.288.1", "azuracast/azuracast": "<0.18.3", "b13/seo_basics": "<0.8.2", - "backdrop/backdrop": "<1.27.3|>=1.28,<1.28.2", + "backdrop/backdrop": "<=1.32", "backpack/crud": "<3.4.9", "backpack/filemanager": "<2.0.2|>=3,<3.0.9", "bacula-web/bacula-web": "<9.7.1", "badaso/core": "<=2.9.11", - "bagisto/bagisto": "<2.1", + "bagisto/bagisto": "<=2.3.7", "barrelstrength/sprout-base-email": "<1.2.7", "barrelstrength/sprout-forms": "<3.9", "barryvdh/laravel-translation-manager": "<0.6.8", @@ -11860,12 +12061,14 @@ "clickstorm/cs-seo": ">=6,<6.8|>=7,<7.5|>=8,<8.4|>=9,<9.3", "co-stack/fal_sftp": "<0.2.6", "cockpit-hq/cockpit": "<2.11.4", + "code16/sharp": "<9.11.1", "codeception/codeception": "<3.1.3|>=4,<4.1.22", "codeigniter/framework": "<3.1.10", "codeigniter4/framework": "<4.6.2", "codeigniter4/shield": "<1.0.0.0-beta8", "codiad/codiad": "<=2.8.4", "codingms/additional-tca": ">=1.7,<1.15.17|>=1.16,<1.16.9", + "codingms/modules": "<4.3.11|>=5,<5.7.4|>=6,<6.4.2|>=7,<7.5.5", "commerceteam/commerce": ">=0.9.6,<0.9.9", "components/jquery": ">=1.0.3,<3.5", "composer/composer": "<1.10.27|>=2,<2.2.24|>=2.3,<2.7.7", @@ -11875,7 +12078,7 @@ "contao/comments-bundle": ">=2,<4.13.40|>=5.0.0.0-RC1-dev,<5.3.4", "contao/contao": ">=3,<3.5.37|>=4,<4.4.56|>=4.5,<4.13.56|>=5,<5.3.38|>=5.4.0.0-RC1-dev,<5.6.1", "contao/core": "<3.5.39", - "contao/core-bundle": "<4.13.56|>=5,<5.3.38|>=5.4,<5.6.1", + "contao/core-bundle": "<4.13.57|>=5,<5.3.42|>=5.4,<5.6.5", "contao/listing-bundle": ">=3,<=3.5.30|>=4,<4.4.8", "contao/managed-edition": "<=1.5", "corveda/phpsandbox": "<1.3.5", @@ -11899,6 +12102,7 @@ "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1|>=7,<7.4", "desperado/xml-bundle": "<=0.1.7", "dev-lancer/minecraft-motd-parser": "<=1.0.5", + "devcode-it/openstamanager": "<=2.9.4", "devgroup/dotplant": "<2020.09.14-dev", "digimix/wp-svg-upload": "<=1", "directmailteam/direct-mail": "<6.0.3|>=7,<7.0.3|>=8,<9.5.2", @@ -11914,33 +12118,45 @@ "doctrine/mongodb-odm": "<1.0.2", "doctrine/mongodb-odm-bundle": "<3.0.1", "doctrine/orm": ">=1,<1.2.4|>=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", - "dolibarr/dolibarr": "<=21.0.2", + "dolibarr/dolibarr": "<21.0.3", "dompdf/dompdf": "<2.0.4", "doublethreedigital/guest-entries": "<3.1.2", + "drupal-pattern-lab/unified-twig-extensions": "<=0.1", + "drupal/access_code": "<2.0.5", + "drupal/acquia_dam": "<1.1.5", "drupal/admin_audit_trail": "<1.0.5", "drupal/ai": "<1.0.5", "drupal/alogin": "<2.0.6", "drupal/cache_utility": "<1.2.1", + "drupal/civictheme": "<1.12", "drupal/commerce_alphabank_redirect": "<1.0.3", "drupal/commerce_eurobank_redirect": "<2.1.1", "drupal/config_split": "<1.10|>=2,<2.0.2", - "drupal/core": ">=6,<6.38|>=7,<7.102|>=8,<10.3.14|>=10.4,<10.4.5|>=11,<11.0.13|>=11.1,<11.1.5", + "drupal/core": ">=6,<6.38|>=7,<7.102|>=8,<10.4.9|>=10.5,<10.5.6|>=11,<11.1.9|>=11.2,<11.2.8", "drupal/core-recommended": ">=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", + "drupal/currency": "<3.5", "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", + "drupal/email_tfa": "<2.0.6", "drupal/formatter_suite": "<2.1", "drupal/gdpr": "<3.0.1|>=3.1,<3.1.2", "drupal/google_tag": "<1.8|>=2,<2.0.8", "drupal/ignition": "<1.0.4", + "drupal/json_field": "<1.5", "drupal/lightgallery": "<1.6", "drupal/link_field_display_mode_formatter": "<1.6", "drupal/matomo": "<1.24", "drupal/oauth2_client": "<4.1.3", "drupal/oauth2_server": "<2.1", "drupal/obfuscate": "<2.0.1", + "drupal/plausible_tracking": "<1.0.2", "drupal/quick_node_block": "<2", "drupal/rapidoc_elements_field_formatter": "<1.0.1", + "drupal/reverse_proxy_header": "<1.1.2", + "drupal/simple_multistep": "<2", + "drupal/simple_oauth": ">=6,<6.0.7", "drupal/spamspan": "<3.2.1", "drupal/tfa": "<1.10", + "drupal/umami_analytics": "<1.0.1", "duncanmcclean/guest-entries": "<3.1.2", "dweeves/magmi": "<=0.7.24", "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.1.2", @@ -11965,7 +12181,7 @@ "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1-dev", "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1-dev|>=5.4,<5.4.11.1-dev|>=2017.12,<2017.12.0.1-dev", "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", - "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.38|>=3.3,<3.3.39", + "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.39|>=3.3,<3.3.39", "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1|>=5.3.0.0-beta1,<5.3.5", "ezsystems/ezplatform-graphql": ">=1.0.0.0-RC1-dev,<1.0.13|>=2.0.0.0-beta1,<2.3.12", "ezsystems/ezplatform-http-cache": "<2.3.16", @@ -12027,9 +12243,9 @@ "genix/cms": "<=1.1.11", "georgringer/news": "<1.3.3", "geshi/geshi": "<=1.0.9.1", - "getformwork/formwork": "<1.13.1|>=2.0.0.0-beta1,<2.0.0.0-beta4", - "getgrav/grav": "<1.7.46", - "getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1", + "getformwork/formwork": "<2.2", + "getgrav/grav": "<1.11.0.0-beta1", + "getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1|>=5,<5.1.4", "getkirby/kirby": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1", "getkirby/panel": "<2.5.14", "getkirby/starterkit": "<=3.7.0.2", @@ -12040,6 +12256,7 @@ "gogentooss/samlbase": "<1.2.7", "google/protobuf": "<3.4", "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", + "gp247/core": "<1.1.24", "gree/jose": "<2.2.1", "gregwar/rst": "<1.0.3", "grumpydictator/firefly-iii": "<6.1.17", @@ -12058,15 +12275,15 @@ "hov/jobfair": "<1.0.13|>=2,<2.0.2", "httpsoft/http-message": "<1.0.12", "hyn/multi-tenant": ">=5.6,<5.7.2", - "ibexa/admin-ui": ">=4.2,<4.2.3|>=4.6,<4.6.21", + "ibexa/admin-ui": ">=4.2,<4.2.3|>=4.6,<4.6.25|>=5,<5.0.3", "ibexa/admin-ui-assets": ">=4.6.0.0-alpha1,<4.6.21", "ibexa/core": ">=4,<4.0.7|>=4.1,<4.1.4|>=4.2,<4.2.3|>=4.5,<4.5.6|>=4.6,<4.6.2", - "ibexa/fieldtype-richtext": ">=4.6,<4.6.21", + "ibexa/fieldtype-richtext": ">=4.6,<4.6.25|>=5,<5.0.3", "ibexa/graphql": ">=2.5,<2.5.31|>=3.3,<3.3.28|>=4.2,<4.2.3", "ibexa/http-cache": ">=4.6,<4.6.14", "ibexa/post-install": "<1.0.16|>=4.6,<4.6.14", "ibexa/solr": ">=4.5,<4.5.4", - "ibexa/user": ">=4,<4.4.3", + "ibexa/user": ">=4,<4.4.3|>=5,<5.0.3", "icecoder/icecoder": "<=8.1", "idno/known": "<=1.3.1", "ilicmiljan/secure-props": ">=1.2,<1.2.2", @@ -12102,7 +12319,7 @@ "joomla/archive": "<1.1.12|>=2,<2.0.1", "joomla/database": ">=1,<2.2|>=3,<3.4", "joomla/filesystem": "<1.6.2|>=2,<2.0.1", - "joomla/filter": "<1.4.4|>=2,<2.0.1", + "joomla/filter": "<2.0.6|>=3,<3.0.5|==4", "joomla/framework": "<1.5.7|>=2.5.4,<=3.8.12", "joomla/input": ">=2,<2.0.2", "joomla/joomla-cms": "<3.9.12|>=4,<4.4.13|>=5,<5.2.6", @@ -12163,7 +12380,7 @@ "luyadev/yii-helpers": "<1.2.1", "macropay-solutions/laravel-crud-wizard-free": "<3.4.17", "maestroerror/php-heic-to-jpg": "<1.0.5", - "magento/community-edition": "<=2.4.5.0-patch14|==2.4.6|>=2.4.6.0-patch1,<=2.4.6.0-patch12|>=2.4.7.0-beta1,<=2.4.7.0-patch7|>=2.4.8.0-beta1,<=2.4.8.0-patch2|>=2.4.9.0-alpha1,<=2.4.9.0-alpha2|==2.4.9", + "magento/community-edition": "<2.4.6.0-patch13|>=2.4.7.0-beta1,<2.4.7.0-patch8|>=2.4.8.0-beta1,<2.4.8.0-patch3|>=2.4.9.0-alpha1,<2.4.9.0-alpha3|==2.4.9", "magento/core": "<=1.9.4.5", "magento/magento1ce": "<1.9.4.3-dev", "magento/magento1ee": ">=1,<1.14.4.3-dev", @@ -12174,24 +12391,27 @@ "maikuolan/phpmussel": ">=1,<1.6", "mainwp/mainwp": "<=4.4.3.3", "manogi/nova-tiptap": "<=3.2.6", - "mantisbt/mantisbt": "<=2.26.3", + "mantisbt/mantisbt": "<2.27.2", "marcwillmann/turn": "<0.3.3", "marshmallow/nova-tiptap": "<5.7", "matomo/matomo": "<1.11", "matyhtf/framework": "<3.0.6", - "mautic/core": "<5.2.8|>=6.0.0.0-alpha,<6.0.5", + "mautic/core": "<5.2.9|>=6,<6.0.7", "mautic/core-lib": ">=1.0.0.0-beta,<4.4.13|>=5.0.0.0-alpha,<5.1.1", + "mautic/grapes-js-builder-bundle": ">=4,<4.4.18|>=5,<5.2.9|>=6,<6.0.7", "maximebf/debugbar": "<1.19", "mdanter/ecc": "<2", "mediawiki/abuse-filter": "<1.39.9|>=1.40,<1.41.3|>=1.42,<1.42.2", - "mediawiki/cargo": "<3.6.1", + "mediawiki/cargo": "<3.8.3", "mediawiki/core": "<1.39.5|==1.40", "mediawiki/data-transfer": ">=1.39,<1.39.11|>=1.41,<1.41.3|>=1.42,<1.42.2", "mediawiki/matomo": "<2.4.3", "mediawiki/semantic-media-wiki": "<4.0.2", "mehrwert/phpmyadmin": "<3.2", "melisplatform/melis-asset-manager": "<5.0.1", - "melisplatform/melis-cms": "<5.0.1", + "melisplatform/melis-cms": "<5.3.4", + "melisplatform/melis-cms-slider": "<5.3.1", + "melisplatform/melis-core": "<5.3.11", "melisplatform/melis-front": "<5.0.1", "mezzio/mezzio-swoole": "<3.7|>=4,<4.3", "mgallegos/laravel-jqgrid": "<=1.3", @@ -12206,17 +12426,17 @@ "modx/revolution": "<=3.1", "mojo42/jirafeau": "<4.4", "mongodb/mongodb": ">=1,<1.9.2", + "mongodb/mongodb-extension": "<1.21.2", "monolog/monolog": ">=1.8,<1.12", - "moodle/moodle": "<4.3.12|>=4.4,<4.4.8|>=4.5.0.0-beta,<4.5.4", + "moodle/moodle": "<4.4.11|>=4.5.0.0-beta,<4.5.7|>=5.0.0.0-beta,<5.0.3", "moonshine/moonshine": "<=3.12.5", "mos/cimage": "<0.7.19", "movim/moxl": ">=0.8,<=0.10", "movingbytes/social-network": "<=1.2.1", "mpdf/mpdf": "<=7.1.7", - "munkireport/comment": "<4.1", + "munkireport/comment": "<4", "munkireport/managedinstalls": "<2.6", "munkireport/munki_facts": "<1.5", - "munkireport/munkireport": ">=2.5.3,<5.6.3", "munkireport/reportdata": "<3.5", "munkireport/softwareupdate": "<1.6", "mustache/mustache": ">=2,<2.14.1", @@ -12242,6 +12462,7 @@ "notrinos/notrinos-erp": "<=0.7", "noumo/easyii": "<=0.9", "novaksolutions/infusionsoft-php-sdk": "<1", + "novosga/novosga": "<=2.2.12", "nukeviet/nukeviet": "<4.5.02", "nyholm/psr7": "<1.6.1", "nystudio107/craft-seomatic": "<3.4.12", @@ -12256,10 +12477,10 @@ "omeka/omeka-s": "<4.0.3", "onelogin/php-saml": "<2.10.4", "oneup/uploader-bundle": ">=1,<1.9.3|>=2,<2.1.5", - "open-web-analytics/open-web-analytics": "<1.7.4", + "open-web-analytics/open-web-analytics": "<1.8.1", "opencart/opencart": ">=0", "openid/php-openid": "<2.3", - "openmage/magento-lts": "<20.12.3", + "openmage/magento-lts": "<20.16", "opensolutions/vimbadmin": "<=3.0.15", "opensource-workshop/connect-cms": "<1.8.7|>=2,<2.4.7", "orchid/platform": ">=8,<14.43", @@ -12300,11 +12521,12 @@ "phpmailer/phpmailer": "<6.5", "phpmussel/phpmussel": ">=1,<1.6", "phpmyadmin/phpmyadmin": "<5.2.2", - "phpmyfaq/phpmyfaq": "<3.2.5|==3.2.5|>=3.2.10,<=4.0.1", + "phpmyfaq/phpmyfaq": "<=4.0.13", "phpoffice/common": "<0.2.9", "phpoffice/math": "<=0.2", "phpoffice/phpexcel": "<=1.8.2", "phpoffice/phpspreadsheet": "<1.30|>=2,<2.1.12|>=2.2,<2.4|>=3,<3.10|>=4,<5", + "phppgadmin/phppgadmin": "<=7.13", "phpseclib/phpseclib": "<2.0.47|>=3,<3.0.36", "phpservermon/phpservermon": "<3.6", "phpsysinfo/phpsysinfo": "<3.4.3", @@ -12335,12 +12557,13 @@ "prestashop/gamification": "<2.3.2", "prestashop/prestashop": "<8.2.3", "prestashop/productcomments": "<5.0.2", + "prestashop/ps_checkout": "<4.4.1|>=5,<5.0.5", "prestashop/ps_contactinfo": "<=3.3.2", "prestashop/ps_emailsubscription": "<2.6.1", "prestashop/ps_facetedsearch": "<3.4.1", "prestashop/ps_linklist": "<3.1", - "privatebin/privatebin": "<1.4|>=1.5,<1.7.4", - "processwire/processwire": "<=3.0.229", + "privatebin/privatebin": "<1.4|>=1.5,<1.7.4|>=1.7.7,<2.0.3", + "processwire/processwire": "<=3.0.246", "propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7", "propel/propel1": ">=1,<=1.7.1", "pterodactyl/panel": "<=1.11.10", @@ -12361,7 +12584,7 @@ "rap2hpoutre/laravel-log-viewer": "<0.13", "react/http": ">=0.7,<1.9", "really-simple-plugins/complianz-gdpr": "<6.4.2", - "redaxo/source": "<5.18.3", + "redaxo/source": "<5.20.1", "remdex/livehelperchat": "<4.29", "renolit/reint-downloadmanager": "<4.0.2|>=5,<5.0.1", "reportico-web/reportico": "<=8.1", @@ -12372,7 +12595,7 @@ "roundcube/roundcubemail": "<1.5.10|>=1.6,<1.6.11", "rudloff/alltube": "<3.0.3", "rudloff/rtmpdump-bin": "<=2.3.1", - "s-cart/core": "<6.9", + "s-cart/core": "<=9.0.5", "s-cart/s-cart": "<6.9", "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", "sabre/dav": ">=1.6,<1.7.11|>=1.8,<1.8.9", @@ -12383,8 +12606,8 @@ "setasign/fpdi": "<2.6.4", "sfroemken/url_redirect": "<=1.2.1", "sheng/yiicms": "<1.2.1", - "shopware/core": "<6.5.8.18-dev|>=6.6,<6.6.10.3-dev|>=6.7,<6.7.2.1-dev", - "shopware/platform": "<=6.6.10.4|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev", + "shopware/core": "<6.6.10.9-dev|>=6.7,<6.7.4.1-dev", + "shopware/platform": "<6.6.10.7-dev|>=6.7,<6.7.3.1-dev", "shopware/production": "<=6.3.5.2", "shopware/shopware": "<=5.7.17|>=6.7,<6.7.2.1-dev", "shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev", @@ -12428,7 +12651,7 @@ "slim/slim": "<2.6", "slub/slub-events": "<3.0.3", "smarty/smarty": "<4.5.3|>=5,<5.1.1", - "snipe/snipe-it": "<8.1.18", + "snipe/snipe-it": "<=8.3.4", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", "solspace/craft-freeform": ">=5,<5.10.16", @@ -12437,14 +12660,16 @@ "spatie/image-optimizer": "<1.7.3", "spencer14420/sp-php-email-handler": "<1", "spipu/html2pdf": "<5.2.8", + "spiral/roadrunner": "<2025.1", "spoon/library": "<1.4.1", "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", "ssddanbrown/bookstack": "<24.05.1", - "starcitizentools/citizen-skin": ">=1.9.4,<3.4", + "starcitizentools/citizen-skin": ">=1.9.4,<3.9", "starcitizentools/short-description": ">=4,<4.0.1", "starcitizentools/tabber-neue": ">=1.9.1,<2.7.2|>=3,<3.1.1", - "statamic/cms": "<=5.16", + "starcitizenwiki/embedvideo": "<=4", + "statamic/cms": "<=5.22", "stormpath/sdk": "<9.9.99", "studio-42/elfinder": "<=2.1.64", "studiomitte/friendlycaptcha": "<0.1.4", @@ -12475,7 +12700,7 @@ "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<5.3.15|>=5.4.3,<5.4.4|>=6.0.3,<6.0.4", "symfony/http-client": ">=4.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", - "symfony/http-foundation": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7", + "symfony/http-foundation": "<5.4.50|>=6,<6.4.29|>=7,<7.3.7", "symfony/http-kernel": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", "symfony/maker-bundle": ">=1.27,<1.29.2|>=1.30,<1.31.1", @@ -12494,7 +12719,7 @@ "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", - "symfony/symfony": "<5.4.47|>=6,<6.4.15|>=7,<7.1.8", + "symfony/symfony": "<5.4.50|>=6,<6.4.29|>=7,<7.3.7", "symfony/translation": ">=2,<2.0.17", "symfony/twig-bridge": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8", "symfony/ux-autocomplete": "<2.11.2", @@ -12518,7 +12743,7 @@ "thelia/thelia": ">=2.1,<2.1.3", "theonedemon/phpwhois": "<=4.2.5", "thinkcmf/thinkcmf": "<6.0.8", - "thorsten/phpmyfaq": "<=4.0.1", + "thorsten/phpmyfaq": "<=4.0.13", "tikiwiki/tiki-manager": "<=17.1", "timber/timber": ">=0.16.6,<1.23.1|>=1.24,<1.24.1|>=2,<2.1", "tinymce/tinymce": "<7.2", @@ -12529,12 +12754,12 @@ "topthink/framework": "<6.0.17|>=6.1,<=8.0.4", "topthink/think": "<=6.1.1", "topthink/thinkphp": "<=3.2.3|>=6.1.3,<=8.0.4", - "torrentpier/torrentpier": "<=2.4.3", + "torrentpier/torrentpier": "<=2.8.8", "tpwd/ke_search": "<4.0.3|>=4.1,<4.6.6|>=5,<5.0.2", "tribalsystems/zenario": "<=9.7.61188", "truckersmp/phpwhois": "<=4.3.1", "ttskch/pagination-service-provider": "<1", - "twbs/bootstrap": "<3.4.1|>=4,<=4.6.2", + "twbs/bootstrap": "<3.4.1|>=4,<4.3.1", "twig/twig": "<3.11.2|>=3.12,<3.14.1|>=3.16,<3.19", "typo3/cms": "<9.5.29|>=10,<10.4.35|>=11,<11.5.23|>=12,<12.2", "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", @@ -12598,6 +12823,7 @@ "webklex/laravel-imap": "<5.3", "webklex/php-imap": "<5.3", "webpa/webpa": "<3.1.2", + "webreinvent/vaahcms": "<=2.3.1", "wikibase/wikibase": "<=1.39.3", "wikimedia/parsoid": "<0.12.2", "willdurand/js-translation-bundle": "<2.1.1", @@ -12710,7 +12936,7 @@ "type": "tidelift" } ], - "time": "2025-09-19T18:07:33+00:00" + "time": "2025-12-02T22:05:39+00:00" }, { "name": "sebastian/cli-parser", @@ -13160,16 +13386,16 @@ }, { "name": "sebastian/exporter", - "version": "5.1.2", + "version": "5.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + "reference": "0735b90f4da94969541dac1da743446e276defa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", + "reference": "0735b90f4da94969541dac1da743446e276defa6", "shasum": "" }, "require": { @@ -13178,7 +13404,7 @@ "sebastian/recursion-context": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { @@ -13226,15 +13452,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-03-02T07:17:12+00:00" + "time": "2025-09-24T06:09:11+00:00" }, { "name": "sebastian/global-state", @@ -13655,28 +13893,28 @@ }, { "name": "symfony/yaml", - "version": "v7.3.3", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "d4f4a66866fe2451f61296924767280ab5732d9d" + "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d", - "reference": "d4f4a66866fe2451f61296924767280ab5732d9d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/6c84a4b55aee4cd02034d1c528e83f69ddf63810", + "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -13707,7 +13945,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.3" + "source": "https://github.com/symfony/yaml/tree/v7.4.0" }, "funding": [ { @@ -13727,7 +13965,7 @@ "type": "tidelift" } ], - "time": "2025-08-27T11:34:33+00:00" + "time": "2025-11-16T10:14:42+00:00" }, { "name": "ta-tikoma/phpunit-architecture-test", @@ -13790,16 +14028,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -13828,7 +14066,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -13836,7 +14074,65 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^7.2 || ^8.0" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.12.1" + }, + "time": "2025-10-29T15:56:20+00:00" } ], "aliases": [], @@ -13856,5 +14152,5 @@ "ext-zip": "*" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/config/model-states.php b/config/model-states.php new file mode 100644 index 00000000..57ef8cb2 --- /dev/null +++ b/config/model-states.php @@ -0,0 +1,10 @@ + Spatie\ModelStates\DefaultTransition::class, + +]; diff --git a/legacy/lib/forms/FormTemplater.php b/legacy/lib/forms/FormTemplater.php index aa3773cd..aa75eeb8 100644 --- a/legacy/lib/forms/FormTemplater.php +++ b/legacy/lib/forms/FormTemplater.php @@ -186,44 +186,6 @@ public static function jsonEditable(string $key, $function = 'edit', $type = '', return '
'.(($values_out) ? $values_out[$value] : $value).'
'; } - public function getWikiLinkForm($name, $value = '', $width = 12, $placeholder = '', $label_text = '', $validator = [], $linkPrefix = ''): string - { - $unique_id = htmlspecialchars($this->getUniqueIdFromName($name)); - $editable = $this->checkWritePermission($name); - $out = ''; - - if ($editable) { - $additonal_array = $this->constructValidatorStrings($validator); - $type = 'text'; - if (isset($validator['email'])) { - $type = 'email'; - } - $additonal_str = implode(' ', $additonal_array); - - $value = htmlspecialchars($value); - if (isset($linkPrefix) && ! empty($linkPrefix)) { - $out .= "
"; - $out .= "
".$linkPrefix.'
'; - } - $out .= ""; - if (isset($linkPrefix) && ! empty($linkPrefix)) { - $out .= '
'; - } - } else { - if (! empty($linkPrefix) && ! empty($value)) { - $out .= "'; - } else { - $out .= "
".$this->getReadOnlyValue($value).'
'; - } - } - - return $this->getOutputWrapped($out, $width, $editable, $name, $unique_id, $label_text, $validator); - } - private function getUniqueIdFromName($name): string { return htmlspecialchars(explode('[', $name)[0].self::$ID_DELIMITER.uniqid('', true)); diff --git a/legacy/lib/forms/FormTemplaterProject.php b/legacy/lib/forms/FormTemplaterProject.php new file mode 100644 index 00000000..64e0c083 --- /dev/null +++ b/legacy/lib/forms/FormTemplaterProject.php @@ -0,0 +1,591 @@ +project = $p; + $this->noValueStringInReadOnly = $noValue; + } + + public function wantToEdit(): void + { + $this->editAction = true; + } + + public static function generateTitelSelectable($hhp_id): array + { + $all_titels = DBConnector::getInstance()->dbFetchAll( + 'haushaltsgruppen', + [DBConnector::FETCH_ASSOC, DBConnector::FETCH_GROUPED], + ['haushaltsgruppen.id', 'haushaltstitel.id', 'gruppen_name', 'titel_name', 'titel_nr', 'type'], + ['haushaltsgruppen.hhp_id' => $hhp_id], + [ + ['type' => 'left', 'table' => 'haushaltstitel', 'on' => ['haushaltsgruppen.id', 'haushaltstitel.hhpgruppen_id']], + ], + ['type' => false, 'haushaltsgruppen.id' => true, 'titel_nr' => true] + ); + $selectable = []; + foreach ($all_titels as /* $g_id => */ $group) { + $ret_group = [ + 'label' => ($group[0]['type'] ? 'Ausgabe' : 'Einnahme').' - '.$group[0]['gruppen_name'], + 'options' => [], + ]; + foreach ($group as $titel) { + $option = []; + $option['label'] = $titel['titel_name']; + $option['subtext'] = $titel['titel_nr']; + $option['value'] = $titel['id']; + // set in parent + $ret_group['options'][] = $option; + } + // set in parent + $selectable['groups'][] = $ret_group; + } + + return $selectable; + } + + public static function generateGremienSelectable(): array + { + $userGremien = AuthHandler::getInstance()->getUserGremien(); + $showAll = AuthHandler::getInstance()->hasGroup('ref-finanzen'); + + foreach (GREMIEN as $groupName => $gremien) { + $group = []; + $group['label'] = $groupName; + + $options = []; + sort($gremien); + foreach ($gremien as $gremiumName) { + if ($showAll || in_array($gremiumName, $userGremien, true)) { + $options[] = ['label' => $gremiumName]; + } + } + $group['options'] = $options; + $selectable['groups'][] = $group; + } + + $selectable['groups'][] = ['label' => 'Sonstige / Keine', 'options' => [['label' => 'Sonstige / Keine Organisation']]]; + + return $selectable; + } + + public static function generateSelectable(array $list): array + { + $selectable = []; + $options = []; + foreach ($list as $key => $item) { + $opt = ['label' => $item]; + if (is_string($key)) { + $opt += ['value' => $key]; + } + $options[] = $opt; + } + // only 1 group + $selectable['groups'][0]['options'] = $options; + + return $selectable; + } + + /** + * generate List + * + * @param array $list + * @param string $label + * @param bool $wrapped + * @param bool $linebreak + * @param string $wrapped_class + * @param string $default_tag + * @param string $width_class + */ + public static function generateListGroup($list, $label = '', $wrapped = true, $linebreak = true, $wrapped_class = 'col-xs-12 form-group', $default_tag = 'div', $width_class = 'col-xs-12'): string + { + if (! is_array($list)) { + $list = [$list]; + } + $out = ''; + if ($label) { + $out .= ''; + } + $out .= '
'; + foreach ($list as $entry) { + if (is_string($entry)) { + $out .= "<$default_tag class=\"list-group-item\">".$entry.""; + } else { + $tag = $entry['tag'] ?? $default_tag; + $text = htmlspecialchars($entry['text'] ?? ''); + $html = $entry['html'] ?? ''; + if (! isset($entry['attr']['class'])) { + $entry['attr']['class'] = 'list-group-item'; + } + $attr = ''; + if (isset($entry['attr'])) { + foreach ($entry['attr'] as $k => $v) { + $attr .= ' '."{$k}=\"{$v}\""; + } + } + $out .= "<{$tag}{$attr}>".$text.$html.""; + } + } + $out .= '
'; + if ($wrapped) { + return '
'.$out.'
'.(($linebreak) ? '
' : ''); + } + + return $out.(($linebreak) ? '
' : ''); + } + + private function getUniqueIdFromName($name): string + { + return htmlspecialchars(explode('[', $name)[0].self::$ID_DELIMITER.uniqid('', true)); + } + + private function checkWritePermission($name): bool + { + return $this->editAction && \Auth::user()->can('update-field', [$this->project, $name]); + } + + private function constructValidatorStrings($validatorArray): array + { + if (! isset($validatorArray) || empty($validatorArray)) { + return []; + } + $ret = ['required']; + + if (isset($validatorArray['min-length'])) { + $ret[] = 'data-minlength='.$validatorArray['min-length']; + } + if (isset($validatorArray['email'])) { + $ret[] = "data-remote='".URIBASE.'validate.php?ajax=1&action=validate.email&nonce='.csrf_token()."'"; + } + if (isset($validatorArray['iban'])) { + $ret[] = "data-validateiban='1'"; + } + + return $ret; + } + + private function getReadOnlyValue($values): string + { + if (is_array($values)) { + if (! empty($values)) { + return implode(',', array_map([$this, 'getReadOnlyValue'], $values)); + } + + return ''.htmlspecialchars($this->noValueStringInReadOnly).''; + } + if (empty($values)) { + return ''.htmlspecialchars($this->noValueStringInReadOnly).''; + } + + return htmlspecialchars($values); + } + + private function getOutputWrapped($content, $width, $editable, $name, $unique_id, $label_text, $validator): string + { + $out = ''; + + if ($name !== '' && $this->checkVisibility($name) === false) { + return ''; + } + + $classes_array = $this->constructWidthClasses($width); + $classes_array[] = 'form-group'; + if (! empty($validator) && $editable) { + $classes_array[] = 'has-feedback'; + } + $classes_str = implode(' ', $classes_array); + $out .= "
"; + if (! empty($label_text)) { + $out .= ""; + } + $out .= $content; + if (! empty($validator) && $editable) { + // $out .= ""; + $out .= "
"; + } + $out .= '
'; + + return $out; + } + + private function checkVisibility($name): bool + { + $state = $this->project->state; + return match ($state::class) { + Draft::class => !($name === "recht" || $name === "recht-additional" || $name === "posten-titel"), + default => true + }; + } + + private function constructWidthClasses($width): array + { + if (! isset($width)) { + return []; + } + $base_cls = ['col-xs-', 'col-xs-', 'col-md-', 'col-lg-']; + $ret_cls = []; + if (! is_array($width)) { + $width = [$width]; + } + for ($i = 0; $i < count($width) && $i < 4; $i++) { + $ret_cls[] = $base_cls[$i].$width[$i]; + } + + return $ret_cls; + } + + public function getMailForm($name, $value = '', $width = 12, $placeholder = '', $label_text = '', $validator = [], $domainSuffix = ''): string + { + $unique_id = htmlspecialchars($this->getUniqueIdFromName($name)); + $editable = $this->checkWritePermission($name); + $out = ''; + + if ($editable) { + $additonal_array = $this->constructValidatorStrings($validator); + $type = 'text'; + if (isset($validator['email'])) { + $type = 'email'; + } + $additonal_str = implode(' ', $additonal_array); + + $value = htmlspecialchars($value); + if (isset($domainSuffix) && ! empty($domainSuffix)) { + $out .= "
"; + } + $out .= ""; + if (isset($domainSuffix) && ! empty($domainSuffix)) { + $out .= "
".$domainSuffix.'
'; + $out .= '
'; + } + } elseif (! empty($domainSuffix)) { + $out .= "'; + } else { + $out .= "
".$this->getReadOnlyValue($value).'
'; + } + + return $this->getOutputWrapped($out, $width, $editable, $name, $unique_id, $label_text, $validator); + } + + public function getFileForm($name, $value = '', $width = 12, $placeholder = '', $label_text = '', $validator = []): string + { + $unique_id = htmlspecialchars($this->getUniqueIdFromName($name)); + + $editable = ! $name || $this->checkWritePermission($name); + $out = "
"; + $out .= ""; + $out .= '
'; + + /* + $myOut = "
"; + $myOut .= ""; + $myOut .= "
"; + if ($file){ + $renameFileFieldName = "formdata[{$layout["id"]}][newFileName]"; + $renameFileFieldNameOrig = $renameFileFieldName; + foreach ($ctrl["suffix"] as $suffix){ + $renameFileFieldName .= "[{$suffix}]"; + $renameFileFieldNameOrig .= "[]"; + } + + echo "
"; + echo "" . $tPattern . ""; + echo " "; + echo "" . newTemplatePattern($ctrl, $file["size"]) . ""; + if (!$ctrl["readonly"]){ + echo ""; + echo ""; + } + echo ""; + echo $oldFieldName; + echo "
"; + }else if ($ctrl["readonly"]){ + echo "
"; + echo "
"; + }else{ + echo $myOut; + }*/ + return $this->getOutputWrapped($out, $width, $editable, $name, $unique_id, $label_text, $validator); + } + + public function getTextForm($name, $value = '', $width = 12, $placeholder = '', $label_text = '', $validator = [], $textPrefix = ''): string + { + $unique_id = htmlspecialchars($this->getUniqueIdFromName($name)); + $editable = $this->checkWritePermission($name); + $out = ''; + + if ($editable) { + $additonal_array = $this->constructValidatorStrings($validator); + $type = 'text'; + if (isset($validator['email'])) { + $type = 'email'; + } + $additonal_str = implode(' ', $additonal_array); + + $value = htmlspecialchars($value); + if (isset($textPrefix) && ! empty($textPrefix)) { + $out .= "
"; + $out .= "
".$textPrefix.'
'; + } + $out .= ""; + if (isset($textPrefix) && ! empty($textPrefix)) { + $out .= '
'; + } + } elseif (! empty($textPrefix)) { + $out .= "
".($textPrefix).' - '.$this->getReadOnlyValue($value).'
'; + } else { + $out .= "
".$this->getReadOnlyValue($value).'
'; + } + + return $this->getOutputWrapped($out, $width, $editable, $name, $unique_id, $label_text, $validator); + } + + public function getMoneyForm($name, $value = 0, $width = 12, $placeholder = '0.00', $label_text = '', $validator = [], $sum_id = ''): string + { + $out = ''; + $unique_id = $this->getUniqueIdFromName($name); + $editable = $this->checkWritePermission($name); + + if ($editable) { + $additonal_array = $this->constructValidatorStrings($validator); + $additonal_str = implode(' ', $additonal_array); + $value = number_format($value, 2, ',', ''); + $out .= "
"; + $out .= ""; + $out .= ""; + $out .= '
'; + } else { + $out .= "
".htmlspecialchars(number_format($value, 2, ',', '.')).' €
'; + } + + return $this->getOutputWrapped($out, $width, $editable, $name, $unique_id, $label_text, $validator); + } + + public function getDropdownForm($name, $selectable = [], $width = 12, $placeholder = '', $label_text = '', $validator = [], $searchable = true): string + { + $out = ''; + $editable = $this->checkWritePermission($name); + $unique_id = $this->getUniqueIdFromName($name); + + $values = []; + if (isset($selectable['values'])) { + if (is_array($selectable['values'])) { + $values = $selectable['values']; + } else { + $values = explode(';', $selectable['values']); + } + } + // var_dump($selectable); + if ($editable) { + $additonal_array = $this->constructValidatorStrings($validator); + $additonal_array[] = 'data-live-search='.($searchable ? "'true'" : "'false'"); + $additonal_array[] = "data-style=''"; + $additonal_array[] = "data-style-base='form-control'"; + $additonal_str = implode(' ', $additonal_array); + $out .= '
'; // needed for new line after label + $out .= "'; + $out .= '
'; + } else { + // re-substitute ids => names + $tmp_vals = []; // value => [label, subtext] + foreach ($selectable['groups'] as $group) { + foreach ($group['options'] as $option) { + // do not add the strict option to in_array here! + if (isset($option['value']) && in_array($option['value'], $values)) { + $subtext = $option['subtext'] ?? ''; + $tmp_vals[$option['value']] = ['label' => $option['label'], 'subtext' => $subtext]; + } + } + } + $values = array_merge(array_diff($values, array_keys($tmp_vals)), array_values($tmp_vals)); + + // build subtext for read only + $res = []; + foreach ($values as $value) { + if (is_array($value)) { + $res[] = $this->getReadOnlyValue($value['label'])." ".htmlspecialchars($value['subtext']).''; + } else { + $res[] = $this->getReadOnlyValue($value); + } + } + + $out .= "
"; + $out .= implode(',', $res); + $out .= '
'; + } + + return $this->getOutputWrapped($out, $width, $editable, $name, $unique_id, $label_text, $validator); + } + + public function getTextareaForm($name, $value = '', $width = 12, $placeholder = '', $label_text = '', $validator = [], $min_rows = 5): string + { + $out = ''; + + $editable = ! $name || $this->checkWritePermission($name); + $unique_id = $this->getUniqueIdFromName($name); + + if ($editable) { + $additonal_array = $this->constructValidatorStrings($validator); + $additonal_str = implode(' ', $additonal_array); + $out .= ""; + } else { + $out .= "
". + Helper::make_links_clickable($this->getReadOnlyValue($value)). + '
'; + } + + return $this->getOutputWrapped($out, $width, $editable, $name, $unique_id, $label_text, $validator); + } + + /** + * @param string|array $value + * @param int $width + * @param string|array $placeholder + * @param string $label_text + * @param array $validator + * @param false $daterange + * @param string $startDate + */ + public function getDatePickerForm($names, $value = '', $width = 12, $placeholder = '', $label_text = '', $validator = [], $daterange = false, $startDate = ''): string + { + if (! is_array($value)) { + $value = [$value, $value]; + } + if (! isset($placeholder) || ! is_array($placeholder)) { + $placeholder = [$placeholder, $placeholder]; + } + if (! is_array($names)) { + if ($daterange) { + $names = [$names.'[]', $names.'[]']; + } else { + $names = [$names]; + } + } + $out = ''; + + $editable = $this->editAction && Auth::user()->can('update', $this->project); + $unique_id0 = $this->getUniqueIdFromName($names[0]); + + if ($editable) { + $additonal_array = $this->constructValidatorStrings($validator); + + $additonal_str = implode(' ', $additonal_array); + $out .= "
"; + if ($daterange) { + $out .= "
von
"; + $out .= "
"; + } + $out .= " "; + $out .= "
"; + $out .= " "; + $out .= '
'; + if ($daterange) { + $unique_id1 = $this->getUniqueIdFromName($names[1]); + $out .= '
'; + $out .= "
bis
"; + $out .= "
"; + $out .= " "; + $out .= "
"; + $out .= " "; + $out .= '
'; + $out .= '
'; + } + } else { + $out .= "
"; + + if ($daterange) { + $out .= 'von '; + $out .= "{$this->getReadOnlyValue($value[0])}"; + $out .= ' bis '; + $out .= "{$this->getReadOnlyValue($value[1])}"; + } else { + $out .= 'am '; + $out .= "{$this->getReadOnlyValue($value[0])}"; + } + } + $out .= '
'; + + return $this->getOutputWrapped($out, $width, $editable, $names[0], $unique_id0, $label_text, $validator); + } + + public function getHiddenActionInput($actionName): string + { + return ""; + } + + public function getWikiLinkForm($name, $value = '', $width = 12, $placeholder = '', $label_text = '', $validator = [], $linkPrefix = ''): string + { + $unique_id = htmlspecialchars($this->getUniqueIdFromName($name)); + $editable = $this->checkWritePermission($name); + $out = ''; + + if ($editable) { + $additonal_array = $this->constructValidatorStrings($validator); + $type = 'text'; + if (isset($validator['email'])) { + $type = 'email'; + } + $additonal_str = implode(' ', $additonal_array); + + $value = htmlspecialchars($value); + if (isset($linkPrefix) && ! empty($linkPrefix)) { + $out .= "
"; + $out .= "
".$linkPrefix.'
'; + } + $out .= ""; + if (isset($linkPrefix) && ! empty($linkPrefix)) { + $out .= '
'; + } + } else { + if (! empty($linkPrefix) && ! empty($value)) { + $out .= "'; + } else { + $out .= "
".$this->getReadOnlyValue($value).'
'; + } + } + + return $this->getOutputWrapped($out, $width, $editable, $name, $unique_id, $label_text, $validator); + } +} diff --git a/legacy/lib/forms/projekte/ProjektHandler.php b/legacy/lib/forms/projekte/ProjektHandler.php index b31167ca..ff48254d 100644 --- a/legacy/lib/forms/projekte/ProjektHandler.php +++ b/legacy/lib/forms/projekte/ProjektHandler.php @@ -3,14 +3,14 @@ namespace forms\projekte; use App\Exceptions\LegacyDieException; -use App\Models\Legacy\Expense; -use App\Models\Legacy\ExpenseReceipt; use App\Models\Legacy\ExpenseReceiptPost; +use App\Models\Legacy\Project; use App\Models\Legacy\ProjectPost; use App\Models\User; +use App\States\Project\Draft; +use App\States\Project\ProjectState; use forms\chat\ChatHandler; -use forms\FormHandlerInterface; -use forms\FormTemplater; +use forms\FormTemplaterProject; use forms\projekte\auslagen\AuslagenHandler2; use forms\projekte\exceptions\IllegalStateException; use forms\projekte\exceptions\InvalidDataException; @@ -18,18 +18,16 @@ use framework\auth\AuthHandler; use framework\DBConnector; use framework\render\HTMLPageRenderer; +use framework\render\Renderer; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Gate; use PDOException; -class ProjektHandler extends FormHandlerInterface +class ProjektHandler extends Renderer { - private static $emptyData; - - private static $states; - - private static $stateChanges; - private static $printModes; + private static $emptyData; private static $visibleFields; @@ -44,7 +42,6 @@ class ProjektHandler extends FormHandlerInterface /** * @var PermissionHandler */ - private $permissionHandler; private $id; @@ -62,9 +59,10 @@ public function __construct($pathInfo) $this->action = $pathInfo['action']; if ($this->action === 'create' || ! isset($pathInfo['pid'])) { $this->data = self::$emptyData; - $stateNow = 'draft'; + $this->templater = new FormTemplaterProject(new Project()); } else { $this->id = $pathInfo['pid']; + $project = Project::findOrFail($this->id); $res = DBConnector::getInstance()->dbFetchAll( 'projekte', [DBConnector::FETCH_ASSOC], @@ -94,93 +92,13 @@ public function __construct($pathInfo) $this->data['posten-ausgaben'][$idx+1] = $row['ausgaben']; $this->data['posten-titel'][$idx+1] = $row['titel_id']; } - $stateNow = $this->data['state']; + $this->templater = new FormTemplaterProject($project); } - $editMode = $this->action === 'create' || $this->action === 'edit'; - $owners = [ - 'gremien' => $this->data['org'], - 'mail' => $this->data['responsible'].'@'.ORG_DATA['mail-domain'], - ]; - $this->stateHandler = new StateHandler('projekte', self::$states, self::$stateChanges, [], [], $stateNow, $owners); - $this->permissionHandler = new PermissionHandler( - self::$emptyData, - $this->stateHandler, - self::$writePermissionAll, - self::$writePermissionFields, - self::$visibleFields, - $editMode - ); - $this->templater = new FormTemplater($this->permissionHandler); } public static function initStaticVars(): bool { - if (isset(self::$states)) { - return false; - } - self::$states = [ - 'draft' => ['Entwurf'], - 'wip' => ['Beantragt', 'beantragen'], - 'ok-by-hv' => ['Genehmigt durch HV (nicht verkündet)'], - 'need-stura' => ['Warte auf Gremien-Beschluss'], - 'ok-by-stura' => ['Genehmigt durch Gremien-Beschluss'], - 'done-hv' => ['verkündet durch HV'], - 'done-other' => ['Genehmigt'], - 'revoked' => [ - 'Abgelehnt / Zurückgezogen (KEINE Genehmigung oder Antragsteller verzichtet)', - 'zurückziehen / ablehnen', - ], - 'terminated' => ['Abgeschlossen (keine weiteren Ausgaben)', 'beenden'], - ]; - self::$stateChanges = [ - 'draft' => [ - 'wip' => ['owner' => true, 'groups' => ['ref-finanzen-belege']], - ], - 'wip' => [ - 'draft' => ['owner' => true, 'groups' => ['ref-finanzen-belege']], - 'need-stura' => ['groups' => ['ref-finanzen-hv']], - 'ok-by-hv' => ['groups' => ['ref-finanzen-hv']], - 'done-other' => ['groups' => ['ref-finanzen-hv']], - 'revoked' => ['owner' => true, 'groups' => ['ref-finanzen-belege']], - ], - 'ok-by-hv' => [ - 'done-hv' => ['groups' => ['ref-finanzen-hv']], - 'need-stura' => ['groups' => ['ref-finanzen-hv']], - ], - 'need-stura' => [ - 'ok-by-stura' => ['groups' => ['ref-finanzen-hv']], - 'ok-by-hv' => ['groups' => ['ref-finanzen-hv']], - 'revoked' => ['groups' => ['ref-finanzen-hv']], - ], - 'done-hv' => [ - 'terminated' => ['owner' => true, 'groups' => ['ref-finanzen-hv']], - ], - 'done-other' => [ - 'terminated' => ['owner' => true, 'groups' => ['ref-finanzen-hv']], - ], - 'ok-by-stura' => [ - 'terminated' => ['owner' => true, 'groups' => ['ref-finanzen-hv']], - ], - 'revoked' => [ - 'wip' => ['groups' => ['ref-finanzen-belege']], - 'draft' => ['owner' => true, 'groups' => ['ref-finanzen-belege']], - ], - 'terminated' => [ - 'done-hv' => ['owner' => true, 'groups' => ['ref-finanzen-hv']], - 'done-other' => ['owner' => true, 'groups' => ['ref-finanzen-hv']], - 'ok-by-stura' => ['owner' => true, 'groups' => ['ref-finanzen-hv']], - ], - ]; - self::$printModes = [ - 'zahlungsanweisung' => [ - 'title' => 'Titelseite drucken', - 'condition' => [ - ['state' => 'draft', 'group' => 'ref-finanzen'], - ['state' => 'ok-by-stura', 'group' => 'ref-finanzen'], - ], - ], - ]; self::$emptyData = [ 'id' => '', @@ -328,21 +246,20 @@ public static function createNewProjekt($data): ProjektHandler return new ProjektHandler(['pid' => $projekt_id, 'action' => 'none']); } - public static function getStateStringFromName($statename) + public static function getStateStringFromName(string $statename) { - self::initStaticVars(); - - return self::$states[$statename][0]; + $state = ProjectState::make($statename, new Project()); + return $state->label(); } /** * @throws PDOException * @throws WrongVersionException - * @throws InvalidDataException */ public function updateSavedData($data): bool { DB::beginTransaction(); + $project = Project::findOrFail($this->id); $data = array_intersect_key($data, self::$emptyData); $version = (int) $data['version']; @@ -412,7 +329,7 @@ public function updateSavedData($data): bool // check if fields editable $fields = $generatedFields; foreach ($data as $name => $content) { - if ($this->permissionHandler->isEditable($name) && $this->permissionHandler->isVisibleField($name)) { + if (Auth::user()->can('update-field', [$project, $name])) { if (! empty($content)) { $fields[$name] = $content; } else { @@ -423,53 +340,50 @@ public function updateSavedData($data): bool } } $retMetaUpdate = DBConnector::getInstance()->dbUpdate( - 'projekte', - ['id' => $this->id, 'version' => $version], - $fields - ) === 1; - - if ($this->permissionHandler->isEditable( - ['posten-name', 'posten-bemerkung', 'posten-einnahmen', 'posten-ausgaben'], - 'and' - )) { - // update old posten, create new, delete old - - // update old posten (last minrow is empty all the time - $nextFreeId = max($extractFields['posten-id']); - - // protocol which ids got used, so we can delete everything else afterwards - $used_ids = []; - - for ($i = 0; $i < $minRows - 1; $i++) { - $id = ((int)$extractFields['posten-id'][$i]); - if ($id === 0) { - $id = ++$nextFreeId; - } - $used_ids[] = $id; - ProjectPost::updateOrInsert(['id' => $id , 'projekt_id' => $this->id], [ - 'name' => $extractFields['posten-name'][$i], - 'bemerkung' => $extractFields['posten-bemerkung'][$i], - 'einnahmen' => DBConnector::getInstance()->convertUserValueToDBValue($extractFields['posten-einnahmen'][$i], 'money'), - 'ausgaben' => DBConnector::getInstance()->convertUserValueToDBValue($extractFields['posten-ausgaben'][$i], 'money'), - 'titel_id' => $extractFields['posten-titel'][$i], - ]); - } - $project_id = $this->id; - $used_posten_deleted = ExpenseReceiptPost::whereNotIn('projekt_posten_id', $used_ids) - ->whereHas('expensesReceipt.expense', function ($query) use ($project_id) { - $query->where('projekt_id', $project_id); - })->exists(); - - if ($used_posten_deleted) { - throw new InvalidDataException('Posten mit denen noch eine Abrechnung existiert dürfen nicht gelöscht werden'); - } + 'projekte', + ['id' => $this->id, 'version' => $version], + $fields + ) === 1; - ProjectPost::whereNotIn('id', $used_ids)->delete(); - DB::commit(); - return true; + // update old posten, create new, delete old + + // update old posten (last minrow is empty all the time + $nextFreeId = max($extractFields['posten-id']); + + // protocol which ids got used, so we can delete everything else afterwards + $used_ids = []; + + for ($i = 0; $i < $minRows - 1; $i++) { + $id = ((int)$extractFields['posten-id'][$i]); + if ($id === 0) { + $id = ++$nextFreeId; + } + $used_ids[] = $id; + ProjectPost::updateOrInsert(['id' => $id , 'projekt_id' => $this->id], [ + 'name' => $extractFields['posten-name'][$i], + 'bemerkung' => $extractFields['posten-bemerkung'][$i], + // FIXME: use new money type + 'einnahmen' => DBConnector::getInstance()->convertUserValueToDBValue($extractFields['posten-einnahmen'][$i], 'money'), + 'ausgaben' => DBConnector::getInstance()->convertUserValueToDBValue($extractFields['posten-ausgaben'][$i], 'money'), + 'titel_id' => $extractFields['posten-titel'][$i], + ]); + } + $project_id = $this->id; + $used_posten_deleted = ExpenseReceiptPost::whereNotIn('projekt_posten_id', $used_ids) + ->whereHas('expensesReceipt.expense', function ($query) use ($project_id) { + $query->where('projekt_id', $project_id); + })->exists(); + + if ($used_posten_deleted) { + throw new InvalidDataException(__('project.error.posten_illegal_deleted')); } + ProjectPost::whereNotIn('id', $used_ids)->delete(); + + DB::commit(); + + return $retMetaUpdate; } @@ -478,34 +392,37 @@ public function updateSavedData($data): bool */ public function setState($stateName): bool { - if (! in_array($stateName, $this->getNextPossibleStates(), true)) { - throw new IllegalStateException("In den Status $stateName kann nicht gewechselt werden"); - } + $project = Project::findOrFail($this->id); + $newState = ProjectState::make($stateName, $project); + + // Check if transtion is possible and user is authorized to make this transition + Gate::authorize('transition-to', [$project, $newState]); + + // Start database transaction + return DB::transaction(function () use ($project, $stateName, $newState) { + + // Create chat message for state transition + $chat = new ChatHandler('projekt', $this->id); + $chat->_createComment( + 'projekt', + $this->id, + now()->format('Y-m-d H:i:s'), + 'system', + '', + $project->state->label() . ' -> ' . $newState->label(), + 1 + ); - $user_id = DBConnector::getInstance()->getUser()['id']; - DBConnector::getInstance()->dbUpdate( - 'projekte', - ['id' => $this->id, 'version' => $this->data['version']], - [ + // Update project state + $project->update([ 'state' => $stateName, - 'stateCreator_id' => $user_id, - 'lastupdated' => date('Y-m-d H:i:s'), - 'version' => ($this->data['version'] + 1), - ] - ); - $chat = new ChatHandler('projekt', $this->id); - $chat->_createComment( - 'projekt', - $this->id, - date_create()->format('Y-m-d H:i:s'), - 'system', - '', - self::$states[$this->data['state']][0].' -> '.self::$states[$stateName][0], - 1 - ); - $this->stateHandler->transitionTo($stateName); + 'stateCreator_id' => Auth::id(), + 'lastupdated' => now(), + 'version' => $project->version + 1 + ]); - return true; + return true; + }); } public function getNextPossibleStates(): array @@ -516,20 +433,19 @@ public function getNextPossibleStates(): array public function render(): void { if ($this->action === 'create' || ! isset($this->id)) { - $this->renderProjekt('neues Projekt anlegen'); + $this->renderProjekt('neues Projekt anlegen', true); return; } - switch ($this->action) { case 'edit': $this->renderBackButton(); - $this->renderProjekt('Projekt bearbeiten'); + $this->renderProjekt('Projekt bearbeiten', true); break; case 'view': $this->renderInteractionPanel(); // echo $this->templater->getStateChooser($this->stateHandler); - $this->renderProjekt('Projekt '.$this->id); + $this->renderProjekt('Projekt '.$this->id, false); $this->render_chat_box(); $this->renderProjektSizeGrafic(); $this->renderAuslagenList(); @@ -540,20 +456,21 @@ public function render(): void } } - private function renderProjekt($title): void + private function renderProjekt($title, bool $edit): void { + if($edit) $this->templater->wantToEdit(); $auth = AuthHandler::getInstance(); + $model = Project::find($this->id) ?? new Project(); $validateMe = false; - $editable = $this->permissionHandler->isAnyDataEditable(); + $editable = $edit && Auth::user()->can('update', $model); // build dropdowns - $selectable_gremien = FormTemplater::generateGremienSelectable(); + $selectable_gremien = FormTemplaterProject::generateGremienSelectable(); $selectable_gremien['values'] = $this->data['org']; $mailingLists = $auth->hasGroup('ref-finanzen') ? MAILINGLISTS : AuthHandler::getInstance()->getUserMailinglists(); - $selectable_mail = FormTemplater::generateSelectable($mailingLists); + $selectable_mail = FormTemplaterProject::generateSelectable($mailingLists); $selectable_mail['values'] = $this->data['org-mail']; - - $sel_recht = FormTemplater::generateSelectable(array_combine( + $sel_recht = FormTemplaterProject::generateSelectable(array_combine( array_keys(ORG_DATA['rechtsgrundlagen']), array_map(static function ($val) { return $val['label']; @@ -578,11 +495,12 @@ private function renderProjekt($title): void throw new LegacyDieException(400, 'HHP-id kann nicht ermittelt werden. Bitte benachrichtigen sie den Administrator'); } $hhpId = $hhpId[0]['id']; - $selectable_titel = FormTemplater::generateTitelSelectable($hhpId); ?> + $selectable_titel = FormTemplaterProject::generateTitelSelectable($hhpId); + ?>
-
templater->getHiddenActionInput(isset($this->id) ? 'update' : 'create'); ?> @@ -591,12 +509,12 @@ private function renderProjekt($title): void - permissionHandler->isVisibleField('recht')) { ?> + state->equals(Draft::class)) { ?>

Genehmigung

- templater->getDropdownForm( + templater->getDropdownForm( 'recht', $sel_recht, 6, @@ -623,15 +541,15 @@ private function renderProjekt($title): void
+ ?>
-

+

- templater->getTextForm( + templater->getTextForm( 'name', $this->data['name'], 6, @@ -639,7 +557,7 @@ private function renderProjekt($title): void 'Projektname', ['required'] ); ?> - templater->getMailForm( + templater->getMailForm( 'responsible', $this->data['responsible'], 6, @@ -649,7 +567,7 @@ private function renderProjekt($title): void '@'.ORG_DATA['mail-domain'] ); ?>
- templater->getDropdownForm( + templater->getDropdownForm( 'org', $selectable_gremien, 6, @@ -681,7 +599,7 @@ private function renderProjekt($title): void ORG_DATA['projekt-form']['protokoll-prefix'] ?? '' ); } ?> - templater->getDatePickerForm( + templater->getDatePickerForm( ['date-start', 'date-end'], [$this->data['date-start'], $this->data['date-end']], 12, @@ -691,7 +609,7 @@ private function renderProjekt($title): void true, 'today' ); ?> - templater->getDatePickerForm( + templater->getDatePickerForm( 'createdat', $this->data['createdat'], 12, @@ -701,11 +619,11 @@ private function renderProjekt($title): void
- permissionHandler->isEditable( + can('update', $model) /*$this->permissionHandler->isEditable( ['posten-name', 'posten-bemerkung', 'posten-einnahmen', 'posten-ausgaben'], 'and' - ); ?> - + );*/ ?> +
@@ -713,26 +631,26 @@ private function renderProjekt($title): void - + data['posten-name'][] = ''; - foreach ($this->data['posten-name'] as $row_nr => $null) { - $new_row = ($row_nr) === count($this->data['posten-name']); - if ($new_row && ! $tablePartialEditable) { - continue; - } - $sel_titel = $selectable_titel; - if (isset($this->data['posten-titel'][$row_nr])) { - $sel_titel['values'] = $this->data['posten-titel'][$row_nr]; - } ?> - - + $this->data['posten-name'][] = ''; + foreach ($this->data['posten-name'] as $row_nr => $null) { + $new_row = ($row_nr) === count($this->data['posten-name']); + if ($new_row && ! $tablePartialEditable) { + continue; + } + $sel_titel = $selectable_titel; + if (isset($this->data['posten-titel'][$row_nr])) { + $sel_titel['values'] = $this->data['posten-titel'][$row_nr]; + } ?> + + '; } ?> - - - - - + + + + + + } ?> @@ -820,7 +738,7 @@ class='fa fa-fw fa-trash'>
Ein/Ausgabengruppe BemerkungpermissionHandler->isVisibleField('posten-titel') ? 'Titel' : ''; ?>state->equals(Draft::class) ? '' : 'Titel'; ?> Einnahmen Ausgaben
- . + . templater->getTextForm( - 'posten-name[]', - ! $new_row ? $this->data['posten-name'][$row_nr] : '', - null, - 'Name des Postens', - '', - ['required'] - ); ?>templater->getTextForm( - 'posten-bemerkung[]', - ! $new_row ? $this->data['posten-bemerkung'][$row_nr] : '', - null, - 'optional', - '', - [] - ); ?>templater->getDropdownForm( - 'posten-titel[]', - $sel_titel, - null, - 'HH-Titel', - '', - [], - true - ); ?>templater->getMoneyForm( - 'posten-einnahmen[]', - ! $new_row ? $this->data['posten-einnahmen'][$row_nr] : 0, - null, - '', - '', - ['required'], - 'einnahmen' - ); ?>templater->getMoneyForm( - 'posten-ausgaben[]', - ! $new_row ? $this->data['posten-ausgaben'][$row_nr] : 0, - null, - '', - '', - ['required'], - 'ausgaben' - ); ?>templater->getTextForm( + 'posten-name[]', + ! $new_row ? $this->data['posten-name'][$row_nr] : '', + null, + 'Name des Postens', + '', + ['required'] + ); ?>templater->getTextForm( + 'posten-bemerkung[]', + ! $new_row ? $this->data['posten-bemerkung'][$row_nr] : '', + null, + 'optional', + '', + [] + ); ?>templater->getDropdownForm( + 'posten-titel[]', + $sel_titel, + null, + 'HH-Titel', + '', + [], + true + ); ?>templater->getMoneyForm( + 'posten-einnahmen[]', + ! $new_row ? $this->data['posten-einnahmen'][$row_nr] : 0, + null, + '', + '', + ['required'], + 'einnahmen' + ); ?>templater->getMoneyForm( + 'posten-ausgaben[]', + ! $new_row ? $this->data['posten-ausgaben'][$row_nr] : 0, + null, + '', + '', + ['required'], + 'ausgaben' + ); ?>
- templater->getTextareaForm( + templater->getTextareaForm( 'beschreibung', $this->data['beschreibung'], 12, @@ -834,19 +752,11 @@ class='fa fa-fw fa-trash'>
- $state], "canEdit"); - // $stateTxt = "Entwurf"; - // $state = "draft"; - - ?> + data-name="state" data-value="state); ?>" + id="state-state); ?>">Speichern + als state->label()); ?>
@@ -868,32 +778,42 @@ private function renderBackButton(): void private function renderInteractionPanel(): void { $url = str_replace('//', '/', URIBASE.'projekt/'.$this->id.'/'); - $nextValidStates = $this->stateHandler->getNextStates(true); - $disabledStates = array_diff($this->stateHandler->getAllAllowedTransitionableStates(), $nextValidStates); ?> + + $project = Project::findOrFail($this->id); + $nextStates = $project->state->transitionableStateInstances(); + $nextValidStates = []; + $disabledStates = []; + foreach ($nextStates as $nextState) { + if(Auth::user()->can('transition-to', [$project, $nextState])){ + $nextValidStates[] = $nextState; + } else { + $disabledStates[] = $nextState; + } + } + ?>
From 1f3ff14c5019e7b9349b99be3ab97a71315c3804 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Thu, 4 Dec 2025 00:49:04 +0100 Subject: [PATCH 051/108] WIP preview: new project.show --- app/Http/Controllers/ProjectController.php | 66 ++++ app/Models/Legacy/ProjectPost.php | 16 +- lang/de/project.php | 29 ++ resources/views/project/show.blade.php | 381 +++++++++++++++++++++ routes/web-preview.php | 3 + 5 files changed, 491 insertions(+), 4 deletions(-) create mode 100644 app/Http/Controllers/ProjectController.php create mode 100644 lang/de/project.php create mode 100644 resources/views/project/show.blade.php diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php new file mode 100644 index 00000000..ac6607d4 --- /dev/null +++ b/app/Http/Controllers/ProjectController.php @@ -0,0 +1,66 @@ +authorize('view', $project); + return view('project.show', compact('project')); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(Project $project) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, Project $project) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(Project $project) + { + // + } +} diff --git a/app/Models/Legacy/ProjectPost.php b/app/Models/Legacy/ProjectPost.php index ac3cbf3e..0eb2fe0c 100644 --- a/app/Models/Legacy/ProjectPost.php +++ b/app/Models/Legacy/ProjectPost.php @@ -45,12 +45,20 @@ class ProjectPost extends Model /** * @var array */ - protected $fillable = ['titel_id', 'einnahmen', 'ausgaben', 'name', 'bemerkung', - 'id' - ]; + protected $fillable = ['titel_id', 'einnahmen', 'ausgaben', 'name', 'bemerkung', 'id']; public function project(): BelongsTo { - return $this->belongsTo(Project::class, 'projekt_id'); + return $this->belongsTo(Project::class, 'projekt_id', 'id'); } + + public function expensePosts() : \Illuminate\Database\Eloquent\Builder + { + $expenses_id = $this->project->expenses()->get('id'); + return ExpenseReceiptPost::where('projekt_posten_id', $this->id) + ->whereHas('expensesReceipt', function ($query) use ($expenses_id) { + $query->whereIn('auslagen_id', $expenses_id); + }); + } + } diff --git a/lang/de/project.php b/lang/de/project.php new file mode 100644 index 00000000..400cf670 --- /dev/null +++ b/lang/de/project.php @@ -0,0 +1,29 @@ + [ + 'draft' => 'Entwurf', + 'wip' => 'Beantragt', + 'ok-by-hv' => 'Genehmigt durch HV (nicht verkündet)', + 'need-stura' => 'Warte auf Gremien-Beschluss', + 'ok-by-stura' => 'Genehmigt durch Gremien-Beschluss', + 'done-hv' => 'verkündet durch HV', + 'done-other' => 'Genehmigt', + 'revoked' => 'Abgelehnt / Zurückgezogen (KEINE Genehmigung oder Antragsteller verzichtet)', + 'terminated' => 'Abgeschlossen (keine weiteren Ausgaben)', + ], + 'stateActions' => [ + 'draft' => '', + 'wip' => 'beantragen', + 'ok-by-hv' => '', + 'need-stura' => '', + 'ok-by-stura' => '', + 'done-hv' => '', + 'done-other' => '', + 'revoked' => 'zurückziehen / ablehnen', + 'terminated' => 'beenden', + ], + 'error' => [ + 'posten_illegal_deleted' => 'Posten mit denen noch eine Abrechnung existiert dürfen nicht gelöscht werden!' + ] +]; diff --git a/resources/views/project/show.blade.php b/resources/views/project/show.blade.php new file mode 100644 index 00000000..ffaf97f9 --- /dev/null +++ b/resources/views/project/show.blade.php @@ -0,0 +1,381 @@ + +
+
+ + +
+
+
+

Projekt {{ $project->id }}

+

Erstellt am {{ $project->createdat->format('d.m.Y') }}

+
+ +
+ + {{ $project->state->label() }} + + + Status ändern + + + + Bearbeiten + + + + Löschen + +
+
+ + +
+
+
+
+

Gesamtbudget

+

{{ $project->posts()->sum('ausgaben') }}

+
+
+ + + +
+
+
+ +
+
+
+

Ausgegeben

+

+ ??? +

+
+
+ + + +
+
+
+ +
+
+
+

Verfügbar

+

+ ??? +

+
+
+ + + +
+
+
+ +
+
+
+

Auslastung

+

+ drölf % +

+
+
+ + + +
+
+
+
+
+ + +
+

Genehmigung

+
+
+ + @isset($project->recht) +

{{ $project->legal_basis }}

+ @else +

Keine Angabe

+ @endisset +
+ TODO: Recht additional + Hintweistexte +
+
+ + +
+

Projektdetails

+ +
+
+ +

{{ $project->name }}

+
+ +
+ + + + + + {{ $project->responsible }} + +
+ +
+ +

{{ $project->org }}

+
+ +
+ +

+ von {{ $project->date_start->format('d.m.Y') }} + bis {{ $project->date_end->format('d.m.Y') }} +

+
+ +
+ +

Keine Angabe

+
+ +
+
+ + +
+
+

Budget & Posten

+

Budgetplanung mit Ausgabenverfolgung

+
+ +
+ + + + + + + + + + + + + + + @foreach($project->posts as $post) + + + + + + + + + + + @endforeach + + + + + + + + + + +
+ Nr. + + Ein/Ausgabengruppe + + Bemerkung + + Titel + + Einnahmen (Soll) + + Ausgaben (Soll) + + Claimed (Ist) + + Status +
{{ $loop->iteration }}.{{ $post->name }}{{ $post->bemerkung }}{{ $post->titel_id }} $post->einnahmen > 0, + "text-gray-400" => $post->einnahmen === "0.00", + ])>{{ $post->einnahmen }} $post->ausgaben > 0, + "text-gray-400" => $post->ausgaben === "0.00", + ])> + {{ $post->ausgaben }} + + {{ $post->expensePosts()->get() }} + + @if($post->expensePosts()->exists()) + + N/A + + @else +
+
+
+
+ +
+ @endif +
Summe + {{ $project->posts()->sum('einnahmen') }} + + {{ $project->posts()->sum('ausgaben') }} + + ??? + +
+
+
+
+ +
+
+
+
+ + +
+

Projektbeschreibung

+

{{ $project->beschreibung }}

+
+ + +
+

Im Projekt vorhandene Abrechnungen

+ @if($project->expenses_count > 0) + @else +
+ Keine +
+ @endif +
+ +
+ +
+ +
+ + + +
+

Status wechseln

+ + @foreach($project->state->transitionableStateInstances() as $state) + {{ $state->label() }} + @endforeach + +
+
+ + +
+
+ + + +
+
+
+ + + +
+
+

Wirklich Löschen?

+
+

Dieses Projekt kann endgültig gelöscht werden wenn:

+
    +
  • du Projektersteller*in oder Haushaltsverantwortliche*r bist
  • +
  • im Projekt keine Abrechnungen (mehr) vorhanden sind
  • +
+

Wenn das Projekt gelöscht wird, werden alle Daten dazu entfernt und können nicht wieder hergestellt werden.

+
+
+
+
+ + +
+
+
+
+
diff --git a/routes/web-preview.php b/routes/web-preview.php index 897798fb..1d3650d1 100644 --- a/routes/web-preview.php +++ b/routes/web-preview.php @@ -1,3 +1,6 @@ group(function (): void { + Route::resource('project' , \App\Http\Controllers\ProjectController::class); +}); From b1ffaa24ce7e23d2b8b111f55fa8031f428f5b6e Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Thu, 4 Dec 2025 01:09:38 +0100 Subject: [PATCH 052/108] refactor db names to _ instead of - --- .../Commands/LegacyMigrateEncryption.php | 4 +- app/Models/Legacy/Expense.php | 2 +- app/Models/Legacy/Project.php | 2 +- app/Policies/ProjectPolicy.php | 2 +- database/factories/Legacy/ProjectFactory.php | 8 +- ..._03_185120_alter_hyphen_to_underscores.php | 52 ++++++ legacy/lib/booking/BookingHandler.php | 4 +- legacy/lib/booking/HHPHandler.php | 8 +- legacy/lib/forms/FormTemplaterProject.php | 2 +- legacy/lib/forms/RestHandler.php | 14 +- legacy/lib/forms/projekte/ProjektHandler.php | 38 ++-- .../projekte/auslagen/AuslagenHandler2.php | 168 +++++++++--------- legacy/lib/framework/DBConnector.php | 24 +-- legacy/lib/framework/render/MenuRenderer.php | 14 +- 14 files changed, 197 insertions(+), 145 deletions(-) create mode 100644 database/migrations/2025_12_03_185120_alter_hyphen_to_underscores.php diff --git a/app/Console/Commands/LegacyMigrateEncryption.php b/app/Console/Commands/LegacyMigrateEncryption.php index f0724330..170f0cb9 100644 --- a/app/Console/Commands/LegacyMigrateEncryption.php +++ b/app/Console/Commands/LegacyMigrateEncryption.php @@ -69,12 +69,12 @@ public function handle(): int $count = 0; Expense::all()->each(function ($expense) use (&$count): void { - $cryptIban = $expense->getAttribute('zahlung-iban'); + $cryptIban = $expense->getAttribute('zahlung_iban'); try { \Crypt::decryptString($cryptIban); } catch (DecryptException) { $iban = AuslagenHandler2::legacyDecryptStr($cryptIban); - $expense->setAttribute('zahlung-iban', \Crypt::encryptString($iban)); + $expense->setAttribute('zahlung_iban', \Crypt::encryptString($iban)); $expense->etag = \Str::random(32); $expense->save(); $count++; diff --git a/app/Models/Legacy/Expense.php b/app/Models/Legacy/Expense.php index e75fd619..4e7042fe 100644 --- a/app/Models/Legacy/Expense.php +++ b/app/Models/Legacy/Expense.php @@ -75,7 +75,7 @@ class Expense extends Model /** * @var array */ - protected $fillable = ['projekt_id', 'name_suffix', 'state', 'ok-belege', 'ok-hv', 'ok-kv', 'payed', 'rejected', 'zahlung-iban', 'zahlung-name', 'zahlung-vwzk', 'address', 'last_change', 'last_change_by', 'etag', 'version', 'created']; + protected $fillable = ['projekt_id', 'name_suffix', 'state', 'ok_belege', 'ok_hv', 'ok_kv', 'payed', 'rejected', 'zahlung_iban', 'zahlung_name', 'zahlung_vwzk', 'address', 'last_change', 'last_change_by', 'etag', 'version', 'created']; public function project(): BelongsTo { diff --git a/app/Models/Legacy/Project.php b/app/Models/Legacy/Project.php index 8d77427a..e7317835 100644 --- a/app/Models/Legacy/Project.php +++ b/app/Models/Legacy/Project.php @@ -89,7 +89,7 @@ class Project extends Model /** * @var array */ - protected $fillable = ['creator_id', 'createdat', 'lastupdated', 'version', 'state', 'stateCreator_id', 'name', 'responsible', 'org', 'org-mail', 'protokoll', 'recht', 'recht-additional', 'date-start', 'date-end', 'beschreibung']; + protected $fillable = ['creator_id', 'createdat', 'lastupdated', 'version', 'state', 'stateCreator_id', 'name', 'responsible', 'org', 'org_mail', 'protokoll', 'recht', 'recht_additional', 'date_start', 'date_end', 'beschreibung']; protected $dispatchesEvents = [ 'updating' => UpdatingModel::class, diff --git a/app/Policies/ProjectPolicy.php b/app/Policies/ProjectPolicy.php index 04aeab26..561ea0fe 100644 --- a/app/Policies/ProjectPolicy.php +++ b/app/Policies/ProjectPolicy.php @@ -113,7 +113,7 @@ public function updateField(User $user, Project $project, string $field) return false; } - if($field === 'recht' || $field === 'recht-additional' || $field === 'posten-titel') { + if($field === 'recht' || $field === 'recht_additional' || $field === 'posten-titel') { return match ($project->state::class) { Draft::class => false, default => true diff --git a/database/factories/Legacy/ProjectFactory.php b/database/factories/Legacy/ProjectFactory.php index 79ab1a96..246afba4 100644 --- a/database/factories/Legacy/ProjectFactory.php +++ b/database/factories/Legacy/ProjectFactory.php @@ -29,12 +29,12 @@ public function definition(): array 'name' => fake()->text(30), 'responsible' => fake()->userName(), 'org' => fake()->company(), - 'org-mail' => fake()->companyEmail(), + 'org_mail' => fake()->companyEmail(), 'protokoll' => fake()->url(), 'recht' => 'stura', - 'recht-additional' => fake()->text(10), - 'date-start' => fake()->date(), - 'date-end' => fake()->date(), + 'recht_additional' => fake()->text(10), + 'date_start' => fake()->date(), + 'date_end' => fake()->date(), 'beschreibung' => fake()->text(500), ]; } diff --git a/database/migrations/2025_12_03_185120_alter_hyphen_to_underscores.php b/database/migrations/2025_12_03_185120_alter_hyphen_to_underscores.php new file mode 100644 index 00000000..67a6bb62 --- /dev/null +++ b/database/migrations/2025_12_03_185120_alter_hyphen_to_underscores.php @@ -0,0 +1,52 @@ +renameColumn('recht-additional', 'recht_additional'); + $table->renameColumn('date-start', 'date_start'); + $table->renameColumn('date-end', 'date_end'); + $table->renameColumn('org-mail', 'org_mail'); + }); + + Schema::table('auslagen', function (Blueprint $table) { + $table->renameColumn('ok-hv', 'ok_hv'); + $table->renameColumn('ok-kv', 'ok_kv'); + $table->renameColumn('ok-belege', 'ok_belege'); + $table->renameColumn('zahlung-iban', 'zahlung_iban'); + $table->renameColumn('zahlung-name', 'zahlung_name'); + $table->renameColumn('zahlung-vwzk', 'zahlung_vwzk'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('projekte', function (Blueprint $table) { + $table->renameColumn('recht_additional', 'recht-additional'); + $table->renameColumn('date_start', 'date-start'); + $table->renameColumn('date_end', 'date-end'); + $table->renameColumn('org_mail', 'org-mail'); + }); + + Schema::table('auslagen', function (Blueprint $table) { + $table->renameColumn('ok_hv', 'ok-hv'); + $table->renameColumn('ok_kv', 'ok-kv'); + $table->renameColumn('ok_belege', 'ok-belege'); + $table->renameColumn('zahlung_iban', 'zahlung-iban'); + $table->renameColumn('zahlung_name', 'zahlung-name'); + $table->renameColumn('zahlung_vwzk', 'zahlung-vwzk'); + }); + } +}; diff --git a/legacy/lib/booking/BookingHandler.php b/legacy/lib/booking/BookingHandler.php index 9e301ab6..f0fee7f0 100644 --- a/legacy/lib/booking/BookingHandler.php +++ b/legacy/lib/booking/BookingHandler.php @@ -68,7 +68,7 @@ private function renderCSV(): void 'beleg_name' => 'Beleg', 'datum' => 'Buchungsdatum', 'user' => 'Buchender Nutzer', - 'zahlung-name' => 'Zahlungsnummer', + 'zahlung_name' => 'Zahlungsnummer', 'zahlung-datum' => 'Zahlungsdatum', 'comment' => 'Buchungstext', ]; @@ -95,7 +95,7 @@ private function renderCSV(): void 'beleg_name' => $belegStr, 'datum' => $row['timestamp'], 'user' => $userStr, - 'zahlung-name' => $kontoTypes[$row['zahlung_type']]['short'].$row['zahlung_id'], + 'zahlung_name' => $kontoTypes[$row['zahlung_type']]['short'].$row['zahlung_id'], 'zahlung-datum' => $row['zahlung_date'], 'comment' => $row['comment'], ]; diff --git a/legacy/lib/booking/HHPHandler.php b/legacy/lib/booking/HHPHandler.php index bbe42e95..bb73ba92 100644 --- a/legacy/lib/booking/HHPHandler.php +++ b/legacy/lib/booking/HHPHandler.php @@ -180,7 +180,7 @@ public function renderImporter(): void
von
- +
@@ -204,7 +204,7 @@ class="btn btn-primary hasGroup( public function renderImportPreview(): void { [$groups, $titel] = $this->reverseCSV($_POST['importCSV']); - $dateStart = date_create($_POST['date-start'])->format('Y-m-d'); + $dateStart = date_create($_POST['date_start'])->format('Y-m-d'); $mergedGroups = []; foreach ($groups as $number => $group) { $typeName = $group['type'] === 0 ? '[EINNAHME]' : '[AUSGABE]'; @@ -218,7 +218,7 @@ public function renderImportPreview(): void $this->renderHeadline('Speichern'); ?>
renderHiddenInput('importCSV', htmlspecialchars($_POST['importCSV'])); ?> - renderHiddenInput('date-start', htmlspecialchars($_POST['date-start'])); ?> + renderHiddenInput('date_start', htmlspecialchars($_POST['date_start'])); ?> renderNonce(); ?>
@@ -353,14 +365,14 @@ class="w-full inline-flex justify-center rounded-lg border border-transparent sh
-

Wirklich Löschen?

+

{{ __('project.view.delete_modal.heading') }}

-

Dieses Projekt kann endgültig gelöscht werden wenn:

+

{{ __('project.view.delete_modal.intro') }}

    -
  • du Projektersteller*in oder Haushaltsverantwortliche*r bist
  • -
  • im Projekt keine Abrechnungen (mehr) vorhanden sind
  • +
  • {{ __('project.view.delete_modal.conditions.owner') }}
  • +
  • {{ __('project.view.delete_modal.conditions.no_expenses') }}
-

Wenn das Projekt gelöscht wird, werden alle Daten dazu entfernt und können nicht wieder hergestellt werden.

+

{{ __('project.view.delete_modal.warning') }}

@@ -368,11 +380,11 @@ class="w-full inline-flex justify-center rounded-lg border border-transparent sh
From 8123e9b856214c2de0a8840c50740c5e03ab71bd Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Thu, 4 Dec 2025 11:10:49 +0100 Subject: [PATCH 054/108] fix: cleaning unwanted chat html tags --- app/Livewire/ChatPanel.php | 6 ++++-- resources/views/livewire/chat-panel.blade.php | 4 +--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Livewire/ChatPanel.php b/app/Livewire/ChatPanel.php index 7b96945b..6de66ec7 100644 --- a/app/Livewire/ChatPanel.php +++ b/app/Livewire/ChatPanel.php @@ -32,10 +32,12 @@ public function render() public function save() { - $this->validate(['content' => 'required|min:1|']); + $this->validate(['content' => 'required|min:1']); + + $cleanContent = strip_tags($this->content, '


-

{{ $message->text }}

+

{!! $message->text !!}

@elseif($message->type === ChatMessageType::SUPPORT) - tbd @elseif($message->type === ChatMessageType::FINANCE) - tbd @endif @endforeach From 6fa39672ac11863069bcb2f6f81bb52edceae019 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Thu, 4 Dec 2025 19:25:40 +0100 Subject: [PATCH 055/108] feature: added colors and icons to ProjectStates --- app/Models/BudgetPlan.php | 2 +- app/Models/Legacy/ProjectPost.php | 11 +++++++++-- app/States/Project/Applied.php | 9 +++++++++ app/States/Project/ApprovedByFinance.php | 9 +++++++++ app/States/Project/ApprovedByOrg.php | 9 +++++++++ app/States/Project/ApprovedByOther.php | 10 ++++++++++ app/States/Project/Draft.php | 9 +++++++++ app/States/Project/NeedFinanceApproval.php | 11 ++++++++++- app/States/Project/NeedOrgApproval.php | 9 +++++++++ app/States/Project/ProjectState.php | 8 +++++++- app/States/Project/Revoked.php | 11 +++++++++-- app/States/Project/Terminated.php | 11 ++++++++++- lang/de/project.php | 23 ++++++++++++---------- 13 files changed, 114 insertions(+), 18 deletions(-) diff --git a/app/Models/BudgetPlan.php b/app/Models/BudgetPlan.php index 4c39a768..d8a04d7f 100644 --- a/app/Models/BudgetPlan.php +++ b/app/Models/BudgetPlan.php @@ -65,7 +65,7 @@ public function budgetItems(): \Illuminate\Database\Eloquent\Relations\HasMany{ } public function budgetItemsTree(BudgetType $budgetType) { - // this is not accessible from the closure scope + // $this is not accessible from the closure scope $plan_id = $this->id; $constraint = static fn($query) => diff --git a/app/Models/Legacy/ProjectPost.php b/app/Models/Legacy/ProjectPost.php index 0eb2fe0c..6f75a065 100644 --- a/app/Models/Legacy/ProjectPost.php +++ b/app/Models/Legacy/ProjectPost.php @@ -2,6 +2,8 @@ namespace App\Models\Legacy; +use Cknow\Money\Casts\MoneyDecimalCast; +use Cknow\Money\Money; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -11,8 +13,8 @@ * @property int $id * @property int $projekt_id * @property int $titel_id - * @property float $einnahmen - * @property float $ausgaben + * @property Money $einnahmen + * @property Money $ausgaben * @property string $name * @property string $bemerkung * @property Project $projekte @@ -42,6 +44,11 @@ class ProjectPost extends Model public $timestamps = false; + protected $casts = [ + 'einnahmen' => MoneyDecimalCast::class, + 'ausgaben' => MoneyDecimalCast::class, + ]; + /** * @var array */ diff --git a/app/States/Project/Applied.php b/app/States/Project/Applied.php index 9834e1cd..805b8afd 100644 --- a/app/States/Project/Applied.php +++ b/app/States/Project/Applied.php @@ -8,4 +8,13 @@ class Applied extends ProjectState { public static string $name = 'wip'; + + public function iconName() : string + { + return 'fas-paper-plane'; + + } + public function color() : string { + return 'sky'; + } } diff --git a/app/States/Project/ApprovedByFinance.php b/app/States/Project/ApprovedByFinance.php index ca2e2bf8..428985bf 100644 --- a/app/States/Project/ApprovedByFinance.php +++ b/app/States/Project/ApprovedByFinance.php @@ -14,4 +14,13 @@ public function expensable(): bool return true; } + public function iconName() : string + { + return 'fas-scroll'; + + } + public function color() : string { + return 'green'; + } + } diff --git a/app/States/Project/ApprovedByOrg.php b/app/States/Project/ApprovedByOrg.php index 033843b8..c291e72b 100644 --- a/app/States/Project/ApprovedByOrg.php +++ b/app/States/Project/ApprovedByOrg.php @@ -13,4 +13,13 @@ public function expensable(): bool { return true; } + + public function iconName() : string + { + return 'fas-check'; + + } + public function color() : string { + return 'green'; + } } diff --git a/app/States/Project/ApprovedByOther.php b/app/States/Project/ApprovedByOther.php index f5fb24ed..2e2093d1 100644 --- a/app/States/Project/ApprovedByOther.php +++ b/app/States/Project/ApprovedByOther.php @@ -14,4 +14,14 @@ public function expensable(): bool return true; } + public function iconName() : string + { + return 'fas-check'; + + } + public function color() : string { + return 'green'; + } + + } diff --git a/app/States/Project/Draft.php b/app/States/Project/Draft.php index 59b4a12c..041b45f3 100644 --- a/app/States/Project/Draft.php +++ b/app/States/Project/Draft.php @@ -9,4 +9,13 @@ class Draft extends ProjectState { public static string $name = 'draft'; + + public function iconName() : string + { + return 'fas-file-pen'; + + } + public function color() : string { + return 'zinc'; + } } diff --git a/app/States/Project/NeedFinanceApproval.php b/app/States/Project/NeedFinanceApproval.php index dccdd071..8114861a 100644 --- a/app/States/Project/NeedFinanceApproval.php +++ b/app/States/Project/NeedFinanceApproval.php @@ -5,6 +5,15 @@ class NeedFinanceApproval extends ProjectState { - public static string $name = 'ok-hv'; + public static string $name = 'ok-by-hv'; + + public function iconName() : string + { + return 'fas-hourglass-half'; + + } + public function color() : string { + return 'yellow'; + } } diff --git a/app/States/Project/NeedOrgApproval.php b/app/States/Project/NeedOrgApproval.php index a31f88d9..e7020d84 100644 --- a/app/States/Project/NeedOrgApproval.php +++ b/app/States/Project/NeedOrgApproval.php @@ -7,4 +7,13 @@ class NeedOrgApproval extends ProjectState public static string $name = 'need-stura'; + public function iconName() : string + { + return 'fas-hourglass-half'; + + } + public function color() : string { + return 'yellow'; + } + } diff --git a/app/States/Project/ProjectState.php b/app/States/Project/ProjectState.php index 4666b9e2..0ca239e3 100644 --- a/app/States/Project/ProjectState.php +++ b/app/States/Project/ProjectState.php @@ -9,6 +9,12 @@ abstract class ProjectState extends State { public static string $name; + + public function iconName() : string + { + return 'file-pen'; + + } public function color() : string { return "bg-indigo-500"; } @@ -33,7 +39,7 @@ public static function config(): StateConfig ->default(Draft::class) ->allowTransition(Draft::class, Applied::class) ->allowTransition([ApprovedByOrg::class, ApprovedByFinance::class, ApprovedByOther::class], Terminated::class) - ->allowTransition([NeedOrgApproval::class, NeedFinanceApproval::class], Revoked::class) + ->allowTransition([Applied::class, NeedOrgApproval::class, NeedFinanceApproval::class], Revoked::class) ->allowTransition([Revoked::class], Draft::class) ; diff --git a/app/States/Project/Revoked.php b/app/States/Project/Revoked.php index b3e04a2d..dc62da66 100644 --- a/app/States/Project/Revoked.php +++ b/app/States/Project/Revoked.php @@ -2,9 +2,16 @@ namespace App\States\Project; -use App\States\Project\ProjectState; class Revoked extends ProjectState { - public static string $name = 'terminated'; + public static string $name = 'revoked'; + + public function iconName() : string + { + return 'fas-ban'; + } + public function color() : string { + return 'rose'; + } } diff --git a/app/States/Project/Terminated.php b/app/States/Project/Terminated.php index a6335dfb..3c8f5442 100644 --- a/app/States/Project/Terminated.php +++ b/app/States/Project/Terminated.php @@ -6,5 +6,14 @@ class Terminated extends ProjectState { - public static string $name = 'revoked'; + public static string $name = 'terminated'; + + public function iconName() : string + { + return 'fas-flag-checkered'; + + } + public function color() : string { + return 'zinc'; + } } diff --git a/lang/de/project.php b/lang/de/project.php index eddd01f5..cee7cf40 100644 --- a/lang/de/project.php +++ b/lang/de/project.php @@ -5,21 +5,21 @@ 'draft' => 'Entwurf', 'wip' => 'Beantragt', 'ok-by-hv' => 'Genehmigt durch HV (nicht verkündet)', + 'done-hv' => 'verkündet durch HV', 'need-stura' => 'Warte auf Gremien-Beschluss', 'ok-by-stura' => 'Genehmigt durch Gremien-Beschluss', - 'done-hv' => 'verkündet durch HV', 'done-other' => 'Genehmigt', - 'revoked' => 'Abgelehnt / Zurückgezogen (KEINE Genehmigung oder Antragsteller verzichtet)', - 'terminated' => 'Abgeschlossen (keine weiteren Ausgaben)', + 'revoked' => 'Abgelehnt / Zurückgezogen', + 'terminated' => 'Abgeschlossen', ], 'stateActions' => [ - 'draft' => '', + 'draft' => 'bearbeiten', 'wip' => 'beantragen', - 'ok-by-hv' => '', - 'need-stura' => '', - 'ok-by-stura' => '', - 'done-hv' => '', - 'done-other' => '', + 'ok-by-hv' => 'geprüft', + 'done-hv' => 'genehmigen', + 'need-stura' => 'geprüft', + 'ok-by-stura' => 'genehmigen', + 'done-other' => 'genehmigen', 'revoked' => 'zurückziehen / ablehnen', 'terminated' => 'beenden', ], @@ -27,6 +27,9 @@ 'posten_illegal_deleted' => 'Posten mit denen noch eine Abrechnung existiert dürfen nicht gelöscht werden!' ], 'view' => [ + 'summary_cards' => [ + 'state' => 'Status' + ], 'header' => [ 'title' => 'Projekt', 'created_at' => 'Erstellt am', @@ -76,7 +79,7 @@ 'heading' => 'Im Projekt vorhandene Abrechnungen', 'none' => 'Keine', ], - 'status_modal' => [ + 'state-modal' => [ 'heading' => 'Status wechseln', 'placeholder' => 'Neuen Status auswählen', 'cancel' => 'Abbrechen', From d9332e68721286be5a933be72283890bd73f6580 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Thu, 4 Dec 2025 19:26:07 +0100 Subject: [PATCH 056/108] feature: State change possible --- app/Livewire/Project/ShowProject.php | 52 +++ resources/views/livewire/chat-panel.blade.php | 6 +- .../views/livewire/project/show.blade.php | 436 ++++++++++++++++++ resources/views/project/show.blade.php | 393 ---------------- routes/web-preview.php | 4 +- 5 files changed, 495 insertions(+), 396 deletions(-) create mode 100644 app/Livewire/Project/ShowProject.php create mode 100644 resources/views/livewire/project/show.blade.php delete mode 100644 resources/views/project/show.blade.php diff --git a/app/Livewire/Project/ShowProject.php b/app/Livewire/Project/ShowProject.php new file mode 100644 index 00000000..ca640900 --- /dev/null +++ b/app/Livewire/Project/ShowProject.php @@ -0,0 +1,52 @@ +project_id); + return view('livewire.project.show', compact('project')); + } + + public function changeState(): void + { + $project = Project::findOrFail($this->project_id); + $filtered = $this->validate(['newState' => ['required', new ValidStateRule(ProjectState::class)]]); + $newState = ProjectState::make($filtered['newState'], $project); + $this->authorize('transition-to', [$project, $newState]); + + try { + $oldState = $project->state; + $project->state->transitionTo($this->newState); + ChatMessage::create([ + 'text' => "{$oldState->label()} -> {$newState->label()}", + 'type' => ChatMessageType::SYSTEM, + 'target' => 'projekt', + 'target_id' => $project->id, + 'creator' => \Auth::user()->username, + 'creator_alias' => \Auth::user()->name, + 'timestamp' => now(), + ]); + Flux::modal('state-modal')->close(); + } catch (CouldNotPerformTransition $e) { + $this->addError('newState', $e->getMessage()); + } + } +} diff --git a/resources/views/livewire/chat-panel.blade.php b/resources/views/livewire/chat-panel.blade.php index 19b66ee1..9c29836a 100644 --- a/resources/views/livewire/chat-panel.blade.php +++ b/resources/views/livewire/chat-panel.blade.php @@ -14,7 +14,9 @@

Statuswechsel {{ $message->text }}

- + @elseif($message->type === ChatMessageType::PUBLIC)
  • @@ -28,7 +30,7 @@
    {{ $message->user->name }} commented + class="font-medium text-gray-900">{{ $message->user->name ?? $message->creator_alias ?? "Unknown" }} commented
    diff --git a/resources/views/livewire/project/show.blade.php b/resources/views/livewire/project/show.blade.php new file mode 100644 index 00000000..eef1ea87 --- /dev/null +++ b/resources/views/livewire/project/show.blade.php @@ -0,0 +1,436 @@ +@php use Cknow\Money\Money; @endphp +
    +
    + + +
    +
    +
    +

    {{ __('project.view.header.title') }} {{ $project->id }}

    +

    {{ __('project.view.header.created_at') }} {{ $project->createdat->format('d.m.Y') }}

    +
    + +
    + + {{ __('project.view.header.change_status') }} + + + + Secret New Feature :) + + + + {{ __('project.view.header.edit') }} + + + + {{ __('project.view.header.delete') }} + +
    +
    + + +
    + +
    +
    +
    +

    {{ __('project.view.summary_cards.state') }}

    +

    $project->state->color() === "zinc", + "text-sky-600" => $project->state->color() === "sky", + "text-yellow-600" => $project->state->color() === "yellow", + "text-green-600" => $project->state->color() === "green", + "text-rose-600" => $project->state->color() === "rose", + ])> + {{ $project->state->label() }} +

    +
    +
    $project->state->color() === "zinc", + "bg-sky-200" => $project->state->color() === "sky", + "bg-yellow-200" => $project->state->color() === "yellow", + "bg-green-200" => $project->state->color() === "green", + "bg-rose-200" => $project->state->color() === "rose", + ])> + $project->state->color() === "zinc", + "text-sky-600" => $project->state->color() === "sky", + "text-yellow-600" => $project->state->color() === "yellow", + "text-green-600" => $project->state->color() === "green", + "text-rose-600" => $project->state->color() === "rose", + ])/> +
    +
    +
    + +
    +
    +
    +

    {{ __('project.view.budget_summary.total') }}

    +

    {{ Money::parseByDecimal($project->posts()->sum('ausgaben'), 'EUR') }}

    +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    {{ __('project.view.budget_summary.available') }}

    +

    + Ähmm?? +

    +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    {{ __('project.view.budget_summary.usage') }}

    +

    + drölf % +

    +
    +
    + + + +
    +
    +
    +
    +
    + + +
    +

    {{ __('project.view.approval.heading') }}

    +
    +
    + + @isset($project->recht) +

    {{ $project->getLegal()['label'] }}

    + @else +

    {{ __('project.view.approval.none') }}

    + @endisset +
    +
    + @if($project->getLegal()) + + @if($project->recht_additional) +

    {{ $project->recht_additional }}

    + @else +

    {{ __('project.view.approval.none') }}

    + @endif + @endif +
    +
    +

    {{ $project->getLegal()['hint-text'] ?? '' }}

    +
    +
    +
    + + +
    +

    {{ __('project.view.details.heading') }}

    + +
    +
    + +

    {{ $project->name }}

    +
    + +
    + +
    + +

    {{ $project->org }}

    +
    + +
    + +

    + {{ __('project.view.details.from') }} {{ $project->date_start->format('d.m.Y') }} + {{ __('project.view.details.to') }} {{ $project->date_end->format('d.m.Y') }} +

    +
    + +
    + +

    {{ __('project.view.details.none') }}

    +
    + +
    +
    + + +
    +
    +

    {{ __('project.view.budget_table.heading') }}

    +

    {{ __('project.view.budget_table.subheading') }}

    +
    + +
    + + + + + + + + + + + + + + + @foreach($project->posts as $post) + + + + + + + + + + + @endforeach + + + + + + + + + + +
    + {{ __('project.view.budget_table.nr') }} + + {{ __('project.view.budget_table.group') }} + + {{ __('project.view.budget_table.remark') }} + + {{ __('project.view.budget_table.title') }} + + {{ __('project.view.budget_table.income') }} + + {{ __('project.view.budget_table.expenses') }} + + {{ __('project.view.budget_table.claimed') }} + + {{ __('project.view.budget_table.status') }} +
    {{ $loop->iteration }}. + {{ $post->name }}{{ $post->bemerkung }}{{ $post->titel_id }} $post->einnahmen->greaterThan(Money::EUR(0)), + "text-gray-400" => $post->einnahmen->equals(Money::EUR(0)), + ])>{{ $post->einnahmen }} $post->ausgaben->greaterThan(Money::EUR(0)), + "text-gray-400" => $post->ausgaben->equals(Money::EUR(0)), + ])> + {{ $post->ausgaben }} + + {{ $post->expensePosts()->get() }} + + @if($post->expensePosts()->exists()) + + N/A + + @else +
    +
    +
    +
    + +
    + @endif +
    Summe + {{ Money::parseByDecimal($project->posts()->sum('einnahmen'), 'EUR') }} + + {{ Money::parseByDecimal($project->posts()->sum('ausgaben'), 'EUR') }} + + ??? + +
    +
    +
    +
    + +
    +
    +
    +
    + + +
    +

    {{ __('project.view.description.heading') }}

    +

    {{ $project->beschreibung }}

    +
    + + +
    +

    {{ __('project.view.expenses.heading') }}

    + @if($project->expenses_count > 0) + @else +
    + {{ __('project.view.expenses.none') }} +
    + @endif +
    +
    + +
    + +
    + + + +
    +

    {{ __('project.view.state-modal.heading') }}

    + + @foreach($project->state->transitionableStateInstances() as $state) + +
    + $state->color() === "zinc", + "text-sky-600" => $state->color() === "sky", + "text-yellow-600" => $state->color() === "yellow", + "text-green-600" => $state->color() === "green", + "text-rose-600" => $state->color() === "rose", + ])/>{{ $state->label() }} +
    +
    + @endforeach +
    +
    +
    + + {{ __('project.view.state-modal.cancel') }} + + + {{ __('project.view.state-modal.save') }} + +
    +
    + + + +
    +
    +
    + + + +
    +
    +

    {{ __('project.view.delete_modal.heading') }}

    +
    +

    {{ __('project.view.delete_modal.intro') }}

    +
      +
    • {{ __('project.view.delete_modal.conditions.owner') }}
    • +
    • {{ __('project.view.delete_modal.conditions.no_expenses') }}
    • +
    +

    {{ __('project.view.delete_modal.warning') }}

    +
    +
    +
    +
    + + {{ __('project.view.delete_modal.cancel') }} + + + {{ __('project.view.delete_modal.confirm') }} + +
    +
    +
    +
    diff --git a/resources/views/project/show.blade.php b/resources/views/project/show.blade.php deleted file mode 100644 index 803104fa..00000000 --- a/resources/views/project/show.blade.php +++ /dev/null @@ -1,393 +0,0 @@ - -
    -
    - - -
    -
    -
    -

    {{ __('project.view.header.title') }} {{ $project->id }}

    -

    {{ __('project.view.header.created_at') }} {{ $project->createdat->format('d.m.Y') }}

    -
    - -
    - - {{ $project->state->label() }} - - - {{ __('project.view.header.change_status') }} - - - - {{ __('project.view.header.edit') }} - - - - {{ __('project.view.header.delete') }} - -
    -
    - - -
    -
    -
    -
    -

    {{ __('project.view.budget_summary.total') }}

    -

    {{ $project->posts()->sum('ausgaben') }}

    -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    {{ __('project.view.budget_summary.spent') }}

    -

    - ??? -

    -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    {{ __('project.view.budget_summary.available') }}

    -

    - ??? -

    -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    {{ __('project.view.budget_summary.usage') }}

    -

    - drölf % -

    -
    -
    - - - -
    -
    -
    -
    -
    - - -
    -

    {{ __('project.view.approval.heading') }}

    -
    -
    - - @isset($project->recht) -

    {{ $project->getLegal()['label'] }}

    - @else -

    {{ __('project.view.approval.none') }}

    - @endisset -
    -
    - @if($project->getLegal()) - - @if($project->recht_additional) -

    {{ $project->recht_additional }}

    - @else -

    {{ __('project.view.approval.none') }}

    - @endif - @endif -
    -
    -

    {{ $project->getLegal()['hint-text'] }}

    -
    -
    -
    - - -
    -

    {{ __('project.view.details.heading') }}

    - -
    -
    - -

    {{ $project->name }}

    -
    - -
    - - - - - - {{ $project->responsible }} - -
    - -
    - -

    {{ $project->org }}

    -
    - -
    - -

    - {{ __('project.view.details.from') }} {{ $project->date_start->format('d.m.Y') }} - {{ __('project.view.details.to') }} {{ $project->date_end->format('d.m.Y') }} -

    -
    - -
    - -

    {{ __('project.view.details.none') }}

    -
    - -
    -
    - - -
    -
    -

    {{ __('project.view.budget_table.heading') }}

    -

    {{ __('project.view.budget_table.subheading') }}

    -
    - -
    - - - - - - - - - - - - - - - @foreach($project->posts as $post) - - - - - - - - - - - @endforeach - - - - - - - - - - -
    - {{ __('project.view.budget_table.nr') }} - - {{ __('project.view.budget_table.group') }} - - {{ __('project.view.budget_table.remark') }} - - {{ __('project.view.budget_table.title') }} - - {{ __('project.view.budget_table.income') }} - - {{ __('project.view.budget_table.expenses') }} - - {{ __('project.view.budget_table.claimed') }} - - {{ __('project.view.budget_table.status') }} -
    {{ $loop->iteration }}.{{ $post->name }}{{ $post->bemerkung }}{{ $post->titel_id }} $post->einnahmen > 0, - "text-gray-400" => $post->einnahmen === "0.00", - ])>{{ $post->einnahmen }} $post->ausgaben > 0, - "text-gray-400" => $post->ausgaben === "0.00", - ])> - {{ $post->ausgaben }} - - {{ $post->expensePosts()->get() }} - - @if($post->expensePosts()->exists()) - - N/A - - @else -
    -
    -
    -
    - -
    - @endif -
    Summe - {{ $project->posts()->sum('einnahmen') }} - - {{ $project->posts()->sum('ausgaben') }} - - ??? - -
    -
    -
    -
    - -
    -
    -
    -
    - - -
    -

    {{ __('project.view.description.heading') }}

    -

    {{ $project->beschreibung }}

    -
    - - -
    -

    {{ __('project.view.expenses.heading') }}

    - @if($project->expenses_count > 0) - @else -
    - {{ __('project.view.expenses.none') }} -
    - @endif -
    - -
    - -
    - -
    - - - -
    -

    {{ __('project.view.status_modal.heading') }}

    - - @foreach($project->state->transitionableStateInstances() as $state) - {{ $state->label() }} - @endforeach - -
    -
    - - -
    -
    - - - -
    -
    -
    - - - -
    -
    -

    {{ __('project.view.delete_modal.heading') }}

    -
    -

    {{ __('project.view.delete_modal.intro') }}

    -
      -
    • {{ __('project.view.delete_modal.conditions.owner') }}
    • -
    • {{ __('project.view.delete_modal.conditions.no_expenses') }}
    • -
    -

    {{ __('project.view.delete_modal.warning') }}

    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    diff --git a/routes/web-preview.php b/routes/web-preview.php index 1d3650d1..6b59b03c 100644 --- a/routes/web-preview.php +++ b/routes/web-preview.php @@ -2,5 +2,7 @@ // in this file all routes are included which are only visible in STUFIS_FEATURE_BRANCH=preview Route::middleware(['auth'])->group(function (): void { - Route::resource('project' , \App\Http\Controllers\ProjectController::class); + Route::get('project/{project_id}' , \App\Livewire\Project\ShowProject::class)->name('project.show'); + Route::get('project/{project_id}/history' , \App\Livewire\Project\ShowProject::class)->name('project.history'); + // Route::resource('project' , \App\Http\Controllers\ProjectController::class); }); From cd91a9b534e32ac52eee8d7c028480ec15a16dcf Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Fri, 5 Dec 2025 00:45:46 +0100 Subject: [PATCH 057/108] refactor: updated route handling for legacy project links and alerts --- legacy/lib/framework/render/MenuRenderer.php | 4 ++-- routes/legacy.php | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/legacy/lib/framework/render/MenuRenderer.php b/legacy/lib/framework/render/MenuRenderer.php index 69bafa98..40618a0a 100644 --- a/legacy/lib/framework/render/MenuRenderer.php +++ b/legacy/lib/framework/render/MenuRenderer.php @@ -205,7 +205,7 @@ static function ($res, $key) use (&$pids) {
    + {{-- Button Row --}} - {{ __('project.view.header.change_status') }} + + {{ __('project.view.header.change_status') }} + + @can('update', $project) + + {{ __('project.view.header.edit') }} + + @else + +
    + {{ __('project.view.header.edit') }} +
    +
    + @endcan + @can('create-expense', $project) + + {{ __('project.view.header.new-expense') }} + + @else + +
    + {{ __('project.view.header.new-expense') }} +
    +
    + @endcan - - Secret New Feature :) - - - - {{ __('project.view.header.edit') }} - + + - - {{ __('project.view.header.delete') }} - + + + Secret New Feature :) + + + Duplicate (WIP) + + + {{ __('project.view.header.old-view') }} + + + {{ __('project.view.header.delete') }} + + +
    @@ -71,7 +105,9 @@

    {{ __('project.view.budget_summary.total') }}

    -

    {{ Money::parseByDecimal($project->posts()->sum('ausgaben'), 'EUR') }}

    +

    + {{ $postTable['footer']['out'] }} +

    + @php + $totalRemaining = $postTable['footer']['out']->subtract($postTable['footer']['used'])->getAmount() + @endphp

    {{ __('project.view.budget_summary.available') }}

    -

    - Ähmm?? +

    $totalRemaining > 0, + 'text-red-600' => $totalRemaining <= 0 + ])> + {{ $postTable['footer']['out']->subtract($postTable['footer']['used']) }}

    -
    - $totalRemaining > 0, + 'bg-red-100' => $totalRemaining <= 0 + ])> + $totalRemaining > 0, + 'text-red-600' => $totalRemaining <= 0 + ]) fill="none" stroke="currentColor" viewBox="0 0 24 24"> @@ -109,27 +155,27 @@

    {{ __('project.view.budget_summary.usage') }}

    -

    - drölf % +

    $postTable['footer']['ratio'] <= 50, + "text-yellow-600" => $postTable['footer']['ratio'] > 50 && $postTable['footer']['ratio'] <= 90, + "text-red-600" => $postTable['footer']['ratio'] > 90 + ])> + {{ $postTable['footer']['ratio'] }} %

    -
    - $postTable['footer']['ratio'] <= 50, + "bg-yellow-100" => $postTable['footer']['ratio'] > 50 && $postTable['footer']['ratio'] <= 90, + "bg-red-100" => $postTable['footer']['ratio'] > 90 + ])> + $postTable['footer']['ratio'] <= 50, + "text-yellow-600" => $postTable['footer']['ratio'] > 50 && $postTable['footer']['ratio'] <= 90, + "text-red-600" => $postTable['footer']['ratio'] > 90 + ]) fill="none" stroke="currentColor" viewBox="0 0 24 24"> @@ -164,7 +210,7 @@ class="block text-sm font-medium text-gray-700 mb-1">{{ $project->getLegal()['la @endif @endif
    -
    +

    {{ $project->getLegal()['hint-text'] ?? '' }}

    @@ -195,14 +241,16 @@ class="inline-flex items-center text-indigo-600 hover:text-indigo-800 transition
    - +

    {{ $project->org }}

    - +

    {{ __('project.view.details.from') }} {{ $project->date_start->format('d.m.Y') }} @@ -261,11 +309,15 @@ class="block text-sm font-medium text-gray-700 mb-1">{{ __('project.view.details @foreach($project->posts as $post) - {{ $loop->iteration }}. + {{ $loop->iteration }}. {{ $post->name }} {{ $post->bemerkung }} - {{ $post->titel_id }} + + @if($post->budgetItem) + {{ $post->budgetItem->titel_name }} ({{ $post->budgetItem->titel_nr }}) + @endif + $post->einnahmen->greaterThan(Money::EUR(0)), "text-gray-400" => $post->einnahmen->equals(Money::EUR(0)), @@ -277,68 +329,63 @@ class="block text-sm font-medium text-gray-700 mb-1">{{ __('project.view.details {{ $post->ausgaben }} - {{ $post->expensePosts()->get() }} + {{ Money::parseByDecimal($post->expensePosts()->sum('ausgaben'), 'EUR') }} @if($post->expensePosts()->exists()) - - N/A - + @php $ratio = (int) Money::parseByDecimal($post->expensePosts()->sum('ausgaben') * 100, 'EUR')->ratioOf($post->ausgaben) @endphp @else -

    -
    -
    -
    - -
    + @php $ratio = 0 @endphp @endif +
    +
    +
    $ratio <= 50, + 'bg-yellow-500' => $ratio > 50 && $ratio <= 90, + 'bg-red-500' => $ratio > 90 ]) + style="width: {{ min($ratio,100) }}%">
    +
    + $ratio <= 50, + 'text-yellow-600'=> $ratio > 50 && $ratio <= 90, + 'text-red-600' => $ratio > 90 + ])> + {{ $ratio }}% + +
    @endforeach - + Summe - {{ Money::parseByDecimal($project->posts()->sum('einnahmen'), 'EUR') }} + {{ $postTable['footer']['in'] }} - {{ Money::parseByDecimal($project->posts()->sum('ausgaben'), 'EUR') }} + {{ $postTable['footer']['out'] }} - ??? + {{ $postTable['footer']['used'] }}
    + @php $ratio = $postTable['footer']['ratio'] @endphp
    -
    +
    $ratio <= 50, + 'bg-yellow-500' => $ratio > 50 && $ratio <= 90, + 'bg-red-500' => $ratio > 90 ]) + style="width: {{ min($ratio,100) }}%">
    - + $ratio <= 50, + 'text-yellow-600'=> $ratio > 50 && $ratio <= 90, + 'text-red-600' => $ratio > 90 + ])> + {{ $ratio }}% +
    @@ -350,13 +397,53 @@ class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium b

    {{ __('project.view.description.heading') }}

    -

    {{ $project->beschreibung }}

    + @empty($project->beschreibung) +

    {{ __('project.view.description.none') }}

    + @else +

    + {!! Str::markdown($project->beschreibung) !!} +

    + @endempty +

    {{ __('project.view.expenses.heading') }}

    - @if($project->expenses_count > 0) + @if($project->expenses()->count() > 0) + @else
    {{ __('project.view.expenses.none') }} @@ -376,7 +463,7 @@ class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium b @foreach($project->state->transitionableStateInstances() as $state) - +
    Date: Fri, 5 Dec 2025 00:47:20 +0100 Subject: [PATCH 059/108] refactor: simplify ProjektHandler by leveraging ORM methods and reducing old SQL usage --- legacy/lib/forms/projekte/ProjektHandler.php | 50 +++++--------------- 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/legacy/lib/forms/projekte/ProjektHandler.php b/legacy/lib/forms/projekte/ProjektHandler.php index 06cfe373..f96bf61e 100644 --- a/legacy/lib/forms/projekte/ProjektHandler.php +++ b/legacy/lib/forms/projekte/ProjektHandler.php @@ -63,34 +63,15 @@ public function __construct($pathInfo) } else { $this->id = $pathInfo['pid']; $project = Project::findOrFail($this->id); - $res = DBConnector::getInstance()->dbFetchAll( - 'projekte', - [DBConnector::FETCH_ASSOC], - ['projekte.*'], - ['projekte.id' => $this->id], - [ - ['type' => 'left', 'table' => 'user', 'on' => [['user.id', 'projekte.creator_id']]], - ], - ['version' => true] - ); - if (! empty($res)) { - $this->data = $res[0]; - } else { - abort(404); - } - $tmp = DBConnector::getInstance()->dbFetchAll( - 'projektposten', - [DBConnector::FETCH_ASSOC], - [], - ['projekt_id' => $this->id] - ); - foreach ($tmp as $idx => $row) { - $this->data['posten-id'][$idx+1] = $row['id']; - $this->data['posten-name'][$idx+1] = $row['name']; - $this->data['posten-bemerkung'][$idx+1] = $row['bemerkung']; - $this->data['posten-einnahmen'][$idx+1] = $row['einnahmen']; - $this->data['posten-ausgaben'][$idx+1] = $row['ausgaben']; - $this->data['posten-titel'][$idx+1] = $row['titel_id']; + $this->data = $project->getAttributes(); + + foreach ($project->posts as $idx => $post) { + $this->data['posten-id'][$idx+1] = $post->id; + $this->data['posten-name'][$idx+1] = $post->name; + $this->data['posten-bemerkung'][$idx+1] = $post->bemerkung; + $this->data['posten-einnahmen'][$idx+1] = $post->einnahmen->getAmount()/100; + $this->data['posten-ausgaben'][$idx+1] = $post->ausgaben->getAmount()/100; + $this->data['posten-titel'][$idx+1] = $post->titel_id; } $this->templater = new FormTemplaterProject($project); } @@ -339,12 +320,7 @@ public function updateSavedData($data): bool throw new LegacyDieException(403, "Du hast keine Berechtigung '$name' zu schreiben."); } } - $retMetaUpdate = DBConnector::getInstance()->dbUpdate( - 'projekte', - ['id' => $this->id, 'version' => $version], - $fields - ) === 1; - + $project->update($fields); // update old posten, create new, delete old @@ -382,9 +358,7 @@ public function updateSavedData($data): bool ProjectPost::whereNotIn('id', $used_ids)->delete(); DB::commit(); - - - return $retMetaUpdate; + return true; } /** @@ -619,7 +593,7 @@ private function renderProjekt($title, bool $edit): void
    - can('update', $model) /*$this->permissionHandler->isEditable( + can('update', $model) /*$this->permissionHandler->isEditable( ['posten-name', 'posten-bemerkung', 'posten-einnahmen', 'posten-ausgaben'], 'and' );*/ ?> From 38502c9bd81f6cdb33605b6934caf74dc9c15848 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 9 Dec 2025 14:20:03 +0100 Subject: [PATCH 060/108] refactor: adjusted models, routes, and database schema; improved money handling and project calculations --- app/Models/Legacy/LegacyBudgetPlan.php | 30 ++++- app/Models/Legacy/Project.php | 79 +++++++++-- app/Models/Legacy/ProjectPost.php | 29 +++- app/Policies/ProjectPolicy.php | 3 - app/Providers/AppServiceProvider.php | 9 +- app/States/Project/ProjectState.php | 17 ++- composer.lock | 134 +++++++++++-------- legacy/lib/framework/render/MenuRenderer.php | 4 +- routes/breadcrumbs.php | 2 +- storage/demo/stufis-demo-data.sql | 6 +- 10 files changed, 226 insertions(+), 87 deletions(-) diff --git a/app/Models/Legacy/LegacyBudgetPlan.php b/app/Models/Legacy/LegacyBudgetPlan.php index 6f3213cc..5f1e96c4 100644 --- a/app/Models/Legacy/LegacyBudgetPlan.php +++ b/app/Models/Legacy/LegacyBudgetPlan.php @@ -2,6 +2,7 @@ namespace App\Models\Legacy; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasOne; @@ -50,10 +51,35 @@ public function budgetGroups(): \Illuminate\Database\Eloquent\Relations\HasMany public function budgetItems(): \Illuminate\Database\Eloquent\Relations\HasManyThrough { - return $this->hasManyThrough(LegacyBudgetItem::class, LegacyBudgetGroup::class); + return $this->throughBudgetGroups()->hasBudgetItems(); } - public static function latest() : LegacyBudgetPlan { + public static function latest() : \Eloquent|static { return self::orderBy('id', 'desc')->first(); } + + public static function findByDate(Carbon $date) : static + { + return self::query()->where('von', '<=', $date) + ->where(fn ($query) => + $query->where('bis', '>=', $date) + ->orWhereNull('bis')) + ->first(); + } + + public function label() : string { + $format = "M y"; + if ($this->bis === null){ + return "HPP$this->id ab {$this->von->format($format)}"; + }else{ + return "HHP$this->id {$this->von->format($format)} - {$this->bis->format($format)}"; + } + } + + protected $casts = [ + 'von' => 'date', + 'bis' => 'date', + ]; + + } diff --git a/app/Models/Legacy/Project.php b/app/Models/Legacy/Project.php index 8b74b888..74a15f46 100644 --- a/app/Models/Legacy/Project.php +++ b/app/Models/Legacy/Project.php @@ -3,14 +3,19 @@ namespace App\Models\Legacy; use App\Events\UpdatingModel; +use App\Models\BudgetPlan; use App\Models\User; use App\States\Project\ProjectState; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\HasManyThrough; +use Illuminate\Support\Collection; +use Cknow\Money\Money; use Spatie\ModelStates\HasStates; /** @@ -18,8 +23,8 @@ * * @property int $id * @property int $creator_id - * @property string $createdat - * @property string $lastupdated + * @property Carbon $createdat + * @property Carbon $lastupdated * @property int $version * @property ProjectState $state * @property int $stateCreator_id @@ -29,15 +34,15 @@ * @property string $protokoll * @property string $recht * @property string $recht_additional - * @property string $date_start - * @property string $date_end + * @property Carbon $date_start + * @property Carbon $date_end * @property string $beschreibung - * @property Expense[] $expenses + * @property Collection $expenses * @property User $user - * @property ProjectPost[] $posts + * @property-read Collection $posts + * @property-read int|null $posts_count * @property-read User $creator * @property-read int|null $expenses_count - * @property-read int|null $posts_count * @property-read User $stateCreator * * @method static Builder|Project newModelQuery() @@ -78,7 +83,8 @@ class Project extends Model */ protected $table = 'projekte'; - public $timestamps = false; + const CREATED_AT = 'createdat'; + const UPDATED_AT = 'lastupdated'; public $casts = [ 'state' => ProjectState::class, @@ -125,9 +131,64 @@ public function stateCreator(): BelongsTo return $this->belongsTo(\App\Models\User::class, 'stateCreator_id'); } + public function relatedBudgetPlan() : LegacyBudgetPlan + { + return LegacyBudgetPlan::findByDate($this->createdat); + } + + /** + * Get the ordered posts associated with the project. + * + * @return HasMany + */ public function posts(): HasMany { - return $this->hasMany(ProjectPost::class, 'projekt_id'); + return $this->hasMany(ProjectPost::class, 'projekt_id')->orderBy('position'); + } + + public function expensePosts() : HasManyThrough + { + return $this->throughPosts()->hasExpensePosts(); + } + + public function totalAusgaben() : Money + { + return $this->posts()->sumMoney('ausgaben'); + } + + public function totalUsedAusgaben() : Money + { + return $this->expensePosts()->sumMoney('beleg_posten.ausgaben'); + } + + public function totalRemainingAusgaben(): Money + { + return $this->posts()->sumMoney('ausgaben')->subtract($this->totalUsedAusgaben()); + } + + public function totalRatioAusgaben() : int + { + return (int) ($this->totalUsedAusgaben()->ratioOf($this->totalAusgaben()) * 100); + } + + public function totalEinnahmen() : Money + { + return $this->posts()->sumMoney('einnahmen'); + } + + public function totalUsedEinnahmen() : Money + { + return $this->expensePosts()->sumMoney('beleg_posten.einnahmen'); + } + + public function totalRemainingEinnahmen(): Money + { + return $this->posts()->sumMoney('einnahmen')->subtract($this->totalUsedEinnahmen()); + } + + public function totalRatioEinnahmen() : int + { + return (int) ($this->totalUsedEinnahmen()->ratioOf($this->totalEinnahmen()) * 100); } } diff --git a/app/Models/Legacy/ProjectPost.php b/app/Models/Legacy/ProjectPost.php index 0ad1f191..e79ad931 100644 --- a/app/Models/Legacy/ProjectPost.php +++ b/app/Models/Legacy/ProjectPost.php @@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; /** * App\Models\Legacy\ProjectPost @@ -56,10 +57,7 @@ class ProjectPost extends Model 'updating' => UpdatingModel::class, ]; - /** - * @var array - */ - protected $fillable = ['titel_id', 'einnahmen', 'ausgaben', 'name', 'bemerkung', 'id']; + protected $guarded = ['id', 'projekt_id']; public function project(): BelongsTo { @@ -73,13 +71,16 @@ public function project(): BelongsTo * For not this stays, it should/could be changed as soon as the legacy code is removed. * Disadvantages: no good eager loading, no good aggregation and so on. */ - public function expensePosts() : Builder + public function expensePosts() : HasMany { + return $this->hasMany(ExpenseReceiptPost::class, 'projekt_posten_id'); + /* old version before key fixing $expenses_id = $this->project->expenses()->get('id'); return ExpenseReceiptPost::where('projekt_posten_id', $this->id) ->whereHas('expensesReceipt', function ($query) use ($expenses_id) { $query->whereIn('auslagen_id', $expenses_id); }); + */ } public function budgetItem() : BelongsTo @@ -87,4 +88,22 @@ public function budgetItem() : BelongsTo return $this->belongsTo(LegacyBudgetItem::class, 'titel_id'); } + public function expendedSum() : Money + { + if($this->ausgaben->isZero()){ + return Money::EUR($this->expensePosts()->sum('einnahmen'), true); + } + return Money::EUR($this->expensePosts()->sum('ausgaben'), true); + } + + public function expendedRatio() : int { + if($this->expensePosts()->exists() && !$this->ausgaben->isZero()){ + return (int) ($this->expendedSum()->ratioOf($this->ausgaben) * 100); + } + if($this->expensePosts()->exists() && !$this->einnahmen->isZero()){ + return (int) ($this->expendedSum()->ratioOf($this->einnahmen) * 100); + } + return -1; + } + } diff --git a/app/Policies/ProjectPolicy.php b/app/Policies/ProjectPolicy.php index 561ea0fe..a5d7cd3f 100644 --- a/app/Policies/ProjectPolicy.php +++ b/app/Policies/ProjectPolicy.php @@ -15,7 +15,6 @@ use App\States\Project\Revoked; use App\States\Project\Terminated; use Illuminate\Auth\Access\HandlesAuthorization; -use function PHPUnit\Framework\matches; class ProjectPolicy { @@ -122,6 +121,4 @@ public function updateField(User $user, Project $project, string $field) return true; } - - } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 8c455550..d1c5f961 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -4,8 +4,10 @@ use App\Services\Auth\AuthService; use App\Support\Money\MoneySynth; +use Cknow\Money\Money; use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Contracts\Foundation\Application; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\RateLimiter; @@ -48,7 +50,7 @@ public function boot(): void $this->bootRoute(); - $this->bootLivewire(); + $this->bootMoney(); // Carbon::setLocale(config('app.locale')); } @@ -82,8 +84,11 @@ public function registerAuth(): void }); } - private function bootLivewire(): void + private function bootMoney(): void { Livewire::propertySynthesizer(MoneySynth::class); + Builder::macro('sumMoney', function (string $column): Money { + return Money::EUR($this->sum($column)); + }); } } diff --git a/app/States/Project/ProjectState.php b/app/States/Project/ProjectState.php index 53361d35..c4206f69 100644 --- a/app/States/Project/ProjectState.php +++ b/app/States/Project/ProjectState.php @@ -2,11 +2,13 @@ namespace App\States\Project; +use App\Models\Legacy\Project; +use Livewire\Wireable; use Spatie\ModelStates\Exceptions\InvalidConfig; use Spatie\ModelStates\State; use Spatie\ModelStates\StateConfig; -abstract class ProjectState extends State +abstract class ProjectState extends State implements Wireable { public static string $name; @@ -84,7 +86,7 @@ public static function config(): StateConfig ApprovedByOrg::class, ApprovedByOther::class, Terminated::class - ], ApprovedByOrg::class); + ], ApprovedByFinance::class); $config = $config->allowTransition([ Applied::class, @@ -99,4 +101,15 @@ public static function config(): StateConfig return $config; } + public function toLivewire(): array + { + return [$this->getValue(), $this->getModel()->getKey()]; + } + + public static function fromLivewire($value){ + [$name, $id] = $value; + $model = Project::find($id); + return ProjectState::make($name, $model); + } + } diff --git a/composer.lock b/composer.lock index 1ca3cb01..8a78ae3f 100644 --- a/composer.lock +++ b/composer.lock @@ -1197,31 +1197,31 @@ }, { "name": "fruitcake/php-cors", - "version": "v1.3.0", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/fruitcake/php-cors.git", - "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", - "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", "shasum": "" }, "require": { - "php": "^7.4|^8.0", - "symfony/http-foundation": "^4.4|^5.4|^6|^7" + "php": "^8.1", + "symfony/http-foundation": "^5.4|^6.4|^7.3|^8" }, "require-dev": { - "phpstan/phpstan": "^1.4", + "phpstan/phpstan": "^2", "phpunit/phpunit": "^9", - "squizlabs/php_codesniffer": "^3.5" + "squizlabs/php_codesniffer": "^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -1252,7 +1252,7 @@ ], "support": { "issues": "https://github.com/fruitcake/php-cors/issues", - "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" + "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0" }, "funding": [ { @@ -1264,20 +1264,20 @@ "type": "github" } ], - "time": "2023-10-12T05:21:21+00:00" + "time": "2025-12-03T09:33:47+00:00" }, { "name": "giggsey/libphonenumber-for-php-lite", - "version": "9.0.19", + "version": "9.0.20", "source": { "type": "git", "url": "https://github.com/giggsey/libphonenumber-for-php-lite.git", - "reference": "b8b7fcb1a78c1423d633d28fc79297c246686d1b" + "reference": "3b0b569a0fe8ec5aecbd972f64ddbc2ce30ebd0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/b8b7fcb1a78c1423d633d28fc79297c246686d1b", - "reference": "b8b7fcb1a78c1423d633d28fc79297c246686d1b", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/3b0b569a0fe8ec5aecbd972f64ddbc2ce30ebd0a", + "reference": "3b0b569a0fe8ec5aecbd972f64ddbc2ce30ebd0a", "shasum": "" }, "require": { @@ -1342,7 +1342,7 @@ "issues": "https://github.com/giggsey/libphonenumber-for-php-lite/issues", "source": "https://github.com/giggsey/libphonenumber-for-php-lite" }, - "time": "2025-11-20T18:22:51+00:00" + "time": "2025-12-05T12:07:18+00:00" }, { "name": "globalcitizen/php-iban", @@ -3106,16 +3106,16 @@ }, { "name": "livewire/flux", - "version": "v2.9.1", + "version": "v2.9.2", "source": { "type": "git", "url": "https://github.com/livewire/flux.git", - "reference": "041cdd07c74508fb2884d4614ee968c8f51765e7" + "reference": "6572847f70a18e7cf136bb31201d4064f5c8ade1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/flux/zipball/041cdd07c74508fb2884d4614ee968c8f51765e7", - "reference": "041cdd07c74508fb2884d4614ee968c8f51765e7", + "url": "https://api.github.com/repos/livewire/flux/zipball/6572847f70a18e7cf136bb31201d4064f5c8ade1", + "reference": "6572847f70a18e7cf136bb31201d4064f5c8ade1", "shasum": "" }, "require": { @@ -3166,29 +3166,33 @@ ], "support": { "issues": "https://github.com/livewire/flux/issues", - "source": "https://github.com/livewire/flux/tree/v2.9.1" + "source": "https://github.com/livewire/flux/tree/v2.9.2" }, - "time": "2025-12-01T23:27:11+00:00" + "time": "2025-12-04T17:09:39+00:00" }, { "name": "livewire/flux-pro", - "version": "2.9.1", + "version": "2.9.2", "dist": { "type": "zip", - "url": "https://composer.fluxui.dev/download/a07e1167-9208-4075-be99-609f21ead139/flux-pro-2.9.1.zip", - "reference": "bdacab332f5749d40859b35d988e248fc2237413", - "shasum": "ad0f224a07614a66d1bda9fa2e7cec4b92022bdf" + "url": "https://composer.fluxui.dev/download/a0843966-980f-472e-ac02-3fff5b0dd424/flux-pro-2.9.2.zip", + "reference": "9a53a80c3922fc44df95b0653164efc1180019f2", + "shasum": "e45cbb87c256d537ec5d0bb9c62e7da59dd4893a" }, "require": { "illuminate/console": "^10.0|^11.0|^12.0", "illuminate/support": "^10.0|^11.0|^12.0", "illuminate/view": "^10.0|^11.0|^12.0", "laravel/prompts": "^0.1.24|^0.2|^0.3", - "livewire/flux": "2.9.1|dev-main", + "livewire/flux": "2.9.2|dev-main", "livewire/livewire": "^3.7.0|^4.0", "php": "^8.1", "symfony/console": "^6.0|^7.0" }, + "require-dev": { + "livewire/volt": "*", + "orchestra/testbench": "^10.8" + }, "type": "library", "extra": { "laravel": { @@ -3208,6 +3212,18 @@ "FluxPro\\": "src/" } }, + "autoload-dev": { + "psr-4": { + "App\\": "workbench/app/" + } + }, + "scripts": { + "serve": [ + "Composer\\Config::disableProcessTimeout", + "@php vendor/bin/testbench workbench:build --ansi", + "@php vendor/bin/testbench serve --port 3000 --ansi" + ] + }, "license": [ "proprietary" ], @@ -3225,20 +3241,20 @@ "livewire", "ui" ], - "time": "2025-12-01T23:53:05+00:00" + "time": "2025-12-05T01:19:53+00:00" }, { "name": "livewire/livewire", - "version": "v3.7.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/livewire/livewire.git", - "reference": "f5f9efe6d5a7059116bd695a89d95ceedf33f3cb" + "reference": "214da8f3a1199a88b56ab2fe901d4a607f784805" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/livewire/zipball/f5f9efe6d5a7059116bd695a89d95ceedf33f3cb", - "reference": "f5f9efe6d5a7059116bd695a89d95ceedf33f3cb", + "url": "https://api.github.com/repos/livewire/livewire/zipball/214da8f3a1199a88b56ab2fe901d4a607f784805", + "reference": "214da8f3a1199a88b56ab2fe901d4a607f784805", "shasum": "" }, "require": { @@ -3293,7 +3309,7 @@ "description": "A front-end framework for Laravel.", "support": { "issues": "https://github.com/livewire/livewire/issues", - "source": "https://github.com/livewire/livewire/tree/v3.7.0" + "source": "https://github.com/livewire/livewire/tree/v3.7.1" }, "funding": [ { @@ -3301,7 +3317,7 @@ "type": "github" } ], - "time": "2025-11-12T17:58:16+00:00" + "time": "2025-12-03T22:41:13+00:00" }, { "name": "maatwebsite/excel", @@ -9973,16 +9989,16 @@ }, { "name": "laravel/sail", - "version": "v1.49.0", + "version": "v1.50.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "070c7f34ca8dbece4350fbfe0bab580047dfacc7" + "reference": "9177d5de1c8247166b92ea6049c2b069d2a1802f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/070c7f34ca8dbece4350fbfe0bab580047dfacc7", - "reference": "070c7f34ca8dbece4350fbfe0bab580047dfacc7", + "url": "https://api.github.com/repos/laravel/sail/zipball/9177d5de1c8247166b92ea6049c2b069d2a1802f", + "reference": "9177d5de1c8247166b92ea6049c2b069d2a1802f", "shasum": "" }, "require": { @@ -10032,7 +10048,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2025-11-25T21:15:57+00:00" + "time": "2025-12-03T17:16:36+00:00" }, { "name": "laravel/tinker", @@ -10372,16 +10388,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.2", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -10424,9 +10440,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-10-21T19:32:17+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "nunomaduro/collision", @@ -11805,16 +11821,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.15", + "version": "v0.12.16", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "38953bc71491c838fcb6ebcbdc41ab7483cd549c" + "reference": "ee6d5028be4774f56c6c2c85ec4e6bc9acfe6b67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/38953bc71491c838fcb6ebcbdc41ab7483cd549c", - "reference": "38953bc71491c838fcb6ebcbdc41ab7483cd549c", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/ee6d5028be4774f56c6c2c85ec4e6bc9acfe6b67", + "reference": "ee6d5028be4774f56c6c2c85ec4e6bc9acfe6b67", "shasum": "" }, "require": { @@ -11822,8 +11838,8 @@ "ext-tokenizer": "*", "nikic/php-parser": "^5.0 || ^4.0", "php": "^8.0 || ^7.4", - "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", - "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" }, "conflict": { "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" @@ -11878,9 +11894,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.15" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.16" }, - "time": "2025-11-28T00:00:14+00:00" + "time": "2025-12-07T03:39:01+00:00" }, { "name": "rector/rector", @@ -11947,12 +11963,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "d4ee198ff9c57b012547e180eb99ffb5c4911be8" + "reference": "10c1e6abcb8094a428b92e7d8c3126371f9f9126" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/d4ee198ff9c57b012547e180eb99ffb5c4911be8", - "reference": "d4ee198ff9c57b012547e180eb99ffb5c4911be8", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/10c1e6abcb8094a428b92e7d8c3126371f9f9126", + "reference": "10c1e6abcb8094a428b92e7d8c3126371f9f9126", "shasum": "" }, "conflict": { @@ -11964,6 +11980,7 @@ "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", "aimeos/ai-admin-jsonadm": "<2020.10.13|>=2021.04.1,<2021.10.6|>=2022.04.1,<2022.10.3|>=2023.04.1,<2023.10.4|==2024.04.1", "aimeos/ai-client-html": ">=2020.04.1,<2020.10.27|>=2021.04.1,<2021.10.22|>=2022.04.1,<2022.10.13|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.04.7", + "aimeos/ai-cms-grapesjs": ">=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.9|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.10.8|>=2025.04.1,<2025.10.2", "aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9|==2024.04.1", "aimeos/aimeos-core": ">=2022.04.1,<2022.10.17|>=2023.04.1,<2023.10.17|>=2024.04.1,<2024.04.7", "aimeos/aimeos-typo3": "<19.10.12|>=20,<20.10.5", @@ -11971,6 +11988,7 @@ "akaunting/akaunting": "<2.1.13", "akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53", "alextselegidis/easyappointments": "<1.5.2.0-beta1", + "alexusmai/laravel-file-manager": "<=3.3.1", "alt-design/alt-redirect": "<1.6.4", "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", "amazing/media2click": ">=1,<1.3.3", @@ -12366,7 +12384,7 @@ "leantime/leantime": "<3.3", "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", "libreform/libreform": ">=2,<=2.0.8", - "librenms/librenms": "<2017.08.18", + "librenms/librenms": "<25.11", "liftkit/database": "<2.13.2", "lightsaml/lightsaml": "<1.3.5", "limesurvey/limesurvey": "<6.5.12", @@ -12936,7 +12954,7 @@ "type": "tidelift" } ], - "time": "2025-12-02T22:05:39+00:00" + "time": "2025-12-05T21:05:14+00:00" }, { "name": "sebastian/cli-parser", diff --git a/legacy/lib/framework/render/MenuRenderer.php b/legacy/lib/framework/render/MenuRenderer.php index 40618a0a..4db5b889 100644 --- a/legacy/lib/framework/render/MenuRenderer.php +++ b/legacy/lib/framework/render/MenuRenderer.php @@ -629,11 +629,11 @@ private function fetchAuslagenWithState(string $stateString, string $missingColu 'projekte.org', // Org 'einnahmen' => ['beleg_posten.einnahmen', DBConnector::GROUP_SUM_ROUND2], 'ausgaben' => ['beleg_posten.ausgaben', DBConnector::GROUP_SUM_ROUND2], - 'last_change', // letzte änderung + 'last_change', // letzte Änderung ], [ 'auslagen.state' => ['LIKE', "$stateString%"], - "auslagen.ok-$missingColumn" => '', + "auslagen.ok_$missingColumn" => '', ], [ ['table' => 'projekte', 'type' => 'inner', 'on' => ['projekte.id', 'auslagen.projekt_id']], diff --git a/routes/breadcrumbs.php b/routes/breadcrumbs.php index 1f12898a..0c163eaf 100644 --- a/routes/breadcrumbs.php +++ b/routes/breadcrumbs.php @@ -15,7 +15,7 @@ // Home Breadcrumbs::for('legacy.dashboard', static function (BreadcrumbTrail $trail): void { - $trail->push('Home', route('legacy.dashboard', ['sub' => 'mygremium'])); + $trail->push('Home', route('home')); }); // Home > TODOS diff --git a/storage/demo/stufis-demo-data.sql b/storage/demo/stufis-demo-data.sql index f40116af..85bce7e3 100644 --- a/storage/demo/stufis-demo-data.sql +++ b/storage/demo/stufis-demo-data.sql @@ -12,7 +12,7 @@ SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -INSERT INTO `demo__auslagen` (`id`,`projekt_id`,`name_suffix`,`state`,`ok-belege`,`ok-hv`,`ok-kv`,`payed`,`rejected`,`zahlung-iban`,`zahlung-name`,`zahlung-vwzk`,`address`,`last_change`,`last_change_by`,`etag`,`version`,`created`) VALUES +INSERT INTO `demo__auslagen` (`id`,`projekt_id`,`name_suffix`,`state`,`ok_belege`,`ok_hv`,`ok_kv`,`payed`,`rejected`,`zahlung_iban`,`zahlung_name`,`zahlung_vwzk`,`address`,`last_change`,`last_change_by`,`etag`,`version`,`created`) VALUES (1,1,'April 2023','instructed;2024-10-01 14:06:12;michelle;Michelle Storandt','2024-10-01 14:06:09;michelle;Michelle Storandt','2024-10-01 14:06:03;michelle;Michelle Storandt','2024-10-01 14:06:06;michelle;Michelle Storandt','2024-10-01 14:06:15;michelle;Michelle Storandt','','eyJpdiI6IkdnUUtOMzRxb09TNURyNi9CbWMvbmc9PSIsInZhbHVlIjoiQWF3YmRqQVBPVVNjc0ZmNGpWUmZiM2pRejBYUEp5RGp6WWNoQ2pIMkVLOD0iLCJtYWMiOiJkYzFiY2M5ZTJmMmMyNzRmNGJmZGYyOWQwMDQ0MGY2ZTg3Yzg2MWYwNWE3OWUyZjdkYTM0NzE3YmY5YmRjMmUwIiwidGFnIjoiIn0=','Studierendenrat der fiktiven Uni','','Straße des StuRa 1,98693 fiktive Stadt','2023-08-11 18:10:02','michelle;Michelle Storandt','XMjGWLELiPDcIbMBo0na0ef0wmb4dhg8',14,'2023-05-12 09:55:24;michelle;Michelle Storandt'), (2,2,'Hostsharing März','booked;2024-10-01 14:17:07;michelle;Michelle Storandt','2023-08-11 18:20:51;demo_hv;Service','2023-08-11 18:19:36;demo_hv;Service','2023-08-11 21:43:58;demo_kv;Service','2023-08-11 21:44:29;demo_kv;Service','','eyJpdiI6Ijk1TDNsd1BXNHdTUDYyS2E1Q3lvaGc9PSIsInZhbHVlIjoiUTVuRzFDTmVRNlNkdjFFRzVZdmF0SWovQ2Y2QXlhNGQvSWZvbHhzdzFqQT0iLCJtYWMiOiJmNTY0MDdhMmYzN2IyMWYwMDVmZjcxYmQ2OWY4NDdhNTY2MjI2NjMwZDZkMjA1M2I0NjNlNTFmZTNiMDE3Mjk1IiwidGFnIjoiIn0=','Hostsharing eG','2023-0821-11042','Flughafenstraße 52a,22335 Hamburg','2023-08-11 18:19:17','demo_hv;Service','9p4U5vdOkFN12qbceTdZuj0Cq43xdh80',8,'2023-08-11 18:19:17;demo_hv;Service'), (3,2,'Hostsharing April','booked;2024-10-01 14:17:09;michelle;Michelle Storandt','2023-08-11 18:23:10;demo_hv;Service','2023-08-11 18:23:07;demo_hv;Service','2023-08-11 21:44:00;demo_kv;Service','2023-08-11 21:44:31;demo_kv;Service','','eyJpdiI6Iktva280R2Y5N09palVFTVdhbHprVEE9PSIsInZhbHVlIjoiWXk0RmVFWGZOUkZKbzlXZWlET2FuUUNkS1ljYWNkOThKdnVRekxxTVFuQT0iLCJtYWMiOiJmYmMzZDFiYjViOThjNzc0ODNmMWUzNjQ5MWE2NzEyYjkzNjM3NzIyNTRhMWI3M2Q5MzlkNDc0MWZjYzU2NGI3IiwidGFnIjoiIn0=','Hostsharing eG','2023-1058-11042','Flughafenstraße 52a,22335 Hamburg','2023-08-11 18:22:52','demo_hv;Service','xHr9m7wO3hpxd5hSWkQneNmvvMHmMZVn',8,'2023-08-11 18:22:52;demo_hv;Service'), @@ -86,7 +86,7 @@ INSERT INTO `demo__auslagen` (`id`,`projekt_id`,`name_suffix`,`state`,`ok-belege (71,26,'April','booked;2025-02-04 14:47:58;michelle;Michelle Storandt','2025-02-04 13:23:47;michelle;Michelle Storandt','2025-02-04 13:23:41;michelle;Michelle Storandt','2025-02-04 13:23:44;michelle;Michelle Storandt','2025-02-04 14:43:05;michelle;Michelle Storandt','','eyJpdiI6IlM0dlFJdFdHMFRCRUkzakxnZXBVWlE9PSIsInZhbHVlIjoib0FoeFF3dGF6QktST3RpZi9OWEtnbDEzbHJGVDRpTm5uaVlkc3R6TitsQT0iLCJtYWMiOiI3MDZhMGE5Yzg5NDczOGJiMTNjZWI1Y2QwODkwMGJhZDE0ZTExMmU1ZDUzYjgxZjllYjE1OTE5MTQ0NmJiNzUyIiwidGFnIjoiIn0=','Hostsharing','','Musterstraße 42,12345 Musterstadt','2025-02-04 13:23:22','michelle;Michelle Storandt','QsMAy0yY6puiIsmQjSgbIhpVFluq2N0Z',8,'2025-02-04 13:23:22;michelle;Michelle Storandt'), (72,26,'Mai','booked;2025-02-04 14:47:58;michelle;Michelle Storandt','2025-02-04 13:24:44;michelle;Michelle Storandt','2025-02-04 13:24:38;michelle;Michelle Storandt','2025-02-04 13:24:41;michelle;Michelle Storandt','2025-02-04 14:42:56;michelle;Michelle Storandt','','eyJpdiI6IkMwOEdqTkNqQlhPa2kyMGhCN3R5dGc9PSIsInZhbHVlIjoia0FMSU12cjc1OGxrNlpiKzUxUXRNVVZTTmJkRnB1WWJLZEJNWlBkbXA5az0iLCJtYWMiOiJkNmI0MmM5NDM4NTZiZWJjNmRkODc0YzJiYTAzNGFlMzA5Zjk0YTE3NmM0Zjc0ZTc0OWJiM2U5MWI3NTA0ZjBkIiwidGFnIjoiIn0=','Hostsharing','','Musterstraße 42,12345 Musterstadt','2025-02-04 13:24:29','michelle;Michelle Storandt','0roqdBdg7IJqgvyujNQOgm6VwQgDclkj',8,'2025-02-04 13:24:29;michelle;Michelle Storandt'), (73,26,'Juni','booked;2025-02-04 14:47:58;michelle;Michelle Storandt','2025-02-04 13:26:37;michelle;Michelle Storandt','2025-02-04 13:26:30;michelle;Michelle Storandt','2025-02-04 13:26:34;michelle;Michelle Storandt','2025-02-04 14:44:56;michelle;Michelle Storandt','','eyJpdiI6ImtUbG5SSmRod3hhZEtSRnp2Y0ZZSlE9PSIsInZhbHVlIjoibXM3L2VSWmJWVHl4RDR0Y3JuVGRaZjc2MWtMeHBtR0o5ZjQwUEZ6YWZSQT0iLCJtYWMiOiI3MzI5ZWJlZmVmNWJlYmQ5NTU4YTYzNjYyNzkxNGU2ZDI5ODAxZjNmMjNhMmJhMjIwODcxNTFhNTlkNmRlMDYyIiwidGFnIjoiIn0=','Hostsharing','','Musterstraße 42,12345 Musterstadt','2025-02-04 13:25:59','michelle;Michelle Storandt','ktKRzL8FhP85YrrFS67yLnXYXmUZiqt4',8,'2025-02-04 13:25:59;michelle;Michelle Storandt'); -INSERT INTO `demo__auslagen` (`id`,`projekt_id`,`name_suffix`,`state`,`ok-belege`,`ok-hv`,`ok-kv`,`payed`,`rejected`,`zahlung-iban`,`zahlung-name`,`zahlung-vwzk`,`address`,`last_change`,`last_change_by`,`etag`,`version`,`created`) VALUES +INSERT INTO `demo__auslagen` (`id`,`projekt_id`,`name_suffix`,`state`,`ok_belege`,`ok_hv`,`ok_kv`,`payed`,`rejected`,`zahlung_iban`,`zahlung_name`,`zahlung_vwzk`,`address`,`last_change`,`last_change_by`,`etag`,`version`,`created`) VALUES (74,26,'Juli','booked;2025-02-04 14:54:42;michelle;Michelle Storandt','2025-02-04 13:28:26;michelle;Michelle Storandt','2025-02-04 13:27:51;michelle;Michelle Storandt','2025-02-04 13:28:22;michelle;Michelle Storandt','2025-02-04 14:49:59;michelle;Michelle Storandt','','eyJpdiI6IjBDVDN3YVd1NjVlc0dOWHVIZHllZkE9PSIsInZhbHVlIjoicHEyTXB6ZzV6bjVNOCs4YUI5RHdRbnRrTTZmdjZhWi95b0pKN2tmRUhwST0iLCJtYWMiOiI3YTk2NzM4ZWU1MmUwZGZhODJiM2M3YjMwNjY2ZmRjN2I2ZWVlYjZjNWZjN2EzMzQyM2E3NGEwZTZiZmM1Y2VkIiwidGFnIjoiIn0=','Hostsharing','','Musterstraße 42,12345 Musterstadt','2025-02-04 13:27:42','michelle;Michelle Storandt','fazVOpEWVzBmWNKS5Te2WxxnO5OokyWK',8,'2025-02-04 13:27:42;michelle;Michelle Storandt'), (75,26,'August','booked;2025-02-04 14:54:42;michelle;Michelle Storandt','2025-02-04 13:29:58;michelle;Michelle Storandt','2025-02-04 13:29:52;michelle;Michelle Storandt','2025-02-04 13:29:55;michelle;Michelle Storandt','2025-02-04 14:54:12;michelle;Michelle Storandt','','eyJpdiI6IlZiK2kxd0ZVMVFpZy94YmlBMXpXaVE9PSIsInZhbHVlIjoiRlg3RkFYZ09weDJTWEpuZlBEYVR0cnFDV3cxUHNZeHA2ditTZ0xyZkxvOD0iLCJtYWMiOiIzOWVmZWE1MzE2ZmJiNTNkNzgzNzE2YTExYmE0NTU3NDk1MjRiN2NmZjE2ZDJhM2ExYjBhZjU4MmVmZWNhZDgyIiwidGFnIjoiIn0=','Hostsharing','','Musterstraße 42,12345 Musterstadt','2025-02-04 13:29:41','michelle;Michelle Storandt','GuJU5B0sbuE5zuhRuewdQEKuGukZCbSv',8,'2025-02-04 13:29:41;michelle;Michelle Storandt'), (76,26,'September','booked;2025-02-04 15:39:54;michelle;Michelle Storandt','2025-02-04 13:31:07;michelle;Michelle Storandt','2025-02-04 13:30:59;michelle;Michelle Storandt','2025-02-04 13:31:02;michelle;Michelle Storandt','2025-02-04 15:01:28;michelle;Michelle Storandt','','eyJpdiI6InBZTDJEZlkyc2JMWlRaL21iMTFmTGc9PSIsInZhbHVlIjoiQ1J0L3lVMDg1R0Jxd0huaSs2cFBDYlBYVjZ1cHY4MEF1UlFZQkplUXJ2OD0iLCJtYWMiOiI0YjllZmVhOWViYmJiYmY2MjA0YWUyMjA4NjU0MTFmM2MyOTU1NTA5MThhYWE0OGU2MzcxNTkyMDk0ZjhkMTY0IiwidGFnIjoiIn0=','Hostsharing','','Musterstraße 42,12345 Musterstadt','2025-02-04 13:30:50','michelle;Michelle Storandt','26H2TyveFBtbkL7tFrHj98gxmaX4qv5f',8,'2025-02-04 13:30:50;michelle;Michelle Storandt'), @@ -1067,7 +1067,7 @@ INSERT INTO `demo__konto_type` (`id`,`name`,`short`,`sync_from`,`sync_until`,`ib -- Daten für Tabelle `demo__projekte` -- -INSERT INTO `demo__projekte` (`id`,`creator_id`,`createdat`,`lastupdated`,`version`,`state`,`stateCreator_id`,`name`,`responsible`,`org`,`org-mail`,`protokoll`,`recht`,`recht-additional`,`date-start`,`date-end`,`beschreibung`) VALUES +INSERT INTO `demo__projekte` (`id`,`creator_id`,`createdat`,`lastupdated`,`version`,`state`,`stateCreator_id`,`name`,`responsible`,`org`,`org_mail`,`protokoll`,`recht`,`recht_additional`,`date_start`,`date_end`,`beschreibung`) VALUES (1,1,'2023-05-12 09:32:34','2024-01-11 15:02:27',6,'terminated',1,'Semesterbeiträge','michelle.storandt','Studierendenrat (StuRa)','',NULL,'andere','Haushaltsplan','2023-04-01','2024-03-31',NULL), (2,2,'2023-04-04 00:00:00','2024-01-11 15:02:24',8,'terminated',1,'Hosting',NULL,'Referat IT','',NULL,'andere','Haushaltsplan','2023-04-01','2024-03-31',NULL), (3,3,'2023-07-25 00:00:00','2024-01-11 14:44:09',9,'terminated',1,'Hackathon Dresden','lukas','Fachschaftsrat IA','',NULL,'stura','2023-08-03-F01','2023-08-05','2023-08-07','Für die überregionale Vernetzung unter den IT-lern sowie zum Ausbau der IT-Infrastruktur soll ein Hackathon stattfinden. An diesem nehmen ausschließlich ehrenamtlich Aktive teil. Der Hackathon soll vom 5.8.22 bis 7.8.22 in Dresden stattfinden. Es ist noch unklar,ob die Kosten für die Unterkunft benötigt werden.'), From b86f1cd0a0413ac3e242125027033836a7b90068 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 9 Dec 2025 14:20:39 +0100 Subject: [PATCH 061/108] refactor: clean up projektposten db schema with new ID handling and foreign key adjustments --- ...025_12_08_013433_tidy_up_projektposten.php | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 database/migrations/2025_12_08_013433_tidy_up_projektposten.php diff --git a/database/migrations/2025_12_08_013433_tidy_up_projektposten.php b/database/migrations/2025_12_08_013433_tidy_up_projektposten.php new file mode 100644 index 00000000..4c4aea1d --- /dev/null +++ b/database/migrations/2025_12_08_013433_tidy_up_projektposten.php @@ -0,0 +1,77 @@ +unsignedBigInteger('id')->change(); + }); + + // Store old and new IDs mapping + DB::statement(' + CREATE TEMPORARY TABLE id_mapping AS + SELECT pp.id as old_id, t.new_id, pp.projekt_id + FROM projektposten pp + JOIN ( + SELECT id, projekt_id, ROW_NUMBER() OVER (ORDER BY projekt_id, id) as new_id FROM projektposten + ) as t ON pp.id = t.id AND pp.projekt_id = t.projekt_id + '); + + // Update projektposten IDs + DB::statement(' + UPDATE projektposten pp + JOIN id_mapping im ON pp.id = im.old_id and pp.projekt_id = im.projekt_id + SET pp.id = im.new_id + '); + + // Update beleg_posten foreign keys + DB::statement(' + UPDATE beleg_posten bp + JOIN belege b ON b.id = bp.beleg_id + JOIN auslagen a ON a.id = b.auslagen_id + JOIN id_mapping im ON bp.projekt_posten_id = im.old_id AND a.projekt_id = im.projekt_id + SET bp.projekt_posten_id = im.new_id + '); + + Schema::table('projektposten', function (Blueprint $table) { + $table->dropPrimary(['id', 'projekt_id']); + $table->primary('id')->autoIncrement(); + $table->foreign('titel_id')->references('id')->on('haushaltstitel'); + $table->unsignedInteger('position')->default(1); // for better legacy support with stupid default + }); + + // add missing foreign key + Schema::table('beleg_posten', function (Blueprint $table) { + $table->unsignedBigInteger('projekt_posten_id')->change(); + $table->foreign('projekt_posten_id')->references('id')->on('projektposten'); + }); + + // Drop temporary table + DB::statement('DROP TEMPORARY TABLE IF EXISTS id_mapping'); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // just accept the fact that every project does not start with 1 as post number + Schema::table('beleg_posten', function (Blueprint $table) { + $table->dropForeign(['projekt_posten_id']); + }); + Schema::table('projektposten', function (Blueprint $table) { + $table->dropPrimary(); + $table->dropForeign(['titel_id']); + $table->primary(['id', 'projekt_id']); + }); + } +}; From 8dd287e3375cab65ae9fc3e1c5eca83c0f42181f Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 9 Dec 2025 14:21:08 +0100 Subject: [PATCH 062/108] refactor: enhance new project view with detailed budget summary cards, and update routes and translations --- app/Livewire/Forms/ProjectForm.php | 38 -- app/Livewire/Project/EditProject.php | 250 +++++++++++++ app/Livewire/Project/ProjectForm.php | 105 ++++++ app/Livewire/Project/ShowProject.php | 24 +- lang/de/project.php | 17 +- .../livewire/project/edit-project.blade.php | 351 ++++++++++++++++++ ...{show.blade.php => show-project.blade.php} | 341 ++++++++++------- routes/web-preview.php | 1 + 8 files changed, 920 insertions(+), 207 deletions(-) delete mode 100644 app/Livewire/Forms/ProjectForm.php create mode 100644 app/Livewire/Project/EditProject.php create mode 100644 app/Livewire/Project/ProjectForm.php create mode 100644 resources/views/livewire/project/edit-project.blade.php rename resources/views/livewire/project/{show.blade.php => show-project.blade.php} (65%) diff --git a/app/Livewire/Forms/ProjectForm.php b/app/Livewire/Forms/ProjectForm.php deleted file mode 100644 index 9f78e352..00000000 --- a/app/Livewire/Forms/ProjectForm.php +++ /dev/null @@ -1,38 +0,0 @@ - 'required|min:3', - 'start_date' => 'required|date', - 'end_date' => 'required|date', - 'description' => 'required|min:3', - 'target_group' => 'required', - 'student_body_duties' => 'required|array', - ]; - } - - public function store(): void - { - $this->validate(); - - Project::create($this->all()); - } -} diff --git a/app/Livewire/Project/EditProject.php b/app/Livewire/Project/EditProject.php new file mode 100644 index 00000000..c3c54815 --- /dev/null +++ b/app/Livewire/Project/EditProject.php @@ -0,0 +1,250 @@ +isNew = is_null($this->project_id); + + if ($this->isNew) { + Gate::authorize('create', Project::class); + $this->form->initializeNew(); + $this->addEmptyPost(); + $this->attachments = []; + } else { + $project = Project::findOrFail($this->project_id); + Gate::authorize('update', $project); + $this->form->setProject($project); + $this->state_name = $project->state->getValue(); + $this->posts = $project->posts->map(function (ProjectPost $post) { + return [ + 'id' => $post->id, + 'name' => $post->name, + 'bemerkung' => $post->bemerkung ?? '', + 'einnahmen' => $post->einnahmen, + 'ausgaben' => $post->ausgaben, + 'titel_id' => $post->titel_id, + ]; + }); + $this->attachments = []; // FIXME: load Attachments + } + } + + public function isPostDeletable(int $index) : bool { + return + $this->posts->count() > 1 && ( + (isset($this->posts[$index]['id']) && ExpenseReceiptPost::where('projekt_posten_id', $this->posts[$index]['id'])->doesntExist()) + || !isset($this->posts[$index]['id']) ) + ; + } + + /** + * Remove a post by index + */ + public function removePost(int $index): void + { + if ($this->isPostDeletable($index)) { + $this->posts->forget($index); + } + } + + /** + * Save the project + */ + public function save() + { + //$this->validate(); + //$this->form->validate(); + try { + DB::beginTransaction(); + if($this->isNew){ + $project = Project::create([ + 'creator_id' => Auth::id(), + 'stateCreator_id' => Auth::id(), + ...($this->form->getValues()) + ]); + }else{ + $project = Project::findOrFail($this->project_id); + // Check if the project has been modified since the last load + if ($project->version != $this->form->version) { + $this->addError('save', 'Das Projekt wurde zwischenzeitlich von jemand anderem bearbeitet. Bitte laden Sie die Seite neu.'); + return; + } + $project->update([ + ...$this->form->getValues(), + 'version' => $project->version + 1, + ]); + } + foreach ($this->posts as $post) { + if(isset($post['id'])){ + $project->posts()->findOrFail($post['id'])->update($post); + }else{ + $project->posts()->create($post); + } + } + DB::commit(); + return redirect()->route('project.show', $project->id); + } catch (\Exception $e) { + DB::rollBack(); + $this->addError('save', 'Fehler beim Speichern: ' . $e->getMessage()); + } + } + + /** + * Update existing project + */ + protected function updateProject() + { + $project = Project::findOrFail($this->project_id); + + $updateData = [ + 'lastupdated' => now(), + 'version' => $project->version + 1, + ]; + + // Only update fields that the user has permission to edit + foreach ($this->form->toArray() as $field => $value) { + if (Gate::allows('update-field', [$project, $field])) { + $updateData[$field] = $value ?: null; + } + } + + // FIXME: increment version + // FIXME: save posts + $project->update($updateData); + $this->form->version = $project->version; + + } + + /** + * Add an empty post row + */ + public function addEmptyPost(): void + { + $this->posts->add([ + 'name' => '', + 'bemerkung' => '', + 'einnahmen' => Money::EUR(0), + 'ausgaben' => Money::EUR(0), + 'titel_id' => null, + ]); + } + + /** + * Get the sum of all income posts + */ + public function getTotalIncome(): Money + { + return $this->posts->reduce(function (?Money $carry, array $post) { + return $carry ? $carry->add($post['einnahmen']) : $post['einnahmen']; + }, Money::EUR(0)); + } + + /** + * Get the sum of all expense posts + */ + public function getTotalExpenses(): Money + { + return $this->posts->reduce(function (?Money $carry, array $post) { + return $carry ? $carry->add($post['ausgaben']) : $post['ausgaben']; + }, Money::EUR(0)); + } + + public function removeAttachment(int $index): void + { + $photo = $this->attachments[$index]; + $photo->delete(); + unset($this->attachments[$index]); + $this->attachments = array_values($this->attachments); + } + + /** + * Get budget title options based on the project creation date + */ + protected function getBudgetTitleOptions(): \Illuminate\Database\Eloquent\Collection + { + $plan = LegacyBudgetPlan::findOrFail($this->form->hhp_id); + return $plan->budgetItems; + } + + /** + * Get Rechtsgrundlagen options + */ + protected function getRechtsgrundlagenOptions(): array + { + $rechtsgrundlagen = config('stufis.project_legal', []); + + return collect($rechtsgrundlagen)->map(function ($def, $key) { + return [ + 'key' => $key, + 'label' => $def['label'] ?? $key, + 'placeholder' => $def['placeholder'] ?? '', + 'label_additional' => $def['label-additional'] ?? 'Zusatzinformationen', + 'hint' => $def['hint-text'] ?? '', + 'has_additional' => isset($def['placeholder'], $def['label-additional']), + ]; + })->toArray(); + } + + /** + * Get mailing list options + */ + protected function getMailingListOptions(): array + { + $hasFinanceGroup = Auth::user()->getGroups()->contains('ref-finanzen'); + + if ($hasFinanceGroup) { + return config('org_data.mailinglists', []); + } + // Return only user's mailing lists + return Auth::user()->mailinglists ?? []; + } + + public function render() + { + $gremien = Auth::user()->getCommittees(); + $mailingLists = []; + $rechtsgrundlagen = $this->getRechtsgrundlagenOptions(); + $budgetTitles = $this->getBudgetTitleOptions(); + $state = ProjectState::make($this->state_name, new Project()); + $budgetPlans = LegacyBudgetPlan::all(); + return view('livewire.project.edit-project', compact( + 'gremien', 'mailingLists', 'budgetTitles', 'rechtsgrundlagen', 'state', 'budgetPlans' + )); + } +} diff --git a/app/Livewire/Project/ProjectForm.php b/app/Livewire/Project/ProjectForm.php new file mode 100644 index 00000000..a1fd46b0 --- /dev/null +++ b/app/Livewire/Project/ProjectForm.php @@ -0,0 +1,105 @@ + 'required|string|max:255', + 'responsible' => 'required|email', + 'org' => 'required|string', + 'org_mail' => 'nullable|email', + 'protokoll' => 'nullable|string', + 'beschreibung' => 'required|string', + 'recht' => 'nullable|string', + 'recht_additional' => 'nullable|string', + 'dateRange' => 'required|array|size:2', + 'dateRange.*' => 'required|date', + 'createdat' => 'nullable|date' + ]; + } + + public int $version = 1; + + /** + * Set the project and load its data into the form + */ + public function setProject(Project $project): void + { + $this->name = $project->name ?? ''; + $this->responsible = $project->responsible ?? ''; + $this->org = $project->org ?? ''; + $this->org_mail = $project->org_mail ?? ''; + $this->protokoll = $project->protokoll ?? ''; + $this->beschreibung = $project->beschreibung ?? ''; + $this->recht = $project->recht ?? ''; + $this->recht_additional = $project->recht_additional ?? ''; + $this->dateRange = ["start" => $project->date_start, "end" => $project->date_end]; + $this->version = $project->version; + $this->hhp_id = LegacyBudgetPlan::findByDate($project->createdat)->id; + } + + /** + * Initialize form for new project + */ + public function initializeNew(): void + { + $this->hhp_id = LegacyBudgetPlan::findByDate(now())->id; + $this->version = 1; + } + + /** + * Prepare data for saving + */ + public function getValues(): array + { + return [ + 'name' => $this->name, + 'responsible' => $this->responsible, + 'org' => $this->org, + 'org_mail' => $this->org_mail, + 'protokoll' => $this->protokoll, + 'beschreibung' => $this->beschreibung, + 'recht' => $this->recht, + 'recht_additional' => $this->recht_additional, + // make compatible with legacy database + 'date_start' => $this->dateRange["start"], + 'date_end' => $this->dateRange["end"], + 'version' => $this->version, + 'createdat' => Carbon::parse(LegacyBudgetPlan::find($this->hhp_id)->von) + ]; + } + +} diff --git a/app/Livewire/Project/ShowProject.php b/app/Livewire/Project/ShowProject.php index 3ba911c4..540a6ea4 100644 --- a/app/Livewire/Project/ShowProject.php +++ b/app/Livewire/Project/ShowProject.php @@ -23,29 +23,7 @@ class ShowProject extends Component public function render() { $project = Project::findOrFail($this->project_id); - $postTable = [ - 'footer' => ['used' => Money::EUR(0), 'ration' => null], - ]; - foreach ($project->posts as $post){ - - $in = Money::EUR($post->expensePosts()->sum('einnahmen')); - $out = Money::EUR($post->expensePosts()->sum('ausgaben')); - $used = $out->subtract($in); - - $postTable[$post->id]['used'] = $used; - $postTable[$post->id]['ration'] = !$used->isZero() ? $post->ausgaben->ratioOf($used) : 0; - - $postTable['footer']['used'] = $postTable['footer']['used']->add($used); - } - $postTable['footer']['in'] = Money::EUR($project->posts()->sum('einnahmen')); - $postTable['footer']['out'] = Money::EUR($project->posts()->sum('ausgaben')); - if ($postTable['footer']['out']->isZero()) { - $postTable['footer']['ratio'] = 0; - }else{ - $postTable['footer']['ratio'] = (int) $postTable['footer']['used']->multiply(100)->ratioOf($postTable['footer']['out']); - } - - return view('livewire.project.show', compact('project', 'postTable')); + return view('livewire.project.show-project', compact('project')); } public function changeState(): void diff --git a/lang/de/project.php b/lang/de/project.php index 1a58a5d5..75dc36b8 100644 --- a/lang/de/project.php +++ b/lang/de/project.php @@ -28,7 +28,14 @@ ], 'view' => [ 'summary_cards' => [ - 'state' => 'Status' + 'state' => 'Status', + 'budgetplan' => 'Haushaltsplan', + 'out_total' => 'Geplante Ausgaben', + 'in_total' => 'Geplante Einnahmen', + 'out_available' => 'Noch Verfügbar', + 'in_available' => 'Noch Verfügbar', + 'out_ratio' => 'Auslastung', + 'in_ratio' => 'Auslastung', ], 'header' => [ 'title' => 'Projekt', @@ -36,12 +43,7 @@ 'change_status' => 'Status ändern', 'edit' => 'Bearbeiten', 'delete' => 'Löschen', - ], - 'budget_summary' => [ - 'total' => 'Gesamtbudget', - 'spent' => 'Ausgegeben', - 'available' => 'Verfügbar', - 'usage' => 'Auslastung', + 'new-expense' => 'Abrechnung erstellen' ], 'approval' => [ 'heading' => 'Genehmigung', @@ -72,6 +74,7 @@ 'status' => 'Status', 'sum' => 'Summe', ], + 'description' => [ 'heading' => 'Projektbeschreibung', ], diff --git a/resources/views/livewire/project/edit-project.blade.php b/resources/views/livewire/project/edit-project.blade.php new file mode 100644 index 00000000..d6173a62 --- /dev/null +++ b/resources/views/livewire/project/edit-project.blade.php @@ -0,0 +1,351 @@ +
    + + {{-- Header --}} +
    +
    +

    + {{ $isNew ? 'Neues Projekt anlegen' : 'Projekt bearbeiten' }} +

    + @if (!$isNew) +

    + Projekt #{{ $project_id }} - {{ $state->label() }} +

    + @else +

    + Neues Projekt +

    + @endif +
    + + {{-- Form Actions --}} +
    +
    +
    + Zurück + + Speichern + +
    + @error('save') +

    {{ $message }}

    + @enderror +
    +
    +
    + + {{-- Approval Section (only visible for non-draft projects) --}} + @if(true) +
    +
    +
    +

    Genehmigung

    + + + + + + {{ __('project.view.approval.info_toggle') }} + + +
    + +
    + {{-- Rechtsgrundlage Dropdown --}} + + @foreach ($rechtsgrundlagen as $rg) + {{ $rg['label'] }} + @endforeach + + + {{-- Dynamic Additional Fields per Rechtsgrundlage --}} +
    + @if ($rechtsgrundlagen[$form->recht]['has_additional']) + + @endif +
    +
    + @if (!empty($rechtsgrundlagen[$form->recht]['hint'])) +

    {{ $rechtsgrundlagen[$form->recht]['hint'] }}

    + @endif +
    +
    +
    +
    + @endif + + {{-- Main Project Information --}} +
    +
    +
    +

    Projektinformationen

    + + + + + + {{ __('project.view.details.info_toggle') }} + + +
    + +
    + {{-- Project Name --}} +
    + +
    + + {{-- Responsible Person --}} +
    + + Projektveratantwortlich (E-Mail) + + + {{-- @domain.com --}} + + +
    + + {{-- Organization --}} +
    + + @foreach ($gremien as $label) + {{ $label }} + @endforeach + +
    + + {{-- Organization Mail --}} + @if (false) +
    + + @foreach($mailingLists as $mailingLists) + {{ $mailingLists }} + @endforeach + +
    + @endif + + {{-- Project Duration --}} +
    + +
    + + {{-- Protocol Link (optional based on config) --}} + @if (!in_array('hide-protokoll', config('stufis.project.show-link', []))) +
    + +
    + @endif + + {{-- Creation Date --}} +
    + + @foreach ($budgetPlans as $plan) + {{ $plan->label() }} + @endforeach + +
    +
    +
    +
    + + {{-- Project Posts (Budget Items) Table --}} +
    +
    +
    +

    Budget-Posten

    + + + + + + {{ __('project.view.budget_table.info_toggle') }} + + +
    + +
    + + + + + + + @if (true) + + @endif + + + + + + + @foreach ($posts as $index => $post) + + {{-- Row Number --}} + + + {{-- Post Name --}} + + + {{-- Remarks --}} + + + {{-- Budget Title --}} + @if (true) + + @endif + + {{-- Income --}} + + + {{-- Expenses --}} + + + {{-- Actions --}} + + + @endforeach + + + + + + + + + + + +
    + Nr. + + Ein/Ausgabengruppe + + Bemerkung + + Titel + + Einnahmen + + Ausgaben + + Aktionen +
    + {{ $loop->iteration }}. + + + + + + @if (true) + + @foreach ($budgetTitles as $title) + + {{ $title->titel_name }} + ({{ $title->titel_nr }}) + + @endforeach + + @else + - + @endif + + + + + + @if($this->isPostDeletable($index)) + + + + @endif +
    + Posten hinzufügen + + Summen: + +
    + {{ $this->getTotalIncome() }} +
    +
    +
    + {{ $this->getTotalExpenses() }} +
    +
    +
    +
    +
    + + {{-- Project Description --}} +
    +
    +
    +

    Projektbeschreibung

    + + + + + + {{ __('project.view.description.info_toggle') }} + + +
    +
    + +
    +
    +
    + +
    +
    + + + + + +
    + @foreach($attachments as $attachment) + @dump($attachments) + + + + + + @endforeach +
    +
    +
    + + {{-- Form Actions --}} +
    +
    +
    + Zurück + + Speichern + +
    + @error('save') +

    {{ $message }}

    + @enderror +
    +
    +
    diff --git a/resources/views/livewire/project/show.blade.php b/resources/views/livewire/project/show-project.blade.php similarity index 65% rename from resources/views/livewire/project/show.blade.php rename to resources/views/livewire/project/show-project.blade.php index ab59d72a..ec817919 100644 --- a/resources/views/livewire/project/show.blade.php +++ b/resources/views/livewire/project/show-project.blade.php @@ -1,9 +1,18 @@ @php use Cknow\Money\Money; @endphp + +@php $totalAusgaben = $project->totalAusgaben() @endphp +@php $totalRemainingAusgaben = $project->totalRemainingAusgaben() @endphp +@php $totalRatioAusgaben = $project->totalRatioAusgaben(); @endphp + +@php $totalEinnahmen = $project->totalEinnahmen() @endphp +@php $totalRemainingEinnahmen = $project->totalRemainingEinnahmen() @endphp +@php $totalRatioEinnahmen = $project->totalRatioEinnahmen(); @endphp +
    -
    +
    -
    +

    {{ __('project.view.header.title') }} {{ $project->id }}

    @@ -18,7 +27,7 @@ @can('update', $project) - {{ __('project.view.header.edit') }} @@ -26,8 +35,8 @@ @else
    - {{ __('project.view.header.edit') }} -
    + {{ __('project.view.header.edit') }} +
    @endcan @can('create-expense', $project) @@ -39,13 +48,13 @@ @else
    - {{ __('project.view.header.new-expense') }} -
    + {{ __('project.view.header.new-expense') }} +
    @endcan - + @@ -64,130 +73,201 @@
    +
    - -
    - -
    -
    -
    -

    {{ __('project.view.summary_cards.state') }}

    -

    +

    + +
    +
    +
    +

    {{ __('project.view.summary_cards.state') }}

    +

    $project->state->color() === "zinc", "text-sky-600" => $project->state->color() === "sky", "text-yellow-600" => $project->state->color() === "yellow", "text-green-600" => $project->state->color() === "green", "text-rose-600" => $project->state->color() === "rose", ])> - {{ $project->state->label() }} -

    -
    -
    state->label() }} +

    +
    +
    $project->state->color() === "zinc", "bg-sky-200" => $project->state->color() === "sky", "bg-yellow-200" => $project->state->color() === "yellow", "bg-green-200" => $project->state->color() === "green", "bg-rose-200" => $project->state->color() === "rose", ])> - $project->state->color() === "zinc", "text-sky-600" => $project->state->color() === "sky", "text-yellow-600" => $project->state->color() === "yellow", "text-green-600" => $project->state->color() === "green", "text-rose-600" => $project->state->color() === "rose", ])/> -
    - -
    -
    -
    -

    {{ __('project.view.budget_summary.total') }}

    -

    - {{ $postTable['footer']['out'] }} -

    -
    -
    - - - -
    +
    + +
    +
    +
    +

    {{ __('project.view.summary_cards.out_total') }}

    +

    + {{ $project->posts()->sumMoney('ausgaben') }} +

    +
    +
    + +
    +
    +
    + +
    +
    +
    +

    {{ __('project.view.summary_cards.out_available') }}

    +

    $totalRatioAusgaben < 75, + 'text-yellos-600' => 75 <= $totalRatioAusgaben && $totalRatioAusgaben <=100, + 'text-red-600' => $totalRatioAusgaben > 100, + ])> + {{ $totalRemainingAusgaben }} +

    +
    +
    $totalRatioAusgaben < 75, + 'bg-yellow-100' => 75 <= $totalRatioAusgaben && $totalRatioAusgaben <=100, + 'bg-red-100' => $totalRatioAusgaben > 100, + ])> + $totalRatioAusgaben < 75, + 'text-yellow-600' => 75 <= $totalRatioAusgaben && $totalRatioAusgaben <=100, + 'text-red-600' => $totalRatioAusgaben > 100, + ])/>
    +
    + +
    +
    +
    +

    {{ __('project.view.summary_cards.out_ratio') }}

    +

    $totalRatioAusgaben < 75, + 'text-green-600' => 75 <= $totalRatioAusgaben && $totalRatioAusgaben <=100, + 'text-red-600' => $totalRatioAusgaben > 100, + ])> + {{ $totalRatioAusgaben }} % +

    +
    +
    $totalRatioAusgaben < 75, + 'bg-green-100' => 75 <= $totalRatioAusgaben && $totalRatioAusgaben <=100, + 'bg-red-100' => $totalRatioAusgaben > 100, + ])> + $totalRatioAusgaben < 75, + 'text-green-600' => 75 <= $totalRatioAusgaben && $totalRatioAusgaben <=100, + 'text-red-600' => $totalRatioAusgaben > 100, + ])/> +
    +
    +
    -
    -
    - @php - $totalRemaining = $postTable['footer']['out']->subtract($postTable['footer']['used'])->getAmount() - @endphp -
    -

    {{ __('project.view.budget_summary.available') }}

    -

    $totalRemaining > 0, - 'text-red-600' => $totalRemaining <= 0 + +

    +
    +
    +

    {{ __('project.view.summary_cards.budgetplan') }}

    +

    + {{ $project->relatedBudgetPlan()->label() }} +

    +
    +
    + +
    +
    +
    + +
    +
    +
    +

    {{ __('project.view.summary_cards.in_total') }}

    +

    + {{ $totalEinnahmen }} +

    +
    +
    + +
    +
    +
    + +
    +
    +
    +

    {{ __('project.view.summary_cards.in_available') }}

    +

    $totalRatioEinnahmen < 75, + 'text-yellow-600' => 75 <= $totalRatioEinnahmen && $totalRatioEinnahmen <=100, + 'text-red-600' => $totalRatioEinnahmen > 100, ])> - {{ $postTable['footer']['out']->subtract($postTable['footer']['used']) }} -

    -
    -
    +
    +
    $totalRemaining > 0, - 'bg-red-100' => $totalRemaining <= 0 + 'bg-green-100' => $totalRatioEinnahmen < 75, + 'bg-yellow-100' => 75 <= $totalRatioEinnahmen && $totalRatioEinnahmen <=100, + 'bg-red-100' => $totalRatioEinnahmen > 100, ])> - $totalRemaining > 0, - 'text-red-600' => $totalRemaining <= 0 - ]) - fill="none" stroke="currentColor" viewBox="0 0 24 24"> - - -
    + $totalRatioEinnahmen < 75, + 'text-yellow-600' => 75 <= $totalRatioEinnahmen && $totalRatioEinnahmen <=100, + 'text-red-600' => $totalRatioEinnahmen > 100, + ])/>
    - -
    -
    -
    -

    {{ __('project.view.budget_summary.usage') }}

    -

    + +

    +
    +
    +

    {{ __('project.view.summary_cards.in_ratio') }}

    +

    $postTable['footer']['ratio'] <= 50, - "text-yellow-600" => $postTable['footer']['ratio'] > 50 && $postTable['footer']['ratio'] <= 90, - "text-red-600" => $postTable['footer']['ratio'] > 90 + 'text-yellow-600' => $totalRatioEinnahmen < 75, + 'text-green-600' => 75 <= $totalRatioEinnahmen && $totalRatioEinnahmen <=100, + 'text-red-600' => $totalRatioEinnahmen > 100, ])> - {{ $postTable['footer']['ratio'] }} % -

    -
    -
    +
    +
    $postTable['footer']['ratio'] <= 50, - "bg-yellow-100" => $postTable['footer']['ratio'] > 50 && $postTable['footer']['ratio'] <= 90, - "bg-red-100" => $postTable['footer']['ratio'] > 90 + 'bg-yellow-100' => $totalRatioEinnahmen < 75, + 'bg-green-100' => 75 <= $totalRatioEinnahmen && $totalRatioEinnahmen <=100, + 'bg-red-100' => $totalRatioEinnahmen > 100, ])> - $postTable['footer']['ratio'] <= 50, - "text-yellow-600" => $postTable['footer']['ratio'] > 50 && $postTable['footer']['ratio'] <= 90, - "text-red-600" => $postTable['footer']['ratio'] > 90 - ]) - fill="none" stroke="currentColor" viewBox="0 0 24 24"> - - -
    + $totalRatioEinnahmen < 75, + 'text-green-600' => 75 <= $totalRatioEinnahmen && $totalRatioEinnahmen <=100, + 'text-red-600' => $totalRatioEinnahmen > 100, + ])/>
    +
    -
    +

    {{ __('project.view.approval.heading') }}

    @@ -217,7 +297,7 @@ class="block text-sm font-medium text-gray-700 mb-1">{{ $project->getLegal()['la
    -
    +

    {{ __('project.view.details.heading') }}

    @@ -269,7 +349,7 @@ class="block text-sm font-medium text-gray-700 mb-1">{{ __('project.view.details
    -

    {{ __('project.view.budget_table.heading') }}

    @@ -319,36 +399,39 @@ class="block text-sm font-medium text-gray-700 mb-1">{{ __('project.view.details @endif $post->einnahmen->greaterThan(Money::EUR(0)), + "text-gray-900" => $post->einnahmen->greaterThan(Money::EUR(0)), "text-gray-400" => $post->einnahmen->equals(Money::EUR(0)), ])>{{ $post->einnahmen }} $post->ausgaben->greaterThan(Money::EUR(0)), "text-gray-400" => $post->ausgaben->equals(Money::EUR(0)), - ])> + ])> {{ $post->ausgaben }} - - {{ Money::parseByDecimal($post->expensePosts()->sum('ausgaben'), 'EUR') }} + @php $ratio = $post->expendedRatio() @endphp + $ratio >= 75 && $ratio <= 100, + "text-yellow-600" => $ratio < 75, + "text-red-600" => $ratio > 100 + ])> + {{ $post->expendedSum() }} - @if($post->expensePosts()->exists()) - @php $ratio = (int) Money::parseByDecimal($post->expensePosts()->sum('ausgaben') * 100, 'EUR')->ratioOf($post->ausgaben) @endphp - @else - @php $ratio = 0 @endphp - @endif
    -
    $ratio <= 50, - 'bg-yellow-500' => $ratio > 50 && $ratio <= 90, - 'bg-red-500' => $ratio > 90 ]) - style="width: {{ min($ratio,100) }}%">
    +
    $ratio >= 75 && $ratio <= 100, + "bg-yellow-500"=> $ratio < 75, + "bg-red-500" => $ratio > 100 + ]) style="width: {{ min($ratio,100) }}%">
    - $ratio <= 50, - 'text-yellow-600'=> $ratio > 50 && $ratio <= 90, - 'text-red-600' => $ratio > 90 + $ratio >= 75 && $ratio <= 100, + "text-yellow-600"=> $ratio < 75, + "text-red-600" => $ratio > 100 ])> {{ $ratio }}% @@ -360,34 +443,14 @@ class="block text-sm font-medium text-gray-700 mb-1">{{ __('project.view.details Summe - - {{ $postTable['footer']['in'] }} - - {{ $postTable['footer']['out'] }} + {{ $totalEinnahmen }} - - {{ $postTable['footer']['used'] }} - - -
    - @php $ratio = $postTable['footer']['ratio'] @endphp -
    -
    $ratio <= 50, - 'bg-yellow-500' => $ratio > 50 && $ratio <= 90, - 'bg-red-500' => $ratio > 90 ]) - style="width: {{ min($ratio,100) }}%">
    -
    - $ratio <= 50, - 'text-yellow-600'=> $ratio > 50 && $ratio <= 90, - 'text-red-600' => $ratio > 90 - ])> - {{ $ratio }}% - -
    + + {{ $totalAusgaben }} + + @@ -395,7 +458,7 @@ class="block text-sm font-medium text-gray-700 mb-1">{{ __('project.view.details
    -
    +

    {{ __('project.view.description.heading') }}

    @empty($project->beschreibung)

    {{ __('project.view.description.none') }}

    diff --git a/routes/web-preview.php b/routes/web-preview.php index 6b59b03c..57359fb0 100644 --- a/routes/web-preview.php +++ b/routes/web-preview.php @@ -4,5 +4,6 @@ Route::middleware(['auth'])->group(function (): void { Route::get('project/{project_id}' , \App\Livewire\Project\ShowProject::class)->name('project.show'); Route::get('project/{project_id}/history' , \App\Livewire\Project\ShowProject::class)->name('project.history'); + Route::get('project/{project_id}/edit' , \App\Livewire\Project\EditProject::class)->name('project.edit'); // Route::resource('project' , \App\Http\Controllers\ProjectController::class); }); From ffad9e9635f64874601eb9f49f38877fde4bd498 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 9 Dec 2025 14:27:28 +0100 Subject: [PATCH 063/108] refactor: use database prefix config in projektposten migration for improved compatibility --- .../2025_12_08_013433_tidy_up_projektposten.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/database/migrations/2025_12_08_013433_tidy_up_projektposten.php b/database/migrations/2025_12_08_013433_tidy_up_projektposten.php index 4c4aea1d..66a3bd24 100644 --- a/database/migrations/2025_12_08_013433_tidy_up_projektposten.php +++ b/database/migrations/2025_12_08_013433_tidy_up_projektposten.php @@ -12,30 +12,31 @@ */ public function up(): void { + $db_prefix = config('database.connections.mariadb.prefix'); Schema::table('projektposten', function (Blueprint $table) { $table->unsignedBigInteger('id')->change(); }); // Store old and new IDs mapping - DB::statement(' + DB::statement(" CREATE TEMPORARY TABLE id_mapping AS SELECT pp.id as old_id, t.new_id, pp.projekt_id - FROM projektposten pp + FROM $db_prefixprojektposten pp JOIN ( - SELECT id, projekt_id, ROW_NUMBER() OVER (ORDER BY projekt_id, id) as new_id FROM projektposten + SELECT id, projekt_id, ROW_NUMBER() OVER (ORDER BY projekt_id, id) as new_id FROM $db_prefixprojektposten ) as t ON pp.id = t.id AND pp.projekt_id = t.projekt_id - '); + "); // Update projektposten IDs DB::statement(' - UPDATE projektposten pp + UPDATE $db_prefixprojektposten pp JOIN id_mapping im ON pp.id = im.old_id and pp.projekt_id = im.projekt_id SET pp.id = im.new_id '); // Update beleg_posten foreign keys DB::statement(' - UPDATE beleg_posten bp + UPDATE $db_prefixbeleg_posten bp JOIN belege b ON b.id = bp.beleg_id JOIN auslagen a ON a.id = b.auslagen_id JOIN id_mapping im ON bp.projekt_posten_id = im.old_id AND a.projekt_id = im.projekt_id From 055658e265262811028121abad7dcd597494d0ee Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 9 Dec 2025 14:29:06 +0100 Subject: [PATCH 064/108] fix: syntaxerror --- .../2025_12_08_013433_tidy_up_projektposten.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/database/migrations/2025_12_08_013433_tidy_up_projektposten.php b/database/migrations/2025_12_08_013433_tidy_up_projektposten.php index 66a3bd24..fd373b1d 100644 --- a/database/migrations/2025_12_08_013433_tidy_up_projektposten.php +++ b/database/migrations/2025_12_08_013433_tidy_up_projektposten.php @@ -21,22 +21,22 @@ public function up(): void DB::statement(" CREATE TEMPORARY TABLE id_mapping AS SELECT pp.id as old_id, t.new_id, pp.projekt_id - FROM $db_prefixprojektposten pp + FROM {$db_prefix}projektposten pp JOIN ( - SELECT id, projekt_id, ROW_NUMBER() OVER (ORDER BY projekt_id, id) as new_id FROM $db_prefixprojektposten + SELECT id, projekt_id, ROW_NUMBER() OVER (ORDER BY projekt_id, id) as new_id FROM {$db_prefix}projektposten ) as t ON pp.id = t.id AND pp.projekt_id = t.projekt_id "); // Update projektposten IDs DB::statement(' - UPDATE $db_prefixprojektposten pp + UPDATE {$db_prefix}projektposten pp JOIN id_mapping im ON pp.id = im.old_id and pp.projekt_id = im.projekt_id SET pp.id = im.new_id '); // Update beleg_posten foreign keys DB::statement(' - UPDATE $db_prefixbeleg_posten bp + UPDATE {$db_prefix}beleg_posten bp JOIN belege b ON b.id = bp.beleg_id JOIN auslagen a ON a.id = b.auslagen_id JOIN id_mapping im ON bp.projekt_posten_id = im.old_id AND a.projekt_id = im.projekt_id From 09689bffa073192eee894ef94ab3f9d0560832ed Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 9 Dec 2025 14:46:00 +0100 Subject: [PATCH 065/108] refactor: refine projektposten migration to improve prefix handling and foreign key updates --- .../2025_12_08_013433_tidy_up_projektposten.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/database/migrations/2025_12_08_013433_tidy_up_projektposten.php b/database/migrations/2025_12_08_013433_tidy_up_projektposten.php index fd373b1d..5631ca0a 100644 --- a/database/migrations/2025_12_08_013433_tidy_up_projektposten.php +++ b/database/migrations/2025_12_08_013433_tidy_up_projektposten.php @@ -12,7 +12,7 @@ */ public function up(): void { - $db_prefix = config('database.connections.mariadb.prefix'); + $db_prefix = config('database.connections.mariadb.prefix', ''); Schema::table('projektposten', function (Blueprint $table) { $table->unsignedBigInteger('id')->change(); }); @@ -28,20 +28,20 @@ public function up(): void "); // Update projektposten IDs - DB::statement(' + DB::statement(" UPDATE {$db_prefix}projektposten pp JOIN id_mapping im ON pp.id = im.old_id and pp.projekt_id = im.projekt_id SET pp.id = im.new_id - '); + "); // Update beleg_posten foreign keys - DB::statement(' + DB::statement(" UPDATE {$db_prefix}beleg_posten bp - JOIN belege b ON b.id = bp.beleg_id - JOIN auslagen a ON a.id = b.auslagen_id + JOIN {$db_prefix}belege b ON b.id = bp.beleg_id + JOIN {$db_prefix}auslagen a ON a.id = b.auslagen_id JOIN id_mapping im ON bp.projekt_posten_id = im.old_id AND a.projekt_id = im.projekt_id SET bp.projekt_posten_id = im.new_id - '); + "); Schema::table('projektposten', function (Blueprint $table) { $table->dropPrimary(['id', 'projekt_id']); @@ -73,6 +73,7 @@ public function down(): void $table->dropPrimary(); $table->dropForeign(['titel_id']); $table->primary(['id', 'projekt_id']); + $table->dropColumn('position'); }); } }; From 5087ab7e978e23c3dfd6bb71076b53dcdf10bcca Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Tue, 9 Dec 2025 14:52:39 +0100 Subject: [PATCH 066/108] legacy-feature: add link to new project view in ProjektHandler --- legacy/lib/forms/projekte/ProjektHandler.php | 1 + 1 file changed, 1 insertion(+) diff --git a/legacy/lib/forms/projekte/ProjektHandler.php b/legacy/lib/forms/projekte/ProjektHandler.php index f96bf61e..d90cb46c 100644 --- a/legacy/lib/forms/projekte/ProjektHandler.php +++ b/legacy/lib/forms/projekte/ProjektHandler.php @@ -787,6 +787,7 @@ private function renderInteractionPanel(): void
  • Projekt löschen 
  • +
  • Zur neuen Ansicht 
  • @@ -76,7 +76,7 @@
    -
    +
    @@ -290,9 +290,11 @@ class="block text-sm font-medium text-gray-700 mb-1">{{ $project->getLegal()['la @endif @endif
    -
    -

    {{ $project->getLegal()['hint-text'] ?? '' }}

    -
    + @if(!empty($project->getLegal()['hint-text'])) +
    +

    {{ $project->getLegal()['hint-text'] ?? '' }}

    +
    + @endif
    @@ -429,7 +431,8 @@ class="inline-flex items-center text-indigo-600 hover:text-indigo-800 transition @php $ratio = $post->expendedRatio() @endphp $ratio < 75, + "text-grey-200" => $ratio <= 0, + "text-yellow-600" => 0 < $ratio && $ratio < 75, "text-green-600" => $ratio >= 75 && $ratio <= 100, "text-red-600" => $ratio > 100 ])> @@ -481,7 +484,7 @@ class="inline-flex items-center text-indigo-600 hover:text-indigo-800 transition

    {{ __('project.view.description.heading') }}

    @empty($project->beschreibung) -

    {{ __('project.view.description.none') }}

    + @else

    {!! Str::markdown($project->beschreibung) !!} From 2069909722cbcf4a9a8c4167c351bd67a644b794 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Wed, 10 Dec 2025 11:21:46 +0100 Subject: [PATCH 090/108] composer update and small fixes --- app/Http/Middleware/Authenticate.php | 2 +- composer.json | 10 +- composer.lock | 1710 +++++++++++++++----------- config/app.php | 2 +- 4 files changed, 1026 insertions(+), 698 deletions(-) diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index fa5fa4ba..2a7536f0 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -11,7 +11,7 @@ class Authenticate extends Middleware { #[\Override] - public function handle(Request $request, Closure $next, ...$guards): Response + public function handle($request, Closure $next, ...$guards): Response { // do it like in the parent $this->authenticate($request, $guards); diff --git a/composer.json b/composer.json index ed22d9be..aef7bf33 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "require-dev": { "barryvdh/laravel-debugbar": "^3.15", "barryvdh/laravel-ide-helper": "^3.5", - "driftingly/rector-laravel": "^1.2", + "driftingly/rector-laravel": "^2.1", "fakerphp/faker": "^1.23", "larastan/larastan": "^3.1", "laravel/pint": "^1.20", @@ -49,10 +49,10 @@ "magentron/laravel-blade-lint": "^2.0", "mockery/mockery": "^1.6", "nunomaduro/collision": "^8.6", - "pestphp/pest": "^3.0", - "pestphp/pest-plugin-laravel": "^3.1", - "pestphp/pest-plugin-livewire": "^2.1", - "rector/rector": "^1.2", + "pestphp/pest": "^4.0", + "pestphp/pest-plugin-laravel": "^4.0", + "pestphp/pest-plugin-livewire": "^v4.0", + "rector/rector": "^2.2", "roave/security-advisories": "dev-latest", "laravel/pail": "^1.2.2" }, diff --git a/composer.lock b/composer.lock index 8a78ae3f..73e638d6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d5fd08169bcad2301477195c7e75cbff", + "content-hash": "8954582024b952d6f36972d0d5e0496f", "packages": [ { "name": "blade-ui-kit/blade-heroicons", @@ -2064,20 +2064,20 @@ }, { "name": "laravel/framework", - "version": "v11.47.0", + "version": "v12.42.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "86693ffa1ba32f56f8c44e31416c6665095a62c5" + "reference": "509b33095564c5165366d81bbaa0afaac28abe75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/86693ffa1ba32f56f8c44e31416c6665095a62c5", - "reference": "86693ffa1ba32f56f8c44e31416c6665095a62c5", + "url": "https://api.github.com/repos/laravel/framework/zipball/509b33095564c5165366d81bbaa0afaac28abe75", + "reference": "509b33095564c5165366d81bbaa0afaac28abe75", "shasum": "" }, "require": { - "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12|^0.13|^0.14", + "brick/math": "^0.11|^0.12|^0.13|^0.14", "composer-runtime-api": "^2.2", "doctrine/inflector": "^2.0.5", "dragonmantank/cron-expression": "^3.4", @@ -2092,32 +2092,34 @@ "fruitcake/php-cors": "^1.3", "guzzlehttp/guzzle": "^7.8.2", "guzzlehttp/uri-template": "^1.0", - "laravel/prompts": "^0.1.18|^0.2.0|^0.3.0", + "laravel/prompts": "^0.3.0", "laravel/serializable-closure": "^1.3|^2.0", "league/commonmark": "^2.7", "league/flysystem": "^3.25.1", "league/flysystem-local": "^3.25.1", "league/uri": "^7.5.1", "monolog/monolog": "^3.0", - "nesbot/carbon": "^2.72.6|^3.8.4", + "nesbot/carbon": "^3.8.4", "nunomaduro/termwind": "^2.0", "php": "^8.2", "psr/container": "^1.1.1|^2.0.1", "psr/log": "^1.0|^2.0|^3.0", "psr/simple-cache": "^1.0|^2.0|^3.0", "ramsey/uuid": "^4.7", - "symfony/console": "^7.0.3", - "symfony/error-handler": "^7.0.3", - "symfony/finder": "^7.0.3", + "symfony/console": "^7.2.0", + "symfony/error-handler": "^7.2.0", + "symfony/finder": "^7.2.0", "symfony/http-foundation": "^7.2.0", - "symfony/http-kernel": "^7.0.3", - "symfony/mailer": "^7.0.3", - "symfony/mime": "^7.0.3", - "symfony/polyfill-php83": "^1.31", - "symfony/process": "^7.0.3", - "symfony/routing": "^7.0.3", - "symfony/uid": "^7.0.3", - "symfony/var-dumper": "^7.0.3", + "symfony/http-kernel": "^7.2.0", + "symfony/mailer": "^7.2.0", + "symfony/mime": "^7.2.0", + "symfony/polyfill-php83": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/polyfill-php85": "^1.33", + "symfony/process": "^7.2.0", + "symfony/routing": "^7.2.0", + "symfony/uid": "^7.2.0", + "symfony/var-dumper": "^7.2.0", "tijsverkoyen/css-to-inline-styles": "^2.2.5", "vlucas/phpdotenv": "^5.6.1", "voku/portable-ascii": "^2.0.2" @@ -2149,6 +2151,7 @@ "illuminate/filesystem": "self.version", "illuminate/hashing": "self.version", "illuminate/http": "self.version", + "illuminate/json-schema": "self.version", "illuminate/log": "self.version", "illuminate/macroable": "self.version", "illuminate/mail": "self.version", @@ -2158,6 +2161,7 @@ "illuminate/process": "self.version", "illuminate/queue": "self.version", "illuminate/redis": "self.version", + "illuminate/reflection": "self.version", "illuminate/routing": "self.version", "illuminate/session": "self.version", "illuminate/support": "self.version", @@ -2181,17 +2185,18 @@ "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "orchestra/testbench-core": "^9.16.1", - "pda/pheanstalk": "^5.0.6", + "opis/json-schema": "^2.4.1", + "orchestra/testbench-core": "^10.8.1", + "pda/pheanstalk": "^5.0.6|^7.0.0", "php-http/discovery": "^1.15", "phpstan/phpstan": "^2.0", - "phpunit/phpunit": "^10.5.35|^11.3.6|^12.0.1", - "predis/predis": "^2.3", - "resend/resend-php": "^0.10.0", - "symfony/cache": "^7.0.3", - "symfony/http-client": "^7.0.3", - "symfony/psr-http-message-bridge": "^7.0.3", - "symfony/translation": "^7.0.3" + "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", + "predis/predis": "^2.3|^3.0", + "resend/resend-php": "^0.10.0|^1.0", + "symfony/cache": "^7.2.0", + "symfony/http-client": "^7.2.0", + "symfony/psr-http-message-bridge": "^7.2.0", + "symfony/translation": "^7.2.0" }, "suggest": { "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", @@ -2206,7 +2211,7 @@ "ext-pdo": "Required to use all database features.", "ext-posix": "Required to use all features of the queue worker.", "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).", - "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).", "filp/whoops": "Required for friendly error pages in development (^2.14.3).", "laravel/tinker": "Required to use the tinker console command (^2.0).", "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", @@ -2217,22 +2222,22 @@ "mockery/mockery": "Required to use mocking (^1.6).", "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", - "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.3.6|^12.0.1).", - "predis/predis": "Required to use the predis connector (^2.3).", + "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).", + "predis/predis": "Required to use the predis connector (^2.3|^3.0).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", - "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^7.0).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^7.0).", - "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.0).", - "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.0).", - "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.0).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.0)." + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0|^1.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -2243,6 +2248,7 @@ "src/Illuminate/Filesystem/functions.php", "src/Illuminate/Foundation/helpers.php", "src/Illuminate/Log/functions.php", + "src/Illuminate/Reflection/helpers.php", "src/Illuminate/Support/functions.php", "src/Illuminate/Support/helpers.php" ], @@ -2251,7 +2257,8 @@ "Illuminate\\Support\\": [ "src/Illuminate/Macroable/", "src/Illuminate/Collections/", - "src/Illuminate/Conditionable/" + "src/Illuminate/Conditionable/", + "src/Illuminate/Reflection/" ] } }, @@ -2275,7 +2282,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-11-28T18:20:11+00:00" + "time": "2025-12-09T15:51:23+00:00" }, { "name": "laravel/prompts", @@ -2399,16 +2406,16 @@ }, { "name": "laravel/socialite", - "version": "v5.23.2", + "version": "v5.24.0", "source": { "type": "git", "url": "https://github.com/laravel/socialite.git", - "reference": "41e65d53762d33d617bf0253330d672cb95e624b" + "reference": "1d19358c28e8951dde6e36603b89d8f09e6cfbfd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/socialite/zipball/41e65d53762d33d617bf0253330d672cb95e624b", - "reference": "41e65d53762d33d617bf0253330d672cb95e624b", + "url": "https://api.github.com/repos/laravel/socialite/zipball/1d19358c28e8951dde6e36603b89d8f09e6cfbfd", + "reference": "1d19358c28e8951dde6e36603b89d8f09e6cfbfd", "shasum": "" }, "require": { @@ -2467,7 +2474,7 @@ "issues": "https://github.com/laravel/socialite/issues", "source": "https://github.com/laravel/socialite" }, - "time": "2025-11-21T14:00:38+00:00" + "time": "2025-12-09T15:37:06+00:00" }, { "name": "league/commonmark", @@ -2924,20 +2931,20 @@ }, { "name": "league/uri", - "version": "7.6.0", + "version": "7.7.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "f625804987a0a9112d954f9209d91fec52182344" + "reference": "8d587cddee53490f9b82bf203d3a9aa7ea4f9807" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/f625804987a0a9112d954f9209d91fec52182344", - "reference": "f625804987a0a9112d954f9209d91fec52182344", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/8d587cddee53490f9b82bf203d3a9aa7ea4f9807", + "reference": "8d587cddee53490f9b82bf203d3a9aa7ea4f9807", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.6", + "league/uri-interfaces": "^7.7", "php": "^8.1", "psr/http-factory": "^1" }, @@ -3010,7 +3017,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.6.0" + "source": "https://github.com/thephpleague/uri/tree/7.7.0" }, "funding": [ { @@ -3018,20 +3025,20 @@ "type": "github" } ], - "time": "2025-11-18T12:17:23+00:00" + "time": "2025-12-07T16:02:06+00:00" }, { "name": "league/uri-interfaces", - "version": "7.6.0", + "version": "7.7.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "ccbfb51c0445298e7e0b7f4481b942f589665368" + "reference": "62ccc1a0435e1c54e10ee6022df28d6c04c2946c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/ccbfb51c0445298e7e0b7f4481b942f589665368", - "reference": "ccbfb51c0445298e7e0b7f4481b942f589665368", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/62ccc1a0435e1c54e10ee6022df28d6c04c2946c", + "reference": "62ccc1a0435e1c54e10ee6022df28d6c04c2946c", "shasum": "" }, "require": { @@ -3094,7 +3101,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.6.0" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.7.0" }, "funding": [ { @@ -3102,7 +3109,7 @@ "type": "github" } ], - "time": "2025-11-18T12:17:23+00:00" + "time": "2025-12-07T16:03:21+00:00" }, { "name": "livewire/flux", @@ -3402,16 +3409,16 @@ }, { "name": "maennchen/zipstream-php", - "version": "3.2.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/maennchen/ZipStream-PHP.git", - "reference": "9712d8fa4cdf9240380b01eb4be55ad8dcf71416" + "reference": "682f1098a8fddbaf43edac2306a691c7ad508ec5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/9712d8fa4cdf9240380b01eb4be55ad8dcf71416", - "reference": "9712d8fa4cdf9240380b01eb4be55ad8dcf71416", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/682f1098a8fddbaf43edac2306a691c7ad508ec5", + "reference": "682f1098a8fddbaf43edac2306a691c7ad508ec5", "shasum": "" }, "require": { @@ -3422,7 +3429,7 @@ "require-dev": { "brianium/paratest": "^7.7", "ext-zip": "*", - "friendsofphp/php-cs-fixer": "^3.16", + "friendsofphp/php-cs-fixer": "^3.86", "guzzlehttp/guzzle": "^7.5", "mikey179/vfsstream": "^1.6", "php-coveralls/php-coveralls": "^2.5", @@ -3468,7 +3475,7 @@ ], "support": { "issues": "https://github.com/maennchen/ZipStream-PHP/issues", - "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.0" + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.1" }, "funding": [ { @@ -3476,7 +3483,7 @@ "type": "github" } ], - "time": "2025-07-17T11:15:13+00:00" + "time": "2025-12-10T09:58:31+00:00" }, { "name": "markbaker/complex", @@ -5553,16 +5560,16 @@ }, { "name": "spatie/db-dumper", - "version": "3.8.1", + "version": "3.8.2", "source": { "type": "git", "url": "https://github.com/spatie/db-dumper.git", - "reference": "e974cc7862b8de1bd3b7ea7d4839ba7167acb546" + "reference": "9519c64e4938f0b9e4498b8a8e572061bc6b7cfb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/db-dumper/zipball/e974cc7862b8de1bd3b7ea7d4839ba7167acb546", - "reference": "e974cc7862b8de1bd3b7ea7d4839ba7167acb546", + "url": "https://api.github.com/repos/spatie/db-dumper/zipball/9519c64e4938f0b9e4498b8a8e572061bc6b7cfb", + "reference": "9519c64e4938f0b9e4498b8a8e572061bc6b7cfb", "shasum": "" }, "require": { @@ -5600,7 +5607,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/db-dumper/tree/3.8.1" + "source": "https://github.com/spatie/db-dumper/tree/3.8.2" }, "funding": [ { @@ -5612,51 +5619,52 @@ "type": "github" } ], - "time": "2025-11-26T09:51:23+00:00" + "time": "2025-12-10T09:29:52+00:00" }, { "name": "spatie/laravel-backup", - "version": "8.8.2", + "version": "9.3.7", "source": { "type": "git", "url": "https://github.com/spatie/laravel-backup.git", - "reference": "5b672713283703a74c629ccd67b1d77eb57e24b9" + "reference": "6aa2d0ef42218ba6c1f627a17ade3e1ffd0e18af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-backup/zipball/5b672713283703a74c629ccd67b1d77eb57e24b9", - "reference": "5b672713283703a74c629ccd67b1d77eb57e24b9", + "url": "https://api.github.com/repos/spatie/laravel-backup/zipball/6aa2d0ef42218ba6c1f627a17ade3e1ffd0e18af", + "reference": "6aa2d0ef42218ba6c1f627a17ade3e1ffd0e18af", "shasum": "" }, "require": { "ext-zip": "^1.14.0", - "illuminate/console": "^10.10.0|^11.0", - "illuminate/contracts": "^10.10.0|^11.0", - "illuminate/events": "^10.10.0|^11.0", - "illuminate/filesystem": "^10.10.0|^11.0", - "illuminate/notifications": "^10.10.0|^11.0", - "illuminate/support": "^10.10.0|^11.0", - "league/flysystem": "^3.0", - "php": "^8.1", - "spatie/db-dumper": "^3.0", - "spatie/laravel-package-tools": "^1.6.2", - "spatie/laravel-signal-aware-command": "^1.2|^2.0", - "spatie/temporary-directory": "^2.0", - "symfony/console": "^6.0|^7.0", - "symfony/finder": "^6.0|^7.0" + "illuminate/console": "^12.40", + "illuminate/contracts": "^12.40", + "illuminate/events": "^12.40", + "illuminate/filesystem": "^12.40", + "illuminate/notifications": "^12.40", + "illuminate/support": "^12.40", + "league/flysystem": "^3.30.2", + "php": "^8.3", + "spatie/db-dumper": "^3.8.1", + "spatie/laravel-package-tools": "^1.92.7", + "spatie/laravel-signal-aware-command": "^2.1", + "spatie/temporary-directory": "^2.3", + "symfony/console": "^7.3.6|^8.0", + "symfony/finder": "^7.3.5|^8.0" }, "require-dev": { "composer-runtime-api": "^2.0", "ext-pcntl": "*", - "larastan/larastan": "^2.7.0", - "laravel/slack-notification-channel": "^2.5|^3.0", - "league/flysystem-aws-s3-v3": "^2.0|^3.0", - "mockery/mockery": "^1.4", - "orchestra/testbench": "^8.0|^9.0", - "pestphp/pest": "^1.20|^2.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-phpunit": "^1.1" + "larastan/larastan": "^3.8", + "laravel/slack-notification-channel": "^3.7", + "league/flysystem-aws-s3-v3": "^3.30.1", + "mockery/mockery": "^1.6.12", + "orchestra/testbench": "^10.8", + "pestphp/pest": "^4.1.5", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.8", + "rector/rector": "^2.2.8" }, "suggest": { "laravel/slack-notification-channel": "Required for sending notifications via Slack" @@ -5699,7 +5707,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-backup/issues", - "source": "https://github.com/spatie/laravel-backup/tree/8.8.2" + "source": "https://github.com/spatie/laravel-backup/tree/9.3.7" }, "funding": [ { @@ -5711,37 +5719,41 @@ "type": "other" } ], - "time": "2024-08-07T11:07:52+00:00" + "time": "2025-11-26T15:43:43+00:00" }, { "name": "spatie/laravel-model-states", - "version": "2.12.1", + "version": "2.12.2", "source": { "type": "git", "url": "https://github.com/spatie/laravel-model-states.git", - "reference": "13cccd3ecb4396c9a3360fb710d9dd40c0f1a1fe" + "reference": "516e66ab310a699f23f2e3e9181fdd55c2376491" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-model-states/zipball/13cccd3ecb4396c9a3360fb710d9dd40c0f1a1fe", - "reference": "13cccd3ecb4396c9a3360fb710d9dd40c0f1a1fe", + "url": "https://api.github.com/repos/spatie/laravel-model-states/zipball/516e66ab310a699f23f2e3e9181fdd55c2376491", + "reference": "516e66ab310a699f23f2e3e9181fdd55c2376491", "shasum": "" }, "require": { "ext-json": "*", - "facade/ignition-contracts": "^1.0", - "illuminate/contracts": "^10.0 | ^11.0 | ^12.0", - "illuminate/database": "^10.0 | ^11.0 | ^12.0", - "illuminate/support": "^10.0 | ^11.0 | ^12.0", - "php": "^7.4|^8.0", + "facade/ignition-contracts": "^1.0.2|^2.0", + "illuminate/contracts": "^12.0", + "illuminate/database": "^12.0", + "illuminate/support": "^12.0", + "php": "^8.4", + "spatie/laravel-package-tools": "^1.19", + "spatie/php-structure-discoverer": "^2.3.1" + }, + "require-dev": { + "illuminate/contracts": "^12.0", + "illuminate/database": "^12.0", + "illuminate/support": "^12.0", + "orchestra/testbench": "^10.0", + "pestphp/pest": "^4.1.4", "spatie/laravel-package-tools": "^1.9", - "spatie/php-structure-discoverer": "^2.2" - }, - "require-dev": { - "orchestra/testbench": "^8.0 | ^9.0 | ^10.0", - "pestphp/pest": "^2.0|^3.0", - "phpunit/phpunit": "^10.0|^11.0|^12.0", - "symfony/var-dumper": "^6.0 | ^7.0" + "spatie/php-structure-discoverer": "^2.2", + "symfony/var-dumper": "^7.3.5|^8.0" }, "type": "library", "extra": { @@ -5775,7 +5787,7 @@ "state" ], "support": { - "source": "https://github.com/spatie/laravel-model-states/tree/2.12.1" + "source": "https://github.com/spatie/laravel-model-states/tree/2.12.2" }, "funding": [ { @@ -5787,7 +5799,7 @@ "type": "github" } ], - "time": "2025-08-01T09:20:29+00:00" + "time": "2025-12-10T09:23:31+00:00" }, { "name": "spatie/laravel-package-tools", @@ -6130,20 +6142,20 @@ }, { "name": "staudenmeir/eloquent-has-many-deep-contracts", - "version": "v1.2.1", + "version": "v1.3", "source": { "type": "git", "url": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts.git", - "reference": "3ad76c6eeda60042f262d113bf471dcce584d88b" + "reference": "37ce351e4db919b3af606bc8ca0e62e2e4939cde" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/staudenmeir/eloquent-has-many-deep-contracts/zipball/3ad76c6eeda60042f262d113bf471dcce584d88b", - "reference": "3ad76c6eeda60042f262d113bf471dcce584d88b", + "url": "https://api.github.com/repos/staudenmeir/eloquent-has-many-deep-contracts/zipball/37ce351e4db919b3af606bc8ca0e62e2e4939cde", + "reference": "37ce351e4db919b3af606bc8ca0e62e2e4939cde", "shasum": "" }, "require": { - "illuminate/database": "^11.0", + "illuminate/database": "^12.0", "php": "^8.2" }, "type": "library", @@ -6165,40 +6177,39 @@ "description": "Contracts for staudenmeir/eloquent-has-many-deep", "support": { "issues": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts/issues", - "source": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts/tree/v1.2.1" + "source": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts/tree/v1.3" }, - "time": "2024-09-25T18:24:22+00:00" + "time": "2025-02-15T17:11:01+00:00" }, { "name": "staudenmeir/laravel-adjacency-list", - "version": "v1.23.5", + "version": "v1.25.2", "source": { "type": "git", "url": "https://github.com/staudenmeir/laravel-adjacency-list.git", - "reference": "297e175c5625564b006059800d59fe8c783ef186" + "reference": "a54c5ff70d2d417251f33dfda5646e84d1c6d55e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/staudenmeir/laravel-adjacency-list/zipball/297e175c5625564b006059800d59fe8c783ef186", - "reference": "297e175c5625564b006059800d59fe8c783ef186", + "url": "https://api.github.com/repos/staudenmeir/laravel-adjacency-list/zipball/a54c5ff70d2d417251f33dfda5646e84d1c6d55e", + "reference": "a54c5ff70d2d417251f33dfda5646e84d1c6d55e", "shasum": "" }, "require": { - "illuminate/database": "^11.0", + "illuminate/database": "^12.0", "php": "^8.2", - "staudenmeir/eloquent-has-many-deep-contracts": "^1.2", - "staudenmeir/laravel-cte": "^1.11" + "staudenmeir/eloquent-has-many-deep-contracts": "^1.3", + "staudenmeir/laravel-cte": "^1.12.1" }, "require-dev": { "barryvdh/laravel-ide-helper": "^3.0", - "harrygulliford/laravel-firebird": "^3.3", "larastan/larastan": "^3.0", - "laravel/framework": "^11.0", + "laravel/framework": "^12.0", "mockery/mockery": "^1.5.1", - "orchestra/testbench-core": "^9.5", + "orchestra/testbench-core": "^10.0", "phpunit/phpunit": "^11.0", - "singlestoredb/singlestoredb-laravel": "^1.5.4", - "staudenmeir/eloquent-has-many-deep": "^1.20" + "singlestoredb/singlestoredb-laravel": "^2.0", + "staudenmeir/eloquent-has-many-deep": "^1.21" }, "suggest": { "barryvdh/laravel-ide-helper": "Provide type hints for attributes and relations." @@ -6229,7 +6240,7 @@ "description": "Recursive Laravel Eloquent relationships with CTEs", "support": { "issues": "https://github.com/staudenmeir/laravel-adjacency-list/issues", - "source": "https://github.com/staudenmeir/laravel-adjacency-list/tree/v1.23.5" + "source": "https://github.com/staudenmeir/laravel-adjacency-list/tree/v1.25.2" }, "funding": [ { @@ -6237,34 +6248,33 @@ "type": "custom" } ], - "time": "2025-07-27T18:09:07+00:00" + "time": "2025-07-27T18:15:05+00:00" }, { "name": "staudenmeir/laravel-cte", - "version": "v1.11.2", + "version": "v1.12.4", "source": { "type": "git", "url": "https://github.com/staudenmeir/laravel-cte.git", - "reference": "81a172596e8e222170e371fd680e153425a9500c" + "reference": "48cdb4d22bbf3dc1bce3314650659f17fd0e223f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/staudenmeir/laravel-cte/zipball/81a172596e8e222170e371fd680e153425a9500c", - "reference": "81a172596e8e222170e371fd680e153425a9500c", + "url": "https://api.github.com/repos/staudenmeir/laravel-cte/zipball/48cdb4d22bbf3dc1bce3314650659f17fd0e223f", + "reference": "48cdb4d22bbf3dc1bce3314650659f17fd0e223f", "shasum": "" }, "require": { - "illuminate/database": "^11.0", + "illuminate/database": "^12.0", "php": "^8.2" }, "require-dev": { - "harrygulliford/laravel-firebird": "^3.3", - "laravel/framework": "^11.0", - "orchestra/testbench-core": "^9.5", + "laravel/framework": "^12.0", + "orchestra/testbench-core": "^10.0", "phpstan/phpstan": "^2.0", "phpunit/phpunit": "^11.0", - "singlestoredb/singlestoredb-laravel": "^1.5.4", - "yajra/laravel-oci8": "^11.2.4" + "singlestoredb/singlestoredb-laravel": "^2.0", + "yajra/laravel-oci8": "^12.0" }, "type": "library", "extra": { @@ -6292,7 +6302,7 @@ "description": "Laravel queries with common table expressions", "support": { "issues": "https://github.com/staudenmeir/laravel-cte/issues", - "source": "https://github.com/staudenmeir/laravel-cte/tree/v1.11.2" + "source": "https://github.com/staudenmeir/laravel-cte/tree/v1.12.4" }, "funding": [ { @@ -6300,7 +6310,7 @@ "type": "custom" } ], - "time": "2024-11-24T13:36:31+00:00" + "time": "2025-10-21T07:12:06+00:00" }, { "name": "symfony/clock", @@ -6381,16 +6391,16 @@ }, { "name": "symfony/console", - "version": "v7.4.0", + "version": "v7.4.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8" + "reference": "6d9f0fbf2ec2e9785880096e3abd0ca0c88b506e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8", - "reference": "0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8", + "url": "https://api.github.com/repos/symfony/console/zipball/6d9f0fbf2ec2e9785880096e3abd0ca0c88b506e", + "reference": "6d9f0fbf2ec2e9785880096e3abd0ca0c88b506e", "shasum": "" }, "require": { @@ -6455,7 +6465,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.0" + "source": "https://github.com/symfony/console/tree/v7.4.1" }, "funding": [ { @@ -6475,7 +6485,7 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:27:24+00:00" + "time": "2025-12-05T15:23:39+00:00" }, { "name": "symfony/css-selector", @@ -6926,16 +6936,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.4.0", + "version": "v7.4.1", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "769c1720b68e964b13b58529c17d4a385c62167b" + "reference": "bd1af1e425811d6f077db240c3a588bdb405cd27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/769c1720b68e964b13b58529c17d4a385c62167b", - "reference": "769c1720b68e964b13b58529c17d4a385c62167b", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/bd1af1e425811d6f077db240c3a588bdb405cd27", + "reference": "bd1af1e425811d6f077db240c3a588bdb405cd27", "shasum": "" }, "require": { @@ -6984,7 +6994,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.4.0" + "source": "https://github.com/symfony/http-foundation/tree/v7.4.1" }, "funding": [ { @@ -7004,20 +7014,20 @@ "type": "tidelift" } ], - "time": "2025-11-13T08:49:24+00:00" + "time": "2025-12-07T11:13:10+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.4.0", + "version": "v7.4.2", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "7348193cd384495a755554382e4526f27c456085" + "reference": "f6e6f0a5fa8763f75a504b930163785fb6dd055f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/7348193cd384495a755554382e4526f27c456085", - "reference": "7348193cd384495a755554382e4526f27c456085", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f6e6f0a5fa8763f75a504b930163785fb6dd055f", + "reference": "f6e6f0a5fa8763f75a504b930163785fb6dd055f", "shasum": "" }, "require": { @@ -7103,7 +7113,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.4.0" + "source": "https://github.com/symfony/http-kernel/tree/v7.4.2" }, "funding": [ { @@ -7123,7 +7133,7 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:38:24+00:00" + "time": "2025-12-08T07:43:37+00:00" }, { "name": "symfony/mailer", @@ -7884,6 +7894,86 @@ ], "time": "2025-07-08T02:45:35+00:00" }, + { + "name": "symfony/polyfill-php84", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-24T13:30:11+00:00" + }, { "name": "symfony/polyfill-php85", "version": "v1.33.0", @@ -8286,16 +8376,16 @@ }, { "name": "symfony/string", - "version": "v8.0.0", + "version": "v8.0.1", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f929eccf09531078c243df72398560e32fa4cf4f" + "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f929eccf09531078c243df72398560e32fa4cf4f", - "reference": "f929eccf09531078c243df72398560e32fa4cf4f", + "url": "https://api.github.com/repos/symfony/string/zipball/ba65a969ac918ce0cc3edfac6cdde847eba231dc", + "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc", "shasum": "" }, "require": { @@ -8352,7 +8442,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.0" + "source": "https://github.com/symfony/string/tree/v8.0.1" }, "funding": [ { @@ -8372,20 +8462,20 @@ "type": "tidelift" } ], - "time": "2025-09-11T14:37:55+00:00" + "time": "2025-12-01T09:13:36+00:00" }, { "name": "symfony/translation", - "version": "v8.0.0", + "version": "v8.0.1", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "82ab368a6fca6358d995b6dd5c41590fb42c03e6" + "reference": "770e3b8b0ba8360958abedcabacd4203467333ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/82ab368a6fca6358d995b6dd5c41590fb42c03e6", - "reference": "82ab368a6fca6358d995b6dd5c41590fb42c03e6", + "url": "https://api.github.com/repos/symfony/translation/zipball/770e3b8b0ba8360958abedcabacd4203467333ca", + "reference": "770e3b8b0ba8360958abedcabacd4203467333ca", "shasum": "" }, "require": { @@ -8445,7 +8535,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v8.0.0" + "source": "https://github.com/symfony/translation/tree/v8.0.1" }, "funding": [ { @@ -8465,7 +8555,7 @@ "type": "tidelift" } ], - "time": "2025-11-27T08:09:45+00:00" + "time": "2025-12-01T09:13:36+00:00" }, { "name": "symfony/translation-contracts", @@ -9095,16 +9185,16 @@ }, { "name": "barryvdh/laravel-ide-helper", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "8d00250cba25728373e92c1d8dcebcbf64623d29" + "reference": "b106f7ee85f263c4f103eca49e7bf3862c2e5e75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/8d00250cba25728373e92c1d8dcebcbf64623d29", - "reference": "8d00250cba25728373e92c1d8dcebcbf64623d29", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/b106f7ee85f263c4f103eca49e7bf3862c2e5e75", + "reference": "b106f7ee85f263c4f103eca49e7bf3862c2e5e75", "shasum": "" }, "require": { @@ -9173,7 +9263,7 @@ ], "support": { "issues": "https://github.com/barryvdh/laravel-ide-helper/issues", - "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.6.0" + "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.6.1" }, "funding": [ { @@ -9185,7 +9275,7 @@ "type": "github" } ], - "time": "2025-07-17T20:11:57+00:00" + "time": "2025-12-10T09:11:07+00:00" }, { "name": "barryvdh/reflection-docblock", @@ -9241,16 +9331,16 @@ }, { "name": "brianium/paratest", - "version": "v7.4.8", + "version": "v7.15.0", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "cf16fcbb9b8107a7df6b97e497fc91e819774d8b" + "reference": "272ff9d59b2ed0bd97c86c3cfe97c9784dabf786" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/cf16fcbb9b8107a7df6b97e497fc91e819774d8b", - "reference": "cf16fcbb9b8107a7df6b97e497fc91e819774d8b", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/272ff9d59b2ed0bd97c86c3cfe97c9784dabf786", + "reference": "272ff9d59b2ed0bd97c86c3cfe97c9784dabf786", "shasum": "" }, "require": { @@ -9258,27 +9348,27 @@ "ext-pcre": "*", "ext-reflection": "*", "ext-simplexml": "*", - "fidry/cpu-core-counter": "^1.2.0", - "jean85/pretty-package-versions": "^2.0.6", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "phpunit/php-code-coverage": "^10.1.16", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-timer": "^6.0.0", - "phpunit/phpunit": "^10.5.36", - "sebastian/environment": "^6.1.0", - "symfony/console": "^6.4.7 || ^7.1.5", - "symfony/process": "^6.4.7 || ^7.1.5" - }, - "require-dev": { - "doctrine/coding-standard": "^12.0.0", + "fidry/cpu-core-counter": "^1.3.0", + "jean85/pretty-package-versions": "^2.1.1", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "phpunit/php-code-coverage": "^12.5.0", + "phpunit/php-file-iterator": "^6", + "phpunit/php-timer": "^8", + "phpunit/phpunit": "^12.4.4", + "sebastian/environment": "^8.0.3", + "symfony/console": "^7.3.4 || ^8.0.0", + "symfony/process": "^7.3.4 || ^8.0.0" + }, + "require-dev": { + "doctrine/coding-standard": "^14.0.0", + "ext-pcntl": "*", "ext-pcov": "*", "ext-posix": "*", - "phpstan/phpstan": "^1.12.6", - "phpstan/phpstan-deprecation-rules": "^1.2.1", - "phpstan/phpstan-phpunit": "^1.4.0", - "phpstan/phpstan-strict-rules": "^1.6.1", - "squizlabs/php_codesniffer": "^3.10.3", - "symfony/filesystem": "^6.4.3 || ^7.1.5" + "phpstan/phpstan": "^2.1.32", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.8", + "phpstan/phpstan-strict-rules": "^2.0.7", + "symfony/filesystem": "^7.3.2 || ^8.0.0" }, "bin": [ "bin/paratest", @@ -9318,7 +9408,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.4.8" + "source": "https://github.com/paratestphp/paratest/tree/v7.15.0" }, "funding": [ { @@ -9330,7 +9420,7 @@ "type": "paypal" } ], - "time": "2024-10-15T12:45:19+00:00" + "time": "2025-11-30T08:08:11+00:00" }, { "name": "composer/class-map-generator", @@ -9451,21 +9541,22 @@ }, { "name": "driftingly/rector-laravel", - "version": "1.2.6", + "version": "2.1.6", "source": { "type": "git", "url": "https://github.com/driftingly/rector-laravel.git", - "reference": "010e050488e0c1ec305736b081db04d9b834c709" + "reference": "682d1e73ac79aced7e645141fd61a9ac468a0c44" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/010e050488e0c1ec305736b081db04d9b834c709", - "reference": "010e050488e0c1ec305736b081db04d9b834c709", + "url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/682d1e73ac79aced7e645141fd61a9ac468a0c44", + "reference": "682d1e73ac79aced7e645141fd61a9ac468a0c44", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "rector/rector": "^1.0" + "php": "^7.4 || ^8.0", + "rector/rector": "^2.2.7", + "webmozart/assert": "^1.11" }, "type": "rector-extension", "autoload": { @@ -9480,9 +9571,9 @@ "description": "Rector upgrades rules for Laravel Framework", "support": { "issues": "https://github.com/driftingly/rector-laravel/issues", - "source": "https://github.com/driftingly/rector-laravel/tree/1.2.6" + "source": "https://github.com/driftingly/rector-laravel/tree/2.1.6" }, - "time": "2024-12-05T17:29:03+00:00" + "time": "2025-12-04T13:37:33+00:00" }, { "name": "fakerphp/faker", @@ -9732,16 +9823,16 @@ }, { "name": "iamcal/sql-parser", - "version": "v0.5", + "version": "v0.6", "source": { "type": "git", "url": "https://github.com/iamcal/SQLParser.git", - "reference": "644fd994de3b54e5d833aecf406150aa3b66ca88" + "reference": "947083e2dca211a6f12fb1beb67a01e387de9b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/iamcal/SQLParser/zipball/644fd994de3b54e5d833aecf406150aa3b66ca88", - "reference": "644fd994de3b54e5d833aecf406150aa3b66ca88", + "url": "https://api.github.com/repos/iamcal/SQLParser/zipball/947083e2dca211a6f12fb1beb67a01e387de9b62", + "reference": "947083e2dca211a6f12fb1beb67a01e387de9b62", "shasum": "" }, "require-dev": { @@ -9767,9 +9858,9 @@ "description": "MySQL schema parser", "support": { "issues": "https://github.com/iamcal/SQLParser/issues", - "source": "https://github.com/iamcal/SQLParser/tree/v0.5" + "source": "https://github.com/iamcal/SQLParser/tree/v0.6" }, - "time": "2024-03-22T22:46:32+00:00" + "time": "2025-03-17T16:59:46+00:00" }, { "name": "jean85/pretty-package-versions", @@ -9833,43 +9924,44 @@ }, { "name": "larastan/larastan", - "version": "v2.11.2", + "version": "v3.8.0", "source": { "type": "git", "url": "https://github.com/larastan/larastan.git", - "reference": "1aae902a5851c03dc1a58cbd9010a0c3ef8def63" + "reference": "d13ef96d652d1b2a8f34f1760ba6bf5b9c98112e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/larastan/larastan/zipball/1aae902a5851c03dc1a58cbd9010a0c3ef8def63", - "reference": "1aae902a5851c03dc1a58cbd9010a0c3ef8def63", + "url": "https://api.github.com/repos/larastan/larastan/zipball/d13ef96d652d1b2a8f34f1760ba6bf5b9c98112e", + "reference": "d13ef96d652d1b2a8f34f1760ba6bf5b9c98112e", "shasum": "" }, "require": { "ext-json": "*", - "iamcal/sql-parser": "^0.5.0", - "illuminate/console": "^9.52.20 || ^10.48.28 || ^11.41.3", - "illuminate/container": "^9.52.20 || ^10.48.28 || ^11.41.3", - "illuminate/contracts": "^9.52.20 || ^10.48.28 || ^11.41.3", - "illuminate/database": "^9.52.20 || ^10.48.28 || ^11.41.3", - "illuminate/http": "^9.52.20 || ^10.48.28 || ^11.41.3", - "illuminate/pipeline": "^9.52.20 || ^10.48.28 || ^11.41.3", - "illuminate/support": "^9.52.20 || ^10.48.28 || ^11.41.3", - "php": "^8.0.2", - "phpstan/phpstan": "^1.12.17" + "iamcal/sql-parser": "^0.6.0", + "illuminate/console": "^11.44.2 || ^12.4.1", + "illuminate/container": "^11.44.2 || ^12.4.1", + "illuminate/contracts": "^11.44.2 || ^12.4.1", + "illuminate/database": "^11.44.2 || ^12.4.1", + "illuminate/http": "^11.44.2 || ^12.4.1", + "illuminate/pipeline": "^11.44.2 || ^12.4.1", + "illuminate/support": "^11.44.2 || ^12.4.1", + "php": "^8.2", + "phpstan/phpstan": "^2.1.29" }, "require-dev": { "doctrine/coding-standard": "^13", - "laravel/framework": "^9.52.20 || ^10.48.28 || ^11.41.3", - "mockery/mockery": "^1.5.1", - "nikic/php-parser": "^4.19.1", - "orchestra/canvas": "^7.11.1 || ^8.11.0 || ^9.0.2", - "orchestra/testbench-core": "^7.33.0 || ^8.13.0 || ^9.0.9", - "phpstan/phpstan-deprecation-rules": "^1.2", - "phpunit/phpunit": "^9.6.13 || ^10.5.16" + "laravel/framework": "^11.44.2 || ^12.7.2", + "mockery/mockery": "^1.6.12", + "nikic/php-parser": "^5.4", + "orchestra/canvas": "^v9.2.2 || ^10.0.1", + "orchestra/testbench-core": "^9.12.0 || ^10.1", + "phpstan/phpstan-deprecation-rules": "^2.0.1", + "phpunit/phpunit": "^10.5.35 || ^11.5.15" }, "suggest": { - "orchestra/testbench": "Using Larastan for analysing a package needs Testbench" + "orchestra/testbench": "Using Larastan for analysing a package needs Testbench", + "phpmyadmin/sql-parser": "Install to enable Larastan's optional phpMyAdmin-based SQL parser automatically" }, "type": "phpstan-extension", "extra": { @@ -9879,7 +9971,7 @@ ] }, "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -9910,7 +10002,7 @@ ], "support": { "issues": "https://github.com/larastan/larastan/issues", - "source": "https://github.com/larastan/larastan/tree/v2.11.2" + "source": "https://github.com/larastan/larastan/tree/v3.8.0" }, "funding": [ { @@ -9918,7 +10010,86 @@ "type": "github" } ], - "time": "2025-06-10T22:06:33+00:00" + "time": "2025-10-27T23:09:14+00:00" + }, + { + "name": "laravel/pail", + "version": "v1.2.4", + "source": { + "type": "git", + "url": "https://github.com/laravel/pail.git", + "reference": "49f92285ff5d6fc09816e976a004f8dec6a0ea30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pail/zipball/49f92285ff5d6fc09816e976a004f8dec6a0ea30", + "reference": "49f92285ff5d6fc09816e976a004f8dec6a0ea30", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/console": "^10.24|^11.0|^12.0", + "illuminate/contracts": "^10.24|^11.0|^12.0", + "illuminate/log": "^10.24|^11.0|^12.0", + "illuminate/process": "^10.24|^11.0|^12.0", + "illuminate/support": "^10.24|^11.0|^12.0", + "nunomaduro/termwind": "^1.15|^2.0", + "php": "^8.2", + "symfony/console": "^6.0|^7.0" + }, + "require-dev": { + "laravel/framework": "^10.24|^11.0|^12.0", + "laravel/pint": "^1.13", + "orchestra/testbench-core": "^8.13|^9.17|^10.8", + "pestphp/pest": "^2.20|^3.0|^4.0", + "pestphp/pest-plugin-type-coverage": "^2.3|^3.0|^4.0", + "phpstan/phpstan": "^1.12.27", + "symfony/var-dumper": "^6.3|^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Pail\\PailServiceProvider" + ] + }, + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Pail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Easily delve into your Laravel application's log files directly from the command line.", + "homepage": "https://github.com/laravel/pail", + "keywords": [ + "dev", + "laravel", + "logs", + "php", + "tail" + ], + "support": { + "issues": "https://github.com/laravel/pail/issues", + "source": "https://github.com/laravel/pail" + }, + "time": "2025-11-20T16:29:35+00:00" }, { "name": "laravel/pint", @@ -9989,16 +10160,16 @@ }, { "name": "laravel/sail", - "version": "v1.50.0", + "version": "v1.51.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "9177d5de1c8247166b92ea6049c2b069d2a1802f" + "reference": "1c74357df034e869250b4365dd445c9f6ba5d068" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/9177d5de1c8247166b92ea6049c2b069d2a1802f", - "reference": "9177d5de1c8247166b92ea6049c2b069d2a1802f", + "url": "https://api.github.com/repos/laravel/sail/zipball/1c74357df034e869250b4365dd445c9f6ba5d068", + "reference": "1c74357df034e869250b4365dd445c9f6ba5d068", "shasum": "" }, "require": { @@ -10048,7 +10219,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2025-12-03T17:16:36+00:00" + "time": "2025-12-09T13:33:49+00:00" }, { "name": "laravel/tinker", @@ -10446,38 +10617,39 @@ }, { "name": "nunomaduro/collision", - "version": "v8.5.0", + "version": "v8.8.3", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "f5c101b929c958e849a633283adff296ed5f38f5" + "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/f5c101b929c958e849a633283adff296ed5f38f5", - "reference": "f5c101b929c958e849a633283adff296ed5f38f5", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/1dc9e88d105699d0fee8bb18890f41b274f6b4c4", + "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4", "shasum": "" }, "require": { - "filp/whoops": "^2.16.0", - "nunomaduro/termwind": "^2.1.0", + "filp/whoops": "^2.18.1", + "nunomaduro/termwind": "^2.3.1", "php": "^8.2.0", - "symfony/console": "^7.1.5" + "symfony/console": "^7.3.0" }, "conflict": { - "laravel/framework": "<11.0.0 || >=12.0.0", - "phpunit/phpunit": "<10.5.1 || >=12.0.0" + "laravel/framework": "<11.44.2 || >=13.0.0", + "phpunit/phpunit": "<11.5.15 || >=13.0.0" }, "require-dev": { - "larastan/larastan": "^2.9.8", - "laravel/framework": "^11.28.0", - "laravel/pint": "^1.18.1", - "laravel/sail": "^1.36.0", - "laravel/sanctum": "^4.0.3", - "laravel/tinker": "^2.10.0", - "orchestra/testbench-core": "^9.5.3", - "pestphp/pest": "^2.36.0 || ^3.4.0", - "sebastian/environment": "^6.1.0 || ^7.2.0" + "brianium/paratest": "^7.8.3", + "larastan/larastan": "^3.4.2", + "laravel/framework": "^11.44.2 || ^12.18", + "laravel/pint": "^1.22.1", + "laravel/sail": "^1.43.1", + "laravel/sanctum": "^4.1.1", + "laravel/tinker": "^2.10.1", + "orchestra/testbench-core": "^9.12.0 || ^10.4", + "pestphp/pest": "^3.8.2 || ^4.0.0", + "sebastian/environment": "^7.2.1 || ^8.0" }, "type": "library", "extra": { @@ -10514,6 +10686,7 @@ "cli", "command-line", "console", + "dev", "error", "handling", "laravel", @@ -10539,41 +10712,45 @@ "type": "patreon" } ], - "time": "2024-10-15T16:06:32+00:00" + "time": "2025-11-20T02:55:25+00:00" }, { "name": "pestphp/pest", - "version": "v2.36.0", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/pestphp/pest.git", - "reference": "f8c88bd14dc1772bfaf02169afb601ecdf2724cd" + "reference": "ae419afd363299c29ad5b17e8b70d118b1068bb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/f8c88bd14dc1772bfaf02169afb601ecdf2724cd", - "reference": "f8c88bd14dc1772bfaf02169afb601ecdf2724cd", + "url": "https://api.github.com/repos/pestphp/pest/zipball/ae419afd363299c29ad5b17e8b70d118b1068bb4", + "reference": "ae419afd363299c29ad5b17e8b70d118b1068bb4", "shasum": "" }, "require": { - "brianium/paratest": "^7.3.1", - "nunomaduro/collision": "^7.11.0|^8.4.0", - "nunomaduro/termwind": "^1.16.0|^2.1.0", - "pestphp/pest-plugin": "^2.1.1", - "pestphp/pest-plugin-arch": "^2.7.0", - "php": "^8.1.0", - "phpunit/phpunit": "^10.5.36" + "brianium/paratest": "^7.14.2", + "nunomaduro/collision": "^8.8.3", + "nunomaduro/termwind": "^2.3.3", + "pestphp/pest-plugin": "^4.0.0", + "pestphp/pest-plugin-arch": "^4.0.0", + "pestphp/pest-plugin-mutate": "^4.0.1", + "pestphp/pest-plugin-profanity": "^4.2.0", + "php": "^8.3.0", + "phpunit/phpunit": "^12.4.4", + "symfony/process": "^7.4.0|^8.0.0" }, "conflict": { - "filp/whoops": "<2.16.0", - "phpunit/phpunit": ">10.5.36", - "sebastian/exporter": "<5.1.0", + "filp/whoops": "<2.18.3", + "phpunit/phpunit": ">12.4.4", + "sebastian/exporter": "<7.0.0", "webmozart/assert": "<1.11.0" }, "require-dev": { - "pestphp/pest-dev-tools": "^2.17.0", - "pestphp/pest-plugin-type-coverage": "^2.8.7", - "symfony/process": "^6.4.0|^7.1.5" + "pestphp/pest-dev-tools": "^4.0.0", + "pestphp/pest-plugin-browser": "^4.1.1", + "pestphp/pest-plugin-type-coverage": "^4.0.3", + "psy/psysh": "^0.12.15" }, "bin": [ "bin/pest" @@ -10582,6 +10759,8 @@ "extra": { "pest": { "plugins": [ + "Pest\\Mutate\\Plugins\\Mutate", + "Pest\\Plugins\\Configuration", "Pest\\Plugins\\Bail", "Pest\\Plugins\\Cache", "Pest\\Plugins\\Coverage", @@ -10597,6 +10776,7 @@ "Pest\\Plugins\\Snapshot", "Pest\\Plugins\\Verbose", "Pest\\Plugins\\Version", + "Pest\\Plugins\\Shard", "Pest\\Plugins\\Parallel" ] }, @@ -10636,7 +10816,7 @@ ], "support": { "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v2.36.0" + "source": "https://github.com/pestphp/pest/tree/v4.1.6" }, "funding": [ { @@ -10648,34 +10828,34 @@ "type": "github" } ], - "time": "2024-10-15T15:30:56+00:00" + "time": "2025-11-28T12:04:48+00:00" }, { "name": "pestphp/pest-plugin", - "version": "v2.1.1", + "version": "v4.0.0", "source": { "type": "git", "url": "https://github.com/pestphp/pest-plugin.git", - "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b" + "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e05d2859e08c2567ee38ce8b005d044e72648c0b", - "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/9d4b93d7f73d3f9c3189bb22c220fef271cdf568", + "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568", "shasum": "" }, "require": { "composer-plugin-api": "^2.0.0", "composer-runtime-api": "^2.2.2", - "php": "^8.1" + "php": "^8.3" }, "conflict": { - "pestphp/pest": "<2.2.3" + "pestphp/pest": "<4.0.0" }, "require-dev": { - "composer/composer": "^2.5.8", - "pestphp/pest": "^2.16.0", - "pestphp/pest-dev-tools": "^2.16.0" + "composer/composer": "^2.8.10", + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" }, "type": "composer-plugin", "extra": { @@ -10702,7 +10882,7 @@ "unit" ], "support": { - "source": "https://github.com/pestphp/pest-plugin/tree/v2.1.1" + "source": "https://github.com/pestphp/pest-plugin/tree/v4.0.0" }, "funding": [ { @@ -10718,31 +10898,30 @@ "type": "patreon" } ], - "time": "2023-08-22T08:40:06+00:00" + "time": "2025-08-20T12:35:58+00:00" }, { "name": "pestphp/pest-plugin-arch", - "version": "v2.7.0", + "version": "v4.0.0", "source": { "type": "git", "url": "https://github.com/pestphp/pest-plugin-arch.git", - "reference": "d23b2d7498475354522c3818c42ef355dca3fcda" + "reference": "25bb17e37920ccc35cbbcda3b00d596aadf3e58d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/d23b2d7498475354522c3818c42ef355dca3fcda", - "reference": "d23b2d7498475354522c3818c42ef355dca3fcda", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/25bb17e37920ccc35cbbcda3b00d596aadf3e58d", + "reference": "25bb17e37920ccc35cbbcda3b00d596aadf3e58d", "shasum": "" }, "require": { - "nunomaduro/collision": "^7.10.0|^8.1.0", - "pestphp/pest-plugin": "^2.1.1", - "php": "^8.1", - "ta-tikoma/phpunit-architecture-test": "^0.8.4" + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "ta-tikoma/phpunit-architecture-test": "^0.8.5" }, "require-dev": { - "pestphp/pest": "^2.33.0", - "pestphp/pest-dev-tools": "^2.16.0" + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" }, "type": "library", "extra": { @@ -10777,7 +10956,7 @@ "unit" ], "support": { - "source": "https://github.com/pestphp/pest-plugin-arch/tree/v2.7.0" + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v4.0.0" }, "funding": [ { @@ -10789,31 +10968,31 @@ "type": "github" } ], - "time": "2024-01-26T09:46:42+00:00" + "time": "2025-08-20T13:10:51+00:00" }, { "name": "pestphp/pest-plugin-laravel", - "version": "v2.4.0", + "version": "v4.0.0", "source": { "type": "git", "url": "https://github.com/pestphp/pest-plugin-laravel.git", - "reference": "53df51169a7f9595e06839cce638c73e59ace5e8" + "reference": "e12a07046b826a40b1c8632fd7b80d6b8d7b628e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-laravel/zipball/53df51169a7f9595e06839cce638c73e59ace5e8", - "reference": "53df51169a7f9595e06839cce638c73e59ace5e8", + "url": "https://api.github.com/repos/pestphp/pest-plugin-laravel/zipball/e12a07046b826a40b1c8632fd7b80d6b8d7b628e", + "reference": "e12a07046b826a40b1c8632fd7b80d6b8d7b628e", "shasum": "" }, "require": { - "laravel/framework": "^10.48.9|^11.5.0", - "pestphp/pest": "^2.34.7", - "php": "^8.1.0" + "laravel/framework": "^11.45.2|^12.25.0", + "pestphp/pest": "^4.0.0", + "php": "^8.3.0" }, "require-dev": { - "laravel/dusk": "^7.13.0", - "orchestra/testbench": "^8.22.3|^9.0.4", - "pestphp/pest-dev-tools": "^2.16.0" + "laravel/dusk": "^8.3.3", + "orchestra/testbench": "^9.13.0|^10.5.0", + "pestphp/pest-dev-tools": "^4.0.0" }, "type": "library", "extra": { @@ -10851,7 +11030,7 @@ "unit" ], "support": { - "source": "https://github.com/pestphp/pest-plugin-laravel/tree/v2.4.0" + "source": "https://github.com/pestphp/pest-plugin-laravel/tree/v4.0.0" }, "funding": [ { @@ -10863,30 +11042,30 @@ "type": "github" } ], - "time": "2024-04-27T10:41:54+00:00" + "time": "2025-08-20T12:46:37+00:00" }, { "name": "pestphp/pest-plugin-livewire", - "version": "v2.1.0", + "version": "v4.0.1", "source": { "type": "git", "url": "https://github.com/pestphp/pest-plugin-livewire.git", - "reference": "e72a2f850f727dfdb6bfa6e2ee6ff478ccc93f97" + "reference": "cc30502ef62487d9a0bec5e73b64d59eec3b5f42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-livewire/zipball/e72a2f850f727dfdb6bfa6e2ee6ff478ccc93f97", - "reference": "e72a2f850f727dfdb6bfa6e2ee6ff478ccc93f97", + "url": "https://api.github.com/repos/pestphp/pest-plugin-livewire/zipball/cc30502ef62487d9a0bec5e73b64d59eec3b5f42", + "reference": "cc30502ef62487d9a0bec5e73b64d59eec3b5f42", "shasum": "" }, "require": { - "livewire/livewire": "^2.12.3|^3.0", - "pestphp/pest": "^2.9.1", - "php": "^8.1" + "livewire/livewire": "^3.6.4", + "pestphp/pest": "^4.0.0", + "php": "^8.3" }, "require-dev": { - "orchestra/testbench": "^8.5.10", - "pestphp/pest-dev-tools": "^2.12.0" + "orchestra/testbench": "^10.6.0", + "pestphp/pest-dev-tools": "^4.0.0" }, "type": "library", "autoload": { @@ -10913,7 +11092,7 @@ "unit" ], "support": { - "source": "https://github.com/pestphp/pest-plugin-livewire/tree/v2.1.0" + "source": "https://github.com/pestphp/pest-plugin-livewire/tree/v4.0.1" }, "funding": [ { @@ -10929,7 +11108,139 @@ "type": "patreon" } ], - "time": "2023-07-20T16:28:21+00:00" + "time": "2025-08-21T09:16:34+00:00" + }, + { + "name": "pestphp/pest-plugin-mutate", + "version": "v4.0.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-mutate.git", + "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-mutate/zipball/d9b32b60b2385e1688a68cc227594738ec26d96c", + "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.6.1", + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "psr/simple-cache": "^3.0.0" + }, + "require-dev": { + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0", + "pestphp/pest-plugin-type-coverage": "^4.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Pest\\Mutate\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + }, + { + "name": "Sandro Gehri", + "email": "sandrogehri@gmail.com" + } + ], + "description": "Mutates your code to find untested cases", + "keywords": [ + "framework", + "mutate", + "mutation", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-mutate/tree/v4.0.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-08-21T20:19:25+00:00" + }, + { + "name": "pestphp/pest-plugin-profanity", + "version": "v4.2.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-profanity.git", + "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-profanity/zipball/343cfa6f3564b7e35df0ebb77b7fa97039f72b27", + "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3" + }, + "require-dev": { + "faissaloux/pest-plugin-inside": "^1.9", + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Profanity\\Plugin" + ] + } + }, + "autoload": { + "psr-4": { + "Pest\\Profanity\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest Profanity Plugin", + "keywords": [ + "framework", + "pest", + "php", + "plugin", + "profanity", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-profanity/tree/v4.2.1" + }, + "time": "2025-12-08T00:13:17+00:00" }, { "name": "phar-io/manifest", @@ -11346,15 +11657,15 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.32", + "version": "2.1.33", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/2770dcdf5078d0b0d53f94317e06affe88419aa8", - "reference": "2770dcdf5078d0b0d53f94317e06affe88419aa8", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f", + "reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -11395,39 +11706,38 @@ "type": "github" } ], - "time": "2025-09-30T10:16:31+00:00" + "time": "2025-12-05T10:24:31+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.16", + "version": "12.5.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + "reference": "c467c59a4f6e04b942be422844e7a6352fa01b57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c467c59a4f6e04b942be422844e7a6352fa01b57", + "reference": "c467c59a4f6e04b942be422844e7a6352fa01b57", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.19.1 || ^5.1.0", - "php": ">=8.1", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-text-template": "^3.0.1", - "sebastian/code-unit-reverse-lookup": "^3.0.0", - "sebastian/complexity": "^3.2.0", - "sebastian/environment": "^6.1.0", - "sebastian/lines-of-code": "^2.0.2", - "sebastian/version": "^4.0.1", - "theseer/tokenizer": "^1.2.3" + "nikic/php-parser": "^5.7.0", + "php": ">=8.3", + "phpunit/php-file-iterator": "^6.0", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.0.3", + "sebastian/lines-of-code": "^4.0", + "sebastian/version": "^6.0", + "theseer/tokenizer": "^2.0" }, "require-dev": { - "phpunit/phpunit": "^10.1" + "phpunit/phpunit": "^12.5.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -11436,7 +11746,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1.x-dev" + "dev-main": "12.5.x-dev" } }, "autoload": { @@ -11465,40 +11775,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" } ], - "time": "2024-08-22T04:31:57+00:00" + "time": "2025-12-08T07:17:58+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "4.1.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + "reference": "961bc913d42fe24a257bfff826a5068079ac7782" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/961bc913d42fe24a257bfff826a5068079ac7782", + "reference": "961bc913d42fe24a257bfff826a5068079ac7782", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -11526,7 +11848,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.0" }, "funding": [ { @@ -11534,28 +11856,28 @@ "type": "github" } ], - "time": "2023-08-31T06:24:48+00:00" + "time": "2025-02-07T04:58:37+00:00" }, { "name": "phpunit/php-invoker", - "version": "4.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-pcntl": "*" @@ -11563,7 +11885,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -11589,7 +11911,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" }, "funding": [ { @@ -11597,32 +11920,32 @@ "type": "github" } ], - "time": "2023-02-03T06:56:09+00:00" + "time": "2025-02-07T04:58:58+00:00" }, { "name": "phpunit/php-text-template", - "version": "3.0.1", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -11649,7 +11972,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" }, "funding": [ { @@ -11657,32 +11980,32 @@ "type": "github" } ], - "time": "2023-08-31T14:07:24+00:00" + "time": "2025-02-07T04:59:16+00:00" }, { "name": "phpunit/php-timer", - "version": "6.0.0", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -11708,7 +12031,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" }, "funding": [ { @@ -11716,20 +12040,20 @@ "type": "github" } ], - "time": "2023-02-03T06:57:52+00:00" + "time": "2025-02-07T04:59:38+00:00" }, { "name": "phpunit/phpunit", - "version": "10.5.36", + "version": "12.4.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870" + "reference": "9253ec75a672e39fcc9d85bdb61448215b8162c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870", - "reference": "aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9253ec75a672e39fcc9d85bdb61448215b8162c7", + "reference": "9253ec75a672e39fcc9d85bdb61448215b8162c7", "shasum": "" }, "require": { @@ -11739,29 +12063,25 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.0", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", - "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.16", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-invoker": "^4.0.0", - "phpunit/php-text-template": "^3.0.1", - "phpunit/php-timer": "^6.0.0", - "sebastian/cli-parser": "^2.0.1", - "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.2", - "sebastian/diff": "^5.1.1", - "sebastian/environment": "^6.1.0", - "sebastian/exporter": "^5.1.2", - "sebastian/global-state": "^6.0.2", - "sebastian/object-enumerator": "^5.0.0", - "sebastian/recursion-context": "^5.0.0", - "sebastian/type": "^4.0.0", - "sebastian/version": "^4.0.1" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.4.0", + "phpunit/php-file-iterator": "^6.0.0", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.2.0", + "sebastian/comparator": "^7.1.3", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.0.3", + "sebastian/exporter": "^7.0.2", + "sebastian/global-state": "^8.0.2", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/type": "^6.0.3", + "sebastian/version": "^6.0.0", + "staabm/side-effects-detector": "^1.0.5" }, "bin": [ "phpunit" @@ -11769,7 +12089,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.5-dev" + "dev-main": "12.4-dev" } }, "autoload": { @@ -11801,7 +12121,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.36" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.4.4" }, "funding": [ { @@ -11812,12 +12132,20 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2024-10-08T15:36:51+00:00" + "time": "2025-11-21T07:39:11+00:00" }, { "name": "psy/psysh", @@ -11900,21 +12228,21 @@ }, { "name": "rector/rector", - "version": "1.2.10", + "version": "2.2.14", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "40f9cf38c05296bd32f444121336a521a293fa61" + "reference": "6d56bb0e94d4df4f57a78610550ac76ab403657d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/40f9cf38c05296bd32f444121336a521a293fa61", - "reference": "40f9cf38c05296bd32f444121336a521a293fa61", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/6d56bb0e94d4df4f57a78610550ac76ab403657d", + "reference": "6d56bb0e94d4df4f57a78610550ac76ab403657d", "shasum": "" }, "require": { - "php": "^7.2|^8.0", - "phpstan/phpstan": "^1.12.5" + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.1.33" }, "conflict": { "rector/rector-doctrine": "*", @@ -11939,6 +12267,7 @@ "MIT" ], "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "homepage": "https://getrector.com/", "keywords": [ "automation", "dev", @@ -11947,7 +12276,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/1.2.10" + "source": "https://github.com/rectorphp/rector/tree/2.2.14" }, "funding": [ { @@ -11955,7 +12284,7 @@ "type": "github" } ], - "time": "2024-11-08T13:59:10+00:00" + "time": "2025-12-09T10:57:55+00:00" }, { "name": "roave/security-advisories", @@ -11963,12 +12292,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "10c1e6abcb8094a428b92e7d8c3126371f9f9126" + "reference": "75d4ccd9c135c4ac904cd4211a43e51d12feb1ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/10c1e6abcb8094a428b92e7d8c3126371f9f9126", - "reference": "10c1e6abcb8094a428b92e7d8c3126371f9f9126", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/75d4ccd9c135c4ac904cd4211a43e51d12feb1ef", + "reference": "75d4ccd9c135c4ac904cd4211a43e51d12feb1ef", "shasum": "" }, "conflict": { @@ -12220,6 +12549,7 @@ "feehi/feehicms": "<=2.1.1", "fenom/fenom": "<=2.12.1", "filament/actions": ">=3.2,<3.2.123", + "filament/filament": ">=4,<4.3.1", "filament/infolists": ">=3,<3.2.115", "filament/tables": ">=3,<3.2.115", "filegator/filegator": "<7.8", @@ -12474,6 +12804,7 @@ "netgen/tagsbundle": ">=3.4,<3.4.11|>=4,<4.0.15", "nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6", "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", + "neuron-core/neuron-ai": "<=2.8.11", "nilsteampassnet/teampass": "<3.1.3.1-dev", "nitsan/ns-backup": "<13.0.1", "nonfiction/nterchange": "<4.1.1", @@ -12493,7 +12824,7 @@ "october/system": "<3.7.5", "oliverklee/phpunit": "<3.5.15", "omeka/omeka-s": "<4.0.3", - "onelogin/php-saml": "<2.10.4", + "onelogin/php-saml": "<2.21.1|>=3,<3.8.1|>=4,<4.3.1", "oneup/uploader-bundle": ">=1,<1.9.3|>=2,<2.1.5", "open-web-analytics/open-web-analytics": "<1.8.1", "opencart/opencart": ">=0", @@ -12608,7 +12939,7 @@ "reportico-web/reportico": "<=8.1", "rhukster/dom-sanitizer": "<1.0.7", "rmccue/requests": ">=1.6,<1.8", - "robrichards/xmlseclibs": ">=1,<3.0.4", + "robrichards/xmlseclibs": "<=3.1.3", "roots/soil": "<4.1", "roundcube/roundcubemail": "<1.5.10|>=1.6,<1.6.11", "rudloff/alltube": "<3.0.3", @@ -12627,8 +12958,8 @@ "shopware/core": "<6.6.10.9-dev|>=6.7,<6.7.4.1-dev", "shopware/platform": "<6.6.10.7-dev|>=6.7,<6.7.3.1-dev", "shopware/production": "<=6.3.5.2", - "shopware/shopware": "<=5.7.17|>=6.7,<6.7.2.1-dev", - "shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev", + "shopware/shopware": "<=5.7.17|>=6.4.6,<6.6.10.10-dev|>=6.7,<6.7.5.1-dev", + "shopware/storefront": "<6.6.10.10-dev|>=6.7,<6.7.5.1-dev", "shopxo/shopxo": "<=6.4", "showdoc/showdoc": "<2.10.4", "shuchkin/simplexlsx": ">=1.0.12,<1.1.13", @@ -12879,6 +13210,7 @@ "yoast-seo-for-typo3/yoast_seo": "<7.2.3", "yourls/yourls": "<=1.8.2", "yuan1994/tpadmin": "<=1.3.12", + "yungifez/skuul": "<=2.6.5", "z-push/z-push-dev": "<2.7.6", "zencart/zencart": "<=1.5.7.0-beta", "zendesk/zendesk_api_client_php": "<2.2.11", @@ -12954,32 +13286,32 @@ "type": "tidelift" } ], - "time": "2025-12-05T21:05:14+00:00" + "time": "2025-12-09T18:07:05+00:00" }, { "name": "sebastian/cli-parser", - "version": "2.0.1", + "version": "4.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04", + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "4.2-dev" } }, "autoload": { @@ -13003,155 +13335,59 @@ "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - } - ], - "time": "2024-03-02T07:12:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" - }, - "funding": [ + }, { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:58:43+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" - }, - "funding": [ + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" } ], - "time": "2023-02-03T06:59:15+00:00" + "time": "2025-09-14T09:36:45+00:00" }, { "name": "sebastian/comparator", - "version": "5.0.4", + "version": "7.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e" + "reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e8e53097718d2b53cfb2aa859b06a41abf58c62e", - "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/dc904b4bb3ab070865fa4068cd84f3da8b945148", + "reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148", "shasum": "" }, "require": { "ext-dom": "*", "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/diff": "^5.0", - "sebastian/exporter": "^5.0" + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^12.2" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "7.1-dev" } }, "autoload": { @@ -13191,7 +13427,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.4" + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.3" }, "funding": [ { @@ -13211,33 +13447,33 @@ "type": "tidelift" } ], - "time": "2025-09-07T05:25:07+00:00" + "time": "2025-08-20T11:27:00+00:00" }, { "name": "sebastian/complexity", - "version": "3.2.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68ff824baeae169ec9f2137158ee529584553799" + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", - "reference": "68ff824baeae169ec9f2137158ee529584553799", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.2-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -13261,7 +13497,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" }, "funding": [ { @@ -13269,33 +13505,33 @@ "type": "github" } ], - "time": "2023-12-21T08:37:17+00:00" + "time": "2025-02-07T04:55:25+00:00" }, { "name": "sebastian/diff", - "version": "5.1.1", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0", - "symfony/process": "^6.4" + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -13328,7 +13564,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" }, "funding": [ { @@ -13336,27 +13572,27 @@ "type": "github" } ], - "time": "2024-03-02T07:15:17+00:00" + "time": "2025-02-07T04:55:46+00:00" }, { "name": "sebastian/environment", - "version": "6.1.0", + "version": "8.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/24a711b5c916efc6d6e62aa65aa2ec98fef77f68", + "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-posix": "*" @@ -13364,7 +13600,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -13392,42 +13628,54 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + "source": "https://github.com/sebastianbergmann/environment/tree/8.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" } ], - "time": "2024-03-23T08:47:14+00:00" + "time": "2025-08-12T14:11:56+00:00" }, { "name": "sebastian/exporter", - "version": "5.1.4", + "version": "7.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "0735b90f4da94969541dac1da743446e276defa6" + "reference": "016951ae10980765e4e7aee491eb288c64e505b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", - "reference": "0735b90f4da94969541dac1da743446e276defa6", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7", + "reference": "016951ae10980765e4e7aee491eb288c64e505b7", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/recursion-context": "^5.0" + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -13470,7 +13718,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2" }, "funding": [ { @@ -13490,35 +13738,35 @@ "type": "tidelift" } ], - "time": "2025-09-24T06:09:11+00:00" + "time": "2025-09-24T06:16:11+00:00" }, { "name": "sebastian/global-state", - "version": "6.0.2", + "version": "8.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + "reference": "ef1377171613d09edd25b7816f05be8313f9115d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d", + "reference": "ef1377171613d09edd25b7816f05be8313f9115d", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -13544,41 +13792,53 @@ "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2024-03-02T07:19:19+00:00" + "time": "2025-08-29T11:29:25+00:00" }, { "name": "sebastian/lines-of-code", - "version": "2.0.2", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -13602,7 +13862,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" }, "funding": [ { @@ -13610,34 +13870,34 @@ "type": "github" } ], - "time": "2023-12-21T08:38:20+00:00" + "time": "2025-02-07T04:57:28+00:00" }, { "name": "sebastian/object-enumerator", - "version": "5.0.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -13659,7 +13919,8 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" }, "funding": [ { @@ -13667,32 +13928,32 @@ "type": "github" } ], - "time": "2023-02-03T07:08:32+00:00" + "time": "2025-02-07T04:57:48+00:00" }, { "name": "sebastian/object-reflector", - "version": "3.0.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -13714,7 +13975,8 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" }, "funding": [ { @@ -13722,32 +13984,32 @@ "type": "github" } ], - "time": "2023-02-03T07:06:18+00:00" + "time": "2025-02-07T04:58:17+00:00" }, { "name": "sebastian/recursion-context", - "version": "5.0.1", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -13778,7 +14040,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1" }, "funding": [ { @@ -13798,32 +14060,32 @@ "type": "tidelift" } ], - "time": "2025-08-10T07:50:56+00:00" + "time": "2025-08-13T04:44:59+00:00" }, { "name": "sebastian/type", - "version": "4.0.0", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d", + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -13846,37 +14108,50 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" } ], - "time": "2023-02-03T07:10:45+00:00" + "time": "2025-08-09T06:57:12+00:00" }, { "name": "sebastian/version", - "version": "4.0.1", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -13899,7 +14174,8 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" }, "funding": [ { @@ -13907,20 +14183,72 @@ "type": "github" } ], - "time": "2023-02-07T11:34:05+00:00" + "time": "2025-02-07T05:00:38+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" }, { "name": "symfony/yaml", - "version": "v7.4.0", + "version": "v7.4.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810" + "reference": "24dd4de28d2e3988b311751ac49e684d783e2345" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/6c84a4b55aee4cd02034d1c528e83f69ddf63810", - "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810", + "url": "https://api.github.com/repos/symfony/yaml/zipball/24dd4de28d2e3988b311751ac49e684d783e2345", + "reference": "24dd4de28d2e3988b311751ac49e684d783e2345", "shasum": "" }, "require": { @@ -13963,7 +14291,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.4.0" + "source": "https://github.com/symfony/yaml/tree/v7.4.1" }, "funding": [ { @@ -13983,7 +14311,7 @@ "type": "tidelift" } ], - "time": "2025-11-16T10:14:42+00:00" + "time": "2025-12-04T18:11:45+00:00" }, { "name": "ta-tikoma/phpunit-architecture-test", @@ -14046,23 +14374,23 @@ }, { "name": "theseer/tokenizer", - "version": "1.3.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.1" }, "type": "library", "autoload": { @@ -14084,7 +14412,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.3.1" + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" }, "funding": [ { @@ -14092,7 +14420,7 @@ "type": "github" } ], - "time": "2025-11-17T20:03:58+00:00" + "time": "2025-12-08T11:19:18+00:00" }, { "name": "webmozart/assert", diff --git a/config/app.php b/config/app.php index 8998c047..5b8fca25 100644 --- a/config/app.php +++ b/config/app.php @@ -13,7 +13,7 @@ | */ - 'timezone' => env('APP_TIMEZONE', 'Europe/Berlin'), + 'timezone' => 'Europe/Berlin', /* |-------------------------------------------------------------------------- From e274ca9bc43e6fc596ad709841f5e658c0549f92 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Wed, 10 Dec 2025 11:40:09 +0100 Subject: [PATCH 091/108] npm update --- package-lock.json | 1211 +++++++++++++++++++++++++++------------------ 1 file changed, 743 insertions(+), 468 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65863e6b..bd84b717 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,7 +4,6 @@ "requires": true, "packages": { "": { - "name": "StuFis", "dependencies": { "@alpinejs/sort": "^3.14.9", "@fontsource-variable/inter": "^5.2.6", @@ -13,10 +12,11 @@ "devDependencies": { "@tailwindcss/forms": "^0.5.10", "@tailwindcss/postcss": "^4.1.4", - "axios": "^1.8.4", - "laravel-vite-plugin": "^1.2.0", + "axios": "^1.11.0", + "concurrently": "^9.0.1", + "laravel-vite-plugin": "^2.0.0", "tailwindcss": "^4.1.4", - "vite": "^6.0.0" + "vite": "^7.0.7" }, "engines": { "node": ">=16" @@ -36,29 +36,15 @@ } }, "node_modules/@alpinejs/sort": { - "version": "3.14.9", - "resolved": "https://registry.npmjs.org/@alpinejs/sort/-/sort-3.14.9.tgz", - "integrity": "sha512-lzd0pQT3CmZmosinej4aEtxzPO/GhfKzzb7Klku1oPHsmDv3ItnxBQ8xI+r4fvHmWfFykp1XYGpDrZWu0BcWMQ==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/@alpinejs/sort/-/sort-3.15.2.tgz", + "integrity": "sha512-2U/mr/9g1GfozLMsSJMc07jCxW50yHBpeqyQtorKhoXLRMuiSdMjkcwxYsLwvwskvjWar2YMBtjLBSS0R8+yyA==", "license": "MIT" }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", - "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ "ppc64" ], @@ -73,9 +59,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", - "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ "arm" ], @@ -90,9 +76,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", - "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], @@ -107,9 +93,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", - "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], @@ -124,9 +110,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", - "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], @@ -141,9 +127,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", - "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], @@ -158,9 +144,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", - "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", "cpu": [ "arm64" ], @@ -175,9 +161,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", - "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], @@ -192,9 +178,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", - "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], @@ -209,9 +195,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", - "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], @@ -226,9 +212,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", - "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ "ia32" ], @@ -243,9 +229,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", - "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], @@ -260,9 +246,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", - "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ "mips64el" ], @@ -277,9 +263,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", - "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], @@ -294,9 +280,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", - "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ "riscv64" ], @@ -311,9 +297,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", - "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ "s390x" ], @@ -328,9 +314,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", - "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -345,9 +331,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", - "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", "cpu": [ "arm64" ], @@ -362,9 +348,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", - "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], @@ -379,9 +365,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", - "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", "cpu": [ "arm64" ], @@ -396,9 +382,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", - "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ "x64" ], @@ -413,9 +399,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", - "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", "cpu": [ "arm64" ], @@ -430,9 +416,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", - "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], @@ -447,9 +433,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", - "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], @@ -464,9 +450,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", - "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], @@ -481,9 +467,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", - "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], @@ -498,27 +484,14 @@ } }, "node_modules/@fontsource-variable/inter": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.2.6.tgz", - "integrity": "sha512-jks/bficUPQ9nn7GvXvHtlQIPudW7Wx8CrlZoY8bhxgeobNxlQan8DclUJuYF2loYRrGpfrhCIZZspXYysiVGg==", + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.2.8.tgz", + "integrity": "sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==", "license": "OFL-1.1", "funding": { "url": "https://github.com/sponsors/ayuhito" } }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -530,6 +503,17 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -548,9 +532,9 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { @@ -559,9 +543,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", - "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", "cpu": [ "arm" ], @@ -573,9 +557,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", - "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", "cpu": [ "arm64" ], @@ -587,9 +571,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", - "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", "cpu": [ "arm64" ], @@ -601,9 +585,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", - "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", "cpu": [ "x64" ], @@ -615,9 +599,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", - "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", "cpu": [ "arm64" ], @@ -629,9 +613,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", - "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", "cpu": [ "x64" ], @@ -643,9 +627,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", - "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", "cpu": [ "arm" ], @@ -657,9 +641,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", - "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", "cpu": [ "arm" ], @@ -671,9 +655,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", - "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", "cpu": [ "arm64" ], @@ -685,9 +669,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", - "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", "cpu": [ "arm64" ], @@ -698,10 +682,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", - "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", "cpu": [ "loong64" ], @@ -713,9 +697,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", - "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", "cpu": [ "ppc64" ], @@ -727,9 +711,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", - "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", "cpu": [ "riscv64" ], @@ -741,9 +725,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", - "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", "cpu": [ "riscv64" ], @@ -755,9 +739,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", - "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", "cpu": [ "s390x" ], @@ -769,9 +753,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", - "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", "cpu": [ "x64" ], @@ -783,9 +767,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", - "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", "cpu": [ "x64" ], @@ -796,10 +780,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", - "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", "cpu": [ "arm64" ], @@ -811,9 +809,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", - "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", "cpu": [ "ia32" ], @@ -824,10 +822,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", - "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", "cpu": [ "x64" ], @@ -852,54 +864,49 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", - "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", + "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.3.0", - "enhanced-resolve": "^5.18.1", - "jiti": "^2.4.2", - "lightningcss": "1.30.1", - "magic-string": "^0.30.17", + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.11" + "tailwindcss": "4.1.17" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", - "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", + "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.4", - "tar": "^7.4.3" - }, "engines": { "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-x64": "4.1.11", - "@tailwindcss/oxide-freebsd-x64": "4.1.11", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-x64-musl": "4.1.11", - "@tailwindcss/oxide-wasm32-wasi": "4.1.11", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" + "@tailwindcss/oxide-android-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-x64": "4.1.17", + "@tailwindcss/oxide-freebsd-x64": "4.1.17", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-x64-musl": "4.1.17", + "@tailwindcss/oxide-wasm32-wasi": "4.1.17", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", - "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", + "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", "cpu": [ "arm64" ], @@ -914,9 +921,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", - "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", + "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", "cpu": [ "arm64" ], @@ -931,9 +938,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", - "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", + "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", "cpu": [ "x64" ], @@ -948,9 +955,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", - "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", + "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", "cpu": [ "x64" ], @@ -965,9 +972,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", - "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", + "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", "cpu": [ "arm" ], @@ -982,9 +989,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", - "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", + "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", "cpu": [ "arm64" ], @@ -999,9 +1006,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", - "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", + "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", "cpu": [ "arm64" ], @@ -1016,9 +1023,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", - "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", + "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", "cpu": [ "x64" ], @@ -1033,9 +1040,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", - "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", + "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", "cpu": [ "x64" ], @@ -1050,9 +1057,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", - "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", + "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -1068,21 +1075,21 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.11", - "@tybys/wasm-util": "^0.9.0", - "tslib": "^2.8.0" + "@emnapi/core": "^1.6.0", + "@emnapi/runtime": "^1.6.0", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.0.7", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", - "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", + "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", "cpu": [ "arm64" ], @@ -1097,9 +1104,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", - "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", + "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==", "cpu": [ "x64" ], @@ -1114,17 +1121,17 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.11.tgz", - "integrity": "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.17.tgz", + "integrity": "sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.11", - "@tailwindcss/oxide": "4.1.11", + "@tailwindcss/node": "4.1.17", + "@tailwindcss/oxide": "4.1.17", "postcss": "^8.4.41", - "tailwindcss": "4.1.11" + "tailwindcss": "4.1.17" } }, "node_modules/@types/estree": { @@ -1150,14 +1157,40 @@ "license": "MIT" }, "node_modules/alpinejs": { - "version": "3.14.9", - "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.9.tgz", - "integrity": "sha512-gqSOhTEyryU9FhviNqiHBHzgjkvtukq9tevew29fTj+ofZtfsYriw4zPirHHOAy9bw8QoL3WGhyk7QqCh5AYlw==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.2.tgz", + "integrity": "sha512-2kYF2aG+DTFkE6p0rHG5XmN4VEb6sO9b02aOdU4+i8QN6rL0DbRZQiypDE1gBcGO65yDcqMz5KKYUYgMUxgNkw==", "license": "MIT", "dependencies": { "@vue/reactivity": "~3.1.1" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1191,16 +1224,71 @@ "node": ">= 0.4" } }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1214,6 +1302,31 @@ "node": ">= 0.8" } }, + "node_modules/concurrently": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1225,9 +1338,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1249,6 +1362,13 @@ "node": ">= 0.4" } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -1313,9 +1433,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", - "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1326,40 +1446,53 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.8", - "@esbuild/android-arm": "0.25.8", - "@esbuild/android-arm64": "0.25.8", - "@esbuild/android-x64": "0.25.8", - "@esbuild/darwin-arm64": "0.25.8", - "@esbuild/darwin-x64": "0.25.8", - "@esbuild/freebsd-arm64": "0.25.8", - "@esbuild/freebsd-x64": "0.25.8", - "@esbuild/linux-arm": "0.25.8", - "@esbuild/linux-arm64": "0.25.8", - "@esbuild/linux-ia32": "0.25.8", - "@esbuild/linux-loong64": "0.25.8", - "@esbuild/linux-mips64el": "0.25.8", - "@esbuild/linux-ppc64": "0.25.8", - "@esbuild/linux-riscv64": "0.25.8", - "@esbuild/linux-s390x": "0.25.8", - "@esbuild/linux-x64": "0.25.8", - "@esbuild/netbsd-arm64": "0.25.8", - "@esbuild/netbsd-x64": "0.25.8", - "@esbuild/openbsd-arm64": "0.25.8", - "@esbuild/openbsd-x64": "0.25.8", - "@esbuild/openharmony-arm64": "0.25.8", - "@esbuild/sunos-x64": "0.25.8", - "@esbuild/win32-arm64": "0.25.8", - "@esbuild/win32-ia32": "0.25.8", - "@esbuild/win32-x64": "0.25.8" + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" } }, "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -1391,9 +1524,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, "license": "MIT", "dependencies": { @@ -1432,6 +1565,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -1491,6 +1634,16 @@ "dev": true, "license": "ISC" }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -1533,10 +1686,20 @@ "node": ">= 0.4" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jiti": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", - "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", "bin": { @@ -1544,9 +1707,9 @@ } }, "node_modules/laravel-vite-plugin": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.3.0.tgz", - "integrity": "sha512-P5qyG56YbYxM8OuYmK2OkhcKe0AksNVJUjq9LUZ5tOekU9fBn9LujYyctI4t9XoLjuMvHJXXpCoPntY1oKltuA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-2.0.1.tgz", + "integrity": "sha512-zQuvzWfUKQu9oNVi1o0RZAJCwhGsdhx4NEOyrVQwJHaWDseGP9tl7XUPLY2T8Cj6+IrZ6lmyxlR1KC8unf3RLA==", "dev": true, "license": "MIT", "dependencies": { @@ -1557,16 +1720,16 @@ "clean-orphaned-assets": "bin/clean.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0" + "vite": "^7.0.0" } }, "node_modules/lightningcss": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", - "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -1580,22 +1743,44 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-darwin-arm64": "1.30.1", - "lightningcss-darwin-x64": "1.30.1", - "lightningcss-freebsd-x64": "1.30.1", - "lightningcss-linux-arm-gnueabihf": "1.30.1", - "lightningcss-linux-arm64-gnu": "1.30.1", - "lightningcss-linux-arm64-musl": "1.30.1", - "lightningcss-linux-x64-gnu": "1.30.1", - "lightningcss-linux-x64-musl": "1.30.1", - "lightningcss-win32-arm64-msvc": "1.30.1", - "lightningcss-win32-x64-msvc": "1.30.1" + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", - "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", "cpu": [ "arm64" ], @@ -1614,9 +1799,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", - "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", "cpu": [ "x64" ], @@ -1635,9 +1820,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", - "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", "cpu": [ "x64" ], @@ -1656,9 +1841,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", - "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", "cpu": [ "arm" ], @@ -1677,9 +1862,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", - "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", "cpu": [ "arm64" ], @@ -1698,9 +1883,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", - "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", "cpu": [ "arm64" ], @@ -1719,9 +1904,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", - "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", "cpu": [ "x64" ], @@ -1740,9 +1925,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", - "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", "cpu": [ "x64" ], @@ -1761,9 +1946,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", - "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", "cpu": [ "arm64" ], @@ -1782,9 +1967,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", - "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", "cpu": [ "x64" ], @@ -1803,13 +1988,13 @@ } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/math-intrinsics": { @@ -1855,45 +2040,6 @@ "mini-svg-data-uri": "cli.js" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -1969,10 +2115,20 @@ "dev": true, "license": "MIT" }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/rollup": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", - "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, "license": "MIT", "dependencies": { @@ -1986,29 +2142,54 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.46.2", - "@rollup/rollup-android-arm64": "4.46.2", - "@rollup/rollup-darwin-arm64": "4.46.2", - "@rollup/rollup-darwin-x64": "4.46.2", - "@rollup/rollup-freebsd-arm64": "4.46.2", - "@rollup/rollup-freebsd-x64": "4.46.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", - "@rollup/rollup-linux-arm-musleabihf": "4.46.2", - "@rollup/rollup-linux-arm64-gnu": "4.46.2", - "@rollup/rollup-linux-arm64-musl": "4.46.2", - "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", - "@rollup/rollup-linux-ppc64-gnu": "4.46.2", - "@rollup/rollup-linux-riscv64-gnu": "4.46.2", - "@rollup/rollup-linux-riscv64-musl": "4.46.2", - "@rollup/rollup-linux-s390x-gnu": "4.46.2", - "@rollup/rollup-linux-x64-gnu": "4.46.2", - "@rollup/rollup-linux-x64-musl": "4.46.2", - "@rollup/rollup-win32-arm64-msvc": "4.46.2", - "@rollup/rollup-win32-ia32-msvc": "4.46.2", - "@rollup/rollup-win32-x64-msvc": "4.46.2", + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" } }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -2019,50 +2200,80 @@ "node": ">=0.10.0" } }, - "node_modules/tailwindcss": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", - "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=18" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", + "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -2071,25 +2282,42 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "version": "7.2.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz", + "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -2098,14 +2326,14 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", - "less": "*", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" @@ -2170,14 +2398,61 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=18" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" } } } From 1aa31c18c468176d17f2a749874329c1fe0e0e57 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Wed, 10 Dec 2025 11:47:04 +0100 Subject: [PATCH 092/108] rector --- .../Commands/LegacyMigrateEncryption.php | 7 +-- app/Http/Controllers/BudgetPlanController.php | 2 +- .../Controllers/Legacy/DeleteExpenses.php | 6 +-- app/Http/Controllers/Legacy/DeleteProject.php | 2 +- .../Controllers/Legacy/ExportController.php | 7 +-- app/Livewire/ChatPanel.php | 2 +- app/Livewire/Project/EditProject.php | 46 ++++++++----------- app/Livewire/Project/ProjectForm.php | 3 +- app/Livewire/TransactionImportWire.php | 8 ++-- app/Models/Legacy/BankAccount.php | 2 +- app/Models/Legacy/BankTransaction.php | 2 +- app/Models/User.php | 4 +- app/Providers/AppServiceProvider.php | 4 +- app/Services/Auth/LocalAuthService.php | 4 +- app/States/Project/Applied.php | 2 + app/States/Project/ApprovedByFinance.php | 3 ++ app/States/Project/ApprovedByOrg.php | 3 ++ app/States/Project/ApprovedByOther.php | 3 ++ app/States/Project/Draft.php | 2 + app/States/Project/NeedFinanceApproval.php | 2 + app/States/Project/NeedOrgApproval.php | 2 + app/States/Project/ProjectState.php | 1 + app/States/Project/Revoked.php | 2 + app/States/Project/Terminated.php | 2 + app/Support/helpers.php | 3 +- composer.json | 4 +- routes/web.php | 4 +- 27 files changed, 73 insertions(+), 59 deletions(-) diff --git a/app/Console/Commands/LegacyMigrateEncryption.php b/app/Console/Commands/LegacyMigrateEncryption.php index 170f0cb9..0f70c90f 100644 --- a/app/Console/Commands/LegacyMigrateEncryption.php +++ b/app/Console/Commands/LegacyMigrateEncryption.php @@ -9,6 +9,7 @@ use forms\projekte\auslagen\AuslagenHandler2; use Illuminate\Console\Command; use Illuminate\Contracts\Encryption\DecryptException; +use Illuminate\Support\Env; use Illuminate\Support\Facades\DB; class LegacyMigrateEncryption extends Command @@ -32,7 +33,7 @@ class LegacyMigrateEncryption extends Command */ public function handle(): int { - if (! isset($_ENV['CHAT_PRIVATE_KEY'], $_ENV['CHAT_PUBLIC_KEY'], $_ENV['IBAN_SECRET_KEY'])) { + if (!Env::get('CHAT_PRIVATE_KEY') !== null && Env::get('CHAT_PUBLIC_KEY') !== null && Env::get('IBAN_SECRET_KEY') !== null) { $this->error('Please set chat private key and public key / IBAN_SECRET_KEY'); return self::FAILURE; @@ -49,13 +50,13 @@ public function handle(): int if (str_starts_with($message->text, '$enc$')) { // old prefix $text = substr($text, strlen('$enc$')); - $text = ChatHandler::legacyDecryptMessage($text, $_ENV['CHAT_PRIVATE_KEY']); + $text = ChatHandler::legacyDecryptMessage($text, Env::get('CHAT_PRIVATE_KEY')); $message->text = \Crypt::encryptString($text); $message->save(); $count++; } elseif ($message->type === -1) { // not used productive anymore, was "private message" - $text = ChatHandler::legacyDecryptMessage($text, $_ENV['CHAT_PRIVATE_KEY']); + $text = ChatHandler::legacyDecryptMessage($text, Env::get('CHAT_PRIVATE_KEY')); $message->text = \Crypt::encryptString($text); $message->save(); $count++; diff --git a/app/Http/Controllers/BudgetPlanController.php b/app/Http/Controllers/BudgetPlanController.php index f3628a3a..63603782 100644 --- a/app/Http/Controllers/BudgetPlanController.php +++ b/app/Http/Controllers/BudgetPlanController.php @@ -52,6 +52,6 @@ public function create(): RedirectResponse ]); }); - return redirect()->route('budget-plan.edit', ['plan_id' => $plan->id]); + return to_route('budget-plan.edit', ['plan_id' => $plan->id]); } } diff --git a/app/Http/Controllers/Legacy/DeleteExpenses.php b/app/Http/Controllers/Legacy/DeleteExpenses.php index 1f912064..64d77d50 100644 --- a/app/Http/Controllers/Legacy/DeleteExpenses.php +++ b/app/Http/Controllers/Legacy/DeleteExpenses.php @@ -20,9 +20,9 @@ public function __invoke(int $expense_id) $userPerm = AuthHandler::getInstance()->hasGroup('ref-finanzen-hv') || $project->creator->id === \Auth::user()->id - || explode(';', $expense->created)[1] === \Auth::user()->username; + || explode(';', (string) $expense->created)[1] === \Auth::user()->username; // authorize state - $deletableState = ! in_array(explode(';', $expense->state)[0], ['instructed', 'booked'], true); + $deletableState = ! in_array(explode(';', (string) $expense->state)[0], ['instructed', 'booked'], true); if ($userPerm === false || $deletableState === false) { abort(403); @@ -53,6 +53,6 @@ public function __invoke(int $expense_id) }); \DB::commit(); - return redirect()->route('legacy.dashboard', ['sub' => 'mygremium']); + return to_route('legacy.dashboard', ['sub' => 'mygremium']); } } diff --git a/app/Http/Controllers/Legacy/DeleteProject.php b/app/Http/Controllers/Legacy/DeleteProject.php index c4598de3..9ad9ec63 100644 --- a/app/Http/Controllers/Legacy/DeleteProject.php +++ b/app/Http/Controllers/Legacy/DeleteProject.php @@ -25,6 +25,6 @@ public function __invoke(int $project_id) $project->posts()->delete(); $project->delete(); - return redirect()->route('legacy.dashboard', ['sub' => 'mygremium']); + return to_route('legacy.dashboard', ['sub' => 'mygremium']); } } diff --git a/app/Http/Controllers/Legacy/ExportController.php b/app/Http/Controllers/Legacy/ExportController.php index 9d2702ec..190b7906 100644 --- a/app/Http/Controllers/Legacy/ExportController.php +++ b/app/Http/Controllers/Legacy/ExportController.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Controller; use App\Models\Legacy\LegacyBudgetPlan; use Carbon\Carbon; +use Illuminate\Support\Facades\Date; use Maatwebsite\Excel\Excel; class ExportController extends Controller @@ -18,10 +19,10 @@ public function budgetPlan(int $id, string $filetype) }; $plan = LegacyBudgetPlan::findOrFail($id); $today = today()->format('Y-m-d'); - $start = Carbon::make($plan->von)?->format('y-m'); - $end = Carbon::make($plan->bis)?->format('y-m'); + $start = Date::make($plan->von)?->format('y-m'); + $end = Date::make($plan->bis)?->format('y-m'); $fileName = "$today HHP $start".($end ? " bis $end" : '').".$filetype"; - return (new LegacyBudgetExport($plan))->download($fileName, $writerType); + return new LegacyBudgetExport($plan)->download($fileName, $writerType); } } diff --git a/app/Livewire/ChatPanel.php b/app/Livewire/ChatPanel.php index a56abc29..aa0314c4 100644 --- a/app/Livewire/ChatPanel.php +++ b/app/Livewire/ChatPanel.php @@ -35,7 +35,7 @@ public function save() $this->validate(['content' => 'required|min:1']); - $cleanContent = strip_tags($this->content, '


    + @error('content') +
    {{ $message }}
    + @enderror
    From 120149301265dc33a296270d86cb7a6ade7af464 Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Thu, 11 Dec 2025 12:15:24 +0100 Subject: [PATCH 098/108] refactor: introduce validation rules for project states, changed livewire population logic and error handling --- app/Livewire/Project/EditProject.php | 153 +++++++++++------- app/Livewire/Project/ProjectForm.php | 99 ------------ app/Livewire/Project/ShowProject.php | 5 + app/Models/Legacy/Project.php | 13 +- app/Rules/ExactlyOneZeroMoneyRule.php | 48 ++++++ app/States/Project/Applied.php | 9 ++ app/States/Project/ApprovedByFinance.php | 9 ++ app/States/Project/ApprovedByOrg.php | 10 ++ app/States/Project/ApprovedByOther.php | 10 ++ app/States/Project/Draft.php | 8 + app/States/Project/NeedFinanceApproval.php | 6 + app/States/Project/NeedOrgApproval.php | 5 + app/States/Project/ProjectState.php | 38 +++++ app/States/Project/Revoked.php | 7 + app/States/Project/Terminated.php | 10 ++ lang/de/errors.php | 2 + .../views/components/money-input.blade.php | 1 + .../livewire/project/edit-project.blade.php | 45 ++++-- .../livewire/project/show-project.blade.php | 13 +- 19 files changed, 310 insertions(+), 181 deletions(-) delete mode 100644 app/Livewire/Project/ProjectForm.php create mode 100644 app/Rules/ExactlyOneZeroMoneyRule.php diff --git a/app/Livewire/Project/EditProject.php b/app/Livewire/Project/EditProject.php index 848eff06..0babde65 100644 --- a/app/Livewire/Project/EditProject.php +++ b/app/Livewire/Project/EditProject.php @@ -10,8 +10,11 @@ use Cknow\Money\Money; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Date; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Gate; +use Illuminate\Support\Facades\Validator; +use Livewire\Attributes\Computed; use Livewire\Attributes\Locked; use Livewire\Attributes\Url; use Livewire\Component; @@ -21,20 +24,28 @@ class EditProject extends Component { use WithFileUploads; - #[Locked] - public string $state_name; - - public ProjectForm $form; - #[Url] public ?int $project_id = null; + #[Locked] + public string $state_name; + #[Locked] public bool $isNew; - public Collection $posts; + // Form data + public string $name = ''; + public string $responsible = ''; + public string $org = ''; + public string $org_mail = ''; + public string $protokoll = ''; + public string $beschreibung = ''; + public string $recht = ''; + public string $recht_additional = ''; + public array $dateRange = []; + public int $hhp_id; + public int $version = 1; - // UI state - public string $selectedRechtKey = ''; + public Collection $posts; public array $attachments = []; @@ -44,29 +55,61 @@ public function mount(): void if ($this->isNew) { Gate::authorize('create', Project::class); - $this->form->initializeNew(); - $this->posts = collect(); - $this->attachments = []; - $this->state_name = 'draft'; - + $project = new Project(); + $this->populateData($project); $this->addEmptyPost(); } else { $project = Project::findOrFail($this->project_id); Gate::authorize('update', $project); - $this->form->setProject($project); - $this->state_name = $project->state->getValue(); - $this->posts = $project->posts->map(fn (ProjectPost $post) => [ - 'id' => $post->id, - 'name' => $post->name, - 'bemerkung' => $post->bemerkung ?? '', - 'einnahmen' => $post->einnahmen, - 'ausgaben' => $post->ausgaben, - 'titel_id' => $post->titel_id, - ]); - $this->attachments = []; // FIXME: load Attachments + $this->populateData($project); } } + private function populateData(Project $project): void + { + $this->name = $project->name ?? ''; + $this->responsible = $project->responsible ?? ''; + $this->org = $project->org ?? ''; + $this->org_mail = $project->org_mail ?? ''; + $this->protokoll = $project->protokoll ?? ''; + $this->beschreibung = $project->beschreibung ?? ''; + $this->recht = $project->recht ?? ''; + $this->recht_additional = $project->recht_additional ?? ''; + $this->dateRange = ['start' => $project->date_start, 'end' => $project->date_end]; + $this->version = $project->version; + $this->hhp_id = LegacyBudgetPlan::findByDate($project->createdat)->id; + $this->state_name = $project->state->getValue(); + $this->posts = $project->posts->map(fn (ProjectPost $post) => [ + 'id' => $post->id, + 'name' => $post->name, + 'bemerkung' => $post->bemerkung ?? '', + 'einnahmen' => $post->einnahmen, + 'ausgaben' => $post->ausgaben, + 'titel_id' => $post->titel_id, + ]); + $this->attachments = []; // FIXME: load Attachments + } + + private function getValues(): array + { + return [ + 'name' => $this->name, + 'responsible' => $this->responsible, + 'org' => $this->org, + 'org_mail' => $this->org_mail, + 'protokoll' => $this->protokoll, + 'beschreibung' => $this->beschreibung, + 'recht' => $this->recht, + 'recht_additional' => $this->recht_additional, + // make compatible with legacy database + 'date_start' => $this->dateRange['start'] ?? null, + 'date_end' => $this->dateRange['end'] ?? null, + 'version' => $this->version, + 'createdat' => Date::parse(LegacyBudgetPlan::find($this->hhp_id)->von)->addDays(7), + 'posts' => $this->posts->toArray(), + ]; + } + public function isPostDeletable(int $index): bool { return @@ -85,35 +128,44 @@ public function removePost(int $index): void } } + public function rules() : array + { + return $this->getState()->rules(); + } + /** * Save the project */ public function save() { - // $this->validate(); - // $this->form->validate(); + $validator = Validator::make($this->getValues(), $this->rules()); + $filtered = collect($validator->validate()); + $filteredMeta = $filtered->except('posts')->toArray(); + $filteredPosts = $filtered->get('posts'); + + try { DB::beginTransaction(); if ($this->isNew) { $project = Project::create([ 'creator_id' => Auth::id(), 'stateCreator_id' => Auth::id(), - ...($this->form->getValues()), + ...$filteredMeta, ]); } else { $project = Project::findOrFail($this->project_id); // Check if the project has been modified since the last load - if ($project->version != $this->form->version) { + if ($project->version !== $this->version) { $this->addError('save', 'Das Projekt wurde zwischenzeitlich von jemand anderem bearbeitet. Bitte laden Sie die Seite neu.'); return; } $project->update([ - ...$this->form->getValues(), + ...$filteredMeta, 'version' => $project->version + 1, ]); } - foreach ($this->posts as $post) { + foreach ($filteredPosts as $post) { if (isset($post['id'])) { $project->posts()->findOrFail($post['id'])->update($post); } else { @@ -129,32 +181,6 @@ public function save() } } - /** - * Update existing project - */ - protected function updateProject() - { - $project = Project::findOrFail($this->project_id); - - $updateData = [ - 'lastupdated' => now(), - 'version' => $project->version + 1, - ]; - - // Only update fields that the user has permission to edit - foreach ($this->form->toArray() as $field => $value) { - if (Gate::allows('update-field', [$project, $field])) { - $updateData[$field] = $value ?: null; - } - } - - // FIXME: increment version - // FIXME: save posts - $project->update($updateData); - $this->form->version = $project->version; - - } - /** * Add an empty post row */ @@ -198,7 +224,7 @@ public function removeAttachment(int $index): void */ protected function getBudgetTitleOptions(): \Illuminate\Database\Eloquent\Collection { - $plan = LegacyBudgetPlan::findOrFail($this->form->hhp_id); + $plan = LegacyBudgetPlan::findOrFail($this->hhp_id); return $plan->budgetItems; } @@ -241,11 +267,22 @@ public function render() $mailingLists = []; $rechtsgrundlagen = $this->getRechtsgrundlagenOptions(); $budgetTitles = $this->getBudgetTitleOptions(); - $state = ProjectState::make($this->state_name, new Project); + $state = $this->getState(); $budgetPlans = LegacyBudgetPlan::all(); return view('livewire.project.edit-project', compact( 'gremien', 'mailingLists', 'budgetTitles', 'rechtsgrundlagen', 'state', 'budgetPlans' )); } + + #[Computed] + public function getState(): ProjectState { + return ProjectState::make($this->state_name, $this->getProject()); + } + + #[Computed] + public function getProject(): Project { + return Project::findOrFail($this->project_id); + } + } diff --git a/app/Livewire/Project/ProjectForm.php b/app/Livewire/Project/ProjectForm.php deleted file mode 100644 index 0b65894b..00000000 --- a/app/Livewire/Project/ProjectForm.php +++ /dev/null @@ -1,99 +0,0 @@ - 'required|string|max:255', - 'responsible' => 'required|email', - 'org' => 'required|string', - 'org_mail' => 'nullable|email', - 'protokoll' => 'nullable|string', - 'beschreibung' => 'required|string', - 'recht' => 'nullable|string', - 'recht_additional' => 'nullable|string', - 'dateRange' => 'required|array|size:2', - 'dateRange.*' => 'required|date', - 'createdat' => 'nullable|date', - ]; - } - - public int $version = 1; - - /** - * Set the project and load its data into the form - */ - public function setProject(Project $project): void - { - $this->name = $project->name ?? ''; - $this->responsible = $project->responsible ?? ''; - $this->org = $project->org ?? ''; - $this->org_mail = $project->org_mail ?? ''; - $this->protokoll = $project->protokoll ?? ''; - $this->beschreibung = $project->beschreibung ?? ''; - $this->recht = $project->recht ?? ''; - $this->recht_additional = $project->recht_additional ?? ''; - $this->dateRange = ['start' => $project->date_start, 'end' => $project->date_end]; - $this->version = $project->version; - $this->hhp_id = LegacyBudgetPlan::findByDate($project->createdat)->id; - } - - /** - * Initialize form for new project - */ - public function initializeNew(): void - { - $this->hhp_id = LegacyBudgetPlan::findByDate(now())->id; - $this->version = 1; - } - - /** - * Prepare data for saving - */ - public function getValues(): array - { - return [ - 'name' => $this->name, - 'responsible' => $this->responsible, - 'org' => $this->org, - 'org_mail' => $this->org_mail, - 'protokoll' => $this->protokoll, - 'beschreibung' => $this->beschreibung, - 'recht' => $this->recht, - 'recht_additional' => $this->recht_additional, - // make compatible with legacy database - 'date_start' => $this->dateRange['start'] ?? null, - 'date_end' => $this->dateRange['end'] ?? null, - 'version' => $this->version, - 'createdat' => Date::parse(LegacyBudgetPlan::find($this->hhp_id)->von), - ]; - } -} diff --git a/app/Livewire/Project/ShowProject.php b/app/Livewire/Project/ShowProject.php index b0d9a732..e3149125 100644 --- a/app/Livewire/Project/ShowProject.php +++ b/app/Livewire/Project/ShowProject.php @@ -28,9 +28,13 @@ public function render() public function changeState(): void { + // check if given state string a valid state for this project $project = Project::findOrFail($this->project_id); $filtered = $this->validate(['newState' => ['required', new ValidStateRule(ProjectState::class)]]); $newState = ProjectState::make($filtered['newState'], $project); + // Business Logic check: are some values missing for the new state + $newState->validate(); + // Authorization check: can the user transition to this state $this->authorize('transition-to', [$project, $newState]); try { @@ -46,6 +50,7 @@ public function changeState(): void 'timestamp' => now(), ]); Flux::modal('state-modal')->close(); + $this->reset('newState'); } catch (CouldNotPerformTransition $e) { $this->addError('newState', $e->getMessage()); } diff --git a/app/Models/Legacy/Project.php b/app/Models/Legacy/Project.php index fc51452b..04d2d47d 100644 --- a/app/Models/Legacy/Project.php +++ b/app/Models/Legacy/Project.php @@ -48,6 +48,8 @@ * @method static Builder|Project newModelQuery() * @method static Builder|Project newQuery() * @method static Builder|Project query() + * @method static Builder|Project whereState($field, ProjectState|array $states) + * @method static Builder|Project whereNotState($field, ProjectState|array $states) * @method static Builder|Project whereBeschreibung($value) * @method static Builder|Project whereCreatedat($value) * @method static Builder|Project whereCreatorId($value) @@ -62,7 +64,6 @@ * @method static Builder|Project whereRecht($value) * @method static Builder|Project whereRechtAdditional($value) * @method static Builder|Project whereResponsible($value) - * @method static Builder|Project whereState($value) * @method static Builder|Project whereStateCreatorId($value) * @method static Builder|Project whereVersion($value) * @method static \Database\Factories\Legacy\ProjectFactory factory($count = null, $state = []) @@ -84,14 +85,16 @@ class Project extends Model */ protected $table = 'projekte'; - const CREATED_AT = 'createdat'; + const string CREATED_AT = 'createdat'; - const UPDATED_AT = 'lastupdated'; + const string UPDATED_AT = 'lastupdated'; /** - * @var array + * The attributes that aren't mass-assignable. + * + * @var array */ - protected $fillable = ['creator_id', 'createdat', 'lastupdated', 'version', 'state', 'stateCreator_id', 'name', 'responsible', 'org', 'org_mail', 'protokoll', 'recht', 'recht_additional', 'date_start', 'date_end', 'beschreibung']; + protected $guarded = ['id']; protected function responsible(): Attribute { diff --git a/app/Rules/ExactlyOneZeroMoneyRule.php b/app/Rules/ExactlyOneZeroMoneyRule.php new file mode 100644 index 00000000..1887b495 --- /dev/null +++ b/app/Rules/ExactlyOneZeroMoneyRule.php @@ -0,0 +1,48 @@ +data = $data; + } + + public function validate(string $attribute, mixed $value, Closure $fail): void + { + // pairs every attribute field with the other field, pair[0] has the actual field, pair[1] can have *'s + $otherAccessors = Str::of($attribute)->explode('.') + ->zip(Str::of($this->otherField)->explode('.')) + ->map(fn (Collection $pair) => $pair[1] === '*' ? $pair[0] : $pair[1]) + ; + //dump($otherAccessors); + $otherMoney = $this->data; + while (($idx = $otherAccessors->shift()) !== null) { + //dump($otherMoney, $idx, $otherAccessors); + $otherMoney = $otherMoney[$idx]; + } + //dd($otherMoney); + $oneIsZero = (($value->getAmount() === "0") xor ($otherMoney->getAmount() === "0")); + //dd($value, $otherMoney, ($value->getAmount() === "0"),($otherMoney->getAmount() === "0"), $oneIsZero); + if (!$oneIsZero) { + $fail(__('errors.one-money-has-to-be-zero')); + } + } + + +} diff --git a/app/States/Project/Applied.php b/app/States/Project/Applied.php index 3e542295..1cc8742b 100644 --- a/app/States/Project/Applied.php +++ b/app/States/Project/Applied.php @@ -2,8 +2,12 @@ namespace App\States\Project; +use App\Rules\ExactlyOneZeroMoneyRule; +use App\Rules\FluxEditorRule; + class Applied extends ProjectState { + public static string $name = 'wip'; #[\Override] @@ -18,4 +22,9 @@ public function color(): string { return 'sky'; } + + #[\Override] + public function rules() : array{ + return parent::rules(); + } } diff --git a/app/States/Project/ApprovedByFinance.php b/app/States/Project/ApprovedByFinance.php index b44b8384..ec1fdbfb 100644 --- a/app/States/Project/ApprovedByFinance.php +++ b/app/States/Project/ApprovedByFinance.php @@ -24,4 +24,13 @@ public function color(): string { return 'green'; } + + public function rules(): array + { + return parent::rules() + [ + 'recht' => 'required|string', + 'recht-additional' => 'sometimes|nullable|string', + 'posts.*.titel_id' => 'sometimes|integer|exists:App\Models\Legacy\LegacyBudgetItem,id', + ]; + } } diff --git a/app/States/Project/ApprovedByOrg.php b/app/States/Project/ApprovedByOrg.php index 498836ff..6abe9979 100644 --- a/app/States/Project/ApprovedByOrg.php +++ b/app/States/Project/ApprovedByOrg.php @@ -24,4 +24,14 @@ public function color(): string { return 'green'; } + + public function rules(): array + { + return parent::rules() + [ + 'recht' => 'required|string', + 'recht-additional' => 'sometimes|nullable|string', + 'posts.*.titel_id' => 'sometimes|integer|exists:App\Models\Legacy\LegacyBudgetItem,id', + ]; + } + } diff --git a/app/States/Project/ApprovedByOther.php b/app/States/Project/ApprovedByOther.php index 8c4fe207..3b2e079b 100644 --- a/app/States/Project/ApprovedByOther.php +++ b/app/States/Project/ApprovedByOther.php @@ -24,4 +24,14 @@ public function color(): string { return 'green'; } + + public function rules(): array + { + return parent::rules() + [ + 'recht' => 'required|string', + 'recht-additional' => 'sometimes|nullable|string', + 'posts.*.titel_id' => 'sometimes|integer|exists:App\Models\Legacy\LegacyBudgetItem,id', + ]; + } + } diff --git a/app/States/Project/Draft.php b/app/States/Project/Draft.php index 8ec07cd9..c08ad21b 100644 --- a/app/States/Project/Draft.php +++ b/app/States/Project/Draft.php @@ -18,4 +18,12 @@ public function color(): string { return 'zinc'; } + + #[\Override] + public function rules() : array{ + return [ + 'name' => 'required|string|max:128', + ]; + } + } diff --git a/app/States/Project/NeedFinanceApproval.php b/app/States/Project/NeedFinanceApproval.php index b12b4c75..111e17fd 100644 --- a/app/States/Project/NeedFinanceApproval.php +++ b/app/States/Project/NeedFinanceApproval.php @@ -18,4 +18,10 @@ public function color(): string { return 'yellow'; } + + #[\Override] + public function rules(): array + { + return parent::rules(); + } } diff --git a/app/States/Project/NeedOrgApproval.php b/app/States/Project/NeedOrgApproval.php index 5e5eca19..6065d666 100644 --- a/app/States/Project/NeedOrgApproval.php +++ b/app/States/Project/NeedOrgApproval.php @@ -18,4 +18,9 @@ public function color(): string { return 'yellow'; } + #[\Override] + public function rules(): array + { + return parent::rules(); + } } diff --git a/app/States/Project/ProjectState.php b/app/States/Project/ProjectState.php index 9bd0a1fe..2af68e9f 100644 --- a/app/States/Project/ProjectState.php +++ b/app/States/Project/ProjectState.php @@ -3,6 +3,9 @@ namespace App\States\Project; use App\Models\Legacy\Project; +use App\Rules\ExactlyOneZeroMoneyRule; +use App\Rules\FluxEditorRule; +use Illuminate\Support\Facades\Validator; use Livewire\Wireable; use Spatie\ModelStates\Exceptions\InvalidConfig; use Spatie\ModelStates\State; @@ -106,6 +109,41 @@ public static function config(): StateConfig return $config; } + public function rules() : array { + // some sensible default i dont want to copy paste around + return [ + 'name' => 'required|string|max:128', + 'responsible' => 'required|string|max:128|email', + 'org' => 'required|string|max:64', + 'protocol' => 'sometimes|nullable|string|url', + //'recht' => 'required|string|in:...', + //'recht-additional' => 'sometimes|nullable|string', + 'date_start' => 'required|date', + 'date_end' => 'required|date|after:date_start', + 'beschreibung' => ['required', 'string', new FluxEditorRule], + 'posts' => 'required|array|min:1', + 'posts.*.id' => 'sometimes|integer', + //'posts.*.titel_id' => 'sometimes|integer|exists:App\Models\Legacy\LegacyBudgetItem,id', + 'posts.*.name' => 'required|string|max:128|min:1', + 'posts.*.einnahmen' => 'required|money:EUR', + 'posts.*.ausgaben' => ['required','money:EUR', new ExactlyOneZeroMoneyRule('posts.*.einnahmen')], + 'posts.*.position' => 'sometimes|integer', + 'posts.*.bemerkung' => 'sometimes|string|max:256', + ]; + } + + public function getValidator() : \Illuminate\Validation\Validator + { + $model = $this->getModel(); + $data = [...$model->getAttributes(), 'posts' => $model->posts->all()]; + return Validator::make($data, static::rules()); + } + + public function validate() : array + { + return $this->getValidator()->validate(); + } + public function toLivewire(): array { return [$this->getValue(), $this->getModel()->getKey()]; diff --git a/app/States/Project/Revoked.php b/app/States/Project/Revoked.php index 8921a6c1..af6e3540 100644 --- a/app/States/Project/Revoked.php +++ b/app/States/Project/Revoked.php @@ -17,4 +17,11 @@ public function color(): string { return 'rose'; } + + #[\Override] + public function rules(): array + { + return []; + } + } diff --git a/app/States/Project/Terminated.php b/app/States/Project/Terminated.php index e07d9f7c..b0ec7eec 100644 --- a/app/States/Project/Terminated.php +++ b/app/States/Project/Terminated.php @@ -18,4 +18,14 @@ public function color(): string { return 'zinc'; } + + #[\Override] + public function rules(): array + { + return parent::rules() + [ + 'recht' => 'required|string', + 'recht-additional' => 'sometimes|nullable|string', + 'posts.*.titel_id' => 'sometimes|integer|exists:App\Models\Legacy\LegacyBudgetItem,id', + ]; + } } diff --git a/lang/de/errors.php b/lang/de/errors.php index d6199939..a74fe1a2 100644 --- a/lang/de/errors.php +++ b/lang/de/errors.php @@ -15,4 +15,6 @@ 'doc-link.subtitle' => 'Lies unser Benutzer*innen Handbuch für mehr Infos', 'blog-link.title' => 'Blog und News', 'blog-link.subtitle' => 'Lies den StuFis-Blog mit den neusten Updates', + 'flux-editor-malicious-html' => 'Fehlerhafte HTML-Tags', + 'one-money-has-to-be-zero' => 'Eines der beiden muss 0 sein.' ]; diff --git a/resources/views/components/money-input.blade.php b/resources/views/components/money-input.blade.php index fe01257a..e85e88ee 100644 --- a/resources/views/components/money-input.blade.php +++ b/resources/views/components/money-input.blade.php @@ -4,6 +4,7 @@ @if(!$disabled) merge(['class:input' => 'text-right']) }} /> + @else merge(['class:input' => 'text-right text-black!']) }}/> @endif diff --git a/resources/views/livewire/project/edit-project.blade.php b/resources/views/livewire/project/edit-project.blade.php index c5b06991..0a56a08c 100644 --- a/resources/views/livewire/project/edit-project.blade.php +++ b/resources/views/livewire/project/edit-project.blade.php @@ -51,7 +51,7 @@
    {{-- Rechtsgrundlage Dropdown --}} - + @foreach ($rechtsgrundlagen as $rg) {{ $rg['label'] }} @endforeach @@ -59,15 +59,15 @@ {{-- Dynamic Additional Fields per Rechtsgrundlage --}}
    - @isset ($rechtsgrundlagen[$form->recht]['has_additional']) - + @isset ($rechtsgrundlagen[$recht]['has_additional']) + @endisset
    - @if (isset($rechtsgrundlagen[$form->recht]['hint'])) -

    {{ $rechtsgrundlagen[$form->recht]['hint'] }}

    + @if (isset($rechtsgrundlagen[$recht]['hint'])) +

    {{ $rechtsgrundlagen[$recht]['hint'] }}

    @endisset
    @@ -93,7 +93,7 @@
    {{-- Project Name --}}
    - +
    {{-- Responsible Person --}} @@ -101,7 +101,7 @@ Projektveratantwortlich (E-Mail) - + {{-- @domain.com --}} @@ -109,7 +109,7 @@ {{-- Organization --}}
    - + @foreach ($gremien as $label) {{ $label }} @endforeach @@ -119,7 +119,7 @@ {{-- Organization Mail --}} @if (false)
    - + @foreach($mailingLists as $mailingLists) {{ $mailingLists }} @endforeach @@ -129,19 +129,26 @@ {{-- Project Duration --}}
    - + + + + +
    {{-- Protocol Link (optional based on config) --}} @if (!in_array('hide-protokoll', config('stufis.project.show-link', [])))
    - +
    @endif {{-- Creation Date --}}
    - + @foreach ($budgetPlans as $plan) {{ $plan->label() }} @endforeach @@ -209,12 +216,14 @@ wire:key="post-{{ $index }}-name" value="{{ $post['name'] }}" /> + {{-- Remarks --}} + placeholder="optional"/> + {{-- Budget Title --}} @@ -229,6 +238,7 @@ @endforeach + @else - @endif @@ -299,7 +309,7 @@
    @@ -310,7 +320,7 @@
    - +
    + @dump($this->getErrorBag()->keys())
    diff --git a/resources/views/livewire/project/show-project.blade.php b/resources/views/livewire/project/show-project.blade.php index 589d9e6a..cf674c9a 100644 --- a/resources/views/livewire/project/show-project.blade.php +++ b/resources/views/livewire/project/show-project.blade.php @@ -431,9 +431,9 @@ class="inline-flex items-center text-indigo-600 hover:text-indigo-800 transition @php $ratio = $post->expendedRatio() @endphp $ratio <= 0, + "text-gray-400" => $ratio === 0, "text-yellow-600" => 0 < $ratio && $ratio < 75, - "text-green-600" => $ratio >= 75 && $ratio <= 100, + "text-green-600" => 75 <= $ratio && $ratio <= 100, "text-red-600" => $ratio > 100 ])> {{ $post->expendedSum() }} @@ -564,6 +564,15 @@ class="inline-flex items-center text-indigo-600 hover:text-indigo-800 transition @endforeach
    + @if ($errors->any()) +
    +
      + @foreach ($errors->all() as $error) +
    • {{ $error }}
    • + @endforeach +
    +
    + @endif
    {{ __('project.view.state-modal.cancel') }} From bcfa4a646503dd5a07945f9f4d444f4e9fcc661d Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Sat, 13 Dec 2025 15:51:28 +0100 Subject: [PATCH 099/108] fix: handle nullable values in responsible attribute --- app/Models/Legacy/Project.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Legacy/Project.php b/app/Models/Legacy/Project.php index 04d2d47d..3241cb71 100644 --- a/app/Models/Legacy/Project.php +++ b/app/Models/Legacy/Project.php @@ -99,7 +99,7 @@ class Project extends Model protected function responsible(): Attribute { return Attribute::make( - get: fn (string $value) => empty($value) || str_contains($value, '@') ? $value : $value.'@'.config('stufis.mail_domain'), + get: fn (?string $value) => empty($value) || str_contains($value, '@') ? $value : $value.'@'.config('stufis.mail_domain'), set: fn (string $value) => empty($value) || str_contains($value, '@') ? $value : $value.'@'.config('stufis.mail_domain'), ); } From 58856283ae096e29cec00ed77e5cab98f5748f7d Mon Sep 17 00:00:00 2001 From: Lukas Staab Date: Mon, 15 Dec 2025 22:18:48 +0100 Subject: [PATCH 100/108] fix: rector --- .../Commands/ConvertLegacyBudgetPlans.php | 7 +- app/Livewire/ChatPanel.php | 2 +- app/Livewire/ProjectOverview.php | 5 +- app/Models/BudgetItem.php | 68 +++++++++++++++++++ app/Models/BudgetPlan.php | 24 ++++++- app/Models/Legacy/LegacyBudgetPlan.php | 4 +- app/Policies/BudgetPlanPolicy.php | 8 +-- app/Rules/ExactlyOneZeroMoneyRule.php | 21 +++--- app/Rules/FluxEditorRule.php | 2 +- app/States/Project/Applied.php | 7 +- app/States/Project/ApprovedByFinance.php | 1 + app/States/Project/ApprovedByOrg.php | 2 +- app/States/Project/ApprovedByOther.php | 10 +-- app/States/Project/Draft.php | 4 +- app/States/Project/NeedOrgApproval.php | 1 + app/States/Project/ProjectState.php | 16 +++-- app/States/Project/Revoked.php | 1 - app/States/Project/Terminated.php | 8 +-- lang/de/errors.php | 2 +- 19 files changed, 137 insertions(+), 56 deletions(-) diff --git a/app/Console/Commands/ConvertLegacyBudgetPlans.php b/app/Console/Commands/ConvertLegacyBudgetPlans.php index 3c831285..f7c135fc 100644 --- a/app/Console/Commands/ConvertLegacyBudgetPlans.php +++ b/app/Console/Commands/ConvertLegacyBudgetPlans.php @@ -147,9 +147,8 @@ protected function convertPlan(LegacyBudgetPlan $legacyPlan, string $organizatio 'organization' => $organization, 'fiscal_year_id' => $fiscalYear->id, 'state' => $state, - 'resolution_date' => $legacyPlan->von, + 'resolution_date' => null, // Legacy doesn't have this 'approval_date' => null, // Legacy doesn't have this - 'parent_plan' => null, ]); // Force the ID to match the legacy plan @@ -300,7 +299,7 @@ protected function getOrCreateFiscalYear(LegacyBudgetPlan $legacyPlan, bool $dry // Create a new fiscal year $fiscalYear = new FiscalYear([ 'start_date' => $legacyPlan->von, - 'end_date' => $legacyPlan->bis, + 'end_date' => $legacyPlan->bis ?? $legacyPlan->von->addYear()->subDay(), ]); if (! $dryRun) { @@ -310,7 +309,7 @@ protected function getOrCreateFiscalYear(LegacyBudgetPlan $legacyPlan, bool $dry $fiscalYear->id = 9999; } - $this->line(" ✓ Created Fiscal Year: {$legacyPlan->von} to {$legacyPlan->bis}"); + $this->line(" ✓ Created Fiscal Year: {$fiscalYear->start_date} to {$fiscalYear->end_date}"); } return $fiscalYear; diff --git a/app/Livewire/ChatPanel.php b/app/Livewire/ChatPanel.php index c1b5c87e..a4431824 100644 --- a/app/Livewire/ChatPanel.php +++ b/app/Livewire/ChatPanel.php @@ -34,7 +34,7 @@ public function render() public function save() { - $cleanContent = $this->validate(['content' => ['required', 'min:1', new FluxEditorRule()]])['content']; + $cleanContent = $this->validate(['content' => ['required', 'min:1', new FluxEditorRule]])['content']; ChatMessage::create([ 'text' => $cleanContent, diff --git a/app/Livewire/ProjectOverview.php b/app/Livewire/ProjectOverview.php index d5bb278c..d3bfef54 100644 --- a/app/Livewire/ProjectOverview.php +++ b/app/Livewire/ProjectOverview.php @@ -7,7 +7,6 @@ use App\Models\Legacy\Project; use App\Services\Auth\AuthService; use Illuminate\Database\Eloquent\Collection; -use Illuminate\Support\Facades\Auth; use Livewire\Attributes\Computed; use Livewire\Attributes\Url; use Livewire\Component; @@ -49,7 +48,7 @@ public function userCommittees(): array public function projectsByCommittee(): Collection { $budgetPlan = $this->currentBudgetPlan(); - if (!$budgetPlan) { + if (! $budgetPlan) { return []; } @@ -91,7 +90,7 @@ public function projectsByCommittee(): Collection $projects = $query->orderBy('org')->orderBy('id', 'desc')->get(); // Group by committee - return $projects->groupBy(fn($project) => $project->org ?: ''); + return $projects->groupBy(fn ($project) => $project->org ?: ''); } #[Computed] diff --git a/app/Models/BudgetItem.php b/app/Models/BudgetItem.php index 8c73bf70..4f020d5b 100644 --- a/app/Models/BudgetItem.php +++ b/app/Models/BudgetItem.php @@ -24,6 +24,74 @@ * @method static Builder|BudgetItem query() * * @mixin Eloquent + * + * @property int $id + * @property int $budget_plan_id + * @property string|null $short_name + * @property string|null $name + * @property \Cknow\Money\Money $value + * @property \App\Models\Enums\BudgetType $budget_type + * @property bool $is_group + * @property string $description + * @property int|null $position + * @property int|null $parent_id + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection $children + * @property-read int|null $children_count + * @property-read \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection $orderedChildren + * @property-read int|null $ordered_children_count + * @property-read \App\Models\BudgetItem|null $parent + * @property-read int $depth + * @property-read string $path + * @property-read string $position_path + * @property-read \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection $ancestors The model's recursive parents. + * @property-read int|null $ancestors_count + * @property-read \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection $ancestorsAndSelf The model's recursive parents and itself. + * @property-read int|null $ancestors_and_self_count + * @property-read \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection $bloodline The model's ancestors, descendants and itself. + * @property-read int|null $bloodline_count + * @property-read \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection $childrenAndSelf The model's direct children and itself. + * @property-read int|null $children_and_self_count + * @property-read \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection $descendants The model's recursive children. + * @property-read int|null $descendants_count + * @property-read \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection $descendantsAndSelf The model's recursive children and itself. + * @property-read int|null $descendants_and_self_count + * @property-read \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection $parentAndSelf The model's direct parent and itself. + * @property-read int|null $parent_and_self_count + * @property-read \App\Models\BudgetItem|null $rootAncestor The model's topmost parent. + * @property-read \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection $siblings The parent's other children. + * @property-read int|null $siblings_count + * @property-read \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection $siblingsAndSelf All the parent's children. + * @property-read int|null $siblings_and_self_count + * + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection all($columns = ['*']) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem breadthFirst() + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem depthFirst() + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem doesntHaveChildren() + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection get($columns = ['*']) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem getExpressionGrammar() + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem hasChildren() + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem hasParent() + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem isLeaf() + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem isRoot() + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem tree($maxDepth = null) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem treeOf(\Illuminate\Database\Eloquent\Model|callable $constraint, $maxDepth = null) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem whereBudgetPlanId($value) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem whereBudgetType($value) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem whereCreatedAt($value) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem whereDepth($operator, $value = null) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem whereDescription($value) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem whereId($value) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem whereIsGroup($value) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem whereName($value) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem whereParentId($value) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem wherePosition($value) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem whereShortName($value) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem whereUpdatedAt($value) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem whereValue($value) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem withGlobalScopes(array $scopes) + * @method static \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|BudgetItem withRelationshipExpression($direction, callable $constraint, $initialDepth, $from = null, $maxDepth = null) */ class BudgetItem extends Model { diff --git a/app/Models/BudgetPlan.php b/app/Models/BudgetPlan.php index dbed5462..ad6309f8 100644 --- a/app/Models/BudgetPlan.php +++ b/app/Models/BudgetPlan.php @@ -10,6 +10,8 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; /** * App\Models\BudgetPlan @@ -33,6 +35,22 @@ * @method static Builder|BudgetPlan query() * * @mixin Eloquent + * + * @property string|null $organization + * @property int|null $fiscal_year_id + * @property-read \App\Models\FiscalYear|null $fiscalYear + * @property-read \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection $rootBudgetItems + * @property-read int|null $root_budget_items_count + * + * @method static \Illuminate\Database\Eloquent\Builder|BudgetPlan whereApprovalDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|BudgetPlan whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|BudgetPlan whereFiscalYearId($value) + * @method static \Illuminate\Database\Eloquent\Builder|BudgetPlan whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|BudgetPlan whereOrganization($value) + * @method static \Illuminate\Database\Eloquent\Builder|BudgetPlan whereParentPlanId($value) + * @method static \Illuminate\Database\Eloquent\Builder|BudgetPlan whereResolutionDate($value) + * @method static \Illuminate\Database\Eloquent\Builder|BudgetPlan whereState($value) + * @method static \Illuminate\Database\Eloquent\Builder|BudgetPlan whereUpdatedAt($value) */ class BudgetPlan extends Model { @@ -60,7 +78,7 @@ protected function casts(): array ]; } - public function budgetItems(): \Illuminate\Database\Eloquent\Relations\HasMany + public function budgetItems(): HasMany { return $this->hasMany(BudgetItem::class); } @@ -78,12 +96,12 @@ public function budgetItemsTree(BudgetType $budgetType) return BudgetItem::treeOf($constraint)->orderBy('position_path')->get(); } - public function rootBudgetItems(): Builder|\Illuminate\Database\Eloquent\Relations\HasMany|BudgetPlan + public function rootBudgetItems(): Builder|HasMany|BudgetPlan { return $this->hasMany(BudgetItem::class)->whereNull('parent_id'); } - public function fiscalYear(): \Illuminate\Database\Eloquent\Relations\BelongsTo + public function fiscalYear(): BelongsTo { return $this->belongsTo(FiscalYear::class); } diff --git a/app/Models/Legacy/LegacyBudgetPlan.php b/app/Models/Legacy/LegacyBudgetPlan.php index 6c2d8bde..9c2e624a 100644 --- a/app/Models/Legacy/LegacyBudgetPlan.php +++ b/app/Models/Legacy/LegacyBudgetPlan.php @@ -60,8 +60,10 @@ public static function latest(): \Eloquent|static return self::orderBy('id', 'desc')->first(); } - public static function findByDate(Carbon $date): static + public static function findByDate(?Carbon $date = null): static { + $date ??= \Illuminate\Support\Facades\Date::now(); + return self::query()->where('von', '<=', $date) ->where(fn ($query) => $query->where('bis', '>=', $date) ->orWhereNull('bis')) diff --git a/app/Policies/BudgetPlanPolicy.php b/app/Policies/BudgetPlanPolicy.php index e1fac15b..f73a414f 100644 --- a/app/Policies/BudgetPlanPolicy.php +++ b/app/Policies/BudgetPlanPolicy.php @@ -12,22 +12,22 @@ class BudgetPlanPolicy public function viewAny(User $user): bool { - return false; + return true; } public function view(User $user, BudgetPlan $budgetPlan): bool { - return false; + return true; } public function create(User $user): bool { - return false; + return $user->getGroups()->contains('ref-finanzen-hv'); } public function update(User $user, BudgetPlan $budgetPlan): bool { - return false; + return $user->getGroups()->contains('ref-finanzen-hv'); } public function delete(User $user, BudgetPlan $budgetPlan): bool diff --git a/app/Rules/ExactlyOneZeroMoneyRule.php b/app/Rules/ExactlyOneZeroMoneyRule.php index 1887b495..63f13cc1 100644 --- a/app/Rules/ExactlyOneZeroMoneyRule.php +++ b/app/Rules/ExactlyOneZeroMoneyRule.php @@ -2,16 +2,14 @@ namespace App\Rules; -use Cknow\Money\Rules\Money; use Closure; use Illuminate\Contracts\Validation\DataAwareRule; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Collection; use Illuminate\Support\Str; -class ExactlyOneZeroMoneyRule implements ValidationRule, DataAwareRule +class ExactlyOneZeroMoneyRule implements DataAwareRule, ValidationRule { - private array $data = []; public function __construct( @@ -28,21 +26,18 @@ public function validate(string $attribute, mixed $value, Closure $fail): void // pairs every attribute field with the other field, pair[0] has the actual field, pair[1] can have *'s $otherAccessors = Str::of($attribute)->explode('.') ->zip(Str::of($this->otherField)->explode('.')) - ->map(fn (Collection $pair) => $pair[1] === '*' ? $pair[0] : $pair[1]) - ; - //dump($otherAccessors); + ->map(fn (Collection $pair) => $pair[1] === '*' ? $pair[0] : $pair[1]); + // dump($otherAccessors); $otherMoney = $this->data; while (($idx = $otherAccessors->shift()) !== null) { - //dump($otherMoney, $idx, $otherAccessors); + // dump($otherMoney, $idx, $otherAccessors); $otherMoney = $otherMoney[$idx]; } - //dd($otherMoney); - $oneIsZero = (($value->getAmount() === "0") xor ($otherMoney->getAmount() === "0")); - //dd($value, $otherMoney, ($value->getAmount() === "0"),($otherMoney->getAmount() === "0"), $oneIsZero); - if (!$oneIsZero) { + // dd($otherMoney); + $oneIsZero = (($value->getAmount() === '0') xor ($otherMoney->getAmount() === '0')); + // dd($value, $otherMoney, ($value->getAmount() === "0"),($otherMoney->getAmount() === "0"), $oneIsZero); + if (! $oneIsZero) { $fail(__('errors.one-money-has-to-be-zero')); } } - - } diff --git a/app/Rules/FluxEditorRule.php b/app/Rules/FluxEditorRule.php index 70f70ffb..39079048 100644 --- a/app/Rules/FluxEditorRule.php +++ b/app/Rules/FluxEditorRule.php @@ -16,7 +16,7 @@ class FluxEditorRule implements ValidationRule public function validate(string $attribute, mixed $value, Closure $fail): void { $cleanContent = strip_tags((string) $value, '