Compare commits
44 Commits
11108bdfcc
...
dev-001
| Author | SHA1 | Date | |
|---|---|---|---|
| 94b1d7b2ac | |||
| 1f5f6bc32e | |||
| 9e39435818 | |||
| 096ba07b4c | |||
| 5bcdc2fb5d | |||
| f4ea07d82c | |||
| 21be212129 | |||
| 8348f677a5 | |||
| f813056bf7 | |||
| 1ee6b9968f | |||
| 6f8be58943 | |||
| 2e8daf78e1 | |||
| 4878f750bd | |||
| 33a9271013 | |||
| ff98f0860c | |||
| ed7f887e3a | |||
| e6bd8c684d | |||
| 653e85b781 | |||
| 43f8621053 | |||
| 2d17e61cc8 | |||
| 7bea223ded | |||
| df7c14442e | |||
| 33b4a249cc | |||
| 37039f082c | |||
| 405591d6dd | |||
| b8341890d3 | |||
| 23af267896 | |||
| b1c333648a | |||
| fcf41c5d13 | |||
| 2b458eccd7 | |||
| 4f4dff2edd | |||
| cd526231ed | |||
| fa886aad4d | |||
| f468814a2f | |||
| ab711109a7 | |||
| 72623df38f | |||
| 9fd6839878 | |||
| fd403f8520 | |||
| 882752472e | |||
| 87531237c7 | |||
| ee7fc637f1 | |||
| bccfc11687 | |||
| 6fc65e195c | |||
| e9ae850002 |
81
.ai/conventions.md
Normal file
81
.ai/conventions.md
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# Projektkonventionen
|
||||||
|
|
||||||
|
## Architektur: Actions (Request-Command-Response)
|
||||||
|
|
||||||
|
Jede fachliche Operation wird in eine eigene Action ausgelagert, die aus drei Klassen besteht.
|
||||||
|
Pfad: `app/Domains/{Domain}/Actions/{ActionName}/`
|
||||||
|
|
||||||
|
### Struktur
|
||||||
|
{ActionName}Request.php → Eingabedaten (Konstruktor oder Factory-Methoden) {ActionName}Command.php → Logik, ruft execute(): {ActionName}Response auf {ActionName}Response.php → Rückgabedaten (public Properties)
|
||||||
|
|
||||||
|
|
||||||
|
### Regeln
|
||||||
|
- Der Controller enthält **keine** fachliche Logik – nur Absicherung, Action-Aufruf und HTTP-Response
|
||||||
|
- Commands sind nicht statisch und werden immer instanziiert
|
||||||
|
- Hat ein Request mehrere Varianten, werden **Factory-Methoden** (`forX()`) statt mehrerer Konstruktoren verwendet
|
||||||
|
- Aufrufreihenfolge im Controller: `new Request → new Command(request) → command->execute() → Response verwenden`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Controller
|
||||||
|
|
||||||
|
- Alle Controller erben von `App\Scopes\CommonController`
|
||||||
|
- `CommonController` stellt folgende Repositories bereit (keine eigene Instanziierung nötig):
|
||||||
|
- `$this->eventParticipants` → `EventParticipantRepository`
|
||||||
|
- `$this->events` → `EventRepository`
|
||||||
|
- `$this->invoices` → `InvoiceRepository`
|
||||||
|
- `$this->costUnits` → `CostUnitRepository`
|
||||||
|
- `$this->users` → `UserRepository`
|
||||||
|
- `$this->tenant` → aktueller `Tenant`
|
||||||
|
|
||||||
|
- Die Controller besitzen ausschließlich eine __invoke() - Funktion
|
||||||
|
- Für die Speichern-Actions werden separate Controller-Klassen erstellt (z. B. `StoreEventParticipantController`)
|
||||||
|
---
|
||||||
|
|
||||||
|
## Repositories
|
||||||
|
|
||||||
|
- Datenbankzugriffe gehören **immer** ins Repository, nie direkt in Controller oder Actions
|
||||||
|
- Sicherheitschecks (z. B. „gehört diese Teilnahme dem eingeloggten User?") werden als eigene Repository-Methoden gekapselt
|
||||||
|
- Tenant-Filter: `app('tenant')->slug`
|
||||||
|
- Eingeloggter User: `auth()->user()`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Models / Ressourcen
|
||||||
|
|
||||||
|
- Models erben von `App\Scopes\InstancedModel` (mit globalem `SiteScope`)
|
||||||
|
- `$model->toResource()->toArray($request)` liefert das aufbereitete Array über die zugehörige Resource-Klasse
|
||||||
|
- Resource-Klassen liegen in `app/Resources/{ModelName}Resource.php`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tenant
|
||||||
|
|
||||||
|
- Der aktuelle Tenant ist per `app('tenant')` verfügbar (gesetzt durch `IdentifyTenant`-Middleware)
|
||||||
|
- Tenant-Slug: `app('tenant')->slug`
|
||||||
|
- Jede tenant-spezifische DB-Abfrage filtert auf `['tenant' => app('tenant')->slug]`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Routing
|
||||||
|
|
||||||
|
- API-Routen liegen in `app/Domains/{Domain}/Routes/api.php`
|
||||||
|
- Alle Routen sind in `IdentifyTenant::class`-Middleware gewrappt
|
||||||
|
- Authentifizierte Routen zusätzlich in `['auth']`-Middleware
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mails
|
||||||
|
|
||||||
|
- Mails erben von `Illuminate\Mail\Mailable`
|
||||||
|
- Attachments werden über `Attachment::fromData(fn () => $content, $filename)->withMime(...)` angehängt
|
||||||
|
- Werden Daten sowohl in `content()` als auch in `attachments()` benötigt, wird eine **private Hilfsmethode mit Lazy-Caching** verwendet (einmaliges Berechnen, Ergebnis in private Property speichern)
|
||||||
|
- Blade-Templates referenzieren Mail-Attachments per `cid:`-Link: `<a href="cid:{{ $filename }}">...</a>`
|
||||||
|
|
||||||
|
## Actions
|
||||||
|
- Die Actions sind in `app/Domains/{Domain}/Actions/{ActionName}/`-Verzeichnissen organisiert
|
||||||
|
- Jede Action besitzt einen Request, einen Command und einen Response
|
||||||
|
- Der Request besitzt auschließlich einen Konstruktor, der die notwendigen Parameter annimmt
|
||||||
|
- Die Response-Klasse enthält ausschließlich die notwendigen Daten für die Antwort
|
||||||
|
- Die Action-Klasse enthält die Logik für die Verarbeitung der Anfrage und die Generierung der Antwort
|
||||||
|
- Die Logik wird in einer execute() - Funktion innerhalb des Commands impplementiert. Private Funktionen, für ausgelagerte Prozesse sind zulässig, wenn der Code damit lesbarer wird.
|
||||||
0
.ai/mcp/mcp.json
Normal file
0
.ai/mcp/mcp.json
Normal file
12
.junie/AGENTS.md
Normal file
12
.junie/AGENTS.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Project Guidelines
|
||||||
|
|
||||||
|
This is a placeholder of the project guidelines for Junie.
|
||||||
|
Replace this text with any project-level instructions for Junie, e.g.:
|
||||||
|
|
||||||
|
* What is the project structure
|
||||||
|
* Whether Junie should run tests to check the correctness of the proposed solution
|
||||||
|
* How does Junie run tests (once it requires any non-standard approach)
|
||||||
|
* Whether Junie should build the project before submitting the result
|
||||||
|
* Any code-style related instructions
|
||||||
|
|
||||||
|
As an option you can ask Junie to create these guidelines for you.
|
||||||
34
app/Casts/AmountCast.php
Normal file
34
app/Casts/AmountCast.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Casts;
|
||||||
|
|
||||||
|
use App\ValueObjects\Amount;
|
||||||
|
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class AmountCast implements CastsAttributes
|
||||||
|
{
|
||||||
|
public function get(Model $model, string $key, mixed $value, array $attributes): ?Amount
|
||||||
|
{
|
||||||
|
if ($value === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Amount((float) $value, 'Euro');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set(Model $model, string $key, mixed $value, array $attributes): ?float
|
||||||
|
{
|
||||||
|
if ($value === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value instanceof Amount) {
|
||||||
|
return $value->getAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (float) $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Actions\ChangeCostUnitDetails;
|
||||||
|
|
||||||
|
class ChangeCostUnitDetailsCommand {
|
||||||
|
private ChangeCostUnitDetailsRequest $request;
|
||||||
|
|
||||||
|
public function __construct(ChangeCostUnitDetailsRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(): ChangeCostUnitDetailsResponse {
|
||||||
|
$response = new ChangeCostUnitDetailsResponse();
|
||||||
|
|
||||||
|
$this->request->costUnit->distance_allowance = $this->request->distanceAllowance->getAmount();
|
||||||
|
$this->request->costUnit->mail_on_new = $this->request->mailOnNew;
|
||||||
|
$this->request->costUnit->billing_deadline = $this->request->billingDeadline;
|
||||||
|
|
||||||
|
$response->success = $this->request->costUnit->save();
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Actions\ChangeCostUnitDetails;
|
||||||
|
|
||||||
|
use App\Models\CostUnit;
|
||||||
|
use App\ValueObjects\Amount;
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
|
class ChangeCostUnitDetailsRequest {
|
||||||
|
public CostUnit $costUnit;
|
||||||
|
|
||||||
|
public Amount $distanceAllowance;
|
||||||
|
public bool $mailOnNew;
|
||||||
|
public ?DateTime $billingDeadline;
|
||||||
|
|
||||||
|
public function __construct(CostUnit $costUnit, Amount $distanceAllowance, bool $mailOnNew, ?DateTime $billingDeadline = null) {
|
||||||
|
$this->costUnit = $costUnit;
|
||||||
|
$this->distanceAllowance = $distanceAllowance;
|
||||||
|
$this->mailOnNew = $mailOnNew;
|
||||||
|
$this->billingDeadline = $billingDeadline;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Actions\ChangeCostUnitDetails;
|
||||||
|
|
||||||
|
class ChangeCostUnitDetailsResponse {
|
||||||
|
public bool $success;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Actions\ChangeCostUnitState;
|
||||||
|
|
||||||
|
class ChangeCostUnitStateCommand {
|
||||||
|
private ChangeCostUnitStateRequest $request;
|
||||||
|
|
||||||
|
public function __construct(ChangeCostUnitStateRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : ChangeCostUnitStateResponse {
|
||||||
|
$response = new ChangeCostUnitStateResponse();
|
||||||
|
|
||||||
|
$this->request->costUnit->allow_new = $this->request->allowNew;
|
||||||
|
$this->request->costUnit->archived = $this->request->isArchived;
|
||||||
|
$response->success = $this->request->costUnit->save();
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Actions\ChangeCostUnitState;
|
||||||
|
|
||||||
|
use App\Models\CostUnit;
|
||||||
|
|
||||||
|
class ChangeCostUnitStateRequest {
|
||||||
|
public bool $allowNew;
|
||||||
|
public bool $isArchived;
|
||||||
|
|
||||||
|
public CostUnit $costUnit;
|
||||||
|
|
||||||
|
public function __construct(CostUnit $costUnit, bool $allowNew, bool $isArchived) {
|
||||||
|
$this->costUnit = $costUnit;
|
||||||
|
$this->allowNew = $allowNew;
|
||||||
|
$this->isArchived = $isArchived;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Actions\ChangeCostUnitState;
|
||||||
|
|
||||||
|
class ChangeCostUnitStateResponse {
|
||||||
|
public bool $success;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Actions\ChangeCostUnitTreasurers;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class ChangeCostUnitTreasurersCommand {
|
||||||
|
private ChangeCostUnitTreasurersRequest $request;
|
||||||
|
|
||||||
|
public function __construct(ChangeCostUnitTreasurersRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : ChangeCostUnitTreasurersResponse {
|
||||||
|
$response = new ChangeCostUnitTreasurersResponse();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->request->costUnit->resetTreasurers();
|
||||||
|
|
||||||
|
foreach ($this->request->treasurers as $treasurer) {
|
||||||
|
$this->request->costUnit->treasurers()->attach($treasurer);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->request->costUnit->save();
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
$response->success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Actions\ChangeCostUnitTreasurers;
|
||||||
|
|
||||||
|
use App\Models\CostUnit;
|
||||||
|
|
||||||
|
class ChangeCostUnitTreasurersRequest {
|
||||||
|
public array $treasurers;
|
||||||
|
public CostUnit $costUnit;
|
||||||
|
|
||||||
|
public function __construct(CostUnit $costUnit, array $treasurers) {
|
||||||
|
$this->treasurers = $treasurers;
|
||||||
|
$this->costUnit = $costUnit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Actions\ChangeCostUnitTreasurers;
|
||||||
|
|
||||||
|
class ChangeCostUnitTreasurersResponse {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Actions\CreateCostUnit;
|
||||||
|
|
||||||
|
use App\Models\CostUnit;
|
||||||
|
|
||||||
|
class CreateCostUnitCommand {
|
||||||
|
private CreateCostUnitRequest $request;
|
||||||
|
|
||||||
|
public function __construct(CreateCostUnitRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : CreateCostUnitResponse {
|
||||||
|
$response = new CreateCostUnitResponse();
|
||||||
|
$costUnit = CostUnit::create([
|
||||||
|
'name' => $this->request->name,
|
||||||
|
'tenant' => app('tenant')->slug,
|
||||||
|
'type' => $this->request->type,
|
||||||
|
'billing_deadline' => $this->request->billingDeadline,
|
||||||
|
'distance_allowance' => $this->request->distanceAllowance->getAmount(),
|
||||||
|
'mail_on_new' => $this->request->mailOnNew,
|
||||||
|
'allow_new' => true,
|
||||||
|
'archived' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (null !== $costUnit) {
|
||||||
|
$response->costUnit = $costUnit;
|
||||||
|
$response->success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Actions\CreateCostUnit;
|
||||||
|
|
||||||
|
use App\Enumerations\CostUnitType;
|
||||||
|
use App\ValueObjects\Amount;
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
|
class CreateCostUnitRequest {
|
||||||
|
public string $name;
|
||||||
|
public string $type;
|
||||||
|
public Amount $distanceAllowance;
|
||||||
|
public bool $mailOnNew;
|
||||||
|
public ?DateTime $billingDeadline;
|
||||||
|
|
||||||
|
public function __construct(string $name, string $type, Amount $distanceAllowance, bool $mailOnNew, ?DateTime $billingDeadline = null) {
|
||||||
|
$this->name = $name;
|
||||||
|
$this->type = $type;
|
||||||
|
$this->distanceAllowance = $distanceAllowance;
|
||||||
|
$this->mailOnNew = $mailOnNew;
|
||||||
|
$this->billingDeadline = $billingDeadline;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Actions\CreateCostUnit;
|
||||||
|
|
||||||
|
use App\Models\CostUnit;
|
||||||
|
|
||||||
|
class CreateCostUnitResponse {
|
||||||
|
public bool $success;
|
||||||
|
public ?CostUnit $costUnit;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->success = false;
|
||||||
|
$this->costUnit = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
47
app/Domains/CostUnit/Controllers/ChangeStateController.php
Normal file
47
app/Domains/CostUnit/Controllers/ChangeStateController.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\CostUnit\Actions\ChangeCostUnitState\ChangeCostUnitStateCommand;
|
||||||
|
use App\Domains\CostUnit\Actions\ChangeCostUnitState\ChangeCostUnitStateRequest;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
||||||
|
class ChangeStateController extends CommonController {
|
||||||
|
public function close(int $costUnitId) : JsonResponse {
|
||||||
|
$costUnit = $this->costUnits->getById($costUnitId);
|
||||||
|
|
||||||
|
$changeStatRequest = new ChangeCostUnitStateRequest($costUnit, false, false);
|
||||||
|
return $this->changeCostUnitState($changeStatRequest, 'Der CostUnit wurde geschlossen.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function open(int $costUnitId) : JsonResponse {
|
||||||
|
$costUnit = $this->costUnits->getById($costUnitId);
|
||||||
|
|
||||||
|
$changeStatRequest = new ChangeCostUnitStateRequest($costUnit, true, false);
|
||||||
|
return $this->changeCostUnitState($changeStatRequest, 'Der CostUnit wurde geöffnet.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function archive(int $costUnitId) : JsonResponse {
|
||||||
|
$costUnit = $this->costUnits->getById($costUnitId);
|
||||||
|
|
||||||
|
$changeStatRequest = new ChangeCostUnitStateRequest($costUnit, false, true);
|
||||||
|
return $this->changeCostUnitState($changeStatRequest, 'Der CostUnit wurde archiviert.');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function changeCostUnitState(ChangeCostUnitStateRequest $request, string $responseMessage) : JsonResponse {
|
||||||
|
$changeStatCommand = new ChangeCostUnitStateCommand($request);
|
||||||
|
|
||||||
|
if ($changeStatCommand->execute()) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => $responseMessage
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Ein Fehler ist aufgetreten.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
app/Domains/CostUnit/Controllers/CreateController.php
Normal file
36
app/Domains/CostUnit/Controllers/CreateController.php
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\CostUnit\Actions\CreateCostUnit\CreateCostUnitCommand;
|
||||||
|
use App\Domains\CostUnit\Actions\CreateCostUnit\CreateCostUnitRequest;
|
||||||
|
use App\Enumerations\CostUnitType;
|
||||||
|
use App\Models\CostUnit;
|
||||||
|
use App\Providers\FlashMessageProvider;
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use App\ValueObjects\Amount;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class CreateController extends CommonController{
|
||||||
|
public function showForm() {
|
||||||
|
$inertiaProvider = new InertiaProvider('CostUnit/Create', []);
|
||||||
|
return $inertiaProvider->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createCostUnitRunningJob(Request $request) : JsonResponse {
|
||||||
|
$createCostUnitRequest = new CreateCostUnitRequest(
|
||||||
|
$request->get('cost_unit_name'),
|
||||||
|
CostUnitType::COST_UNIT_TYPE_RUNNING_JOB,
|
||||||
|
Amount::fromString($request->get('distance_allowance')),
|
||||||
|
$request->get('mailOnNew')
|
||||||
|
);
|
||||||
|
|
||||||
|
$createCostUnitCommand = new CreateCostUnitCommand($createCostUnitRequest);
|
||||||
|
$result = $createCostUnitCommand->execute();
|
||||||
|
new FlashMessageProvider('Die laufende Tätigkeit wurde erfolgreich angelegt.', 'success');
|
||||||
|
|
||||||
|
return response()->json([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Controllers;
|
||||||
|
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
||||||
|
class DistanceAllowanceController extends CommonController {
|
||||||
|
public function __invoke(int $costUnitId) : JsonResponse {
|
||||||
|
$distanceAllowance = 0.00;
|
||||||
|
$costUnit = $this->costUnits->getById($costUnitId, true);
|
||||||
|
if (null !== $costUnit) {
|
||||||
|
$distanceAllowance = $costUnit->distance_allowance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'distanceAllowance' => $distanceAllowance
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
58
app/Domains/CostUnit/Controllers/EditController.php
Normal file
58
app/Domains/CostUnit/Controllers/EditController.php
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\CostUnit\Actions\ChangeCostUnitDetails\ChangeCostUnitDetailsCommand;
|
||||||
|
use App\Domains\CostUnit\Actions\ChangeCostUnitDetails\ChangeCostUnitDetailsRequest;
|
||||||
|
use App\Resources\CostUnitResource;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use App\ValueObjects\Amount;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class EditController extends CommonController{
|
||||||
|
function __invoke(int $costUnitId) : JsonResponse {
|
||||||
|
$costUnit = $this->costUnits->getById($costUnitId);
|
||||||
|
if (null === $costUnit) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Die Kotenstelle konnte nicht geladen werden.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'costUnit' => new CostUnitResource($costUnit)->toArray(request())
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Request $request, int $costUnitId) : JsonResponse {
|
||||||
|
$costUnit = $this->costUnits->getById($costUnitId);
|
||||||
|
if (null === $costUnit) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Die Kotenstelle konnte nicht geladen werden.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$saveParams = $request->get('formData');
|
||||||
|
$distanceAllowance = Amount::fromString($saveParams['distanceAllowance']);
|
||||||
|
$billingDeadline = isset($saveParams['billingDeadline']) ? \DateTime::createFromFormat('Y-m-d', $saveParams['billingDeadline']) : null;
|
||||||
|
|
||||||
|
$request = new ChangeCostUnitDetailsRequest($costUnit, $distanceAllowance, $saveParams['mailOnNew'], $billingDeadline);
|
||||||
|
$command = new ChangeCostUnitDetailsCommand($request);
|
||||||
|
$result = $command->execute();
|
||||||
|
|
||||||
|
if (!$result->success) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Bei der Verarbeitung ist ein Fehler aufgetreten.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Die Kostenstellendetails wurden erfolgreich gespeichert.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
110
app/Domains/CostUnit/Controllers/ExportController.php
Normal file
110
app/Domains/CostUnit/Controllers/ExportController.php
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\Invoice\Actions\ChangeStatus\ChangeStatusCommand;
|
||||||
|
use App\Domains\Invoice\Actions\ChangeStatus\ChangeStatusRequest;
|
||||||
|
use App\Domains\Invoice\Actions\CreateInvoice\CreateInvoiceRequest;
|
||||||
|
use App\Domains\Invoice\Actions\CreateInvoiceReceipt\CreateInvoiceReceiptCommand;
|
||||||
|
use App\Domains\Invoice\Actions\CreateInvoiceReceipt\CreateInvoiceReceiptRequest;
|
||||||
|
use App\Enumerations\InvoiceStatus;
|
||||||
|
use App\Models\Tenant;
|
||||||
|
use App\Providers\FileWriteProvider;
|
||||||
|
use App\Providers\InvoiceCsvFileProvider;
|
||||||
|
use App\Providers\PainFileProvider;
|
||||||
|
use App\Providers\WebDavProvider;
|
||||||
|
use App\Providers\ZipArchiveFileProvider;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
class ExportController extends CommonController {
|
||||||
|
public function __invoke(int $costUnitId) {
|
||||||
|
$costUnit = $this->costUnits->getById($costUnitId);
|
||||||
|
|
||||||
|
$invoicesForExport = $this->invoices->getByStatus($costUnit, InvoiceStatus::INVOICE_STATUS_APPROVED, false);
|
||||||
|
|
||||||
|
$webdavProvider = new WebDavProvider(WebDavProvider::INVOICE_PREFIX . $this->tenant->url . '/' . $costUnit->name);
|
||||||
|
|
||||||
|
$painFileData = $this->painData($invoicesForExport);
|
||||||
|
$csvData = $this->csvData($invoicesForExport);
|
||||||
|
|
||||||
|
$filePrefix = Tenant::getTempDirectory();
|
||||||
|
|
||||||
|
$painFileWriteProvider = new FileWriteProvider($filePrefix . 'abrechnungen-' . date('Y-m-d_H-i') . '-sepa.xml', $painFileData);
|
||||||
|
$painFileWriteProvider->writeToFile();
|
||||||
|
|
||||||
|
$csvFileWriteProvider = new FileWriteProvider($filePrefix . 'abrechnungen-' . date('Y-m-d_H-i') . '.csv', $csvData);
|
||||||
|
$csvFileWriteProvider->writeToFile();
|
||||||
|
|
||||||
|
if ($this->tenant->upload_exports) {
|
||||||
|
$webdavProvider->uploadFile($painFileWriteProvider->fileName);
|
||||||
|
$webdavProvider->uploadFile($csvFileWriteProvider->fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
$downloadZipArchiveFiles = [
|
||||||
|
$painFileWriteProvider->fileName,
|
||||||
|
$csvFileWriteProvider->fileName
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($invoicesForExport as $invoice) {
|
||||||
|
$changeStatusRequest = new ChangeStatusRequest($invoice, InvoiceStatus::INVOICE_STATUS_EXPORTED);
|
||||||
|
$changeStatusCommand = new ChangeStatusCommand($changeStatusRequest);
|
||||||
|
$changeStatusCommand->execute();
|
||||||
|
|
||||||
|
if ($this->tenant->download_exports) {
|
||||||
|
$createInvoiceReceiptRequest = new CreateInvoiceReceiptRequest($invoice);
|
||||||
|
$createInvoiceReceiptCommand = new CreateInvoiceReceiptCommand($createInvoiceReceiptRequest);
|
||||||
|
$response = $createInvoiceReceiptCommand->execute();
|
||||||
|
|
||||||
|
$downloadZipArchiveFiles[] = $response->fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->tenant->download_exports) {
|
||||||
|
$zipFile = Tenant::getTempDirectory() . 'Abrechnungen-' . $costUnit->name . '.zip';
|
||||||
|
$zipFileProvider = new ZipArchiveFileProvider($zipFile);
|
||||||
|
foreach ($downloadZipArchiveFiles as $file) {
|
||||||
|
$zipFileProvider->addFile($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
$zipFileProvider->create();
|
||||||
|
foreach ($downloadZipArchiveFiles as $file) {
|
||||||
|
Storage::delete($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
Storage::delete($painFileWriteProvider->fileName);
|
||||||
|
Storage::delete($csvFileWriteProvider->fileName);
|
||||||
|
|
||||||
|
return response()->download(
|
||||||
|
storage_path('app/private/' . $zipFile),
|
||||||
|
basename($zipFile),
|
||||||
|
['Content-Type' => 'application/zip']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Storage::delete($painFileWriteProvider->fileName);
|
||||||
|
Storage::delete($csvFileWriteProvider->fileName);
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'Die Abrechnungen wurden exportiert.' . PHP_EOL .'Die Belege werden asynchron auf dem Webdav-Server hinterlegt.' . PHP_EOL . PHP_EOL . 'Sollten diese in 15 Minuten nicht vollständig sein, kontaktiere den Adminbistrator.'
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function painData(array $invoices) : string {
|
||||||
|
$invoicesForPainFile = [];
|
||||||
|
foreach ($invoices as $invoice) {
|
||||||
|
if ($invoice->contact_bank_owner !== null && $invoice->contact_bank_iban !== '' && !$invoice->donation) {
|
||||||
|
$invoicesForPainFile[] = $invoice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$painFileProvider = new PainFileProvider($this->tenant->account_iban, $this->tenant->account_name, $this->tenant->account_bic, $invoicesForPainFile);
|
||||||
|
return $painFileProvider->createPainFileContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function csvData(array $invoices) : string {
|
||||||
|
$csvDateProvider = new InvoiceCsvFileProvider($invoices);
|
||||||
|
return $csvDateProvider->createCsvFileContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
48
app/Domains/CostUnit/Controllers/ListController.php
Normal file
48
app/Domains/CostUnit/Controllers/ListController.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Controllers;
|
||||||
|
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ListController extends CommonController {
|
||||||
|
public function __invoke() {
|
||||||
|
$inertiaProvider = new InertiaProvider('CostUnit/List', [
|
||||||
|
'cost_unit_id' => 1
|
||||||
|
]);
|
||||||
|
return $inertiaProvider->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listCurrentEvents(Request $request) : JsonResponse {
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'cost_unit_title' => 'Aktuelle Veranstaltungen',
|
||||||
|
'cost_units' => $this->costUnits->getCurrentEvents(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listCurrentRunningJobs(Request $request) : JsonResponse {
|
||||||
|
return response()->json([
|
||||||
|
'cost_unit_title' => 'Laufende Tätigkeiten',
|
||||||
|
'cost_units' => $this->costUnits->getRunningJobs(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listClosedCostUnits(Request $request) : JsonResponse {
|
||||||
|
return response()->json([
|
||||||
|
'cost_unit_title' => 'Geschlossene Kostenstellen',
|
||||||
|
'cost_units' => $this->costUnits->getClosedCostUnits(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listArchivedCostUnits(Request $request) : JsonResponse {
|
||||||
|
return response()->json([
|
||||||
|
'cost_unit_title' => 'Archivierte Kostenstellen',
|
||||||
|
'cost_units' => $this->costUnits->getArchivedCostUnits(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
31
app/Domains/CostUnit/Controllers/OpenController.php
Normal file
31
app/Domains/CostUnit/Controllers/OpenController.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Controllers;
|
||||||
|
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
||||||
|
class OpenController extends CommonController {
|
||||||
|
public function __invoke(int $costUnitId) {
|
||||||
|
$costUnit = $this->costUnits->getById($costUnitId);
|
||||||
|
|
||||||
|
|
||||||
|
$inertiaProvider = new InertiaProvider('CostUnit/Open', [
|
||||||
|
'costUnit' => $costUnit
|
||||||
|
]);
|
||||||
|
return $inertiaProvider->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listInvoices(int $costUnitId, string $invoiceStatus) : JsonResponse {
|
||||||
|
$costUnit = $this->costUnits->getById($costUnitId);
|
||||||
|
$invoices = $this->invoices->getByStatus($costUnit, $invoiceStatus);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'costUnit' => $costUnit,
|
||||||
|
'invoices' => $invoices,
|
||||||
|
'endpoint' => $invoiceStatus,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\CostUnit\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\CostUnit\Actions\ChangeCostUnitTreasurers\ChangeCostUnitTreasurersCommand;
|
||||||
|
use App\Domains\CostUnit\Actions\ChangeCostUnitTreasurers\ChangeCostUnitTreasurersRequest;
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Resources\CostUnitResource;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class TreasurersEditController extends CommonController {
|
||||||
|
public function __invoke(int $costUnitId) : JsonResponse {
|
||||||
|
$costUnit = $this->costUnits->getById($costUnitId);
|
||||||
|
if (null === $costUnit) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Die Kostenstelle konnte nicht geladen werden.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'costUnit' => new CostUnitResource($costUnit)->toArray(request())
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Request $request, int $costUnitId) : JsonResponse {
|
||||||
|
$costUnit = $this->costUnits->getById($costUnitId);
|
||||||
|
if (null === $costUnit) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Die Kostenstelle konnte nicht geladen werden.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$changeTreasurersRequest = new ChangeCostUnitTreasurersRequest($costUnit, $request->get('selectedTreasurers'));
|
||||||
|
$changeTreasurersCommand = new ChangeCostUnitTreasurersCommand($changeTreasurersRequest);
|
||||||
|
if ($changeTreasurersCommand->execute()) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Die Schatzis wurden erfolgreich gespeichert.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Beim Bearbeiten der Kostenstelle ist ein Fehler aufgetreten.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
55
app/Domains/CostUnit/Routes/api.php
Normal file
55
app/Domains/CostUnit/Routes/api.php
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
use App\Domains\CostUnit\Controllers\ChangeStateController;
|
||||||
|
use App\Domains\CostUnit\Controllers\CreateController;
|
||||||
|
use App\Domains\CostUnit\Controllers\DistanceAllowanceController;
|
||||||
|
use App\Domains\CostUnit\Controllers\EditController;
|
||||||
|
use App\Domains\CostUnit\Controllers\ExportController;
|
||||||
|
use App\Domains\CostUnit\Controllers\ListController;
|
||||||
|
use App\Domains\CostUnit\Controllers\OpenController;
|
||||||
|
use App\Domains\CostUnit\Controllers\TreasurersEditController;
|
||||||
|
use App\Middleware\IdentifyTenant;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
Route::prefix('api/v1')
|
||||||
|
->group(function () {
|
||||||
|
Route::middleware(IdentifyTenant::class)->group(function () {
|
||||||
|
Route::prefix('cost-unit')->group(function () {
|
||||||
|
|
||||||
|
Route::get('/get-distance-allowance/{costUnitId}', DistanceAllowanceController::class);
|
||||||
|
|
||||||
|
|
||||||
|
Route::middleware(['auth'])->group(function () {
|
||||||
|
Route::post('/create-running-job', [CreateController::class, 'createCostUnitRunningJob']);
|
||||||
|
|
||||||
|
|
||||||
|
Route::prefix('/{costUnitId}') ->group(function () {
|
||||||
|
Route::get('/invoice-list/{invoiceStatus}', [OpenController::class, 'listInvoices']);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Route::post('/close', [ChangeStateController::class, 'close']);
|
||||||
|
Route::post('/open', [ChangeStateController::class, 'open']);
|
||||||
|
Route::post('/archive', [ChangeStateController::class, 'archive']);
|
||||||
|
|
||||||
|
Route::get('/details', EditController::class);
|
||||||
|
Route::post('/details', [EditController::class, 'update']);
|
||||||
|
|
||||||
|
Route::get('/treasurers', TreasurersEditController::class);
|
||||||
|
Route::post('/treasurers', [TreasurersEditController::class, 'update']);
|
||||||
|
|
||||||
|
Route::get('/export-payouts', ExportController::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Route::prefix('open')->group(function () {
|
||||||
|
Route::get('/current-events', [ListController::class, 'listCurrentEvents']);
|
||||||
|
Route::get('/current-running-jobs', [ListController::class, 'listCurrentRunningJobs']);
|
||||||
|
Route::get('/closed-cost-units', [ListController::class, 'listClosedCostUnits']);
|
||||||
|
Route::get('/archived-cost-units', [ListController::class, 'listArchivedCostUnits']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
39
app/Domains/CostUnit/Routes/web.php
Normal file
39
app/Domains/CostUnit/Routes/web.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
use App\Domains\CostUnit\Controllers\CreateController;
|
||||||
|
use App\Domains\CostUnit\Controllers\ListController;
|
||||||
|
use App\Domains\CostUnit\Controllers\OpenController;
|
||||||
|
use App\Domains\UserManagement\Controllers\EmailVerificationController;
|
||||||
|
use App\Domains\UserManagement\Controllers\LoginController;
|
||||||
|
use App\Domains\UserManagement\Controllers\LogOutController;
|
||||||
|
use App\Domains\UserManagement\Controllers\RegistrationController;
|
||||||
|
use App\Domains\UserManagement\Controllers\ResetPasswordController;
|
||||||
|
use App\Middleware\IdentifyTenant;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
Route::middleware(IdentifyTenant::class)->group(function () {
|
||||||
|
Route::prefix('cost-unit')->group(function () {
|
||||||
|
Route::middleware(['auth'])->group(function () {
|
||||||
|
Route::get('/create', [CreateController::class, 'showForm']);
|
||||||
|
Route::get('/list', ListController::class);
|
||||||
|
Route::get('/{costUnitId}/', OpenController::class);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
Route::get('/register', [RegistrationController::class, 'loginForm']);
|
||||||
|
Route::get('/register/verifyEmail', [EmailVerificationController::class, 'verifyEmailForm']);
|
||||||
|
|
||||||
|
Route::get('/reset-password', [ResetPasswordController::class, 'resetPasswordForm']);
|
||||||
|
|
||||||
|
route::get('/logout', LogOutController::class);
|
||||||
|
route::post('/login', [LoginController::class, 'doLogin']);
|
||||||
|
route::get('/login', [LoginController::class, 'loginForm']);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
78
app/Domains/CostUnit/Views/Create.vue
Normal file
78
app/Domains/CostUnit/Views/Create.vue
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactive, inject } from 'vue';
|
||||||
|
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue';
|
||||||
|
import AmountInput from "../../../Views/Components/AmountInput.vue";
|
||||||
|
import { useAjax } from "../../../../resources/js/components/ajaxHandler.js";
|
||||||
|
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
||||||
|
import {toast} from "vue3-toastify";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
activeUsers: Object,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const { request } = useAjax();
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
cost_unit_name: '',
|
||||||
|
distance_allowance: '0,25',
|
||||||
|
|
||||||
|
emailAddress: '',
|
||||||
|
mailOnNew: true
|
||||||
|
});
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
const data = await request("/api/v1/cost-unit/create-running-job", {
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
cost_unit_name: formData.cost_unit_name,
|
||||||
|
distance_allowance: formData.distance_allowance,
|
||||||
|
mailOnNew: formData.mailOnNew
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.location.href = '/cost-unit/list';
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AppLayout title="Laufende Tätigkeit hinzufügen">
|
||||||
|
<form method="POST" action="/api/v1/cost-unit/create-running-job" @submit.prevent="save">
|
||||||
|
<input type="hidden" name="_token" :value="csrfToken" />
|
||||||
|
|
||||||
|
<shadowed-box style="width: 90%; margin: 20px auto; padding: 20px;">
|
||||||
|
<p>
|
||||||
|
Über dieses Formular können laufende Tätigkeiten angelegt werden.<br />
|
||||||
|
Eine Kostenstelle für eine Veranstaltung wird automatisch erstellt, sobald die Veranstaltung angelegt wurde.
|
||||||
|
</p>
|
||||||
|
<table style="margin-top: 40px; width: 100%">
|
||||||
|
<tr>
|
||||||
|
<th class="width-medium pr-20">Name der laufenden Tätigkeit</th>
|
||||||
|
<td><input type="text" v-model="formData.cost_unit_name" class="width-half-full" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="pr-20">Kilometerpauschale</th>
|
||||||
|
<td>
|
||||||
|
<AmountInput v-model="formData.distance_allowance" class="width-small" /> Euro / Kilometer
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<label style="display:flex;align-items:center;cursor:pointer;">
|
||||||
|
<input type="checkbox" v-model="formData.mailOnNew" style="margin-right:8px;cursor:pointer;" />
|
||||||
|
<span>E-Mail-Benachrichtigung bei neuen Abrechnungen</span>
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<input type="submit" value="Speichern" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</shadowed-box>
|
||||||
|
</form>
|
||||||
|
</AppLayout>
|
||||||
|
</template>
|
||||||
83
app/Domains/CostUnit/Views/Edit.vue
Normal file
83
app/Domains/CostUnit/Views/Edit.vue
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactive, inject } from 'vue';
|
||||||
|
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue';
|
||||||
|
import AmountInput from "../../../Views/Components/AmountInput.vue";
|
||||||
|
import { useAjax } from "../../../../resources/js/components/ajaxHandler.js";
|
||||||
|
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
activeUsers: Object,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const { request } = useAjax();
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
cost_unit_name: '',
|
||||||
|
distance_allowance: '0,25',
|
||||||
|
|
||||||
|
emailAddress: '',
|
||||||
|
mailOnNew: true
|
||||||
|
});
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
const data = await request("/wp-json/mareike/costunits/create-new-cost-unit", {
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
mareike_nonce: _mareike_nonce(),
|
||||||
|
cost_unit_name: formData.cost_unit_name,
|
||||||
|
distance_allowance: formData.distance_allowance,
|
||||||
|
email_address: formData.emailAddress,
|
||||||
|
mailOnNew: formData.mailOnNew
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
toast('Die laufende Tätigkeit wurde erfolgreich angelegt.', { type: 'success' });
|
||||||
|
window.location.href = '/cost-units';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AppLayout title="Laufende Tätigkeit hinzufügen">
|
||||||
|
<shadowed-box style="width: 90%; margin: 20px auto; padding: 20px;">
|
||||||
|
<p>
|
||||||
|
Über dieses Formular können laufende Tätigkeiten angelegt werden.<br />
|
||||||
|
Eine Kostenstelle für eine Veranstaltung wird automatisch erstellt, sobald die Veranstaltung angelegt wurde.
|
||||||
|
</p>
|
||||||
|
<table style="margin-top: 40px; width: 100%">
|
||||||
|
<tr>
|
||||||
|
<th class="width-medium pr-20">Name der laufenden Tätigkeit</th>
|
||||||
|
<td><input type="text" v-model="formData.cost_unit_name" class="width-half-full" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="pr-20">Kilometerpauschale</th>
|
||||||
|
<td>
|
||||||
|
<AmountInput v-model="formData.distance_allowance" class="width-small" /> Euro / Kilometer
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<label style="display:flex;align-items:center;cursor:pointer;">
|
||||||
|
<input type="checkbox" v-model="formData.mailOnNew" style="margin-right:8px;cursor:pointer;" />
|
||||||
|
<span>E-Mail-Benachrichtigung bei neuen Abrechnungen</span>
|
||||||
|
</label>
|
||||||
|
<hr />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<span v-for="user in props.activeUsers">
|
||||||
|
<input type="checkbox" :id="'user_' + user.id" />
|
||||||
|
<label :for="'user_' + user.id">{{user.fullname}} ({{user.localGroup}})</label>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<input type="button" @click="save" value="Speichern" class="mareike-button button-small button-block" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</shadowed-box>
|
||||||
|
</AppLayout>
|
||||||
|
</template>
|
||||||
82
app/Domains/CostUnit/Views/List.vue
Normal file
82
app/Domains/CostUnit/Views/List.vue
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<script setup>
|
||||||
|
import {reactive, inject, onMounted} from 'vue';
|
||||||
|
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue';
|
||||||
|
import { useAjax } from "../../../../resources/js/components/ajaxHandler.js";
|
||||||
|
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
||||||
|
import TabbedPage from "../../../Views/Components/TabbedPage.vue";
|
||||||
|
import {toast} from "vue3-toastify";
|
||||||
|
|
||||||
|
import ListCostUnits from "./Partials/ListCostUnits.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
message: String,
|
||||||
|
|
||||||
|
data: {
|
||||||
|
type: [Array, Object],
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
cost_unit_id: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
|
||||||
|
invoice_id: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
// Prüfen, ob ein ?id= Parameter in der URL übergeben wurde
|
||||||
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
|
const initialCostUnitId = props.cost_unit_id
|
||||||
|
const initialInvoiceId = props.invoice_id
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
title: 'Aktuelle Veranstaltungen',
|
||||||
|
component: ListCostUnits,
|
||||||
|
endpoint: "/api/v1/cost-unit/open/current-events",
|
||||||
|
deep_jump_id: initialCostUnitId,
|
||||||
|
deep_jump_id_sub: initialInvoiceId,
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Laufende Tätigkeiten',
|
||||||
|
component: ListCostUnits,
|
||||||
|
endpoint: "/api/v1/cost-unit/open/current-running-jobs",
|
||||||
|
deep_jump_id: initialCostUnitId,
|
||||||
|
deep_jump_id_sub: initialInvoiceId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Geschlossene Kostenstellen',
|
||||||
|
component: ListCostUnits,
|
||||||
|
endpoint: "/api/v1/cost-unit/open/closed-cost-units",
|
||||||
|
deep_jump_id: initialCostUnitId,
|
||||||
|
deep_jump_id_sub: initialInvoiceId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Archivierte Kostenstellen',
|
||||||
|
component: ListCostUnits,
|
||||||
|
endpoint: "/api/v1/cost-unit/open/archived-cost-units",
|
||||||
|
deep_jump_id: initialCostUnitId,
|
||||||
|
deep_jump_id_sub: initialInvoiceId,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (undefined !== props.message) {
|
||||||
|
toast.success(props.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AppLayout title="Kostenstellen">
|
||||||
|
<shadowed-box style="width: 95%; margin: 20px auto; padding: 20px; overflow-x: hidden;">
|
||||||
|
<tabbed-page :tabs="tabs" :initial-tab-id="initialCostUnitId" :initial-sub-tab-id="initialInvoiceId" />
|
||||||
|
|
||||||
|
</shadowed-box>
|
||||||
|
</AppLayout>
|
||||||
|
</template>
|
||||||
67
app/Domains/CostUnit/Views/Open.vue
Normal file
67
app/Domains/CostUnit/Views/Open.vue
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<script setup>
|
||||||
|
import {reactive, inject, onMounted} from 'vue';
|
||||||
|
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue';
|
||||||
|
import { useAjax } from "../../../../resources/js/components/ajaxHandler.js";
|
||||||
|
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
||||||
|
import TabbedPage from "../../../Views/Components/TabbedPage.vue";
|
||||||
|
import {toast} from "vue3-toastify";
|
||||||
|
import ListInvoices from "./Partials/ListInvoices.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
costUnit: Object
|
||||||
|
})
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
|
const initialCostUnitId = props.costUnit.id
|
||||||
|
const initialInvoiceId = props.invoice_id
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
title: 'Neue Abrechnungen',
|
||||||
|
component: ListInvoices,
|
||||||
|
endpoint: "/api/v1/cost-unit/" + props.costUnit.id + "/invoice-list/new",
|
||||||
|
deep_jump_id: initialCostUnitId,
|
||||||
|
deep_jump_id_sub: initialInvoiceId,
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Nichtexportierte Abrechnungen',
|
||||||
|
component: ListInvoices,
|
||||||
|
endpoint: "/api/v1/cost-unit/" + props.costUnit.id + "/invoice-list/approved",
|
||||||
|
deep_jump_id: initialCostUnitId,
|
||||||
|
deep_jump_id_sub: initialInvoiceId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Exportierte Abrechnungen',
|
||||||
|
component: ListInvoices,
|
||||||
|
endpoint: "/api/v1/cost-unit/" + props.costUnit.id + "/invoice-list/exported",
|
||||||
|
deep_jump_id: initialCostUnitId,
|
||||||
|
deep_jump_id_sub: initialInvoiceId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Abgelehnte Abrechnungen',
|
||||||
|
component: ListInvoices,
|
||||||
|
endpoint: "/api/v1/cost-unit/" + props.costUnit.id + "/invoice-list/denied",
|
||||||
|
deep_jump_id: initialCostUnitId,
|
||||||
|
deep_jump_id_sub: initialInvoiceId,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (undefined !== props.message) {
|
||||||
|
toast.success(props.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AppLayout :title="'Abrechnungen ' + props.costUnit.name">
|
||||||
|
<shadowed-box style="width: 95%; margin: 20px auto; padding: 20px; overflow-x: hidden;">
|
||||||
|
<tabbed-page :tabs="tabs" :initial-tab-id="initialCostUnitId" :initial-sub-tab-id="initialInvoiceId" />
|
||||||
|
|
||||||
|
</shadowed-box>
|
||||||
|
</AppLayout>
|
||||||
|
</template>
|
||||||
90
app/Domains/CostUnit/Views/Partials/CostUnitDetails.vue
Normal file
90
app/Domains/CostUnit/Views/Partials/CostUnitDetails.vue
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<script setup>
|
||||||
|
|
||||||
|
import {reactive, ref} from "vue";
|
||||||
|
import Modal from "../../../../Views/Components/Modal.vue";
|
||||||
|
import { useAjax } from "../../../../../resources/js/components/ajaxHandler.js";
|
||||||
|
import AmountInput from "../../../../Views/Components/AmountInput.vue";
|
||||||
|
import {toast} from "vue3-toastify";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}, showCostUnit: Boolean
|
||||||
|
})
|
||||||
|
|
||||||
|
const mail_on_new = ref(Boolean(Number(props.data.mail_on_new)))
|
||||||
|
|
||||||
|
const emit = defineEmits(['close'])
|
||||||
|
|
||||||
|
const { request } = useAjax()
|
||||||
|
function close() {
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
billingDeadline: props.data.billingDeadline,
|
||||||
|
mailOnNew: mail_on_new.value,
|
||||||
|
distanceAllowance: props.data.distanceAllowanceSmall,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function updateCostUnit() {
|
||||||
|
const data = await request('/api/v1/cost-unit/' + props.data.id + '/details', {
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
formData
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
close();
|
||||||
|
if (data.status === 'success') {
|
||||||
|
toast.success(data.message);
|
||||||
|
} else {
|
||||||
|
toast.error(data.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
:show="showCostUnit"
|
||||||
|
title="Details anpassen"
|
||||||
|
@close="emit('close')"
|
||||||
|
>
|
||||||
|
Kilometerpauschale:
|
||||||
|
<amount-input v-model="formData.distanceAllowance" class="width-small" /> Euro / km
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
<span v-if="props.data.type !== 'running_job'">
|
||||||
|
Abrechnungsschluss am:
|
||||||
|
<input type="date" style="margin-top: 10px;" id="autoclose_date" v-model="formData.billingDeadline" />
|
||||||
|
<br /><br />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div style="margin-top: 10px;">
|
||||||
|
<label style="display: flex; align-items: center; cursor: pointer;">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="mail_on_new"
|
||||||
|
v-model="formData.mailOnNew"
|
||||||
|
style="margin-right: 8px; cursor: pointer;"
|
||||||
|
/>
|
||||||
|
<span>E-Mail-Benachrichtigung bei neuen Abrechnungen</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<input type="button" value="Speichern" @click="updateCostUnit" />
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.mareike-save-button {
|
||||||
|
background-color: #2271b1 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
230
app/Domains/CostUnit/Views/Partials/ListCostUnits.vue
Normal file
230
app/Domains/CostUnit/Views/Partials/ListCostUnits.vue
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
<script setup>
|
||||||
|
import {createApp, ref} from 'vue'
|
||||||
|
import LoadingModal from "../../../../Views/Components/LoadingModal.vue";
|
||||||
|
import { useAjax } from "../../../../../resources/js/components/ajaxHandler.js";
|
||||||
|
import CostUnitDetails from "./CostUnitDetails.vue";
|
||||||
|
import {toast} from "vue3-toastify";
|
||||||
|
import Treasurers from "./Treasurers.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 show_cost_unit = ref(false)
|
||||||
|
const showTreasurers = ref(false)
|
||||||
|
const costUnit = 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",
|
||||||
|
});
|
||||||
|
|
||||||
|
showLoading.value = false;
|
||||||
|
|
||||||
|
if (data.status === 'success') {
|
||||||
|
costUnit.value = data.costUnit
|
||||||
|
show_cost_unit.value = true
|
||||||
|
} else {
|
||||||
|
toast.error(data.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function editTreasurers(costUnitId) {
|
||||||
|
const data = await request('/api/v1/cost-unit/' + costUnitId + '/treasurers', {
|
||||||
|
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';
|
||||||
|
} 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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="props.data.cost_units && props.data.cost_units.length > 0 && !showInvoiceList">
|
||||||
|
<h2>{{ props.data.cost_unit_title }}</h2>
|
||||||
|
<span v-for="costUnit in props.data.cost_units" class="costunit-list" :id="'costUnitBox_' + costUnit.id">
|
||||||
|
<table style="width: 100%">
|
||||||
|
<thead>
|
||||||
|
<tr><td colspan="5">
|
||||||
|
{{ costUnit.name }}
|
||||||
|
</td></tr>
|
||||||
|
</thead>
|
||||||
|
<tr>
|
||||||
|
<th>Gesamtbeitrag</th>
|
||||||
|
<td>{{ costUnit.totalAmount }}</td>
|
||||||
|
<th>Unbearbeitet</th>
|
||||||
|
<td>{{ costUnit.countNewInvoices }}</td>
|
||||||
|
<td rowspan="4" style="vertical-align: top;">
|
||||||
|
<input v-if="!costUnit.archived" type="button" value="Abrechnungen bearbeiten" @click="loadInvoices(costUnit.id)" />
|
||||||
|
<input v-else type="button" value="Abrechnungen einsehen" />
|
||||||
|
<br />
|
||||||
|
<input v-if="!costUnit.archived" type="button" @click="exportPayouts(costUnit.id)" value="Genehmigte Abrechnungen exportieren" style="margin-top: 10px;" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th>Spenden</th>
|
||||||
|
<td>{{ costUnit.donatedAmount }}</td>
|
||||||
|
|
||||||
|
<th>Nicht exportiert</th>
|
||||||
|
<td>{{ costUnit.countApprovedInvoices }}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2"></td>
|
||||||
|
|
||||||
|
<th>Ohne Auszahlung</th>
|
||||||
|
<td colspan="2">{{ costUnit.countDonatedInvoices }}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2"></td>
|
||||||
|
|
||||||
|
<th>Abgelehnt</th>
|
||||||
|
<td colspan="2">{{ costUnit.countDeniedInvoices }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="5" style="width: 100%; padding-top: 20px;">
|
||||||
|
<strong @click="costUnitDetails(costUnit.id)" v-if="costUnit.allow_new" class="link">Details anpassen</strong>
|
||||||
|
<strong @click="editTreasurers(costUnit.id)" v-if="!costUnit.archived" class="link">Schatzis zuweisen</strong>
|
||||||
|
<strong @click="denyNewRequests(costUnit.id)" v-if="costUnit.allow_new" class="link" style="color: #ff0000">Neue Abrechnungen verbieten</strong>
|
||||||
|
<strong @click="allowNewRequests(costUnit.id)" v-if="!costUnit.allow_new && !costUnit.archived" class="link" style="color: #529a30">Neue Abrechnungen erlauben</strong>
|
||||||
|
<strong @click="archiveCostUnit(costUnit.id)" v-if="!costUnit.allow_new && !costUnit.archived" class="link" style="color: #ff0000">Veranstaltung archivieren</strong>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<CostUnitDetails :data="costUnit" :showCostUnit="show_cost_unit" v-if="show_cost_unit" @close="show_cost_unit = false" />
|
||||||
|
<Treasurers :data="costUnit" :showTreasurers="showTreasurers" v-if="showTreasurers" @closeTreasurers="showTreasurers = false" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="showInvoiceList">
|
||||||
|
<invoices :data="invoices" :load_invoice_id="props.deep_jump_id_sub" :cost_unit_id="current_cost_unit" />
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<strong style="width: 100%; text-align: center; display: block; margin-top: 20px;">
|
||||||
|
Es gibt keine Kostenstellen in dieser Kategorie, für die du verantwortlich bist.
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<LoadingModal :show="showLoading" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.costunit-list {
|
||||||
|
width: 96% !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
80
app/Domains/CostUnit/Views/Partials/ListInvoices.vue
Normal file
80
app/Domains/CostUnit/Views/Partials/ListInvoices.vue
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<script setup>
|
||||||
|
import Icon from "../../../../Views/Components/Icon.vue";
|
||||||
|
import InvoiceDetails from "../../../Invoice/Views/Partials/invoiceDetails/InvoiceDetails.vue";
|
||||||
|
import { useAjax } from "../../../../../resources/js/components/ajaxHandler.js";
|
||||||
|
import {ref} from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: Object
|
||||||
|
})
|
||||||
|
|
||||||
|
const { request } = useAjax()
|
||||||
|
const invoice = ref(null)
|
||||||
|
const show_invoice = ref(false)
|
||||||
|
const localData = ref(props.data)
|
||||||
|
|
||||||
|
async function openInvoiceDetails(invoiceId) {
|
||||||
|
const url = '/api/v1/invoice/details/' + invoiceId
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, { method: 'GET' })
|
||||||
|
|
||||||
|
const result = await response.json()
|
||||||
|
invoice.value = result.invoice
|
||||||
|
show_invoice.value = true
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching invoices:', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reload() {
|
||||||
|
const url = "/api/v1/cost-unit/" + props.data.costUnit.id + "/invoice-list/" + props.data.endpoint
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, { method: 'GET' })
|
||||||
|
if (!response.ok) throw new Error('Fehler beim Laden')
|
||||||
|
|
||||||
|
const result = await response.json()
|
||||||
|
localData.value = result
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching invoices:', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<table v-if="localData.invoices.length > 0" class="invoice-list-table">
|
||||||
|
<tr>
|
||||||
|
<td colspan="6">{{props.data.costUnit.name}}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr v-for="invoice in localData.invoices" :id="'invoice_' + invoice.id">
|
||||||
|
<td>{{invoice.invoiceNumber}}</td>
|
||||||
|
<td>{{invoice.invoiceType}}</td>
|
||||||
|
<td>
|
||||||
|
{{invoice.amount}}
|
||||||
|
</td>
|
||||||
|
<td style="width: 150px;">
|
||||||
|
<Icon v-if="invoice.donation" name="hand-holding-dollar" style="color: #ffffff; background-color: green" />
|
||||||
|
<Icon v-if="invoice.alreadyPaid" name="comments-dollar" style="color: #ffffff; background-color: green" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{invoice.contactName}}<br />
|
||||||
|
<label v-if="invoice.contactEmail !== '--'">{{invoice.contactEmail}}<br /></label>
|
||||||
|
<label v-if="invoice.contactPhone !== '--'">{{invoice.contactPhone}}<br /></label>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<input type="button" value="Abrechnung Anzeigen" @click="openInvoiceDetails(invoice.id)" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<p v-else>Es sind keine Abrechnungen in dieser Kategorie vorhanden.</p>
|
||||||
|
|
||||||
|
<InvoiceDetails :data="invoice" :show-invoice="show_invoice" v-if="show_invoice" @close="show_invoice = false; reload()" />
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
96
app/Domains/CostUnit/Views/Partials/Treasurers.vue
Normal file
96
app/Domains/CostUnit/Views/Partials/Treasurers.vue
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<script setup>
|
||||||
|
|
||||||
|
import {onMounted, reactive, ref} from "vue";
|
||||||
|
import Modal from "../../../../Views/Components/Modal.vue";
|
||||||
|
import { useAjax } from "../../../../../resources/js/components/ajaxHandler.js";
|
||||||
|
import AmountInput from "../../../../Views/Components/AmountInput.vue";
|
||||||
|
import {toast} from "vue3-toastify";
|
||||||
|
|
||||||
|
const selectedTreasurers = ref([])
|
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}, showTreasurers: Boolean
|
||||||
|
})
|
||||||
|
|
||||||
|
const commonProps = reactive({
|
||||||
|
activeUsers: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const response = await fetch('/api/v1/core/retrieve-global-data');
|
||||||
|
const data = await response.json();
|
||||||
|
Object.assign(commonProps, data);
|
||||||
|
|
||||||
|
selectedTreasurers.value = props.data.treasurers?.map(t => t.id) ?? []
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const mail_on_new = ref(Boolean(Number(props.data.mail_on_new)))
|
||||||
|
|
||||||
|
const emit = defineEmits(['closeTreasurers'])
|
||||||
|
|
||||||
|
const { request } = useAjax()
|
||||||
|
function closeTreasurers() {
|
||||||
|
emit('closeTreasurers')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
billingDeadline: props.data.billingDeadline,
|
||||||
|
mailOnNew: mail_on_new.value,
|
||||||
|
distanceAllowance: props.data.distanceAllowanceSmall,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function updateCostUnit() {
|
||||||
|
const data = await request('/api/v1/cost-unit/' + props.data.id + '/treasurers', {
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
selectedTreasurers: selectedTreasurers.value,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
closeTreasurers();
|
||||||
|
if (data.status === 'success') {
|
||||||
|
toast.success(data.message);
|
||||||
|
} else {
|
||||||
|
toast.error(data.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
:show="showTreasurers"
|
||||||
|
title="Schatzis zuweisen"
|
||||||
|
@close="emit('closeTreasurers')"
|
||||||
|
>
|
||||||
|
<h3>Zuständige Schatzis:</h3>
|
||||||
|
<p v-for="user in commonProps.activeUsers">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:id="'user_' + user.id"
|
||||||
|
:value="user.id"
|
||||||
|
v-model="selectedTreasurers"
|
||||||
|
/>
|
||||||
|
<label :for="'user_' + user.id">{{user.fullname}}</label>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<input type="button" value="Speichern" @click="updateCostUnit" />
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.mareike-save-button {
|
||||||
|
background-color: #2271b1 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Dashboard\Actions\UpdatePersonalData;
|
||||||
|
|
||||||
|
use App\Repositories\UserRepository;
|
||||||
|
|
||||||
|
class UpdatePersonalDataCommand
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly UpdatePersonalDataRequest $request,
|
||||||
|
private readonly UserRepository $users
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function execute(): UpdatePersonalDataResponse
|
||||||
|
{
|
||||||
|
$this->users->updatePersonalData($this->request);
|
||||||
|
|
||||||
|
$response = new UpdatePersonalDataResponse();
|
||||||
|
$response->success = true;
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Dashboard\Actions\UpdatePersonalData;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class UpdatePersonalDataRequest
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public readonly User $user,
|
||||||
|
public readonly ?string $nickname,
|
||||||
|
public readonly ?string $email,
|
||||||
|
public readonly ?string $phone,
|
||||||
|
public readonly ?string $address1,
|
||||||
|
public readonly ?string $address2,
|
||||||
|
public readonly ?string $postcode,
|
||||||
|
public readonly ?string $city,
|
||||||
|
public readonly ?string $birthday,
|
||||||
|
public readonly ?string $tetanusVaccination,
|
||||||
|
public readonly ?string $medications,
|
||||||
|
public readonly ?string $allergies,
|
||||||
|
public readonly ?string $intolerances,
|
||||||
|
public readonly ?string $eatingHabits,
|
||||||
|
public readonly ?string $swimmingPermission,
|
||||||
|
public readonly ?string $firstAidPermission,
|
||||||
|
public readonly ?string $bankAccountOwner,
|
||||||
|
public readonly ?string $bankAccountIban,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Dashboard\Actions\UpdatePersonalData;
|
||||||
|
|
||||||
|
class UpdatePersonalDataResponse
|
||||||
|
{
|
||||||
|
public bool $success;
|
||||||
|
}
|
||||||
@@ -5,8 +5,8 @@ namespace App\Domains\Dashboard\Controllers;
|
|||||||
use App\Providers\AuthCheckProvider;
|
use App\Providers\AuthCheckProvider;
|
||||||
use App\Providers\InertiaProvider;
|
use App\Providers\InertiaProvider;
|
||||||
use App\Scopes\CommonController;
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
|
|
||||||
class DashboardController extends CommonController {
|
class DashboardController extends CommonController {
|
||||||
public function __invoke(Request $request) {
|
public function __invoke(Request $request) {
|
||||||
@@ -15,18 +15,36 @@ class DashboardController extends CommonController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->intended('/login');
|
return redirect()->intended('/login');
|
||||||
|
|
||||||
dd('U');
|
|
||||||
return $this->renderForGuest($request);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function renderForLoggedInUser(Request $request) {
|
private function renderForLoggedInUser(Request $request) {
|
||||||
$authCheckProvider = new AuthCheckProvider;
|
$authCheckProvider = new AuthCheckProvider;
|
||||||
$inertiaProvider = new InertiaProvider('Dashboard/Dashboard', ['appName' => app('tenant')->name]);
|
$inertiaProvider = new InertiaProvider('Dashboard/Dashboard', [
|
||||||
|
'myInvoices' => $this->invoices->getMyInvoicesWidget(),
|
||||||
|
'myParticipations' => $this->eventParticipants->getMyParticipations(),
|
||||||
|
|
||||||
|
]);
|
||||||
return $inertiaProvider->render();
|
return $inertiaProvider->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function renderForGuest(Request $request) {
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMyParticipations() : JsonResponse {
|
||||||
|
return response()->json(['myParticipations' => $this->eventParticipants->getMyParticipations()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUpcomingEvents() : JsonResponse {
|
||||||
|
return response()->json(['upcomingEvents' => $this->events->getUpcoming()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
app/Domains/Dashboard/Controllers/MessagesController.php
Normal file
14
app/Domains/Dashboard/Controllers/MessagesController.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Dashboard\Controllers;
|
||||||
|
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
|
||||||
|
class MessagesController extends CommonController {
|
||||||
|
public function __invoke() {
|
||||||
|
$inertiaProvider = new InertiaProvider('Dashboard/Messages', []);
|
||||||
|
|
||||||
|
return $inertiaProvider->render();
|
||||||
|
}
|
||||||
|
}
|
||||||
45
app/Domains/Dashboard/Controllers/PersonalDataController.php
Normal file
45
app/Domains/Dashboard/Controllers/PersonalDataController.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Dashboard\Controllers;
|
||||||
|
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
|
||||||
|
class PersonalDataController extends CommonController
|
||||||
|
{
|
||||||
|
public function __invoke()
|
||||||
|
{
|
||||||
|
if (!$this->checkAuth()) {
|
||||||
|
return redirect()->intended('/login');
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = auth()->user();
|
||||||
|
$data = $this->users->getPersonalData($user);
|
||||||
|
|
||||||
|
$inertiaProvider = new InertiaProvider('Dashboard/PersonalData', [
|
||||||
|
'personalData' => [
|
||||||
|
'firstname' => $data['firstname'],
|
||||||
|
'lastname' => $data['lastname'],
|
||||||
|
'birthday' => $data['birthday'],
|
||||||
|
'nickname' => $data['nickname'],
|
||||||
|
'email' => $data['email'],
|
||||||
|
'phone' => $data['phone'],
|
||||||
|
'address1' => $data['address_1'],
|
||||||
|
'address2' => $data['address_2'],
|
||||||
|
'postcode' => $data['postcode'],
|
||||||
|
'city' => $data['city'],
|
||||||
|
'medications' => $data['medications'],
|
||||||
|
'allergies' => $data['allergies'],
|
||||||
|
'intolerances' => $data['intolerances'],
|
||||||
|
'eatingHabits' => $data['eating_habits'],
|
||||||
|
'swimmingPermission' => $data['swimming_permission'],
|
||||||
|
'firstAidPermission' => $data['first_aid_permission'],
|
||||||
|
'bankAccountOwner' => $data['bank_account_owner'],
|
||||||
|
'bankAccountIban' => $data['bank_account_iban'],
|
||||||
|
'tetanusVaccination' => $data['tetanus_vaccination'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $inertiaProvider->render();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Dashboard\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\Dashboard\Actions\UpdatePersonalData\UpdatePersonalDataCommand;
|
||||||
|
use App\Domains\Dashboard\Actions\UpdatePersonalData\UpdatePersonalDataRequest;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class StorePersonalDataController extends CommonController
|
||||||
|
{
|
||||||
|
public function __invoke(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
$actionRequest = new UpdatePersonalDataRequest(
|
||||||
|
user: $user,
|
||||||
|
nickname: $request->input('nickname'),
|
||||||
|
email: $request->input('email'),
|
||||||
|
phone: $request->input('phone'),
|
||||||
|
address1: $request->input('address1'),
|
||||||
|
address2: $request->input('address2'),
|
||||||
|
postcode: $request->input('postcode'),
|
||||||
|
city: $request->input('city'),
|
||||||
|
medications: $request->input('medications'),
|
||||||
|
allergies: $request->input('allergies'),
|
||||||
|
intolerances: $request->input('intolerances'),
|
||||||
|
eatingHabits: $request->input('eatingHabits'),
|
||||||
|
swimmingPermission: $request->input('swimmingPermission'),
|
||||||
|
firstAidPermission: $request->input('firstAidPermission'),
|
||||||
|
bankAccountOwner: $request->input('bankAccountOwner'),
|
||||||
|
bankAccountIban: $request->input('bankAccountIban'),
|
||||||
|
birthday: $request->input('birthday'),
|
||||||
|
tetanusVaccination: $request->input('tetanusVaccination'),
|
||||||
|
);
|
||||||
|
|
||||||
|
$command = new UpdatePersonalDataCommand($actionRequest, $this->users);
|
||||||
|
$command->execute();
|
||||||
|
|
||||||
|
return response()->json(['success' => true, 'message' => 'Deine Daten wurden erfolgreich gespeichert.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/Domains/Dashboard/Routes/api.php
Normal file
21
app/Domains/Dashboard/Routes/api.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Domains\Dashboard\Controllers\DashboardController;
|
||||||
|
use App\Domains\Dashboard\Controllers\StorePersonalDataController;
|
||||||
|
use App\Middleware\IdentifyTenant;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
Route::middleware(IdentifyTenant::class)->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']);
|
||||||
|
Route::get('/upcoming-events', [DashboardController::class, 'getUpcomingEvents']);
|
||||||
|
Route::get('/my-participations', [DashboardController::class, 'getMyParticipations']);
|
||||||
|
Route::post('/personal-data', StorePersonalDataController::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
16
app/Domains/Dashboard/Routes/web.php
Normal file
16
app/Domains/Dashboard/Routes/web.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Domains\Dashboard\Controllers\DashboardController;
|
||||||
|
use App\Domains\Dashboard\Controllers\MessagesController;
|
||||||
|
use App\Domains\Dashboard\Controllers\PersonalDataController;
|
||||||
|
use App\Middleware\IdentifyTenant;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
Route::middleware(IdentifyTenant::class)->group(function () {
|
||||||
|
Route::middleware(['auth'])->group(function () {
|
||||||
|
Route::get('/personal-data', PersonalDataController::class);
|
||||||
|
Route::get('/messages', MessagesController::class);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,24 +1,33 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue'
|
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue'
|
||||||
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
||||||
|
import MyInvoices from "./Partials/Widgets/MyInvoices.vue";
|
||||||
|
import MyParticipations from "./Partials/Widgets/MyParticipations.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
navbar: Object,
|
myInvoices: Object,
|
||||||
tenant: String,
|
myParticipations: Object,
|
||||||
user: Object,
|
|
||||||
currentPath: String,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function newInvoice() {
|
||||||
|
window.location.href = '/invoice/new';
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AppLayout title='Dashboard' :user="props.user" :navbar="props.navbar" :tenant="props.tenant" :currentPath="props.currentPath">
|
<AppLayout title='Dashboard'>
|
||||||
<diV class="dashboard-widget-container">
|
<diV class="dashboard-widget-container">
|
||||||
<shadowed-box class="dashboard-widget-box" style="width: 60%;">
|
<shadowed-box class="dashboard-widget-box" style="width: 60%;">
|
||||||
Meine Anmeldungen
|
<MyParticipations />
|
||||||
</shadowed-box>
|
</shadowed-box>
|
||||||
|
|
||||||
<shadowed-box class="dashboard-widget-box">
|
<shadowed-box class="dashboard-widget-box" style="height: 275px;">
|
||||||
Meine Abrechnungen
|
<MyInvoices />
|
||||||
|
<input type="button" value="Neue Abrechnung" @click="newInvoice" style="margin-top: 20px;">
|
||||||
|
|
||||||
|
|
||||||
</shadowed-box>
|
</shadowed-box>
|
||||||
</diV>
|
</diV>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
@@ -36,7 +45,17 @@ const props = defineProps({
|
|||||||
|
|
||||||
.dashboard-widget-box {
|
.dashboard-widget-box {
|
||||||
flex-grow: 1; display: inline-block;
|
flex-grow: 1; display: inline-block;
|
||||||
height: 150px;
|
margin: 0 10px;
|
||||||
margin: 0 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-widget-box h2 {
|
||||||
|
border-color: #c0c0c0;
|
||||||
|
border-left-width: 40px;
|
||||||
|
border-left-style: solid;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-size: 13pt;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
25
app/Domains/Dashboard/Views/Messages.vue
Normal file
25
app/Domains/Dashboard/Views/Messages.vue
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<script setup>
|
||||||
|
import AppLayout from "../../../../resources/js/layouts/AppLayout.vue";
|
||||||
|
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AppLayout title='Meine Nachrichten'>
|
||||||
|
<shadowed-box style="width: 95%; margin: 20px auto; padding: 20px; overflow-x: hidden;">
|
||||||
|
Diese Funktion steht aktuell nicht zur Verfügung.<br />
|
||||||
|
Bitte versuche es später noch einmal.
|
||||||
|
</shadowed-box>
|
||||||
|
</AppLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
32
app/Domains/Dashboard/Views/Partials/Widgets/MyInvoices.vue
Normal file
32
app/Domains/Dashboard/Views/Partials/Widgets/MyInvoices.vue
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<script setup>
|
||||||
|
import {onMounted, reactive} from "vue";
|
||||||
|
|
||||||
|
const myInvoices = reactive({
|
||||||
|
'myInvoices': '',
|
||||||
|
'approvedInvoices': '',
|
||||||
|
'deniedInvoices': '',
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const response = await fetch('/api/v1/dashboard/my-invoices');
|
||||||
|
const data = await response.json();
|
||||||
|
Object.assign(myInvoices, data);
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>Meine Abrechnungen</h2>
|
||||||
|
<p v-for="invoice in myInvoices.myInvoices" class="widget-content-item">
|
||||||
|
<a :href="'/invoice/my-invoices/' + invoice.slug" class="link">{{invoice.title}} ({{invoice.count}})</a>
|
||||||
|
<label>
|
||||||
|
{{invoice.amount}}
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<script setup>
|
||||||
|
import {onMounted, reactive} from "vue";
|
||||||
|
import Icon from "../../../../../Views/Components/Icon.vue";
|
||||||
|
|
||||||
|
const myParticipations = reactive({
|
||||||
|
'myParticipations': '',
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const response = await fetch('/api/v1/dashboard/my-participations');
|
||||||
|
const data = await response.json();
|
||||||
|
Object.assign(myParticipations, data);
|
||||||
|
});
|
||||||
|
|
||||||
|
function navigateTo(url) {
|
||||||
|
window.location.href = url;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>Meine Anmeldungen</h2>
|
||||||
|
<p v-for="participation in myParticipations.myParticipations" class="widget-content-item">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 40%; font-weight: bold;">{{participation.eventName}}</td>
|
||||||
|
<td style="width: 30%; font-weight: bold;">{{participation.arrivalDateReadable}} - {{participation.departureDateReadable}}</td>
|
||||||
|
<td style="width: 30%;">
|
||||||
|
<Icon name="euro-sign" style="padding: 5px; font-size: 11pt; color: #ffffff; margin-right: 5px;" :class="participation.needs_payment ? 'bg-red' : 'bg-green'" />
|
||||||
|
<Icon name="award" style="padding: 5px; font-size: 11pt; color: #ffffff; margin-right: 5px;" :class="participation.cocColor" />
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{participation.event.postal_code}} {{participation.event.location}}<br />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a class="link" :href="`/api/v1/event/participant/${participation.identifier}/ical`">In Kalender importieren</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{participation.amountPaid.readable}} / {{participation.amountExpected.readable}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2"> </td>
|
||||||
|
<td>eFZ-Status: {{participation.efzStatusReadable}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
<p v-if="myParticipations.myParticipations.length === 0">Du bist aktuelle für keine Veranstaltung angemeldet.</p>
|
||||||
|
<p>
|
||||||
|
<input type="button" value="Jetzt anmelden" class="button" @click="navigateTo('/event/available-events')" />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.bg-red {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-green {
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-yellow {
|
||||||
|
background-color: #e4e44c;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<script setup>
|
||||||
|
import {onMounted, reactive} from "vue";
|
||||||
|
import Icon from "../../../../../Views/Components/Icon.vue";
|
||||||
|
|
||||||
|
const myParticipations = reactive({
|
||||||
|
'myParticipations': '',
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const response = await fetch('/api/v1/dashboard/my-participations');
|
||||||
|
const data = await response.json();
|
||||||
|
Object.assign(myParticipations, data);
|
||||||
|
});
|
||||||
|
|
||||||
|
function navigateTo(url) {
|
||||||
|
window.location.href = url;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<table v-if="myParticipations.myParticipations.length > 0">
|
||||||
|
<tr v-for="participation in myParticipations.myParticipations.slice(0, 3)" class="widget-content-item">
|
||||||
|
<td>
|
||||||
|
{{participation.eventName}}<br />
|
||||||
|
{{participation.event.location}},
|
||||||
|
{{participation.arrivalDateReadable}} - {{participation.departureDateReadable}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Icon name="euro-sign" style="padding: 2px; font-size: 10pt; color: #ffffff; margin-right: 5px;" :class="participation.needs_payment ? 'bg-red' : 'bg-green'" />
|
||||||
|
<Icon name="award" style="padding: 2px; font-size: 10pt; color: #ffffff; margin-right: 5px;" :class="participation.cocColor" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<p v-else>
|
||||||
|
Du bist aktuelle für keine Veranstaltung angemeldet.<br /><br />
|
||||||
|
<input type="button" value="Jetzt anmelden" class="button" @click="navigateTo('/event/available-events')" />
|
||||||
|
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.bg-red {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-green {
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-yellow {
|
||||||
|
background-color: #e4e44c;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<script setup>
|
||||||
|
import {onMounted, reactive} from "vue";
|
||||||
|
|
||||||
|
const costUnits = reactive({
|
||||||
|
'openCostUnits': '',
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const response = await fetch('/api/v1/dashboard/open-cost-units');
|
||||||
|
const data = await response.json();
|
||||||
|
Object.assign(costUnits, data);
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<table class="widget-content-item" v-if="costUnits.openCostUnits.length > 0">
|
||||||
|
<tr>
|
||||||
|
<td style="font-weight: bold">Kostenstelle</td>
|
||||||
|
<td style="font-weight: bold">Neu</td>
|
||||||
|
<td style="font-weight: bold">Ang</td>
|
||||||
|
<td style="font-weight: bold">Betrag</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr v-for="costUnit in costUnits.openCostUnits">
|
||||||
|
<td><a :href="'/cost-unit/' + costUnit.id" class="link">{{costUnit.name}}</a></td>
|
||||||
|
<td>{{costUnit.new_invoices_count}}</td>
|
||||||
|
<td>{{costUnit.approved_invoices_count}}</td>
|
||||||
|
<td>{{costUnit.totalAmount}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p v-else style="padding: 10px; font-weight: bold">Es existieren im Moment keine Abrechnugnen, um die du dich kümmern musst.</p>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<script setup>
|
||||||
|
import {onMounted, reactive} from "vue";
|
||||||
|
|
||||||
|
const events = reactive({
|
||||||
|
'upcomingEvents': '',
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const response = await fetch('/api/v1/dashboard/upcoming-events');
|
||||||
|
const data = await response.json();
|
||||||
|
Object.assign(events, data);
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<table class="widget-content-item" v-if="events.upcomingEvents.length > 0">
|
||||||
|
<tr>
|
||||||
|
<td style="font-weight: bold">Veranstaltung</td>
|
||||||
|
<td style="font-weight: bold">Teilis</td>
|
||||||
|
<td style="font-weight: bold">Team</td>
|
||||||
|
<td style="font-weight: bold">GruFüs</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr v-for="event in events.upcomingEvents">
|
||||||
|
<td><a :href="'/event/details/' + event.identifier" class="link">{{event.nameShort}}</a></td>
|
||||||
|
<td style="text-align: center;">{{event.countParticipant}}</td>
|
||||||
|
<td style="text-align: center;">{{event.countTeam}}</td>
|
||||||
|
<td style="text-align: center;">{{event.countVolunteer}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p v-else style="padding: 10px; font-weight: bold">Es existieren im Moment keine Veranstaltungen, für die du verantwortlich bist.</p>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
187
app/Domains/Dashboard/Views/PersonalData.vue
Normal file
187
app/Domains/Dashboard/Views/PersonalData.vue
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
import { request } from '../../../../resources/js/components/HttpClient.js'
|
||||||
|
import AppLayout from "../../../../resources/js/layouts/AppLayout.vue";
|
||||||
|
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
||||||
|
import {toast} from "vue3-toastify";
|
||||||
|
import IbanInput from "../../../Views/Components/IbanInput.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
personalData: Object,
|
||||||
|
})
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
nickname: props.personalData.nickname ?? '',
|
||||||
|
email: props.personalData.email ?? '',
|
||||||
|
phone: props.personalData.phone ?? '',
|
||||||
|
address1: props.personalData.address1 ?? '',
|
||||||
|
address2: props.personalData.address2 ?? '',
|
||||||
|
postcode: props.personalData.postcode ?? '',
|
||||||
|
city: props.personalData.city ?? '',
|
||||||
|
birthday: props.personalData.birthday ?? '',
|
||||||
|
tetanusVaccination: props.personalData.tetanusVaccination ?? '',
|
||||||
|
medications: props.personalData.medications ?? '',
|
||||||
|
allergies: props.personalData.allergies ?? '',
|
||||||
|
intolerances: props.personalData.intolerances ?? '',
|
||||||
|
eatingHabits: props.personalData.eatingHabits ?? '',
|
||||||
|
swimmingPermission: props.personalData.swimmingPermission ?? '',
|
||||||
|
firstAidPermission: props.personalData.firstAidPermission ?? '',
|
||||||
|
bankAccountOwner: props.personalData.bankAccountOwner ?? '',
|
||||||
|
bankAccountIban: props.personalData.bankAccountIban ?? '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const saving = ref(false)
|
||||||
|
const successMessage = ref('')
|
||||||
|
const errorMessage = ref('')
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
saving.value = true
|
||||||
|
successMessage.value = ''
|
||||||
|
errorMessage.value = ''
|
||||||
|
|
||||||
|
const result = await request('/api/v1/dashboard/personal-data', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { ...form },
|
||||||
|
})
|
||||||
|
|
||||||
|
saving.value = false
|
||||||
|
|
||||||
|
if (result?.success) {
|
||||||
|
toast.success(result.message)
|
||||||
|
} else {
|
||||||
|
toast.error(result.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AppLayout title='Persönliche Daten'>
|
||||||
|
<shadowed-box style="width: 95%; margin: 20px auto; padding: 20px; overflow-x: hidden;">
|
||||||
|
|
||||||
|
<div class="max-w-2xl mx-auto p-6">
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<table class="form-table" style="width: 90%; margin: 10px;">
|
||||||
|
|
||||||
|
<!-- Nicht veränderbare Felder -->
|
||||||
|
<tr>
|
||||||
|
<td style="width: 200px; padding: 5px;">Vorname:</td>
|
||||||
|
<td><span class="text-gray-700">{{ personalData.firstname }}</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 200px; padding: 5px;">Nachname:</td>
|
||||||
|
<td><span class="text-gray-700">{{ personalData.lastname }}</span></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Veränderbare Felder -->
|
||||||
|
<tr>
|
||||||
|
<td>Pfadiname:</td>
|
||||||
|
<td><input type="text" v-model="form.nickname" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>E-Mail:</td>
|
||||||
|
<td><input type="email" v-model="form.email" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Telefon:</td>
|
||||||
|
<td><input type="text" v-model="form.phone" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Straße / Hausnummer:</td>
|
||||||
|
<td><input type="text" v-model="form.address1" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Adresszusatz:</td>
|
||||||
|
<td><input type="text" v-model="form.address2" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PLZ:</td>
|
||||||
|
<td><input type="text" v-model="form.postcode" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Ort:</td>
|
||||||
|
<td><input type="text" v-model="form.city" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 200px; padding: 5px;">Geburtsdatum:</td>
|
||||||
|
<td><input type="date" v-model="form.birthday" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Medikamente:</td>
|
||||||
|
<td><input type="text" v-model="form.medications" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Allergien:</td>
|
||||||
|
<td><input type="text" v-model="form.allergies" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Unverträglichkeiten:</td>
|
||||||
|
<td><input type="text" v-model="form.intolerances" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Letzte Tetanus-Impfung:</td>
|
||||||
|
<td><input type="date" v-model="form.tetanusVaccination" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Ernährungsgewohnheiten:</td>
|
||||||
|
<td>
|
||||||
|
<select v-model="form.eatingHabits">
|
||||||
|
<option value="EATING_HABIT_VEGAN">Vegan</option>
|
||||||
|
<option value="EATING_HABIT_VEGETARIAN">Vegetarisch</option>
|
||||||
|
<option value="EATING_HABIT_OMNIVOR">Omnivor</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Badeerlaubnis:</td>
|
||||||
|
<td>
|
||||||
|
<select v-model="form.swimmingPermission">
|
||||||
|
<option value="SWIMMING_PERMISSION_ALLOWED">Erteilt, kann schwimmen</option>
|
||||||
|
<option value="SWIMMING_PERMISSION_LIMITED">Erteilt, kann nicht schwimmen</option>
|
||||||
|
<option value="SWIMMING_PERMISSION_DENIED">Nicht erteilt</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Erste-Hilfe-Erlaubnis:</td>
|
||||||
|
<td>
|
||||||
|
<select v-model="form.firstAidPermission">
|
||||||
|
<option value="FIRST_AID_PERMISSION_ALLOWED">Erweiterte Erste Hilfe erlaubt</option>
|
||||||
|
<option value="FIRST_AID_PERMISSION_DENIED">Erweiterte Erste Hilfe verweigert</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Kontoinhaber*in:</td>
|
||||||
|
<td><input type="text" v-model="form.bankAccountOwner" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>IBAN:</td>
|
||||||
|
<td><IbanInput v-model="form.bankAccountIban" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="btn-row" style="padding-top: 20px;">
|
||||||
|
<button type="submit" class="button" :disabled="saving">
|
||||||
|
{{ saving ? 'Wird gespeichert…' : 'Speichern' }}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</shadowed-box>
|
||||||
|
</AppLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\CertificateOfConductionCheck;
|
||||||
|
|
||||||
|
use App\Enumerations\EfzStatus;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
|
class CertificateOfConductionCheckCommand {
|
||||||
|
function __construct(public CertificateOfConductionCheckRequest $request)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public function execute() : CertificateOfConductionCheckResponse {
|
||||||
|
$response = new CertificateOfConductionCheckResponse();
|
||||||
|
|
||||||
|
$localGroup = str_replace('Stamm ', '', $this->request->participant->localGroup()->first()->name);
|
||||||
|
|
||||||
|
$apiResponse = Http::acceptJson()
|
||||||
|
->asJson()
|
||||||
|
->withoutVerifying()
|
||||||
|
->post(env('COC_CHECK_URL'), [
|
||||||
|
'firstName' => trim($this->request->participant->firstname),
|
||||||
|
'lastName' => $this->request->participant->lastname,
|
||||||
|
'nickname' => $this->request->participant->nickname,
|
||||||
|
'address' => trim($this->request->participant->address_1 . ' ' . $this->request->participant->address_2),
|
||||||
|
'zip' => $this->request->participant->zip,
|
||||||
|
'city' => $this->request->participant->city,
|
||||||
|
'birthday' => $this->request->participant->birthday->format('Y-m-d'),
|
||||||
|
'email' => $this->request->participant->email_1,
|
||||||
|
'localGroup' => $localGroup,
|
||||||
|
'checkForDate' => $this->request->participant->departure_date->format('Y-m-d'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (! $apiResponse->successful()) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseParsed = $apiResponse->json();
|
||||||
|
|
||||||
|
if (($responseParsed['status'] ?? null) !== '200/ok') {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->status = match ($responseParsed['efzStatus']) {
|
||||||
|
'NOT_REQUIRED' => EfzStatus::EFZ_STATUS_NOT_REQUIRED,
|
||||||
|
'CHECKED_VALID' => EfzStatus::EFZ_STATUS_CHECKED_VALID,
|
||||||
|
'CHECKED_INVALID' => EfzStatus::EFZ_STATUS_CHECKED_INVALID,
|
||||||
|
default => EfzStatus::EFZ_STATUS_NOT_CHECKED
|
||||||
|
};
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\CertificateOfConductionCheck;
|
||||||
|
|
||||||
|
use App\Models\EventParticipant;
|
||||||
|
|
||||||
|
class CertificateOfConductionCheckRequest {
|
||||||
|
function __construct(public EventParticipant $participant)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\CertificateOfConductionCheck;
|
||||||
|
|
||||||
|
use App\Enumerations\EfzStatus;
|
||||||
|
|
||||||
|
class CertificateOfConductionCheckResponse {
|
||||||
|
public string $status;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->status = EfzStatus::EFZ_STATUS_NOT_CHECKED;
|
||||||
|
}
|
||||||
|
}
|
||||||
76
app/Domains/Event/Actions/CreateEvent/CreateEventCommand.php
Normal file
76
app/Domains/Event/Actions/CreateEvent/CreateEventCommand.php
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\CreateEvent;
|
||||||
|
|
||||||
|
use App\Enumerations\EatingHabit;
|
||||||
|
use App\Models\Event;
|
||||||
|
use App\Models\Tenant;
|
||||||
|
use App\RelationModels\EventEatingHabits;
|
||||||
|
use App\RelationModels\EventLocalGroups;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class CreateEventCommand {
|
||||||
|
private CreateEventRequest $request;
|
||||||
|
public function __construct(CreateEventRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
public function execute(): CreateEventResponse {
|
||||||
|
$response = new CreateEventResponse();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$prefix = $this->request->begin->format('Y-m_');
|
||||||
|
if (!str_starts_with($this->request->name, $prefix)) {
|
||||||
|
$this->request->name = $prefix . $this->request->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$event = Event::create([
|
||||||
|
'tenant' => app('tenant')->slug,
|
||||||
|
'name' => $this->request->name,
|
||||||
|
'identifier' => Str::random(10),
|
||||||
|
'location' => $this->request->location,
|
||||||
|
'postal_code' => $this->request->postalCode,
|
||||||
|
'email' => $this->request->email,
|
||||||
|
'start_date' => $this->request->begin,
|
||||||
|
'end_date' => $this->request->end,
|
||||||
|
'early_bird_end' => $this->request->earlyBirdEnd,
|
||||||
|
'registration_final_end' => $this->request->registrationFinalEnd,
|
||||||
|
'early_bird_end_amount_increase' => $this->request->earlyBirdEndAmountIncrease,
|
||||||
|
'account_owner' => $this->request->accountOwner,
|
||||||
|
'account_iban' => $this->request->accountIban,
|
||||||
|
'participation_fee_type' => $this->request->participationFeeType->slug,
|
||||||
|
'pay_per_day' => $this->request->payPerDay,
|
||||||
|
'pay_direct' => $this->request->payDirect,
|
||||||
|
'total_max_amount' => 0,
|
||||||
|
'support_per_person' => 0,
|
||||||
|
'support_flat' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($event !== null) {
|
||||||
|
EventEatingHabits::create([
|
||||||
|
'event_id' => $event->id,
|
||||||
|
'eating_habit_id' => EatingHabit::where('slug', EatingHabit::EATING_HABIT_VEGAN)->first()->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
EventEatingHabits::create([
|
||||||
|
'event_id' => $event->id,
|
||||||
|
'eating_habit_id' => EatingHabit::where('slug', EatingHabit::EATING_HABIT_VEGETARIAN)->first()->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (app('tenant')->slug === 'lv') {
|
||||||
|
foreach(Tenant::where(['is_active_local_group' => true])->get() as $tenant) {
|
||||||
|
EventLocalGroups::create(['event_id' => $event->id, 'local_group_id' => $tenant->id]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
EventLocalGroups::create(['event_id' => $event->id, 'local_group_id' => app('tenant')->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$response->success = true;
|
||||||
|
$response->event = $event;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
40
app/Domains/Event/Actions/CreateEvent/CreateEventRequest.php
Normal file
40
app/Domains/Event/Actions/CreateEvent/CreateEventRequest.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\CreateEvent;
|
||||||
|
|
||||||
|
use App\Enumerations\ParticipationFeeType;
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
|
class CreateEventRequest {
|
||||||
|
public string $name;
|
||||||
|
public string $location;
|
||||||
|
public string $postalCode;
|
||||||
|
public string $email;
|
||||||
|
public DateTime $begin;
|
||||||
|
public DateTime $end;
|
||||||
|
public DateTime $earlyBirdEnd;
|
||||||
|
public DateTime $registrationFinalEnd;
|
||||||
|
public int $earlyBirdEndAmountIncrease;
|
||||||
|
public ParticipationFeeType $participationFeeType;
|
||||||
|
public string $accountOwner;
|
||||||
|
public string $accountIban;
|
||||||
|
public bool $payPerDay;
|
||||||
|
public bool $payDirect;
|
||||||
|
|
||||||
|
public function __construct(string $name, string $location, string $postalCode, string $email, DateTime $begin, DateTime $end, DateTime $earlyBirdEnd, DateTime $registrationFinalEnd, int $earlyBirdEndAmountIncrease, ParticipationFeeType $participationFeeType, string $accountOwner, string $accountIban, bool $payPerDay, bool $payDirect) {
|
||||||
|
$this->name = $name;
|
||||||
|
$this->location = $location;
|
||||||
|
$this->postalCode = $postalCode;
|
||||||
|
$this->email = $email;
|
||||||
|
$this->begin = $begin;
|
||||||
|
$this->end = $end;
|
||||||
|
$this->earlyBirdEnd = $earlyBirdEnd;
|
||||||
|
$this->registrationFinalEnd = $registrationFinalEnd;
|
||||||
|
$this->earlyBirdEndAmountIncrease = $earlyBirdEndAmountIncrease;
|
||||||
|
$this->participationFeeType = $participationFeeType;
|
||||||
|
$this->accountOwner = $accountOwner;
|
||||||
|
$this->accountIban = $accountIban;
|
||||||
|
$this->payPerDay = $payPerDay;
|
||||||
|
$this->payDirect = $payDirect;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\CreateEvent;
|
||||||
|
|
||||||
|
use App\Models\Event;
|
||||||
|
|
||||||
|
class CreateEventResponse {
|
||||||
|
public bool $success;
|
||||||
|
public ?Event $event;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->success = false;
|
||||||
|
$this->event = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\GenerateIcal;
|
||||||
|
|
||||||
|
class GenerateIcalCommand
|
||||||
|
{
|
||||||
|
public function __construct(public GenerateIcalRequest $request)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(): GenerateIcalResponse
|
||||||
|
{
|
||||||
|
$participant = $this->request->participant;
|
||||||
|
$event = $participant->event;
|
||||||
|
|
||||||
|
$uid = $participant->identifier . '@' . app('tenant')->slug;
|
||||||
|
$dtStart = $event->start_date->format('Ymd');
|
||||||
|
$dtEnd = $event->end_date->copy()->addDay()->format('Ymd');
|
||||||
|
$now = now()->format('Ymd\THis\Z');
|
||||||
|
$summary = $this->escapeIcal($event->name);
|
||||||
|
$location = $this->escapeIcal(trim($event->postal_code . ' ' . $event->location));
|
||||||
|
$description = $this->escapeIcal('Teilnahme als: ' . $participant->getOfficialName());
|
||||||
|
|
||||||
|
$icalContent = implode("\r\n", [
|
||||||
|
'BEGIN:VCALENDAR',
|
||||||
|
'VERSION:2.0',
|
||||||
|
'PRODID:-//' . app('tenant')->name . '//Veranstaltungskalender//DE',
|
||||||
|
'CALSCALE:GREGORIAN',
|
||||||
|
'METHOD:PUBLISH',
|
||||||
|
'BEGIN:VEVENT',
|
||||||
|
'UID:' . $uid,
|
||||||
|
'DTSTAMP:' . $now,
|
||||||
|
'DTSTART;VALUE=DATE:' . $dtStart,
|
||||||
|
'DTEND;VALUE=DATE:' . $dtEnd,
|
||||||
|
'SUMMARY:' . $summary,
|
||||||
|
'LOCATION:' . $location,
|
||||||
|
'DESCRIPTION:' . $description,
|
||||||
|
'END:VEVENT',
|
||||||
|
'END:VCALENDAR',
|
||||||
|
]) . "\r\n";
|
||||||
|
|
||||||
|
$filename = 'veranstaltung-' . $participant->event()->first()->name . '.ics';
|
||||||
|
|
||||||
|
return new GenerateIcalResponse($icalContent, $filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function escapeIcal(string $value): string
|
||||||
|
{
|
||||||
|
return str_replace(
|
||||||
|
['\\', ';', ',', "\n"],
|
||||||
|
['\\\\', '\\;', '\\,', '\\n'],
|
||||||
|
$value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\GenerateIcal;
|
||||||
|
|
||||||
|
use App\Models\EventParticipant;
|
||||||
|
|
||||||
|
class GenerateIcalRequest
|
||||||
|
{
|
||||||
|
public function __construct(public EventParticipant $participant)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\GenerateIcal;
|
||||||
|
|
||||||
|
class GenerateIcalResponse
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $icalContent,
|
||||||
|
public string $filename,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\GenerateIcalForDeadline;
|
||||||
|
|
||||||
|
class GenerateIcalForDeadlineCommand {
|
||||||
|
public function __construct(public GenerateIcalForDeadlineRequest $request)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public function execute(): GenerateIcalForDeadlineResponse
|
||||||
|
{
|
||||||
|
$event = $this->request->event;
|
||||||
|
|
||||||
|
$deadlineDate = $event->registration_final_end->copy()->subDays(2);
|
||||||
|
$dtDate = $deadlineDate->format('Ymd');
|
||||||
|
$now = now()->format('Ymd\THis\Z');
|
||||||
|
$summary = $this->escapeIcal('Zahlungsfrist: ' . $event->name);
|
||||||
|
$description = $this->escapeIcal(
|
||||||
|
'Bitte überweise den Teilnahmebeitrag für "' . $event->name . '" bis zu diesem Datum.'
|
||||||
|
);
|
||||||
|
|
||||||
|
$icalContent = implode("\r\n", [
|
||||||
|
'BEGIN:VCALENDAR',
|
||||||
|
'VERSION:2.0',
|
||||||
|
'PRODID:-//' . app('tenant')->name . '//Veranstaltungskalender//DE',
|
||||||
|
'CALSCALE:GREGORIAN',
|
||||||
|
'METHOD:PUBLISH',
|
||||||
|
'BEGIN:VEVENT',
|
||||||
|
'UID:payment-deadline-' . $event->identifier . '@' . app('tenant')->slug,
|
||||||
|
'DTSTAMP:' . $now,
|
||||||
|
'DTSTART;VALUE=DATE:' . $dtDate,
|
||||||
|
'DTEND;VALUE=DATE:' . $dtDate,
|
||||||
|
'SUMMARY:' . $summary,
|
||||||
|
'DESCRIPTION:' . $description,
|
||||||
|
'END:VEVENT',
|
||||||
|
'END:VCALENDAR',
|
||||||
|
]) . "\r\n";
|
||||||
|
|
||||||
|
$filename = 'zahlungsziel-' . $event->name . '.ics';
|
||||||
|
|
||||||
|
return new GenerateIcalForDeadlineResponse($icalContent, $filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function escapeIcal(string $value): string
|
||||||
|
{
|
||||||
|
return str_replace(
|
||||||
|
['\\', ';', ',', "\n"],
|
||||||
|
['\\\\', '\\;', '\\,', '\\n'],
|
||||||
|
$value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\GenerateIcalForDeadline;
|
||||||
|
|
||||||
|
use App\Models\Event;
|
||||||
|
|
||||||
|
class GenerateIcalForDeadlineRequest {
|
||||||
|
public function __construct(public Event $event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\GenerateIcalForDeadline;
|
||||||
|
|
||||||
|
class GenerateIcalForDeadlineResponse
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $icalContent,
|
||||||
|
public string $filename,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\ManualCertificateOfConductionCheck;
|
||||||
|
|
||||||
|
use App\Enumerations\EfzStatus;
|
||||||
|
use App\Mail\ParticipantCocMails\ParticipantCocCompleteMail;
|
||||||
|
use App\Mail\ParticipantCocMails\ParticipantCocInvalidMail;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
class ManualCertificateOfConductionCheckCommand {
|
||||||
|
function __construct(public ManualCertificateOfConductionCheckRequest $request)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public function execute() : ManualCertificateOfConductionCheckResponse {
|
||||||
|
$response = new ManualCertificateOfConductionCheckResponse();
|
||||||
|
|
||||||
|
|
||||||
|
$this->request->participant->efz_status = EfzStatus::EFZ_STATUS_CHECKED_VALID;
|
||||||
|
$this->request->participant->save();
|
||||||
|
$response->success = true;
|
||||||
|
|
||||||
|
Mail::to($this->request->participant->email_1)->send(new ParticipantCocCompleteMail(
|
||||||
|
participant: $this->request->participant,
|
||||||
|
));
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\ManualCertificateOfConductionCheck;
|
||||||
|
|
||||||
|
use App\Models\EventParticipant;
|
||||||
|
|
||||||
|
class ManualCertificateOfConductionCheckRequest {
|
||||||
|
function __construct(public EventParticipant $participant)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\ManualCertificateOfConductionCheck;
|
||||||
|
|
||||||
|
use App\Enumerations\EfzStatus;
|
||||||
|
|
||||||
|
class ManualCertificateOfConductionCheckResponse {
|
||||||
|
public bool $success;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\ParticipantPayment;
|
||||||
|
|
||||||
|
use App\Mail\ParticipantPaymentMails\ParticipantPaymentMissingPaymentMail;
|
||||||
|
use App\Mail\ParticipantPaymentMails\ParticipantPaymentOverpaidMail;
|
||||||
|
use App\Mail\ParticipantPaymentMails\ParticipantPaymentPaidMail;
|
||||||
|
use App\Providers\MissingPaymentProvider;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
class ParticipantPaymentCommand {
|
||||||
|
public function __construct(public ParticipantPaymentRequest $request) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : ParticipantPaymentResponse {
|
||||||
|
$response = new ParticipantPaymentResponse();
|
||||||
|
|
||||||
|
$this->request->participant->amount_paid = $this->request->amountPaid;
|
||||||
|
$this->request->participant->save();
|
||||||
|
|
||||||
|
$response->amountPaid = $this->request->participant->amount_paid;
|
||||||
|
$response->amountExpected = $this->request->participant->amount;
|
||||||
|
|
||||||
|
$amountToPay = MissingPaymentProvider::calculateMissingPayment(
|
||||||
|
amountPaid: $this->request->participant->amount_paid,
|
||||||
|
amountToPay: $this->request->participant->amount,
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case $amountToPay->getAmount() > 0:
|
||||||
|
Mail::to($this->request->participant->email_1)->send(new ParticipantPaymentMissingPaymentMail(
|
||||||
|
participant: $this->request->participant,
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($this->request->participant->email_2 !== null) {
|
||||||
|
Mail::to($this->request->participant->email_2)->send(new ParticipantPaymentMissingPaymentMail(
|
||||||
|
participant: $this->request->participant,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case $amountToPay->getAmount() < 0:
|
||||||
|
Mail::to($this->request->participant->email_1)->send(new ParticipantPaymentOverpaidMail(
|
||||||
|
participant: $this->request->participant,
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($this->request->participant->email_2 !== null) {
|
||||||
|
Mail::to($this->request->participant->email_2)->send(new ParticipantPaymentOverpaidMail(
|
||||||
|
participant: $this->request->participant,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Mail::to($this->request->participant->email_1)->send(new ParticipantPaymentPaidMail(
|
||||||
|
participant: $this->request->participant,
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($this->request->participant->email_2 !== null) {
|
||||||
|
Mail::to($this->request->participant->email_2)->send(new ParticipantPaymentPaidMail(
|
||||||
|
participant: $this->request->participant,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->participant = $this->request->participant;
|
||||||
|
$response->success = true;
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\ParticipantPayment;
|
||||||
|
|
||||||
|
use App\Models\EventParticipant;
|
||||||
|
use App\ValueObjects\Amount;
|
||||||
|
|
||||||
|
class ParticipantPaymentRequest {
|
||||||
|
public EventParticipant $participant;
|
||||||
|
public Amount $amountPaid;
|
||||||
|
|
||||||
|
public function __construct(EventParticipant $participant, Amount $amountPaid) {
|
||||||
|
$this->participant = $participant;
|
||||||
|
$this->amountPaid = $amountPaid;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\ParticipantPayment;
|
||||||
|
|
||||||
|
use App\Models\EventParticipant;
|
||||||
|
use App\ValueObjects\Amount;
|
||||||
|
|
||||||
|
class ParticipantPaymentResponse {
|
||||||
|
public bool $success;
|
||||||
|
public ?Amount $amountPaid;
|
||||||
|
public ?Amount $amountExpected;
|
||||||
|
public ?EventParticipant $participant;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->amountPaid = null;
|
||||||
|
$this->amountExpected = null;
|
||||||
|
$this->success = false;
|
||||||
|
$this->participant = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SendMissingPaymentMails;
|
||||||
|
|
||||||
|
use App\Mail\ParticipantPaymentMails\ParticipantPaymentMissingPaymentMail;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
class SendMissingPaymentMailsCommand {
|
||||||
|
function __construct(public SendMissingPaymentMailsRequest $request) {}
|
||||||
|
|
||||||
|
public function execute() : SendMissingPaymentMailsResponse {
|
||||||
|
$response = new SendMissingPaymentMailsResponse();
|
||||||
|
|
||||||
|
foreach ($this->request->eventParticipants->getParticipantsWithMissingPayments($this->request->event, $this->request->httpRequest) as $participant) {
|
||||||
|
$participantResource = $participant->toResource()->toArray($this->request->httpRequest);
|
||||||
|
if (!$participantResource['needs_payment']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mail::to($participant->email_1)->send(new ParticipantPaymentMissingPaymentMail(
|
||||||
|
participant: $participant,
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($participant->email_2 !== null && $participant->email_2 !== $participant->email_1) {
|
||||||
|
Mail::to($participant->email_2)->send(new ParticipantPaymentMissingPaymentMail(
|
||||||
|
participant: $participant,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->remindedParticipants++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->success = true;
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SendMissingPaymentMails;
|
||||||
|
|
||||||
|
use App\Models\Event;
|
||||||
|
use App\Repositories\EventParticipantRepository;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class SendMissingPaymentMailsRequest {
|
||||||
|
function __construct(
|
||||||
|
public Event $event,
|
||||||
|
public EventParticipantRepository $eventParticipants,
|
||||||
|
public Request $httpRequest
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SendMissingPaymentMails;
|
||||||
|
|
||||||
|
class SendMissingPaymentMailsResponse {
|
||||||
|
function __construct(
|
||||||
|
public bool $success = false,
|
||||||
|
public int $remindedParticipants = 0
|
||||||
|
) {}
|
||||||
|
}
|
||||||
18
app/Domains/Event/Actions/SetCostUnit/SetCostUnitCommand.php
Normal file
18
app/Domains/Event/Actions/SetCostUnit/SetCostUnitCommand.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SetCostUnit;
|
||||||
|
|
||||||
|
class SetCostUnitCommand {
|
||||||
|
private SetCostUnitRequest $request;
|
||||||
|
|
||||||
|
public function __construct(SetCostUnitRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : SetCostUnitResponse {
|
||||||
|
$response = new SetCostUnitResponse();
|
||||||
|
$this->request->event->cost_unit_id = $this->request->costUnit->id;
|
||||||
|
$response->success = $this->request->event->save();
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Domains/Event/Actions/SetCostUnit/SetCostUnitRequest.php
Normal file
16
app/Domains/Event/Actions/SetCostUnit/SetCostUnitRequest.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SetCostUnit;
|
||||||
|
|
||||||
|
use App\Models\CostUnit;
|
||||||
|
use App\Models\Event;
|
||||||
|
|
||||||
|
class SetCostUnitRequest {
|
||||||
|
public Event $event;
|
||||||
|
public CostUnit $costUnit;
|
||||||
|
|
||||||
|
public function __construct(Event $event, CostUnit $costUnit) {
|
||||||
|
$this->event = $event;
|
||||||
|
$this->costUnit = $costUnit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SetCostUnit;
|
||||||
|
|
||||||
|
class SetCostUnitResponse {
|
||||||
|
public bool $success;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SetParticipationFees;
|
||||||
|
|
||||||
|
use App\RelationModels\EventParticipationFee;
|
||||||
|
|
||||||
|
class SetParticipationFeesCommand {
|
||||||
|
private SetParticipationFeesRequest $request;
|
||||||
|
public function __construct(SetParticipationFeesRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : SetParticipationFeesResponse {
|
||||||
|
$response = new SetParticipationFeesResponse();
|
||||||
|
$this->request->event->sibling_reduction = $this->request->siblingReduction;
|
||||||
|
$this->request->event->save();
|
||||||
|
|
||||||
|
$this->cleanBefore();
|
||||||
|
|
||||||
|
$this->request->event->participationFee1()->associate(EventParticipationFee::create([
|
||||||
|
'tenant' => app('tenant')->slug,
|
||||||
|
'type' => $this->request->participationFeeFirst['type'],
|
||||||
|
'name' => $this->request->participationFeeFirst['name'],
|
||||||
|
'description' => $this->request->participationFeeFirst['description'],
|
||||||
|
'amount_standard' => $this->request->participationFeeFirst['amount_standard']->getAmount(),
|
||||||
|
'amount_reduced' => null !== $this->request->participationFeeFirst['amount_reduced'] ? $this->request->participationFeeFirst['amount_reduced']->getAmount() : null,
|
||||||
|
'amount_solidarity' => null !== $this->request->participationFeeFirst['amount_solidarity'] ? $this->request->participationFeeFirst['amount_solidarity']->getAmount() : null,
|
||||||
|
]))->save();
|
||||||
|
|
||||||
|
if ($this->request->participationFeeSecond !== null) {
|
||||||
|
$this->request->event->participationFee2()->associate(EventParticipationFee::create([
|
||||||
|
'tenant' => app('tenant')->slug,
|
||||||
|
'type' => $this->request->participationFeeSecond['type'],
|
||||||
|
'name' => $this->request->participationFeeSecond['name'],
|
||||||
|
'description' => $this->request->participationFeeSecond['description'],
|
||||||
|
'amount_standard' => $this->request->participationFeeSecond['amount_standard']->getAmount(),
|
||||||
|
'amount_reduced' => null !== $this->request->participationFeeSecond['amount_reduced'] ? $this->request->participationFeeSecond['amount_reduced']->getAmount() : null,
|
||||||
|
'amount_solidarity' => null !== $this->request->participationFeeSecond['amount_solidarity'] ? $this->request->participationFeeSecond['amount_solidarity']->getAmount() : null,
|
||||||
|
]))->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->request->participationFeeThird !== null) {
|
||||||
|
$this->request->event->participationFee3()->associate(EventParticipationFee::create([
|
||||||
|
'tenant' => app('tenant')->slug,
|
||||||
|
'type' => $this->request->participationFeeThird['type'],
|
||||||
|
'name' => $this->request->participationFeeThird['name'],
|
||||||
|
'description' => $this->request->participationFeeThird['description'],
|
||||||
|
'amount_standard' => $this->request->participationFeeThird['amount_standard']->getAmount(),
|
||||||
|
'amount_reduced' => null !== $this->request->participationFeeThird['amount_reduced'] ? $this->request->participationFeeThird['amount_reduced']->getAmount() : null,
|
||||||
|
'amount_solidarity' => null !== $this->request->participationFeeThird['amount_solidarity'] ? $this->request->participationFeeThird['amount_solidarity']->getAmount() : null,
|
||||||
|
]))->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->request->participationFeeFourth !== null) {
|
||||||
|
$this->request->event->participationFee4()->associate(EventParticipationFee::create([
|
||||||
|
'tenant' => app('tenant')->slug,
|
||||||
|
'type' => $this->request->participationFeeFourth['type'],
|
||||||
|
'name' => $this->request->participationFeeFourth['name'],
|
||||||
|
'description' => $this->request->participationFeeFourth['description'],
|
||||||
|
'amount_standard' => $this->request->participationFeeFourth['amount_standard']->getAmount(),
|
||||||
|
'amount_reduced' => null !== $this->request->participationFeeFourth['amount_reduced'] ? $this->request->participationFeeFourth['amount_reduced']->getAmount() : null,
|
||||||
|
'amount_solidarity' => null !== $this->request->participationFeeFourth['amount_solidarity'] ? $this->request->participationFeeFourth['amount_solidarity']->getAmount() : null,
|
||||||
|
]))->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->request->event->save();
|
||||||
|
$response->success = true;
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function cleanBefore() {
|
||||||
|
if ($this->request->event->participationFee1()->first() !== null) {
|
||||||
|
$this->request->event->participationFee1()->first()->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->request->event->participationFee2()->first() !== null) {
|
||||||
|
$this->request->event->participationFee2()->first()->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->request->event->participationFee3()->first() !== null) {
|
||||||
|
$this->request->event->participationFee3()->first()->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->request->event->participationFee4()->first() !== null) {
|
||||||
|
$this->request->event->participationFee4()->first()->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->request->event->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SetParticipationFees;
|
||||||
|
|
||||||
|
use App\Models\Event;
|
||||||
|
use App\RelationModels\EventParticipationFee;
|
||||||
|
|
||||||
|
class SetParticipationFeesRequest {
|
||||||
|
public Event $event;
|
||||||
|
public array $participationFeeFirst;
|
||||||
|
public ?array $participationFeeSecond;
|
||||||
|
public ?array $participationFeeThird;
|
||||||
|
public ?array $participationFeeFourth;
|
||||||
|
|
||||||
|
public bool $siblingReduction;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(Event $event, array $participationFeeFirst, bool $siblingReduction) {
|
||||||
|
$this->event = $event;
|
||||||
|
$this->participationFeeFirst = $participationFeeFirst;
|
||||||
|
$this->participationFeeSecond = null;
|
||||||
|
$this->participationFeeThird = null;
|
||||||
|
$this->participationFeeFourth = null;
|
||||||
|
$this->siblingReduction = $siblingReduction;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SetParticipationFees;
|
||||||
|
|
||||||
|
class SetParticipationFeesResponse {
|
||||||
|
public bool $success;
|
||||||
|
public function __construct() {
|
||||||
|
$this->success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SetParticipationState;
|
||||||
|
|
||||||
|
use App\Mail\ParticipantParticipationMails\EventSignUpSuccessfullMail;
|
||||||
|
use App\Mail\ParticipantParticipationMails\ParticipantSignOffMail;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
class SetParticipationStateCommand {
|
||||||
|
function __construct(private SetParticipationStateSignoffRequest|SetParticipationStateReSignonRequest $request) {}
|
||||||
|
|
||||||
|
public function execute() : SetParticipationStateResponse {
|
||||||
|
$response = new SetParticipationStateResponse();
|
||||||
|
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case $this->request instanceof SetParticipationStateSignoffRequest:
|
||||||
|
$this->request->participant->unregistered_at = $this->request->date;
|
||||||
|
$this->request->participant->save();
|
||||||
|
$response->success = true;
|
||||||
|
Mail::to($this->request->participant->email_1)->send(new ParticipantSignOffMail(
|
||||||
|
participant: $this->request->participant,
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($this->request->participant->email_2 !== null) {
|
||||||
|
Mail::to($this->request->participant->email_2)->send(new ParticipantSignOffMail(
|
||||||
|
participant: $this->request->participant,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case $this->request instanceof SetParticipationStateReSignonRequest:
|
||||||
|
$this->request->participant->unregistered_at = null;
|
||||||
|
$this->request->participant->save();
|
||||||
|
|
||||||
|
Mail::to($this->request->participant->email_1)->send(new EventSignUpSuccessfullMail(
|
||||||
|
participant: $this->request->participant,
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($this->request->participant->email_2 !== null) {
|
||||||
|
Mail::to($this->request->participant->email_2)->send(new EventSignUpSuccessfullMail(
|
||||||
|
participant: $this->request->participant,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SetParticipationState;
|
||||||
|
|
||||||
|
use App\Models\EventParticipant;
|
||||||
|
|
||||||
|
class SetParticipationStateReSignonRequest {
|
||||||
|
function __construct(public EventParticipant $participant) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SetParticipationState;
|
||||||
|
|
||||||
|
class SetParticipationStateResponse {
|
||||||
|
function __construct(public bool $success = false) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SetParticipationState;
|
||||||
|
|
||||||
|
use App\Models\EventParticipant;
|
||||||
|
|
||||||
|
class SetParticipationStateSignoffRequest {
|
||||||
|
function __construct(public EventParticipant $participant, public \DateTime $date) {
|
||||||
|
}
|
||||||
|
}
|
||||||
71
app/Domains/Event/Actions/SignUp/SignUpCommand.php
Normal file
71
app/Domains/Event/Actions/SignUp/SignUpCommand.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SignUp;
|
||||||
|
|
||||||
|
use App\Enumerations\EatingHabit;
|
||||||
|
use App\Enumerations\EfzStatus;
|
||||||
|
use App\ValueObjects\Age;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class SignUpCommand {
|
||||||
|
public function __construct(public SignUpRequest $request) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : SignUpResponse {
|
||||||
|
$response = new SignUpResponse();
|
||||||
|
|
||||||
|
$eatingHabit = match ($this->request->eating_habit) {
|
||||||
|
'vegan' => EatingHabit::EATING_HABIT_VEGAN,
|
||||||
|
'vegetarian' => EatingHabit::EATING_HABIT_VEGETARIAN,
|
||||||
|
default => EatingHabit::EATING_HABIT_OMNIVOR,
|
||||||
|
};
|
||||||
|
|
||||||
|
$participantAge = new Age($this->request->birthday);
|
||||||
|
$response->participant = $this->request->event->participants()->create(
|
||||||
|
[
|
||||||
|
'tenant' => $this->request->event->tenant,
|
||||||
|
'user_id' => $this->request->user_id,
|
||||||
|
'identifier' => Str::random(10),
|
||||||
|
'firstname' => $this->request->firstname,
|
||||||
|
'lastname' => $this->request->lastname,
|
||||||
|
'nickname' => $this->request->nickname,
|
||||||
|
'participation_type' => $this->request->participationType,
|
||||||
|
'local_group' => $this->request->localGroup->slug,
|
||||||
|
'birthday' => $this->request->birthday,
|
||||||
|
'address_1' => $this->request->address_1,
|
||||||
|
'address_2' => $this->request->address_2,
|
||||||
|
'postcode' => $this->request->postcode,
|
||||||
|
'city' => $this->request->city,
|
||||||
|
'email_1' => $this->request->email_1,
|
||||||
|
'email_2' => $this->request->email_2,
|
||||||
|
'phone_1' => $this->request->phone_1,
|
||||||
|
'phone_2' => $this->request->phone_2,
|
||||||
|
'contact_person' => $this->request->contact_person,
|
||||||
|
'allergies' => $this->request->allergies,
|
||||||
|
'intolerances' => $this->request->intolerances,
|
||||||
|
'medications' => $this->request->medications,
|
||||||
|
'tetanus_vaccination' => $this->request->tetanus_vaccination,
|
||||||
|
'eating_habit' => $eatingHabit,
|
||||||
|
'swimming_permission' => $participantAge->isfullAged() ? 'SWIMMING_PERMISSION_ALLOWED' : $this->request->swimming_permission,
|
||||||
|
'first_aid_permission' => $participantAge->isfullAged() ? 'FIRST_AID_PERMISSION_ALLOWED' : $this->request->first_aid_permission,
|
||||||
|
'foto_socialmedia' => $this->request->foto_socialmedia,
|
||||||
|
'foto_print' => $this->request->foto_print,
|
||||||
|
'foto_webseite' => $this->request->foto_webseite,
|
||||||
|
'foto_partner' => $this->request->foto_partner,
|
||||||
|
'foto_intern' => $this->request->foto_intern,
|
||||||
|
'arrival_date' => $this->request->arrival,
|
||||||
|
'departure_date' => $this->request->departure,
|
||||||
|
'arrival_eating' => $this->request->arrival_eating,
|
||||||
|
'departure_eating' => $this->request->departure_eating,
|
||||||
|
'notes' => $this->request->notes,
|
||||||
|
'amount' => $this->request->amount,
|
||||||
|
'payment_purpose' => $this->request->event->name . ' - Beitrag ' . $this->request->firstname . ' ' . $this->request->lastname,
|
||||||
|
'efz_status' => $participantAge->isfullAged() ? EfzStatus::EFZ_STATUS_NOT_CHECKED : EfzStatus::EFZ_STATUS_NOT_REQUIRED,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$response->success = true;
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
50
app/Domains/Event/Actions/SignUp/SignUpRequest.php
Normal file
50
app/Domains/Event/Actions/SignUp/SignUpRequest.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SignUp;
|
||||||
|
|
||||||
|
use App\Enumerations\ParticipationType;
|
||||||
|
use App\Models\Event;
|
||||||
|
use App\Models\Tenant;
|
||||||
|
use App\ValueObjects\Amount;
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
|
class SignUpRequest {
|
||||||
|
function __construct(
|
||||||
|
public Event $event,
|
||||||
|
public ?int $user_id,
|
||||||
|
public string $firstname,
|
||||||
|
public string $lastname,
|
||||||
|
public ?string $nickname,
|
||||||
|
public string $participationType,
|
||||||
|
public Tenant $localGroup,
|
||||||
|
public DateTime $birthday,
|
||||||
|
public string $address_1,
|
||||||
|
public string $address_2,
|
||||||
|
public string $postcode,
|
||||||
|
public string $city,
|
||||||
|
public string $email_1,
|
||||||
|
public ?string $phone_1,
|
||||||
|
public ?string $email_2,
|
||||||
|
public ?string $phone_2,
|
||||||
|
public ?string $contact_person,
|
||||||
|
public ?string $allergies,
|
||||||
|
public ?string $intolerances,
|
||||||
|
public ?string $medications,
|
||||||
|
public ?DateTime $tetanus_vaccination,
|
||||||
|
public string $eating_habit,
|
||||||
|
public ?string $swimming_permission,
|
||||||
|
public ?string $first_aid_permission,
|
||||||
|
public bool $foto_socialmedia,
|
||||||
|
public bool $foto_print,
|
||||||
|
public bool $foto_webseite,
|
||||||
|
public bool $foto_partner,
|
||||||
|
public bool $foto_intern,
|
||||||
|
public DateTime $arrival,
|
||||||
|
public DateTime $departure,
|
||||||
|
public int $arrival_eating,
|
||||||
|
public int $departure_eating,
|
||||||
|
public ?string $notes,
|
||||||
|
public Amount $amount
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Domains/Event/Actions/SignUp/SignUpResponse.php
Normal file
16
app/Domains/Event/Actions/SignUp/SignUpResponse.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\SignUp;
|
||||||
|
|
||||||
|
use App\Models\EventParticipant;
|
||||||
|
|
||||||
|
class SignUpResponse {
|
||||||
|
public bool $success;
|
||||||
|
public ?EventParticipant $participant;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->success = false;
|
||||||
|
$this->participant = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
43
app/Domains/Event/Actions/UpdateEvent/UpdateEventCommand.php
Normal file
43
app/Domains/Event/Actions/UpdateEvent/UpdateEventCommand.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\UpdateEvent;
|
||||||
|
|
||||||
|
class UpdateEventCommand {
|
||||||
|
public UpdateEventRequest $request;
|
||||||
|
public function __construct(UpdateEventRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : UpdateEventResponse {
|
||||||
|
$response = new UpdateEventResponse();
|
||||||
|
|
||||||
|
$this->request->event->name = $this->request->eventName;
|
||||||
|
$this->request->event->location = $this->request->eventLocation;
|
||||||
|
$this->request->event->postal_code = $this->request->postalCode;
|
||||||
|
$this->request->event->email = $this->request->email;
|
||||||
|
$this->request->event->early_bird_end = $this->request->earlyBirdEnd;
|
||||||
|
$this->request->event->registration_final_end = $this->request->registrationFinalEnd;
|
||||||
|
$this->request->event->alcoholics_age = $this->request->alcoholicsAge;
|
||||||
|
$this->request->event->support_per_person = $this->request->supportPerPerson;
|
||||||
|
$this->request->event->support_flat = $this->request->flatSupport;
|
||||||
|
$this->request->event->send_weekly_report = $this->request->sendWeeklyReports;
|
||||||
|
$this->request->event->registration_allowed = $this->request->registrationAllowed;
|
||||||
|
$this->request->event->save();
|
||||||
|
|
||||||
|
$this->request->event->resetAllowedEatingHabits();
|
||||||
|
$this->request->event->resetContributingLocalGroups();
|
||||||
|
|
||||||
|
foreach($this->request->eatingHabits as $eatingHabit) {
|
||||||
|
$this->request->event->eatingHabits()->attach($eatingHabit);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($this->request->contributingLocalGroups as $contributingLocalGroup) {
|
||||||
|
$this->request->event->localGroups()->attach($contributingLocalGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->request->event->save();
|
||||||
|
$response->success = true;
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
41
app/Domains/Event/Actions/UpdateEvent/UpdateEventRequest.php
Normal file
41
app/Domains/Event/Actions/UpdateEvent/UpdateEventRequest.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\UpdateEvent;
|
||||||
|
|
||||||
|
use App\Models\Event;
|
||||||
|
use App\ValueObjects\Amount;
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
|
class UpdateEventRequest {
|
||||||
|
public Event $event;
|
||||||
|
public string $eventName;
|
||||||
|
public string $eventLocation;
|
||||||
|
public string $postalCode;
|
||||||
|
public string $email;
|
||||||
|
public DateTime $earlyBirdEnd;
|
||||||
|
public DateTime $registrationFinalEnd;
|
||||||
|
public int $alcoholicsAge;
|
||||||
|
public bool $sendWeeklyReports;
|
||||||
|
public bool $registrationAllowed;
|
||||||
|
public Amount $flatSupport;
|
||||||
|
public Amount $supportPerPerson;
|
||||||
|
public array $contributingLocalGroups;
|
||||||
|
public array $eatingHabits;
|
||||||
|
|
||||||
|
public function __construct(Event $event, string $eventName, string $eventLocation, string $postalCode, string $email, DateTime $earlyBirdEnd, DateTime $registrationFinalEnd, int $alcoholicsAge, bool $sendWeeklyReports, bool $registrationAllowed, Amount $flatSupport, Amount $supportPerPerson, array $contributingLocalGroups, array $eatingHabits) {
|
||||||
|
$this->event = $event;
|
||||||
|
$this->eventName = $eventName;
|
||||||
|
$this->eventLocation = $eventLocation;
|
||||||
|
$this->postalCode = $postalCode;
|
||||||
|
$this->email = $email;
|
||||||
|
$this->earlyBirdEnd = $earlyBirdEnd;
|
||||||
|
$this->registrationFinalEnd = $registrationFinalEnd;
|
||||||
|
$this->alcoholicsAge = $alcoholicsAge;
|
||||||
|
$this->sendWeeklyReports = $sendWeeklyReports;
|
||||||
|
$this->registrationAllowed = $registrationAllowed;
|
||||||
|
$this->flatSupport = $flatSupport;
|
||||||
|
$this->supportPerPerson = $supportPerPerson;
|
||||||
|
$this->contributingLocalGroups = $contributingLocalGroups;
|
||||||
|
$this->eatingHabits = $eatingHabits;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\UpdateEvent;
|
||||||
|
|
||||||
|
class UpdateEventResponse {
|
||||||
|
public bool $success;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\UpdateManagers;
|
||||||
|
|
||||||
|
class UpdateManagersCommand {
|
||||||
|
private UpdateManagersRequest $request;
|
||||||
|
|
||||||
|
public function __construct(UpdateManagersRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : UpdateManagersResponse {
|
||||||
|
$response = new UpdateManagersResponse();
|
||||||
|
$this->request->event->resetMangers();
|
||||||
|
|
||||||
|
foreach ($this->request->managers as $manager) {
|
||||||
|
$this->request->event->eventManagers()->attach($manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->request->event->save();
|
||||||
|
$response->success = true;
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\UpdateManagers;
|
||||||
|
|
||||||
|
use App\Models\Event;
|
||||||
|
|
||||||
|
class UpdateManagersRequest {
|
||||||
|
public Event $event;
|
||||||
|
public array $managers;
|
||||||
|
|
||||||
|
public function __construct(Event $event, array $managers) {
|
||||||
|
$this->managers = $managers;
|
||||||
|
$this->event = $event;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\UpdateManagers;
|
||||||
|
|
||||||
|
class UpdateManagersResponse {
|
||||||
|
public bool $success;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\UpdateParticipant;
|
||||||
|
|
||||||
|
use App\Enumerations\EfzStatus;
|
||||||
|
use App\Mail\ParticipantCocMails\ParticipantCocCompleteMail;
|
||||||
|
use App\Mail\ParticipantCocMails\ParticipantCocInvalidMail;
|
||||||
|
use App\Mail\ParticipantPaymentMails\ParticipantPaymentMissingPaymentMail;
|
||||||
|
use App\Mail\ParticipantPaymentMails\ParticipantPaymentOverpaidMail;
|
||||||
|
use App\Mail\ParticipantPaymentMails\ParticipantPaymentPaidMail;
|
||||||
|
use App\Models\EventParticipant;
|
||||||
|
use App\Providers\MissingPaymentProvider;
|
||||||
|
use App\ValueObjects\Amount;
|
||||||
|
use DateTime;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
class UpdateParticipantCommand {
|
||||||
|
private UpdateParticipantResponse $response;
|
||||||
|
function __construct(public UpdateParticipantRequest $request) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() {
|
||||||
|
$this->response = new UpdateParticipantResponse();
|
||||||
|
|
||||||
|
$p = clone($this->request->participant);
|
||||||
|
|
||||||
|
$p->firstname = $this->request->firstname;
|
||||||
|
$p->lastname = $this->request->lastname;
|
||||||
|
$p->nickname = $this->request->nickname;
|
||||||
|
$p->address_1 = $this->request->address_1;
|
||||||
|
$p->address_2 = $this->request->address_2;
|
||||||
|
$p->postcode = $this->request->postcode;
|
||||||
|
$p->city = $this->request->city;
|
||||||
|
$p->local_group = $this->request->localgroup;
|
||||||
|
$p->birthday = DateTime::createFromFormat('Y-m-d', $this->request->birthday);
|
||||||
|
$p->email_1 = $this->request->email_1;
|
||||||
|
$p->phone_1 = $this->request->phone_1;
|
||||||
|
$p->contact_person = $this->request->contact_person;
|
||||||
|
$p->email_2 = $this->request->email_2;
|
||||||
|
$p->phone_2 = $this->request->phone_2;
|
||||||
|
$p->arrival_date = DateTime::createFromFormat('Y-m-d', $this->request->arrival);
|
||||||
|
$p->departure_date = DateTime::createFromFormat('Y-m-d', $this->request->departure);
|
||||||
|
$p->participation_type = $this->request->participationType;
|
||||||
|
$p->eating_habit = $this->request->eatingHabit;
|
||||||
|
$p->allergies = $this->request->allergies;
|
||||||
|
$p->intolerances = $this->request->intolerances;
|
||||||
|
$p->medications = $this->request->medications;
|
||||||
|
$p->first_aid_permission = $this->request->extendedFirstAid;
|
||||||
|
$p->swimming_permission = $this->request->swimmingPermission;
|
||||||
|
$p->tetanus_vaccination = $this->request->tetanusVaccination !== null
|
||||||
|
? DateTime::createFromFormat('Y-m-d', $this->request->tetanusVaccination)
|
||||||
|
: null;
|
||||||
|
$p->notes = $this->request->notes;
|
||||||
|
$p->amount_paid = Amount::fromString($this->request->amountPaid);
|
||||||
|
$p->amount = Amount::fromString($this->request->amountExpected);
|
||||||
|
$p->efz_status = $this->request->cocStatus;
|
||||||
|
|
||||||
|
if (
|
||||||
|
MissingPaymentProvider::calculateMissingPayment(amountPaid: $p->amount_paid, amountToPay: $p->amount)->getAmount()
|
||||||
|
!==
|
||||||
|
MissingPaymentProvider::calculateMissingPayment(amountPaid: $this->request->participant->amount_paid, amountToPay: $this->request->participant->amount)->getAmount()
|
||||||
|
) {
|
||||||
|
$this->handleAmountChanges($p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$p->efz_status !== $this->request->participant->efz_status
|
||||||
|
) {
|
||||||
|
$this->handleCocStatusChange($p);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$p->save();
|
||||||
|
$this->response->success = true;
|
||||||
|
$this->response->participant = $p;
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleAmountChanges(EventParticipant $participant) {
|
||||||
|
$this->response->amountPaid = $participant->amount_paid;
|
||||||
|
$this->response->amountExpected = $participant->amount;
|
||||||
|
|
||||||
|
$amountToPay = MissingPaymentProvider::calculateMissingPayment(
|
||||||
|
amountPaid: $participant->amount_paid,
|
||||||
|
amountToPay: $participant->amount,
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case $amountToPay->getAmount() > 0:
|
||||||
|
Mail::to($participant->email_1)->send(new ParticipantPaymentMissingPaymentMail(
|
||||||
|
participant: $participant,
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($participant->email_2 !== null) {
|
||||||
|
Mail::to($participant->email_2)->send(new ParticipantPaymentMissingPaymentMail(
|
||||||
|
participant: $participant,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case $amountToPay->getAmount() < 0:
|
||||||
|
Mail::to($participant->email_1)->send(new ParticipantPaymentOverpaidMail(
|
||||||
|
participant: $participant,
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($participant->email_2 !== null) {
|
||||||
|
Mail::to($participant->email_2)->send(new ParticipantPaymentOverpaidMail(
|
||||||
|
participant: $participant,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Mail::to($participant->email_1)->send(new ParticipantPaymentPaidMail(
|
||||||
|
participant: $participant,
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($participant->email_2 !== null) {
|
||||||
|
Mail::to($participant->email_2)->send(new ParticipantPaymentPaidMail(
|
||||||
|
participant: $participant,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleCocStatusChange(EventParticipant $participant) {
|
||||||
|
$this->response->cocStatus = $participant->efzStatus()->first();
|
||||||
|
|
||||||
|
switch ($participant->efzStatus()->first()->slug) {
|
||||||
|
case EfzStatus::EFZ_STATUS_CHECKED_VALID:
|
||||||
|
case EfzStatus::EFZ_STATUS_NOT_REQUIRED:
|
||||||
|
Mail::to($participant->email_1)->send(new ParticipantCocCompleteMail(
|
||||||
|
participant: $participant,
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EfzStatus::EFZ_STATUS_CHECKED_INVALID:
|
||||||
|
Mail::to($participant->email_1)->send(new ParticipantCocInvalidMail(
|
||||||
|
participant: $participant,
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\UpdateParticipant;
|
||||||
|
|
||||||
|
use App\Models\EventParticipant;
|
||||||
|
|
||||||
|
class UpdateParticipantRequest {
|
||||||
|
function __construct(
|
||||||
|
public EventParticipant $participant,
|
||||||
|
public string $firstname,
|
||||||
|
public string $lastname,
|
||||||
|
public ?string $nickname,
|
||||||
|
public string $address_1,
|
||||||
|
public ?string $address_2,
|
||||||
|
public string $postcode,
|
||||||
|
public string $city,
|
||||||
|
public string $localgroup,
|
||||||
|
public string $birthday,
|
||||||
|
public string $email_1,
|
||||||
|
public string $phone_1,
|
||||||
|
public string $contact_person,
|
||||||
|
public ?string $email_2,
|
||||||
|
public ?string $phone_2,
|
||||||
|
public string $arrival,
|
||||||
|
public string $departure,
|
||||||
|
public string $participationType,
|
||||||
|
public string $eatingHabit,
|
||||||
|
public ?string $allergies,
|
||||||
|
public ?string $intolerances,
|
||||||
|
public ?string $medications,
|
||||||
|
public string $extendedFirstAid,
|
||||||
|
public string $swimmingPermission,
|
||||||
|
public ?string $tetanusVaccination,
|
||||||
|
public ?string $notes,
|
||||||
|
public string $amountPaid,
|
||||||
|
public string $amountExpected,
|
||||||
|
public string $cocStatus,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Actions\UpdateParticipant;
|
||||||
|
|
||||||
|
use App\Enumerations\EfzStatus;
|
||||||
|
use App\Models\EventParticipant;
|
||||||
|
use App\ValueObjects\Amount;
|
||||||
|
|
||||||
|
class UpdateParticipantResponse {
|
||||||
|
public bool $success;
|
||||||
|
public ?EfzStatus $cocStatus;
|
||||||
|
public ?Amount $amountPaid;
|
||||||
|
public ?Amount $amountExpected;
|
||||||
|
public ?EventParticipant $participant;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->success = false;
|
||||||
|
$this->cocStatus =null;
|
||||||
|
$this->amountPaid = null;
|
||||||
|
$this->amountExpected = null;
|
||||||
|
$this->participant = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
app/Domains/Event/Controllers/AvailableEventsController.php
Normal file
22
app/Domains/Event/Controllers/AvailableEventsController.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Controllers;
|
||||||
|
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Inertia\Response;
|
||||||
|
|
||||||
|
class AvailableEventsController extends CommonController
|
||||||
|
{
|
||||||
|
public function __invoke(Request $request) : Response {
|
||||||
|
$events = [];
|
||||||
|
foreach ($this->events->getAvailable(false) as $event) {
|
||||||
|
$events[] = $event->toResource()->toArray($request);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
$inertiaProvider = new InertiaProvider('Event/ListAvailable', ['events' => $events]);
|
||||||
|
return $inertiaProvider->render();
|
||||||
|
}
|
||||||
|
}
|
||||||
100
app/Domains/Event/Controllers/CreateController.php
Normal file
100
app/Domains/Event/Controllers/CreateController.php
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\CostUnit\Actions\CreateCostUnit\CreateCostUnitCommand;
|
||||||
|
use App\Domains\CostUnit\Actions\CreateCostUnit\CreateCostUnitRequest;
|
||||||
|
use App\Domains\Event\Actions\CreateEvent\CreateEventCommand;
|
||||||
|
use App\Domains\Event\Actions\CreateEvent\CreateEventRequest;
|
||||||
|
use App\Domains\Event\Actions\SetCostUnit\SetCostUnitCommand;
|
||||||
|
use App\Domains\Event\Actions\SetCostUnit\SetCostUnitRequest;
|
||||||
|
use App\Enumerations\CostUnitType;
|
||||||
|
use App\Enumerations\ParticipationFeeType;
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Resources\EventResource;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use App\ValueObjects\Amount;
|
||||||
|
use DateTime;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class CreateController extends CommonController {
|
||||||
|
public function __invoke() {
|
||||||
|
return new InertiaProvider('Event/Create', [
|
||||||
|
'emailAddress' => auth()->user()->email,
|
||||||
|
'eventAccount' => $this->tenant->account_name,
|
||||||
|
'eventIban' => $this->tenant->account_iban,
|
||||||
|
'eventPayPerDay' => $this->tenant->slug === 'lv' ? true : false,
|
||||||
|
'participationFeeType' => $this->tenant->slug === 'lv' ?
|
||||||
|
ParticipationFeeType::PARTICIPATION_FEE_TYPE_FIXED :
|
||||||
|
ParticipationFeeType::PARTICIPATION_FEE_TYPE_SOLIDARITY,
|
||||||
|
])->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doCreate(Request $request) : JsonResponse {
|
||||||
|
|
||||||
|
$eventBegin = DateTime::createFromFormat('Y-m-d', $request->input('eventBegin'));
|
||||||
|
$eventEnd = DateTime::createFromFormat('Y-m-d', $request->input('eventEnd'));
|
||||||
|
$eventEarlyBirdEnd = DateTime::createFromFormat('Y-m-d', $request->input('eventEarlyBirdEnd'));
|
||||||
|
$registrationFinalEnd = DateTime::createFromFormat('Y-m-d', $request->input('eventRegistrationFinalEnd'));
|
||||||
|
$participationFeeType = ParticipationFeeType::where('slug', $request->input('eventParticipationFeeType'))->first();
|
||||||
|
$payPerDay = $request->input('eventPayPerDay');
|
||||||
|
$payDirect = $request->input('eventPayDirectly');
|
||||||
|
|
||||||
|
$billingDeadline = $eventEnd->modify('+1 month');
|
||||||
|
|
||||||
|
$createRequest = new CreateEventRequest(
|
||||||
|
$request->input('eventName'),
|
||||||
|
$request->input('eventLocation'),
|
||||||
|
$request->input('eventPostalCode'),
|
||||||
|
$request->input('eventEmail'),
|
||||||
|
$eventBegin,
|
||||||
|
$eventEnd,
|
||||||
|
$eventEarlyBirdEnd,
|
||||||
|
$registrationFinalEnd,
|
||||||
|
$request->input('eventEarlyBirdEndAmountIncrease'),
|
||||||
|
$participationFeeType,
|
||||||
|
$request->input('eventAccount'),
|
||||||
|
$request->input('eventIban'),
|
||||||
|
$payPerDay,
|
||||||
|
$payDirect
|
||||||
|
);
|
||||||
|
|
||||||
|
$wasSuccessful = false;
|
||||||
|
|
||||||
|
$createCommand = new CreateEventCommand($createRequest);
|
||||||
|
$result = $createCommand->execute();
|
||||||
|
if ($result->success) {
|
||||||
|
$createCostUnitRequest = new CreateCostUnitRequest(
|
||||||
|
$result->event->name,
|
||||||
|
CostUnitType::COST_UNIT_TYPE_EVENT,
|
||||||
|
Amount::fromString('0,25'),
|
||||||
|
true,
|
||||||
|
$billingDeadline
|
||||||
|
);
|
||||||
|
|
||||||
|
$createCostUnitCommand = new CreateCostUnitCommand($createCostUnitRequest);
|
||||||
|
$costUnitResponse = $createCostUnitCommand->execute();
|
||||||
|
|
||||||
|
if ($costUnitResponse->success) {
|
||||||
|
$costUnitUpdateRequest = new SetCostUnitRequest($result->event, $costUnitResponse->costUnit);
|
||||||
|
$costUnitUpdateCommand = new SetCostUnitCommand($costUnitUpdateRequest);
|
||||||
|
$costUnitSetResponse = $costUnitUpdateCommand->execute();
|
||||||
|
$wasSuccessful = $costUnitSetResponse->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($wasSuccessful) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'event' => new EventResource($costUnitUpdateRequest->event)->toArray($request)
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Die Veranstaltung konnte nicht angelegt werden.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
198
app/Domains/Event/Controllers/DetailsController.php
Normal file
198
app/Domains/Event/Controllers/DetailsController.php
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\Event\Actions\SetParticipationFees\SetParticipationFeesCommand;
|
||||||
|
use App\Domains\Event\Actions\SetParticipationFees\SetParticipationFeesRequest;
|
||||||
|
use App\Domains\Event\Actions\UpdateEvent\UpdateEventCommand;
|
||||||
|
use App\Domains\Event\Actions\UpdateEvent\UpdateEventRequest;
|
||||||
|
use App\Domains\Event\Actions\UpdateManagers\UpdateManagersCommand;
|
||||||
|
use App\Domains\Event\Actions\UpdateManagers\UpdateManagersRequest;
|
||||||
|
use App\Enumerations\ParticipationFeeType;
|
||||||
|
use App\Enumerations\ParticipationType;
|
||||||
|
use App\Models\EventParticipant;
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Providers\PdfGenerateAndDownloadProvider;
|
||||||
|
use App\Resources\EventResource;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use App\ValueObjects\Amount;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
use function Symfony\Component\String\b;
|
||||||
|
|
||||||
|
class DetailsController extends CommonController {
|
||||||
|
public function __invoke(string $eventId) {
|
||||||
|
$event = $this->events->getByIdentifier($eventId);
|
||||||
|
return new InertiaProvider('Event/Details', ['event' => $event])->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function summary(int $eventId, Request $request) : JsonResponse {
|
||||||
|
$event = $this->events->getById($eventId);
|
||||||
|
return response()->json(['event' => $event->toResource()->toArray($request)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateCommonSettings(int $eventId, Request $request) : JsonResponse {
|
||||||
|
$event = $this->events->getById($eventId);
|
||||||
|
|
||||||
|
$earlyBirdEnd = \DateTime::createFromFormat('Y-m-d', $request->input('earlyBirdEnd'));
|
||||||
|
$registrationFinalEnd = \DateTime::createFromFormat('Y-m-d', $request->input('registrationFinalEnd'));
|
||||||
|
$flatSupport = Amount::fromString($request->input('flatSupport'));
|
||||||
|
$supportPerPerson = Amount::fromString($request->input('supportPerson'));
|
||||||
|
|
||||||
|
$contributinLocalGroups = $request->input('contributingLocalGroups');
|
||||||
|
$eatingHabits = $request->input('eatingHabits');
|
||||||
|
|
||||||
|
$eventUpdateRequest = new UpdateEventRequest(
|
||||||
|
$event,
|
||||||
|
$request->input('eventName'),
|
||||||
|
$request->input('eventLocation'),
|
||||||
|
$request->input('postalCode'),
|
||||||
|
$request->input('email'),
|
||||||
|
$earlyBirdEnd,
|
||||||
|
$registrationFinalEnd,
|
||||||
|
$request->input('alcoholicsAge'),
|
||||||
|
$request->input('sendWeeklyReports'),
|
||||||
|
$request->input('registrationAllowed'),
|
||||||
|
$flatSupport,
|
||||||
|
$supportPerPerson,
|
||||||
|
$contributinLocalGroups,
|
||||||
|
$eatingHabits
|
||||||
|
);
|
||||||
|
|
||||||
|
$eventUpdateCommand = new UpdateEventCommand($eventUpdateRequest);
|
||||||
|
$response = $eventUpdateCommand->execute();
|
||||||
|
|
||||||
|
return response()->json(['status' => $response->success ? 'success' : 'error']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateEventManagers(int $eventId, Request $request) : JsonResponse {
|
||||||
|
$event = $this->events->getById($eventId);
|
||||||
|
|
||||||
|
$updateEventManagersRequest = new UpdateManagersRequest($event, $request->input('selectedManagers'));
|
||||||
|
$updateEventManagersCommand = new UpdateManagersCommand($updateEventManagersRequest);
|
||||||
|
$response = $updateEventManagersCommand->execute();
|
||||||
|
return response()->json(['status' => $response->success ? 'success' : 'error']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateParticipationFees(int $eventId, Request $request) : JsonResponse {
|
||||||
|
$event = $this->events->getById($eventId);
|
||||||
|
|
||||||
|
$participationFeeFirst = [
|
||||||
|
'type' => ParticipationType::PARTICIPATION_TYPE_PARTICIPANT,
|
||||||
|
'name' => 'Teilnehmer',
|
||||||
|
'description' => $request->input('pft_1_description'),
|
||||||
|
'amount_standard' => Amount::fromString($request->input('pft_1_amount_standard')),
|
||||||
|
'amount_reduced' => null !== $request->input('pft_1_amount_reduced') ? Amount::fromString($request->input('pft_1_amount_reduced')) : null,
|
||||||
|
'amount_solidarity' => null !== $request->input('pft_1_amount_solidarity') ? Amount::fromString($request->input('pft_1_amount_solidarity')) : null
|
||||||
|
];
|
||||||
|
|
||||||
|
$siblingReduction = $request->input('sibling_reduction') ?? false;
|
||||||
|
$participationFeeRequest = new SetParticipationFeesRequest($event, $participationFeeFirst, $siblingReduction);
|
||||||
|
|
||||||
|
if ($request->input('pft_2_active')) {
|
||||||
|
$participationFeeRequest->participationFeeSecond = [
|
||||||
|
'type' => ParticipationType::PARTICIPATION_TYPE_TEAM,
|
||||||
|
'name' => $event->participation_fee_type === ParticipationFeeType::PARTICIPATION_FEE_TYPE_FIXED ? 'Kernteam' : 'Solidaritätsbeitrag',
|
||||||
|
'description' => $request->input('pft_2_description'),
|
||||||
|
'amount_standard' => Amount::fromString($request->input('pft_2_amount_standard')),
|
||||||
|
'amount_reduced' => null !== $request->input('pft_2_amount_reduced') ? Amount::fromString($request->input('pft_2_amount_reduced')) : null,
|
||||||
|
'amount_solidarity' => null !== $request->input('pft_2_amount_solidarity') ? Amount::fromString($request->input('pft_2_amount_solidarity')) : null
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->input('pft_3_active')) {
|
||||||
|
$participationFeeRequest->participationFeeThird = [
|
||||||
|
'type' => ParticipationType::PARTICIPATION_TYPE_VOLUNTEER,
|
||||||
|
'name' => $event->participation_fee_type === ParticipationFeeType::PARTICIPATION_FEE_TYPE_FIXED ? 'Unterstützende' : 'Reduzierter Beitrag',
|
||||||
|
'description' => $event->participation_fee_type !== ParticipationFeeType::PARTICIPATION_FEE_TYPE_SOLIDARITY ? $request->input('pft_3_description') : 'Nach Verfügbarkeit',
|
||||||
|
'amount_standard' => Amount::fromString($request->input('pft_3_amount_standard')),
|
||||||
|
'amount_reduced' => null !== $request->input('pft_3_amount_reduced') ? Amount::fromString($request->input('pft_3_amount_reduced')) : null,
|
||||||
|
'amount_solidarity' => null !== $request->input('pft_3_amount_solidarity') ? Amount::fromString($request->input('pft_3_amount_solidarity')) : null
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->input('pft_4_active') && $event->participation_fee_type === ParticipationFeeType::PARTICIPATION_FEE_TYPE_FIXED) {
|
||||||
|
$participationFeeRequest->participationFeeFourth = [
|
||||||
|
'type' => ParticipationType::PARTICIPATION_TYPE_OTHER,
|
||||||
|
'name' => 'Sonstige',
|
||||||
|
'description' => $request->input('pft_4_description'),
|
||||||
|
'amount_standard' => Amount::fromString($request->input('pft_4_amount_standard')),
|
||||||
|
'amount_reduced' => null !== $request->input('pft_4_amount_reduced') ? Amount::fromString($request->input('pft_4_amount_reduced')) : null,
|
||||||
|
'amount_solidarity' => null !== $request->input('pft_4_amount_solidarity') ? Amount::fromString($request->input('pft_4_amount_solidarity')) : null
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$participationFeeCommand = new SetParticipationFeesCommand($participationFeeRequest);
|
||||||
|
$response = $participationFeeCommand->execute();
|
||||||
|
|
||||||
|
return response()->json(['status' => $response->success ? 'success' : 'error']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadPdfList(string $eventId, string $listType, Request $request): Response
|
||||||
|
{
|
||||||
|
$event = $this->events->getByIdentifier($eventId);
|
||||||
|
|
||||||
|
$participants = $this->eventParticipants->getForList($event, $request);
|
||||||
|
$kitchenOverview = $this->eventParticipants->getKitchenOverview($event);
|
||||||
|
$html = view('pdfs.' . $listType, [
|
||||||
|
'event' => $event->name,
|
||||||
|
'eventStart' => $event->start_date,
|
||||||
|
'eventEnd' => $event->end_date,
|
||||||
|
'rows' => $participants,
|
||||||
|
'kitchenRequirements' => $kitchenOverview,
|
||||||
|
'participantsForKitchenList' => $this->eventParticipants->getParticipantsWithIntolerances($event, $request),
|
||||||
|
])->render();
|
||||||
|
|
||||||
|
$pdf = PdfGenerateAndDownloadProvider::fromHtml(
|
||||||
|
$html,
|
||||||
|
'landscape'
|
||||||
|
);
|
||||||
|
|
||||||
|
return response($pdf, 200, [
|
||||||
|
'Content-Type' => 'application/pdf',
|
||||||
|
'Content-Disposition' => 'attachment; filename="' . $listType .'.pdf"',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadCsvList(string $eventId, string $listType, Request $request): Response
|
||||||
|
{
|
||||||
|
$event = $this->events->getByIdentifier($eventId);
|
||||||
|
|
||||||
|
$participants = $this->eventParticipants->getForList($event, $request);
|
||||||
|
$kitchenOverview = $this->eventParticipants->getKitchenOverview($event);
|
||||||
|
|
||||||
|
$csv = view('csvs.' . $listType, [
|
||||||
|
'event' => $event->name,
|
||||||
|
'rows' => $participants,
|
||||||
|
])->render();
|
||||||
|
|
||||||
|
return response($csv, 200, [
|
||||||
|
'Content-Type' => 'text/csv; charset=UTF-8',
|
||||||
|
'Content-Disposition' => 'attachment; filename="' . $listType . '.csv"',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listParticipants(string $eventId, string $listType, Request $request) : JsonResponse {
|
||||||
|
$event = $this->events->getByIdentifier($eventId);
|
||||||
|
switch ($listType) {
|
||||||
|
case 'by-local-group':
|
||||||
|
$participants = $this->eventParticipants->groupByLocalGroup($event, $request);
|
||||||
|
break;
|
||||||
|
case 'by-participation-group':
|
||||||
|
$participants = $this->eventParticipants->groupByParticipationType($event, $request);
|
||||||
|
break;
|
||||||
|
case 'signed-off':
|
||||||
|
$participants = $this->eventParticipants->getSignedOffParticipants($event, $request);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$participants = ['Alle Teilnehmenden' => $this->eventParticipants->getForList($event, $request)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'participants' => $participants,
|
||||||
|
'listType' => $listType,
|
||||||
|
'event' => $event->toResource()->toArray($request)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Controllers\MailCompose;
|
||||||
|
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ByGroupController extends CommonController
|
||||||
|
{
|
||||||
|
public function __invoke(string $eventIdentifier, string $groupType, Request $request) {
|
||||||
|
$event = $this->events->getByIdentifier($eventIdentifier, true);
|
||||||
|
$recipients = [];
|
||||||
|
switch ($groupType) {
|
||||||
|
case 'by-local-group':
|
||||||
|
$participants = $this->eventParticipants->groupByLocalGroup($event, $request, $request->input('groupName'));
|
||||||
|
$recipients = $this->eventParticipants->getMailAddresses($participants[$request->input('groupName')]);
|
||||||
|
break;
|
||||||
|
case 'by-participation-group':
|
||||||
|
$participants = $this->eventParticipants->groupByParticipationType($event, $request, $request->input('groupName'));
|
||||||
|
$recipients = $this->eventParticipants->getMailAddresses($participants[$request->input('groupName')]);
|
||||||
|
break;
|
||||||
|
case 'signed-off':
|
||||||
|
$participants = $this->eventParticipants->getSignedOffParticipants($event, $request, $request->input('groupName'));
|
||||||
|
$recipients = $this->eventParticipants->getMailAddresses($participants[$request->input('groupName')]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$participants = $this->eventParticipants->getForList($event, $request);
|
||||||
|
$recipients = $this->eventParticipants->getMailAddresses($participants);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json(['recipients' => $recipients]);
|
||||||
|
}
|
||||||
|
}
|
||||||
66
app/Domains/Event/Controllers/MailCompose/SendController.php
Normal file
66
app/Domains/Event/Controllers/MailCompose/SendController.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Controllers\MailCompose;
|
||||||
|
|
||||||
|
use App\Mail\ManualMails\ManualMailsCommonMail;
|
||||||
|
use App\Mail\ManualMails\ManualMailsReportMail;
|
||||||
|
use App\Mail\ParticipantCocMails\ParticipantCocCompleteMail;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
class SendController extends CommonController
|
||||||
|
{
|
||||||
|
public function __invoke(string $eventIdentifier, Request $request) {
|
||||||
|
$recipients = $request->input('recipients')
|
||||||
|
|> function (string $value) : string { return str_replace(';', ',', $value); }
|
||||||
|
|> function (string $value) : array { return explode( ',', $value); };
|
||||||
|
|
||||||
|
$event = $this->events->getByIdentifier($eventIdentifier, true)->toResource()->toArray($request);
|
||||||
|
$sentRecipients = [];
|
||||||
|
$allOkay = true;
|
||||||
|
$subject = $request->input('subject') ?? 'Neue Nachricht zu Veranstaltung "' . $event['name'] . '"';
|
||||||
|
|
||||||
|
foreach ($recipients as $recipient) {
|
||||||
|
if (in_array($recipient, $sentRecipients)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sentRecipients[] = $recipient;
|
||||||
|
try {
|
||||||
|
Mail::to(trim($recipient))->send(new ManualMailsCommonMail(
|
||||||
|
mailSubject: $subject,
|
||||||
|
message: $request->input('message'),
|
||||||
|
event: $event,
|
||||||
|
));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$allOkay = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = auth()->user();
|
||||||
|
$reportSubject = sprintf('Sendebericht für Nachricht mit Betreff "%s"', $subject);
|
||||||
|
|
||||||
|
Mail::to($user->email)->send(new ManualMailsReportMail(
|
||||||
|
mailSubject: $reportSubject,
|
||||||
|
message: $request->input('message'),
|
||||||
|
event: $event,
|
||||||
|
originalRecipients: $sentRecipients
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($allOkay) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => sprintf(
|
||||||
|
'E-Mail wurde erfolgreich an %1$s Personen versendet. Du hast eine Kopie an deine Mail-Adresse erhalten.',
|
||||||
|
count($sentRecipients)
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Es gab einen Fehler beim Versenden der Nachrichten.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Domains/Event/Controllers/ParticipantController.php
Normal file
34
app/Domains/Event/Controllers/ParticipantController.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Event\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\Event\Actions\ManualCertificateOfConductionCheck\ManualCertificateOfConductionCheckCommand;
|
||||||
|
use App\Domains\Event\Actions\ManualCertificateOfConductionCheck\ManualCertificateOfConductionCheckRequest;
|
||||||
|
use App\Domains\Event\Actions\ParticipantPayment\ParticipantPaymentCommand;
|
||||||
|
use App\Domains\Event\Actions\ParticipantPayment\ParticipantPaymentRequest;
|
||||||
|
use App\Models\EventParticipant;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Support\Facades\Request;
|
||||||
|
|
||||||
|
class ParticipantController extends CommonController {
|
||||||
|
public function markCocExisting(string $participantIdentifier, Request $request) {
|
||||||
|
$participant = $this->eventParticipants->getByIdentifier($participantIdentifier, $this->events);
|
||||||
|
|
||||||
|
$cocRequest = new ManualCertificateOfConductionCheckRequest($participant);
|
||||||
|
$cocCommand = new ManualCertificateOfConductionCheckCommand($cocRequest);
|
||||||
|
$cocResponse = $cocCommand->execute();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => $cocResponse->success ? 'success' : 'error',
|
||||||
|
'message' => $cocResponse->success ? 'Das eFZ wurde als gültig hinterlegt' : 'Beim Aktualisieren des eFZ-Status ist ein Fehler aufgetreten.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(string $participantIdentifier, Request $request) {
|
||||||
|
$participant = $this->eventParticipants->getByIdentifier($participantIdentifier, $this->events)->toResource()->toArray($request);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'participant' => $participant,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user