diff --git a/app/Domains/Event/Actions/SendMissingPaymentMails/SendMissingPaymentMailsCommand.php b/app/Domains/Event/Actions/SendMissingPaymentMails/SendMissingPaymentMailsCommand.php
new file mode 100644
index 0000000..6cb00c0
--- /dev/null
+++ b/app/Domains/Event/Actions/SendMissingPaymentMails/SendMissingPaymentMailsCommand.php
@@ -0,0 +1,37 @@
+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;
+ }
+}
diff --git a/app/Domains/Event/Actions/SendMissingPaymentMails/SendMissingPaymentMailsRequest.php b/app/Domains/Event/Actions/SendMissingPaymentMails/SendMissingPaymentMailsRequest.php
new file mode 100644
index 0000000..d4e465a
--- /dev/null
+++ b/app/Domains/Event/Actions/SendMissingPaymentMails/SendMissingPaymentMailsRequest.php
@@ -0,0 +1,16 @@
+events->getById($eventId);
+ public function __invoke(string $eventId) {
+ $event = $this->events->getByIdentifier($eventId);
return new InertiaProvider('Event/Details', ['event' => $event])->render();
}
diff --git a/app/Domains/Event/Controllers/PaymentReminderController.php b/app/Domains/Event/Controllers/PaymentReminderController.php
new file mode 100644
index 0000000..f85b5f5
--- /dev/null
+++ b/app/Domains/Event/Controllers/PaymentReminderController.php
@@ -0,0 +1,33 @@
+events->getByIdentifier($eventIdentifier, true);
+
+ $sendPaymentReminderMailsRequest = new SendMissingPaymentMailsRequest(
+ event: $event,
+ eventParticipants: $this->eventParticipants,
+ httpRequest: $request
+ );
+
+ $sendPaymentReminderMailsCommand = new SendMissingPaymentMailsCommand(request: $sendPaymentReminderMailsRequest);
+ $sendPaymentReminderResponse = $sendPaymentReminderMailsCommand->execute();
+
+ return response()->json([
+ 'success' => $sendPaymentReminderResponse->success,
+ 'message' => $sendPaymentReminderResponse->success ?
+ sprintf('Es wurden %1$s Personen über fehlende Teilnahmebeiträge informiert', $sendPaymentReminderResponse->remindedParticipants) :
+ 'Beim Senden der Benachrichtigungen ist ein Fehler aufgetreten.',
+
+ ]);
+ }
+}
diff --git a/app/Domains/Event/Routes/api.php b/app/Domains/Event/Routes/api.php
index 8e9e817..8fbfd52 100644
--- a/app/Domains/Event/Routes/api.php
+++ b/app/Domains/Event/Routes/api.php
@@ -9,6 +9,7 @@ use App\Domains\Event\Controllers\ParticipantPaymentController;
use App\Domains\Event\Controllers\ParticipantReSignOnController;
use App\Domains\Event\Controllers\ParticipantSignOffController;
use App\Domains\Event\Controllers\ParticipantUpdateController;
+use App\Domains\Event\Controllers\PaymentReminderController;
use App\Domains\Event\Controllers\SignupController;
use App\Middleware\IdentifyTenant;
use Illuminate\Support\Facades\Route;
@@ -28,6 +29,8 @@ Route::prefix('api/v1')
Route::post('/send', SendController::class);
});
+ Route::get('{eventIdentifier}/send-payment-reminder', PaymentReminderController::class);
+
Route::prefix('/details/{eventId}') ->group(function () {
Route::get('/summary', [DetailsController::class, 'summary']);
diff --git a/app/Domains/Event/Views/Create.vue b/app/Domains/Event/Views/Create.vue
index 6d62896..2c3c359 100644
--- a/app/Domains/Event/Views/Create.vue
+++ b/app/Domains/Event/Views/Create.vue
@@ -161,17 +161,16 @@
});
if (data.status !== 'success') {
- toas.error(data.message);
+ toast.error(data.message);
return false;
} else {
- console.log(data.event);
newEvent.value = data.event;
showParticipationFees.value = true;
}
}
async function finishCreation() {
- window.location.href = '/event/details/' + newEvent.value.id;
+ window.location.href = '/event/details/' + newEvent.value.identifier;
}
diff --git a/app/Domains/Event/Views/Partials/MailCompose.vue b/app/Domains/Event/Views/Partials/MailCompose.vue
index 6f56e03..cd8a2f0 100644
--- a/app/Domains/Event/Views/Partials/MailCompose.vue
+++ b/app/Domains/Event/Views/Partials/MailCompose.vue
@@ -4,6 +4,7 @@ import {useAjax} from "../../../../../resources/js/components/ajaxHandler.js";
import TextEditor from "../../../../Views/Components/TextEditor.vue";
import ErrorText from "../../../../Views/Components/ErrorText.vue";
import {toast} from "vue3-toastify";
+import InfoText from "../../../../Views/Components/InfoText.vue";
const { request } = useAjax();
@@ -37,6 +38,7 @@ onMounted(async () => {
});
const errorMessage = ref(null)
+const infoMessage = ref(null)
const emit = defineEmits([
'closeComposer',
@@ -49,6 +51,10 @@ function close() {
async function sendMail() {
+ document.getElementById('sendMessageButton').style.display = 'none';
+ infoMessage.value = 'Die Rundmail wird nun gesendet. Dies kann einen Moment dauern. Bitte verlasse diese Seite nicht.'
+
+ toast.info('Die Rundmail wird nun gesendet. Dies kann einen Moment dauern. Bitte verlasse diese Seite nicht.')
const response = await request('/api/v1/event/' + props.event.identifier + '/mailing/send', {
method: "POST",
body: {
@@ -59,10 +65,14 @@ async function sendMail() {
});
if (response.success) {
+ infoMessage.value = null
+ document.getElementById('sendMessageButton').style.display = 'block';
close();
toast.success(response.message)
} else {
+ infoMessage.value = null
+ document.getElementById('sendMessageButton').style.display = 'block';
errorMessage.value = response.message
toast.error(response.message)
}
@@ -94,8 +104,8 @@ const form = reactive({
-
-
+
+
diff --git a/app/Domains/Event/Views/Partials/Overview.vue b/app/Domains/Event/Views/Partials/Overview.vue
index 2811c17..22937a1 100644
--- a/app/Domains/Event/Views/Partials/Overview.vue
+++ b/app/Domains/Event/Views/Partials/Overview.vue
@@ -7,6 +7,7 @@
import Modal from "../../../../Views/Components/Modal.vue";
import MailCompose from "./MailCompose.vue";
import FullScreenModal from "../../../../Views/Components/FullScreenModal.vue";
+ import {toast} from "vue3-toastify";
const props = defineProps({
data: Object,
@@ -54,6 +55,17 @@
function mailToGroup() {
mailCompose.value = true
}
+
+ async function sendPaymentReminder() {
+ toast.info("Die Nachrichten werden gesendet. Bitte verlasse diese Seite nicht.");
+ const response = await fetch("/api/v1/event/" + props.data.event.identifier + "/send-payment-reminder/" );
+ const data = await response.json();
+ if (data.success) {
+ toast.success(data.message)
+ } else {
+ toast.error(data.message)
+ }
+ }
@@ -90,8 +102,8 @@
-
-
+
+
diff --git a/app/Providers/GlobalDataProvider.php b/app/Providers/GlobalDataProvider.php
index 08b7df9..c37a23e 100644
--- a/app/Providers/GlobalDataProvider.php
+++ b/app/Providers/GlobalDataProvider.php
@@ -126,7 +126,7 @@ class GlobalDataProvider {
foreach ($eventRepository->listAll() as $event) {
- $navigation['events'][] = ['url' => '/event/details/' . $event->id, 'display' => $event->name];
+ $navigation['events'][] = ['url' => '/event/details/' . $event->identifier, 'display' => $event->name];
}
$navigation['events'][] = ['url' => '/event/new', 'display' => 'Neue Veranstaltung'];
diff --git a/app/Repositories/EventParticipantRepository.php b/app/Repositories/EventParticipantRepository.php
index 943a052..15b291e 100644
--- a/app/Repositories/EventParticipantRepository.php
+++ b/app/Repositories/EventParticipantRepository.php
@@ -75,6 +75,17 @@ class EventParticipantRepository {
return $participants;
}
+ public function getParticipantsWithMissingPayments(Event$event, Request $request) : array {
+ $participants = [];
+ foreach ($event->participants()->whereNull('unregistered_at')
+ ->whereColumn('amount', '<>', 'amount_paid')
+ ->get() as $participant) {
+ $participants[] = $participant;
+ };
+
+ return $participants;
+ }
+
public function getParticipantsWithIntolerances(Event $event, Request $request) : array {
$participants = [];
foreach ($event->participants()->whereNotNull('intolerances')->whereNot('intolerances' , '=', '')->get() as $participant) {
diff --git a/app/Views/Components/InfoText.vue b/app/Views/Components/InfoText.vue
new file mode 100644
index 0000000..e522106
--- /dev/null
+++ b/app/Views/Components/InfoText.vue
@@ -0,0 +1,10 @@
+
+
+
+ {{ props.message }}
+
+
diff --git a/public/css/elements.css b/public/css/elements.css
index a95134e..88a6983 100644
--- a/public/css/elements.css
+++ b/public/css/elements.css
@@ -68,6 +68,12 @@ input[type="submit"]:hover,
color: #ffffff !important;
}
+.info_text {
+ color: #3369d3;
+ display: block;
+ font-weight: bold;
+}
+
.error_text {
color: red;
display: block;