From 882752472ef9d216ff63d70769093d037a7f9dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=BCnther?= Date: Wed, 11 Feb 2026 19:38:06 +0100 Subject: [PATCH] Invoice Widgets completed --- .../Controllers/DashboardController.php | 16 ++++-- app/Domains/Dashboard/Routes/api.php | 15 ++++++ app/Domains/Dashboard/Routes/web.php | 1 + .../Dashboard/Views/Partials/MyInvoices.vue | 11 ---- .../Views/Partials/Widgets/MyInvoices.vue | 31 +++++++++++ .../Views/Partials/Widgets/OpenCostUnits.vue | 40 ++++++++++++++ .../CreateInvoice/CreateInvoiceCommand.php | 2 +- app/Models/CostUnit.php | 5 ++ app/Models/Invoice.php | 10 ++++ app/Repositories/CostUnitRepository.php | 52 +++++++++++++++++++ app/Repositories/InvoiceRepository.php | 37 +++++++++++++ app/Scopes/CommonController.php | 4 ++ app/ValueObjects/Amount.php | 4 ++ .../Partials/GlobalWidgets/GlobalWidgets.vue | 6 ++- public/css/app.css | 22 ++++++++ routes/web.php | 2 + 16 files changed, 240 insertions(+), 18 deletions(-) create mode 100644 app/Domains/Dashboard/Routes/api.php create mode 100644 app/Domains/Dashboard/Routes/web.php delete mode 100644 app/Domains/Dashboard/Views/Partials/MyInvoices.vue create mode 100644 app/Domains/Dashboard/Views/Partials/Widgets/MyInvoices.vue create mode 100644 app/Domains/Dashboard/Views/Partials/Widgets/OpenCostUnits.vue create mode 100644 app/Repositories/InvoiceRepository.php diff --git a/app/Domains/Dashboard/Controllers/DashboardController.php b/app/Domains/Dashboard/Controllers/DashboardController.php index c37456b..15875db 100644 --- a/app/Domains/Dashboard/Controllers/DashboardController.php +++ b/app/Domains/Dashboard/Controllers/DashboardController.php @@ -5,8 +5,10 @@ namespace App\Domains\Dashboard\Controllers; use App\Providers\AuthCheckProvider; use App\Providers\InertiaProvider; use App\Scopes\CommonController; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Js; class DashboardController extends CommonController { public function __invoke(Request $request) { @@ -15,10 +17,6 @@ class DashboardController extends CommonController { } return redirect()->intended('/login'); - - dd('U'); - return $this->renderForGuest($request); - } private function renderForLoggedInUser(Request $request) { @@ -29,4 +27,14 @@ class DashboardController extends CommonController { private function renderForGuest(Request $request) { } + + public function getMyInvoices() : JsonResponse { + $invoices = $this->invoices->getMyInvoicesWidget(); + return response()->json(['myInvoices' => $invoices]); + } + + public function getOpenCostUnits() : JsonResponse { + $costUnits = $this->costUnits->listForSummary(5); + return response()->json(['openCostUnits' => $costUnits]); + } } diff --git a/app/Domains/Dashboard/Routes/api.php b/app/Domains/Dashboard/Routes/api.php new file mode 100644 index 0000000..2ebe3fa --- /dev/null +++ b/app/Domains/Dashboard/Routes/api.php @@ -0,0 +1,15 @@ +group(function () { + Route::middleware(['auth'])->group(function () { + Route::prefix('api/v1/dashboard')->group(function () { + Route::get('/my-invoices', [DashboardController::class, 'getMyInvoices']); + Route::get('/open-cost-units', [DashboardController::class, 'getOpenCostUnits']); + }); + + }); +}); diff --git a/app/Domains/Dashboard/Routes/web.php b/app/Domains/Dashboard/Routes/web.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/app/Domains/Dashboard/Routes/web.php @@ -0,0 +1 @@ + - - - - - - diff --git a/app/Domains/Dashboard/Views/Partials/Widgets/MyInvoices.vue b/app/Domains/Dashboard/Views/Partials/Widgets/MyInvoices.vue new file mode 100644 index 0000000..7d4b0b8 --- /dev/null +++ b/app/Domains/Dashboard/Views/Partials/Widgets/MyInvoices.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/app/Domains/Dashboard/Views/Partials/Widgets/OpenCostUnits.vue b/app/Domains/Dashboard/Views/Partials/Widgets/OpenCostUnits.vue new file mode 100644 index 0000000..5eea62f --- /dev/null +++ b/app/Domains/Dashboard/Views/Partials/Widgets/OpenCostUnits.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/app/Domains/Invoice/Actions/CreateInvoice/CreateInvoiceCommand.php b/app/Domains/Invoice/Actions/CreateInvoice/CreateInvoiceCommand.php index 4d527e8..73b4fea 100644 --- a/app/Domains/Invoice/Actions/CreateInvoice/CreateInvoiceCommand.php +++ b/app/Domains/Invoice/Actions/CreateInvoice/CreateInvoiceCommand.php @@ -27,7 +27,7 @@ class CreateInvoiceCommand { 'type' => $this->request->invoiceType, 'type_other' => $this->request->invoiceTypeExtended, 'donation' => $this->request->isDonation, - 'userId' => $this->request->userId, + 'user_id' => $this->request->userId, 'contact_name' => $this->request->contactName, 'contact_email' => $this->request->contactEmail, 'contact_phone' => $this->request->contactPhone, diff --git a/app/Models/CostUnit.php b/app/Models/CostUnit.php index d54bfd7..6337cf3 100644 --- a/app/Models/CostUnit.php +++ b/app/Models/CostUnit.php @@ -4,6 +4,7 @@ namespace App\Models; use App\Scopes\InstancedModel; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\Eloquent\Relations\HasMany; /** * @property string $name @@ -37,4 +38,8 @@ class CostUnit extends InstancedModel $this->tresurers()->detach(); $this->save(); } + + public function invoices() : hasMany { + return $this->hasMany(Invoice::class); + } } diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 4356ebf..d34bd89 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -2,7 +2,9 @@ namespace App\Models; +use App\Enumerations\InvoiceStatus; use App\Scopes\InstancedModel; +use Illuminate\Database\Eloquent\Relations\BelongsTo; /** * @property string $tenant @@ -65,4 +67,12 @@ class Invoice extends InstancedModel 'denied_at', 'denied_reason', ]; + + public function costUnit() : BelongsTo{ + return $this->belongsTo(CostUnit::class); + } + + public function status() : BelongsTo { + return $this->belongsTo(InvoiceStatus::class, 'status')->first(); + } } diff --git a/app/Repositories/CostUnitRepository.php b/app/Repositories/CostUnitRepository.php index 90b6de8..23a46a7 100644 --- a/app/Repositories/CostUnitRepository.php +++ b/app/Repositories/CostUnitRepository.php @@ -3,9 +3,12 @@ namespace App\Repositories; use App\Enumerations\CostUnitType; +use App\Enumerations\InvoiceStatus; use App\Enumerations\UserRole; use App\Models\CostUnit; use App\Resources\CostUnitResource; +use App\ValueObjects\Amount; +use Illuminate\Database\Capsule\Manager as Capsule; class CostUnitRepository { public function getCostUnitsForNewInvoice(string $type) : array { @@ -95,4 +98,53 @@ class CostUnitRepository { return $visibleCostUnits; } + + public function listForSummary(int $maxCountCostUnits) : array { + $costUnits = $this->getCostUnitsByCriteria([ + 'archived' => false, + 'tenant' => app('tenant')->slug, + ],false); + + foreach ($costUnits as &$cu) { + $cu->new_invoices_count = $cu->invoices()->where('status', 'new')->count(); + $cu->approved_invoices_count = $cu->invoices()->where('status', 'approved')->count(); + } + + $costUnits = collect($costUnits) + ->sortByDesc('approved_invoices_count') + ->sortByDesc('new_invoices_count') + ->take($maxCountCostUnits) + ->values() + ->all(); + + $returnData = []; + + foreach ($costUnits as $costUnit) { + if($costUnit->approved_invoices_count === 0 && $costUnit->new_invoices_count === 0) { + continue; + } + + if (strlen($costUnit->name) > 15) { + $costUnit->name = substr($costUnit->name, 8, 13) . '...'; + } + $costUnit->totalAmount = $this->sumupAmounts($costUnit)->toString(); + $returnData[] = $costUnit; + } + + return $returnData; + } + + public function sumupAmounts(CostUnit $costUnit) : Amount { + $amount = new Amount(0, ''); + + foreach ($costUnit->invoices()->get() as $invoice) { + if ($invoice->status === InvoiceStatus::INVOICE_STATUS_DENIED) { + continue; + } + + $amount->addAmount(Amount::fromString($invoice->amount)); + } + return $amount; + } + } diff --git a/app/Repositories/InvoiceRepository.php b/app/Repositories/InvoiceRepository.php new file mode 100644 index 0000000..2725f4b --- /dev/null +++ b/app/Repositories/InvoiceRepository.php @@ -0,0 +1,37 @@ + ['slug' => 'new', 'title' => 'Neue Abrechnungen', 'count' => 0, 'amount' => Amount::fromString('0')->toString()], + InvoiceStatus::INVOICE_STATUS_APPROVED => ['slug' => 'approved', 'title' => 'Freigegebene Abrechnungen', 'count' => 0, 'amount' => Amount::fromString('0')->toString()], + InvoiceStatus::INVOICE_STATUS_DENIED => ['slug' => 'declined', 'title' => 'Abgelehnte Abrechnungen', 'count' => 0, 'amount' => Amount::fromString('0')->toString()] + ]; + + + $user = auth()->user(); + if (null === $user) { + return $invoices; + } + + foreach ([InvoiceStatus::INVOICE_STATUS_NEW, InvoiceStatus::INVOICE_STATUS_APPROVED, InvoiceStatus::INVOICE_STATUS_DENIED] as $status) { + $amount = 0; + $count = 0; + foreach (Invoice::where(['user_id' => $user->id, 'status' => $status])->get() as $stack) { + $count++; + $amount += $stack->amount; + } + + $invoices[$status]['count'] = $count; + $invoices[$status]['amount'] = Amount::fromString($amount)->toString(); + } + + return $invoices; + } +} diff --git a/app/Scopes/CommonController.php b/app/Scopes/CommonController.php index cf04fc5..ad1dd20 100644 --- a/app/Scopes/CommonController.php +++ b/app/Scopes/CommonController.php @@ -4,6 +4,7 @@ namespace App\Scopes; use App\Providers\AuthCheckProvider; use App\Repositories\CostUnitRepository; +use App\Repositories\InvoiceRepository; use App\Repositories\PageTextRepository; use App\Repositories\UserRepository; @@ -13,10 +14,13 @@ abstract class CommonController { protected PageTextRepository $pageTexts; + protected InvoiceRepository $invoices; + public function __construct() { $this->users = new UserRepository(); $this->costUnits = new CostUnitRepository(); $this->pageTexts = new PageTextRepository(); + $this->invoices = new InvoiceRepository(); } protected function checkAuth() { diff --git a/app/ValueObjects/Amount.php b/app/ValueObjects/Amount.php index 411f91d..cfeb9cd 100644 --- a/app/ValueObjects/Amount.php +++ b/app/ValueObjects/Amount.php @@ -36,4 +36,8 @@ class Amount { |> trim(...) |> function (string $value) : string { return str_replace('.', ',', $value); }; } + + public function addAmount(Amount $amount) : void { + $this->amount += $amount->getAmount(); + } } diff --git a/app/Views/Partials/GlobalWidgets/GlobalWidgets.vue b/app/Views/Partials/GlobalWidgets/GlobalWidgets.vue index 268200f..8a4dee2 100644 --- a/app/Views/Partials/GlobalWidgets/GlobalWidgets.vue +++ b/app/Views/Partials/GlobalWidgets/GlobalWidgets.vue @@ -1,6 +1,8 @@ diff --git a/public/css/app.css b/public/css/app.css index 22bf1f0..1954920 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -92,6 +92,7 @@ html { flex-direction: column; height: 100%; background-color: #ffffff !important; + overflow-y: auto; } .logo { @@ -143,3 +144,24 @@ th:after { text-align: right; overflow: auto; } + +.widget-content-item { + font-size: 10pt; + margin-bottom: 10px; +} + +.widget-content-item td, +.widget-content-item th { + font-size: 10pt; +} + +.widget-content-item label { + display: block; + margin-top: 5px; +} + +.widget-content-item a { + font-size: 10pt; + font-weight: bold; + text-decoration: none; +} diff --git a/routes/web.php b/routes/web.php index 389c5a7..0805013 100644 --- a/routes/web.php +++ b/routes/web.php @@ -7,6 +7,8 @@ use App\Providers\GlobalDataProvider; use Illuminate\Support\Facades\Route; +require_once __DIR__ . '/../app/Domains/Dashboard/Routes/web.php'; +require_once __DIR__ . '/../app/Domains/Dashboard/Routes/api.php'; require_once __DIR__ . '/../app/Domains/UserManagement/Routes/web.php'; require_once __DIR__ . '/../app/Domains/CostUnit/Routes/web.php'; require_once __DIR__ . '/../app/Domains/CostUnit/Routes/api.php';