From fe3429cd4ee52d0824311b6115cd691386216d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=BCnther?= Date: Tue, 26 May 2026 11:07:33 +0200 Subject: [PATCH 1/3] Displaying estimates --- app/Domains/Event/Views/Partials/Overview.vue | 13 ++++++------ .../Views/Partials/ParticipationSummary.vue | 20 +++++++++++++++---- app/Models/CostUnit.php | 9 +++++++++ app/Repositories/CostUnitRepository.php | 12 +++++++++++ app/Resources/CostUnitResource.php | 6 ++++++ app/Resources/EventResource.php | 10 +++++++++- app/Scopes/CommonController.php | 3 +++ routes/web.php | 2 ++ 8 files changed, 64 insertions(+), 11 deletions(-) diff --git a/app/Domains/Event/Views/Partials/Overview.vue b/app/Domains/Event/Views/Partials/Overview.vue index 0e28bda..920e544 100644 --- a/app/Domains/Event/Views/Partials/Overview.vue +++ b/app/Domains/Event/Views/Partials/Overview.vue @@ -112,8 +112,8 @@
-
-
+
+

@@ -186,6 +186,7 @@     + Budget bearbeiten Ausgabenübersicht Archivieren
@@ -248,13 +249,13 @@ gap: 10px; /* Abstand zwischen den Spalten */ } -.event-flexbox-row.top .left { +.event-flexbox-row.top .actions-left { flex: 0 0 calc(100% - 300px); padding: 10px; } -.event-flexbox-row.top .right { - flex: 0 0 250px; +.event-flexbox-row.top .actions-right { + flex: 0 0 200px; padding: 10px; } @@ -263,7 +264,7 @@ padding: 10px; } -.event-flexbox-row.top .right input[type="button"] { +.event-flexbox-row.top .actions-right input[type="button"] { width: 100% !important; margin-bottom: 10px; } diff --git a/app/Domains/Event/Views/Partials/ParticipationSummary.vue b/app/Domains/Event/Views/Partials/ParticipationSummary.vue index 3394bd6..3c605bd 100644 --- a/app/Domains/Event/Views/Partials/ParticipationSummary.vue +++ b/app/Domains/Event/Views/Partials/ParticipationSummary.vue @@ -96,19 +96,31 @@ const props = defineProps({ + + Budget + + {{ props.event.totalBalance.estimated.readable }} + + + {{props.event.totalBalance.estimated.readable}} + + +

Ausgaben

- +
+ - + +
{{amount.name}} {{amount.string}}({{ amount.estimatedString }})
Gesamt{{props.event.costUnit.overAllAmount.text}}{{props.event.costUnit.overAllAmount.text}}({{props.event.costUnit.overAllEstimatedAmount.text}}))
@@ -121,7 +133,7 @@ const props = defineProps({ .participant-flexbox { display: flex; flex-direction: column; - gap: 10px; + gap: 20px; width: 95%; margin: 20px auto 0; } @@ -135,7 +147,7 @@ const props = defineProps({ .participant-flexbox-row.top .left, .participant-flexbox-row.top .right { - padding: 10px; + padding: 20px; min-width: 0; } diff --git a/app/Models/CostUnit.php b/app/Models/CostUnit.php index afb66e3..8f82621 100644 --- a/app/Models/CostUnit.php +++ b/app/Models/CostUnit.php @@ -6,6 +6,7 @@ use App\Scopes\InstancedModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\HasOne; /** * @property string $name @@ -44,7 +45,15 @@ class CostUnit extends InstancedModel return $this->hasMany(Invoice::class); } + public function estimates() : hasMany { + return $this->hasMany(CostUnitEstimate::class); + } + public function tenant() : BelongsTo { return $this->belongsTo(Tenant::class, 'tenant', 'slug'); } + + public function event() : HasOne { + return $this->hasOne(Event::class); + } } diff --git a/app/Repositories/CostUnitRepository.php b/app/Repositories/CostUnitRepository.php index 75df613..a976501 100644 --- a/app/Repositories/CostUnitRepository.php +++ b/app/Repositories/CostUnitRepository.php @@ -178,6 +178,18 @@ class CostUnitRepository { return $amount; } + public function sumupEstimatedByInvoiceType(CostUnit $costUnit, InvoiceType $invoiceType) : Amount { + $amount = new Amount(0, 'Euro'); + foreach ($costUnit->estimates()->get() as $estimate) { + if ($estimate->type !== $invoiceType->slug) { + continue; + } + + $amount->addAmount($estimate->calculateAmount()); + } + return $amount; + } + public function sumupUnhandledAmounts(CostUnit $costUnit, bool $donatedAmount = false) : Amount { $amount = new Amount(0, ''); diff --git a/app/Resources/CostUnitResource.php b/app/Resources/CostUnitResource.php index 1799798..15d156c 100644 --- a/app/Resources/CostUnitResource.php +++ b/app/Resources/CostUnitResource.php @@ -31,10 +31,15 @@ class CostUnitResource { $amounts = []; $overAllAmount = new Amount(0, 'Euro'); + $overAllEstimatedAmount = new Amount(0, 'Euro'); foreach (InvoiceType::orderBy('sort_order')->get() as $invoiceType) { $overAllAmount->addAmount($costUnitRepository->sumupByInvoiceType($this->costUnit, $invoiceType)); + $overAllEstimatedAmount->addAmount($costUnitRepository->sumupEstimatedByInvoiceType($this->costUnit, $invoiceType)); $amounts[$invoiceType->slug]['string'] = $costUnitRepository->sumupByInvoiceType($this->costUnit, $invoiceType)->toString(); $amounts[$invoiceType->slug]['name'] = $invoiceType->name; + $amounts[$invoiceType->slug]['estimated'] = $costUnitRepository->sumupEstimatedByInvoiceType($this->costUnit, $invoiceType); + $amounts[$invoiceType->slug]['estimatedString'] = $costUnitRepository->sumupEstimatedByInvoiceType($this->costUnit, $invoiceType)->toString(); + } @@ -52,6 +57,7 @@ class CostUnitResource { 'treasurers' => $this->costUnit->treasurers()->get()->map(fn($user) => new UserResource($user))->toArray(), 'amounts' => $amounts, 'overAllAmount' => ['text' => $overAllAmount->toString(), 'value' => $overAllAmount], + 'overAllEstimatedAmount' => ['text' => $overAllEstimatedAmount->toString(), 'value' => $overAllEstimatedAmount], ]); diff --git a/app/Resources/EventResource.php b/app/Resources/EventResource.php index 14bb4cf..ca7428a 100644 --- a/app/Resources/EventResource.php +++ b/app/Resources/EventResource.php @@ -86,6 +86,7 @@ class EventResource extends JsonResource{ $returnArray['eventEnd'] = $this->event->end_date->format('d.m.Y'); $returnArray['eventEndInternal'] = $this->event->end_date; $returnArray['duration'] = $duration; + $returnArray['totalParticipantCount'] = $this->event->participants()->count(); $returnArray['supportPersonIndex'] = $this->event->support_per_person->toString(); $returnArray['supportPerson'] = $this->calculateSupportPerPerson($returnArray['participants']); @@ -95,12 +96,15 @@ class EventResource extends JsonResource{ $totalBalanceReal = new Amount(0, 'Euro'); $totalBalanceExpected = new Amount(0, 'Euro'); + $totalBalanceEstimated = new Amount(0, 'Euro'); $totalBalanceReal->addAmount($returnArray['income']['real']['amount']); $totalBalanceExpected->addAmount($returnArray['income']['expected']['amount']); + $totalBalanceEstimated->addAmount($returnArray['income']['expected']['amount']); $totalBalanceReal->subtractAmount($returnArray['costUnit']['overAllAmount']['value']); $totalBalanceExpected->subtractAmount($returnArray['costUnit']['overAllAmount']['value']); + $totalBalanceEstimated->subtractAmount($returnArray['costUnit']['overAllEstimatedAmount']['value']); $returnArray['totalBalance'] = [ 'real' => [ 'value' => $totalBalanceReal->getAmount(), @@ -108,7 +112,11 @@ class EventResource extends JsonResource{ ], 'expected' => [ 'value' => $totalBalanceExpected->getAmount(), 'readable' => $totalBalanceExpected->toString(), - ] + ], + 'estimated' => [ + 'value' => $totalBalanceEstimated->getAmount(), + 'readable' => $totalBalanceEstimated->toString(), + ] ]; $returnArray['flatSupport'] = $this->event->support_flat->toString(); diff --git a/app/Scopes/CommonController.php b/app/Scopes/CommonController.php index a1bb05d..dd0efae 100644 --- a/app/Scopes/CommonController.php +++ b/app/Scopes/CommonController.php @@ -5,6 +5,7 @@ namespace App\Scopes; use App\Models\Tenant; use App\Providers\AuthCheckProvider; use App\Repositories\CostUnitRepository; +use App\Repositories\EstimatesRepository; use App\Repositories\EventParticipantRepository; use App\Repositories\EventRepository; use App\Repositories\InvoiceRepository; @@ -21,6 +22,7 @@ abstract class CommonController { protected InvoiceRepository $invoices; protected EventRepository $events; protected EventParticipantRepository $eventParticipants; + protected EstimatesRepository $estimates; public function __construct() { $this->tenant = app('tenant'); @@ -30,6 +32,7 @@ abstract class CommonController { $this->invoices = new InvoiceRepository(); $this->events = new EventRepository(); $this->eventParticipants = new EventParticipantRepository(); + $this->estimates = new EstimatesRepository(); } protected function checkAuth() { diff --git a/routes/web.php b/routes/web.php index bbbd546..e521279 100644 --- a/routes/web.php +++ b/routes/web.php @@ -21,6 +21,8 @@ require_once __DIR__ . '/../app/Domains/Invoice/Routes/web.php'; require_once __DIR__ . '/../app/Domains/Invoice/Routes/api.php'; require_once __DIR__ . '/../app/Domains/Event/Routes/web.php'; require_once __DIR__ . '/../app/Domains/Event/Routes/api.php'; +require_once __DIR__ . '/../app/Domains/Budget/Routes/web.php'; +require_once __DIR__ . '/../app/Domains/Budget/Routes/api.php'; Route::get('/LKvDUqWl', function () { From 575fb27018f0212b3a6cf2569e02d4c41a8559d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=BCnther?= Date: Tue, 26 May 2026 11:07:59 +0200 Subject: [PATCH 2/3] Displaying estimates --- .../Budget/Controllers/ListController.php | 24 +++ .../Budget/Controllers/MainController.php | 19 ++ app/Domains/Budget/Routes/api.php | 17 ++ app/Domains/Budget/Routes/web.php | 20 ++ app/Domains/Budget/Views/List.vue | 93 +++++++++ app/Domains/Budget/Views/ListBudgetTypes.vue | 195 ++++++++++++++++++ app/Models/CostUnitEstimate.php | 55 +++++ app/Repositories/EstimatesRepository.php | 16 ++ app/Resources/CostUnitEstimateResource.php | 35 ++++ ...5_25_140010_create_cost_unit_estimates.php | 32 +++ 10 files changed, 506 insertions(+) create mode 100644 app/Domains/Budget/Controllers/ListController.php create mode 100644 app/Domains/Budget/Controllers/MainController.php create mode 100644 app/Domains/Budget/Routes/api.php create mode 100644 app/Domains/Budget/Routes/web.php create mode 100644 app/Domains/Budget/Views/List.vue create mode 100644 app/Domains/Budget/Views/ListBudgetTypes.vue create mode 100644 app/Models/CostUnitEstimate.php create mode 100644 app/Repositories/EstimatesRepository.php create mode 100644 app/Resources/CostUnitEstimateResource.php create mode 100644 database/migrations/2026_05_25_140010_create_cost_unit_estimates.php diff --git a/app/Domains/Budget/Controllers/ListController.php b/app/Domains/Budget/Controllers/ListController.php new file mode 100644 index 0000000..7d0741e --- /dev/null +++ b/app/Domains/Budget/Controllers/ListController.php @@ -0,0 +1,24 @@ +costUnits->getById($costUnitId); + $estimates = $this->estimates->getEstimates($costUnit, $estimateType); + + return response()->json([ + 'status' => 'success', + 'costUnitId' => $costUnitId, + 'title' => InvoiceType::where('slug', $estimateType)->first()->name, + 'estimates' => $estimates, + ]); + } + +} diff --git a/app/Domains/Budget/Controllers/MainController.php b/app/Domains/Budget/Controllers/MainController.php new file mode 100644 index 0000000..0fb6ff9 --- /dev/null +++ b/app/Domains/Budget/Controllers/MainController.php @@ -0,0 +1,19 @@ + $costUnitId + ]); + return $inertiaProvider->render(); + } +} diff --git a/app/Domains/Budget/Routes/api.php b/app/Domains/Budget/Routes/api.php new file mode 100644 index 0000000..2a7d345 --- /dev/null +++ b/app/Domains/Budget/Routes/api.php @@ -0,0 +1,17 @@ +group(function () { + Route::middleware(IdentifyTenant::class)->group(function () { + Route::prefix('budget')->group(function () { + Route::middleware(['auth'])->group(function () { + Route::prefix('/{costUnitId}')->group(function () { + Route::get('/list/{estimateType}', ListController::class); + }); + }); + }); + }); +}); diff --git a/app/Domains/Budget/Routes/web.php b/app/Domains/Budget/Routes/web.php new file mode 100644 index 0000000..e96b0ef --- /dev/null +++ b/app/Domains/Budget/Routes/web.php @@ -0,0 +1,20 @@ +group(function () { + Route::prefix('budget')->group(function () { + Route::middleware(['auth'])->group(function () { + Route::prefix('/{costUnitId}')->group(function() { + Route::get('/', MainController::class); + }); + }); + }); +}); + + + + + diff --git a/app/Domains/Budget/Views/List.vue b/app/Domains/Budget/Views/List.vue new file mode 100644 index 0000000..facf885 --- /dev/null +++ b/app/Domains/Budget/Views/List.vue @@ -0,0 +1,93 @@ + + + diff --git a/app/Domains/Budget/Views/ListBudgetTypes.vue b/app/Domains/Budget/Views/ListBudgetTypes.vue new file mode 100644 index 0000000..bf58ca6 --- /dev/null +++ b/app/Domains/Budget/Views/ListBudgetTypes.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/app/Models/CostUnitEstimate.php b/app/Models/CostUnitEstimate.php new file mode 100644 index 0000000..ac55c5d --- /dev/null +++ b/app/Models/CostUnitEstimate.php @@ -0,0 +1,55 @@ + AmountCast::class, + 'amount_by_user' => AmountCast::class, + ]; + + public function costUnit() : BelongsTo{ + return $this->belongsTo(CostUnit::class); + } + + public function invoiceType() : InvoiceType { + return $this->belongsTo(InvoiceType::class, 'type', 'slug')->first(); + } + + public function calculateAmount() : ?Amount { + switch (true) { + case $this->flat_amount !== null: + return $this->flat_amount; + default: + $event = $this->costUnit()->first()->event()?->first(); + if (null !== $event) { + + $participants = $event->participants()->count(); + return $this->amount_by_user->multiply($participants); + + } else { + dd('U'); + return $this->amount_by_user; + } + } + } +} diff --git a/app/Repositories/EstimatesRepository.php b/app/Repositories/EstimatesRepository.php new file mode 100644 index 0000000..253618d --- /dev/null +++ b/app/Repositories/EstimatesRepository.php @@ -0,0 +1,16 @@ +estimates()->where('type', $estimateType)->get() as $estimate) { + $return[] = $estimate->toResource()->toArray(request()); + } + + return $return; + } +} diff --git a/app/Resources/CostUnitEstimateResource.php b/app/Resources/CostUnitEstimateResource.php new file mode 100644 index 0000000..c8f7221 --- /dev/null +++ b/app/Resources/CostUnitEstimateResource.php @@ -0,0 +1,35 @@ +resource->flat_amount?->toString(); + if ($amountString === null) { + $amountString = $this->resource->amount_by_user?->toString() . ' / Person'; + } else { + $amountString .= ' Gesamt'; + } + + return [ + 'id' => $this->resource->id, + 'title' => $this->resource->description, + 'totalAmountString' => $amountString, + ]; + } +} diff --git a/database/migrations/2026_05_25_140010_create_cost_unit_estimates.php b/database/migrations/2026_05_25_140010_create_cost_unit_estimates.php new file mode 100644 index 0000000..3c0aee3 --- /dev/null +++ b/database/migrations/2026_05_25_140010_create_cost_unit_estimates.php @@ -0,0 +1,32 @@ +id(); + $table->string('tenant'); + $table->foreignId('cost_unit_id')->constrained('cost_units', 'id')->restrictOnDelete()->cascadeOnUpdate(); + $table->string('type'); + $table->string('description'); + $table->float('flat_amount', 2)->nullable(); + $table->float('amount_by_user', 2)->nullable(); + + $table->foreign('tenant')->references('slug')->on('tenants')->restrictOnDelete()->cascadeOnUpdate(); + $table->foreign('type')->references('slug')->on('invoice_types')->restrictOnDelete()->cascadeOnUpdate(); + + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('cost_unit_estimates'); + } +}; From 28ffbdb696e8c3bf60f547547442c2edc60ca83e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=BCnther?= Date: Tue, 26 May 2026 18:12:42 +0200 Subject: [PATCH 3/3] Implemented Event Budget --- .../CreateEstimate/CreateEstimateAction.php | 50 +++++ .../CreateEstimate/CreateEstimateRequest.php | 19 ++ .../CreateEstimate/CreateEstimateResponse.php | 13 ++ .../DeleteEstimate/DeleteEstimateAction.php | 16 ++ .../DeleteEstimate/DeleteEstimateRequest.php | 12 ++ .../DeleteEstimate/DeleteEstimateResponse.php | 12 ++ .../Budget/Controllers/DeleteController.php | 43 ++++ .../Budget/Controllers/ListController.php | 2 + .../Budget/Controllers/SaveController.php | 47 +++++ app/Domains/Budget/Routes/api.php | 4 + .../Budget/Views/AddOrUpdateEstimate.vue | 100 +++++++++ app/Domains/Budget/Views/ListBudgetTypes.vue | 190 ++++++------------ .../CostUnit/Views/Partials/ListInvoices.vue | 1 - app/Models/CostUnitEstimate.php | 6 +- app/Repositories/EstimatesRepository.php | 28 +++ app/Resources/CostUnitEstimateResource.php | 20 +- version | 2 +- 17 files changed, 422 insertions(+), 143 deletions(-) create mode 100644 app/Domains/Budget/Actions/CreateEstimate/CreateEstimateAction.php create mode 100644 app/Domains/Budget/Actions/CreateEstimate/CreateEstimateRequest.php create mode 100644 app/Domains/Budget/Actions/CreateEstimate/CreateEstimateResponse.php create mode 100644 app/Domains/Budget/Actions/DeleteEstimate/DeleteEstimateAction.php create mode 100644 app/Domains/Budget/Actions/DeleteEstimate/DeleteEstimateRequest.php create mode 100644 app/Domains/Budget/Actions/DeleteEstimate/DeleteEstimateResponse.php create mode 100644 app/Domains/Budget/Controllers/DeleteController.php create mode 100644 app/Domains/Budget/Controllers/SaveController.php create mode 100644 app/Domains/Budget/Views/AddOrUpdateEstimate.vue diff --git a/app/Domains/Budget/Actions/CreateEstimate/CreateEstimateAction.php b/app/Domains/Budget/Actions/CreateEstimate/CreateEstimateAction.php new file mode 100644 index 0000000..0f0cf21 --- /dev/null +++ b/app/Domains/Budget/Actions/CreateEstimate/CreateEstimateAction.php @@ -0,0 +1,50 @@ +response = new CreateEstimateResponse(); + + $amount = []; + switch ($this->request->amountType) { + case 'flat': + $amount['flat_amount'] = $this->request->amount; + break; + case 'per_person': + $amount['amount_by_user'] = $this->request->amount; + break; + } + + if ($this->request->estimateId === 0) { + $estimate = CostUnitEstimate::create(array_merge([ + 'tenant' => app('tenant')->slug, + 'cost_unit_id' => $this->request->costUnit->id, + 'type' => $this->request->estimateType, + 'description' => $this->request->description, + ], $amount)); + } else { + $estimate = CostUnitEstimate::find($this->request->estimateId); + $estimate->update(array_merge([ + 'tenant' => app('tenant')->slug, + 'cost_unit_id' => $this->request->costUnit->id, + 'type' => $this->request->estimateType, + 'description' => $this->request->description, + ], $amount)); + } + + if ($estimate !== null) { + $this->response->estimateId = $estimate->id; + $this->response->success = true; + } + + return $this->response; + } +} diff --git a/app/Domains/Budget/Actions/CreateEstimate/CreateEstimateRequest.php b/app/Domains/Budget/Actions/CreateEstimate/CreateEstimateRequest.php new file mode 100644 index 0000000..c34d618 --- /dev/null +++ b/app/Domains/Budget/Actions/CreateEstimate/CreateEstimateRequest.php @@ -0,0 +1,19 @@ +success = false; + $this->estimateId = null; + } +} diff --git a/app/Domains/Budget/Actions/DeleteEstimate/DeleteEstimateAction.php b/app/Domains/Budget/Actions/DeleteEstimate/DeleteEstimateAction.php new file mode 100644 index 0000000..0f8e87e --- /dev/null +++ b/app/Domains/Budget/Actions/DeleteEstimate/DeleteEstimateAction.php @@ -0,0 +1,16 @@ +request->estimate->delete(); + $response->success = true; + return $response; + } +} diff --git a/app/Domains/Budget/Actions/DeleteEstimate/DeleteEstimateRequest.php b/app/Domains/Budget/Actions/DeleteEstimate/DeleteEstimateRequest.php new file mode 100644 index 0000000..85a6476 --- /dev/null +++ b/app/Domains/Budget/Actions/DeleteEstimate/DeleteEstimateRequest.php @@ -0,0 +1,12 @@ +success = false; + } +} diff --git a/app/Domains/Budget/Controllers/DeleteController.php b/app/Domains/Budget/Controllers/DeleteController.php new file mode 100644 index 0000000..612f7de --- /dev/null +++ b/app/Domains/Budget/Controllers/DeleteController.php @@ -0,0 +1,43 @@ +estimates->getById($estimateId); + + if ($estimate === null) { + return response()->json([ + 'status' => 'error', + 'message' => 'Estimate not found' + ], 404); + } + + $deleteEstimateResponse = + new DeleteEstimateAction(request: new DeleteEstimateRequest($estimate) + )->execute(); + + if ($deleteEstimateResponse->success) { + return response()->json([ + 'status' => 'success', + 'message' => 'Der Eintrag wurde erfolgreich gelöscht.' + ]); + } else { + return response()->json([ + 'status' => 'error', + 'message' => 'Beim Löschen des Eintrags ist ein Fehler aufgetreten.' + ]); + } + } +} diff --git a/app/Domains/Budget/Controllers/ListController.php b/app/Domains/Budget/Controllers/ListController.php index 7d0741e..e93602b 100644 --- a/app/Domains/Budget/Controllers/ListController.php +++ b/app/Domains/Budget/Controllers/ListController.php @@ -17,7 +17,9 @@ class ListController extends CommonController 'status' => 'success', 'costUnitId' => $costUnitId, 'title' => InvoiceType::where('slug', $estimateType)->first()->name, + 'estimateType' => $estimateType, 'estimates' => $estimates, + 'totalAmountString' => $this->estimates->getTotalAmount($costUnit, $estimateType)->toString(), ]); } diff --git a/app/Domains/Budget/Controllers/SaveController.php b/app/Domains/Budget/Controllers/SaveController.php new file mode 100644 index 0000000..72c4e1b --- /dev/null +++ b/app/Domains/Budget/Controllers/SaveController.php @@ -0,0 +1,47 @@ +costUnits->getById($costUnitId); + + if ($costUnit === null) { + return response()->json([ + 'status' => 'error', + 'message' => 'Cost unit not found' + ], 404); + } + + $createCostUniResponse = + new CreateEstimateAction(request: new CreateEstimateRequest( + description: $request->input('description'), + amount: Amount::fromString($request->input('amount')), + amountType: $request->input('amount_type'), + estimateType: $request->input('estimateType'), + costUnit: $costUnit, + estimateId: $request->input('estimateId'), + ))->execute(); + + if ($createCostUniResponse->success) { + return response()->json([ + 'status' => 'success', + 'message' => 'Der Eintrag wurde erfolgreich angelegt.' + ]); + } else { + return response()->json([ + 'status' => 'error', + 'message' => 'Beim Anlegen des Eintrags ist ein Fehler aufgetreten.' + ]); + } + } +} diff --git a/app/Domains/Budget/Routes/api.php b/app/Domains/Budget/Routes/api.php index 2a7d345..d01a5ee 100644 --- a/app/Domains/Budget/Routes/api.php +++ b/app/Domains/Budget/Routes/api.php @@ -1,5 +1,7 @@ group(function () { Route::middleware(['auth'])->group(function () { Route::prefix('/{costUnitId}')->group(function () { Route::get('/list/{estimateType}', ListController::class); + Route::get('{estimateId}/delete', DeleteController::class); + Route::post('/save-estimate', SaveController::class); }); }); }); diff --git a/app/Domains/Budget/Views/AddOrUpdateEstimate.vue b/app/Domains/Budget/Views/AddOrUpdateEstimate.vue new file mode 100644 index 0000000..16cfc5a --- /dev/null +++ b/app/Domains/Budget/Views/AddOrUpdateEstimate.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/app/Domains/Budget/Views/ListBudgetTypes.vue b/app/Domains/Budget/Views/ListBudgetTypes.vue index bf58ca6..7073d92 100644 --- a/app/Domains/Budget/Views/ListBudgetTypes.vue +++ b/app/Domains/Budget/Views/ListBudgetTypes.vue @@ -3,189 +3,115 @@ import {createApp, ref} from 'vue' import LoadingModal from "../../../Views/Components/LoadingModal.vue"; import { useAjax } from "../../../../resources/js/components/ajaxHandler.js"; import {toast} from "vue3-toastify"; +import AddOrUpdateEstimate from "./AddOrUpdateEstimate.vue"; const props = defineProps({ data: { type: [Array, Object], default: () => [] }, - - deep_jump_id: { - type: Number, - default: 0 - }, - - deep_jump_id_sub: { - type: Number, - default: 0 - } }) -const showInvoiceList = ref(false) -const invoices = ref(null) -const current_cost_unit = ref(null) -const showLoading = ref(false) -const show_invoice = ref(false) -const invoice = ref(null) +const localData = ref(props.data) -const show_cost_unit = ref(false) -const showTreasurers = ref(false) -const costUnit = ref(null) +const showAddEstimate = ref(false) +const estimateId = ref(null) +const description = ref(null) +const amount = ref(null) +const amountType = ref(null) const { data, loading, error, request, download } = useAjax() -async function costUnitDetails(costUnitId) { - const data = await request('/api/v1/cost-unit/' + costUnitId + '/details', { - method: "GET", - }); +async function reload() { + const url = "/api/v1/budget/" + props.data.costUnitId + "/list/" + props.data.estimateType + try { + const response = await fetch(url, { method: 'GET' }) + if (!response.ok) throw new Error('Fehler beim Laden') - showLoading.value = false; - - if (data.status === 'success') { - costUnit.value = data.costUnit - show_cost_unit.value = true - } else { - toast.error(data.message); + const result = await response.json() + localData.value = result + } catch (err) { + console.error('Error fetching estimates:', err) } } -async function editTreasurers(costUnitId) { - const data = await request('/api/v1/cost-unit/' + costUnitId + '/treasurers', { +async function openAddEstimate() { + estimateId.value = 0 + amount.value = 0.00 + amountType.value = 'flat' + description.value = '' + showAddEstimate.value = true +} + +async function openEditEstimate(localEstimateId, localDescription, localAmount, localAmountType, localEstimateType) { + estimateId.value = localEstimateId + description.value = localDescription + amount.value = localAmount + amountType.value = localAmountType + console.log(localEstimateId, localDescription, localAmount, localAmountType, localEstimateType) + console.log(estimateId.value, description.value, amount.value, amountType.value, localEstimateType) + showAddEstimate.value = true +} + +async function deleteEstimate(currentEstimateId) { + const data = await request('/api/v1/budget/' + props.data.costUnitId + '/' + currentEstimateId + '/delete', { method: "GET", }); - showLoading.value = false; - - if (data.status === 'success') { - costUnit.value = data.costUnit - showTreasurers.value = true - } else { - toast.error(data.message); - } -} - - function loadInvoices(cost_unit_id) { - window.location.href = '/cost-unit/' + cost_unit_id; - } - -async function denyNewRequests(costUnitId) { - changeCostUnitState(costUnitId, 'close'); -} - - - async function archiveCostUnit(costUnitId) { - changeCostUnitState(costUnitId, 'archive'); - } - - - async function allowNewRequests(costUnitId) { - changeCostUnitState(costUnitId, 'open'); - } - - -async function changeCostUnitState(costUnitId, endPoint) { - showLoading.value = true; - const data = await request('/api/v1/cost-unit/' + costUnitId + '/' + endPoint, { - method: "POST", - }); - - showLoading.value = false; if (data.status === 'success') { toast.success(data.message); - document.getElementById('costUnitBox_' + costUnitId).style.display = 'none'; + reload() } else { toast.error(data.message); } } - -async function exportPayouts(costUnitId) { - showLoading.value = true; - - - const response = await fetch('/api/v1/core/retrieve-global-data'); - const data = await response.json(); - const exportUrl = '/api/v1/cost-unit/' + costUnitId + '/export-payouts'; - - try { - if (data.tenant.download_exports) { - const response = await fetch(exportUrl, { - headers: { "Content-Type": "application/json" }, - }); - - if (!response.ok) throw new Error('Fehler beim Export (ZIP)'); - - const blob = await response.blob(); - const downloadUrl = window.URL.createObjectURL(blob); - const a = document.createElement("a"); - a.style.display = "none"; - a.href = downloadUrl; - a.download = "Abrechnungen-Sippenstunden.zip"; - - document.body.appendChild(a); - a.click(); - - setTimeout(() => { - window.URL.revokeObjectURL(downloadUrl); - document.body.removeChild(a); - }, 100); - } else { - const response = await request(exportUrl, { - method: "GET", - }); - - toast.success(response.message); - } - showLoading.value = false; - - } catch (err) { - showLoading.value = false; - toast.error('Beim Export der Abrechnungen ist ein Fehler aufgetreten.'); - } -} -