Merge pull request 'Comments for invoices' (#3) from dev-4.2.0 into main

Comments for invoices
Bugfix: Birthday can be stored
Bugfix: Cronjobs  run correctly
This commit was merged in pull request #3.
This commit is contained in:
2026-05-14 17:00:56 +02:00
17 changed files with 77 additions and 33 deletions
@@ -47,7 +47,5 @@ class ParticipantPaymentController extends CommonController
'class' => $amountLeft->getAmount() != 0 ? 'not-paid' : 'paid', 'class' => $amountLeft->getAmount() != 0 ? 'not-paid' : 'paid',
] ]
]); ]);
dd($participant);
} }
} }
@@ -43,6 +43,7 @@ class CreateInvoiceCommand {
'passengers' => $this->request->passengers, 'passengers' => $this->request->passengers,
'transportation' => $this->request->transportations, 'transportation' => $this->request->transportations,
'payment_purpose' => $this->request->paymentPurpose, 'payment_purpose' => $this->request->paymentPurpose,
'comment' => $this->request->notices,
'document_filename' => $this->request->receiptFile !== null ? $this->request->receiptFile->fullPath : null, 'document_filename' => $this->request->receiptFile !== null ? $this->request->receiptFile->fullPath : null,
]); ]);
@@ -24,6 +24,7 @@ class CreateInvoiceRequest {
public ?int $userId; public ?int $userId;
public ?string $travelReason; public ?string $travelReason;
public ?string $paymentPurpose; public ?string $paymentPurpose;
public ?string $notices;
public function __construct( public function __construct(
@@ -45,6 +46,7 @@ class CreateInvoiceRequest {
?int $transportations, ?int $transportations,
?string $travelReason = null, ?string $travelReason = null,
?string $paymentPurpose = null, ?string $paymentPurpose = null,
?string $notices = null,
) { ) {
$this->costUnit = $costUnit; $this->costUnit = $costUnit;
@@ -65,6 +67,7 @@ class CreateInvoiceRequest {
$this->userId = $userId; $this->userId = $userId;
$this->travelReason = $travelReason; $this->travelReason = $travelReason;
$this->paymentPurpose = $paymentPurpose; $this->paymentPurpose = $paymentPurpose;
$this->notices = $notices;
if ($accountIban === 'undefined') { if ($accountIban === 'undefined') {
$this->accountIban = null; $this->accountIban = null;
@@ -60,7 +60,6 @@ class CreateInvoiceReceiptCommand {
storage_path('app/private/' .$filename) . '.zip', storage_path('app/private/' .$filename) . '.zip',
ZipArchive::CREATE | ZipArchive::OVERWRITE ZipArchive::CREATE | ZipArchive::OVERWRITE
); );
dd($receipt);
$zip->addFile(storage_path('app/private/' . $tmpFileName ), 'antrag.pdf'); $zip->addFile(storage_path('app/private/' . $tmpFileName ), 'antrag.pdf');
$zip->addFile(storage_path('app/private/' . $receipt), 'beleg.pdf'); $zip->addFile(storage_path('app/private/' . $receipt), 'beleg.pdf');
@@ -114,6 +113,9 @@ HTML;
<tr><td>Name:</td><td>%3\$s</td></tr> <tr><td>Name:</td><td>%3\$s</td></tr>
<tr><td>E-Mail:</td><td>%4\$s</td></tr> <tr><td>E-Mail:</td><td>%4\$s</td></tr>
<tr><td>Telefon:</td><td>%5\$s</td></tr> <tr><td>Telefon:</td><td>%5\$s</td></tr>
<tr><td>Anmerkungen:</td><td>%16\$s</td></tr>
<tr><td>Name der Kostenstelle:</td><td>%6\$s</td></tr> <tr><td>Name der Kostenstelle:</td><td>%6\$s</td></tr>
<tr><td>Zahlungsgrund:</td><td>%7\$s</td></tr> <tr><td>Zahlungsgrund:</td><td>%7\$s</td></tr>
<tr><td>Wird der Betrag gespendet:</td><td>%8\$s</td></tr> <tr><td>Wird der Betrag gespendet:</td><td>%8\$s</td></tr>
@@ -186,7 +188,8 @@ HTML;
$invoiceReadable['createdAt'], $invoiceReadable['createdAt'],
$invoiceReadable['approvedAt'], $invoiceReadable['approvedAt'],
$invoiceReadable['approvedBy'], $invoiceReadable['approvedBy'],
$changes $changes,
$invoiceReadable['comment'],
); );
} }
@@ -8,13 +8,13 @@ use App\Models\Invoice;
use App\ValueObjects\Amount; use App\ValueObjects\Amount;
class UpdateInvoiceRequest { class UpdateInvoiceRequest {
public string $comment; public ?string $comment;
public InvoiceType $invoiceType; public InvoiceType $invoiceType;
public CostUnit $costUnit; public CostUnit $costUnit;
public Invoice $invoice; public Invoice $invoice;
public Amount $amount; public Amount $amount;
public function __construct(Invoice $invoice, string $comment, InvoiceType $invoiceType, CostUnit $costUnit, Amount $amount) { public function __construct(Invoice $invoice, ?string $comment, InvoiceType $invoiceType, CostUnit $costUnit, Amount $amount) {
$this->comment = $comment; $this->comment = $comment;
$this->invoiceType = $invoiceType; $this->invoiceType = $invoiceType;
$this->costUnit = $costUnit; $this->costUnit = $costUnit;
@@ -47,7 +47,10 @@ class EditController extends CommonController{
$invoice->travel_direction, $invoice->travel_direction,
$invoice->distance, $invoice->distance,
$invoice->passengers, $invoice->passengers,
$invoice->transportation $invoice->transportation,
$invoice->travel_reason,
$invoice->payment_purpose,
$invoice->comment,
); );
$invoiceCreationCommand = new CreateInvoiceCommand($createInvoiceRequest); $invoiceCreationCommand = new CreateInvoiceCommand($createInvoiceRequest);
@@ -86,7 +89,7 @@ class EditController extends CommonController{
$updateInvoiceRequest = new UpdateInvoiceRequest( $updateInvoiceRequest = new UpdateInvoiceRequest(
$invoice, $invoice,
$modifyData['reason_of_correction'] ?? 'Abrechnungskorrektur', $modifyData['notices'],
$invoiceType, $invoiceType,
$newCostUnit, $newCostUnit,
$newAmount $newAmount
@@ -18,6 +18,7 @@ class SaveInvoiceController extends CommonController
public function __invoke(Request $request, int $costUnitId, string $invoiceType) : JsonResponse { public function __invoke(Request $request, int $costUnitId, string $invoiceType) : JsonResponse {
$costUnit = $this->costUnits->getById($costUnitId, true); $costUnit = $this->costUnits->getById($costUnitId, true);
$paymentPurpose = $request->input('paymentPurpose') ?? null; $paymentPurpose = $request->input('paymentPurpose') ?? null;
$notices = $request->input('notices') ?? null;
if (null === $costUnit) { if (null === $costUnit) {
return response()->json([ return response()->json([
'status' => 'error', 'status' => 'error',
@@ -82,6 +83,8 @@ class SaveInvoiceController extends CommonController
$request->input('havePassengers'), $request->input('havePassengers'),
$request->input('materialTransportation'), $request->input('materialTransportation'),
$request->input('travelReason'), $request->input('travelReason'),
null,
$notices
); );
break; break;
@@ -106,6 +109,7 @@ class SaveInvoiceController extends CommonController
$request->input('materialTransportation'), $request->input('materialTransportation'),
null, null,
$paymentPurpose, $paymentPurpose,
$notices
); );
break; break;
@@ -20,7 +20,7 @@ const formData = reactive({
type_internal: props.newInvoice.internalType || '', type_internal: props.newInvoice.internalType || '',
cost_unit: props.newInvoice.costUnitId || '', cost_unit: props.newInvoice.costUnitId || '',
amount: props.newInvoice.amountPlain || '', amount: props.newInvoice.amountPlain || '',
reason_of_correction: '', notices: props.newInvoice.comments || '',
}) })
const submitForm = () => { const submitForm = () => {
@@ -71,9 +71,13 @@ onMounted(async () => {
</tr> </tr>
<tr> <tr>
<td>Grund der Korrektur:</td> <td>Anmerkungen:</td>
<td> <td>
<input type="text" v-model="formData.reason_of_correction" class="width-half-full" /> <textarea
id="notices"
name="notices" v-model="formData.notices"
maxlength="128"
style="font-size: 14pt; width: 550px;" />
</td> </td>
</tr> </tr>
@@ -41,6 +41,7 @@ const decision = ref('')
const errorMsg = ref('') const errorMsg = ref('')
const confirmation = ref(null) const confirmation = ref(null)
const receiptError = ref('') const receiptError = ref('')
const notices = ref('')
async function sendData() { async function sendData() {
if (!userName.value) return if (!userName.value) return
@@ -62,6 +63,7 @@ async function sendData() {
formData.append('havePassengers', props.havePassengers ? 1 : 0) formData.append('havePassengers', props.havePassengers ? 1 : 0)
formData.append('materialTransportation', props.materialTransportation ? 1 : 0) formData.append('materialTransportation', props.materialTransportation ? 1 : 0)
formData.append('travelReason', props.travelReason) formData.append('travelReason', props.travelReason)
formData.append('notices', notices.value)
if (props.receipt) { if (props.receipt) {
formData.append('receipt', props.receipt) formData.append('receipt', props.receipt)
@@ -121,6 +123,15 @@ async function sendData() {
name="contact_telephone" v-model="userTelephone" name="contact_telephone" v-model="userTelephone"
style="font-size: 14pt; width: 550px;" /><br /><br /> style="font-size: 14pt; width: 550px;" /><br /><br />
<label v-if="userName !== ''">
<strong>Weitere Informationen zur Abrechnung (Max. 128 Zeichen):</strong>
</label><br />
<textarea
id="notices"
name="notices" v-model="notices"
maxlength="128"
style="font-size: 14pt; width: 550px;" /><br /><br />
<span id="decision" v-if="userEmail !== '' && userTelephone !== '' && decision === ''"> <span id="decision" v-if="userEmail !== '' && userTelephone !== '' && decision === ''">
<label><br /> <label><br />
<strong>Möchtest du den Betrag spenden?</strong> <strong>Möchtest du den Betrag spenden?</strong>
+5 -2
View File
@@ -41,10 +41,13 @@ class CronTaskHandleProvider extends CommonController
// --- Daily Tasks --- // --- Daily Tasks ---
if ($task->execution_type === CronTaskType::CRON_TASK_TYPE_DAILY) { if ($task->execution_type === CronTaskType::CRON_TASK_TYPE_DAILY) {
$scheduledTime = $task->schedule_time; $scheduledTime = \DateTime::createFromFormat('Y-m-d H:i:s', date('Y-m-d ') . $task->schedule_time);
$now = Carbon::now();
$alreadyRunToday = $task->last_run?->isToday() ?? false; $alreadyRunToday = $task->last_run?->isToday() ?? false;
if (!$alreadyRunToday && $now->format('H:i') === $scheduledTime) { if (!$alreadyRunToday && $now >= $scheduledTime) {
$this->runTask($task); $this->runTask($task);
} }
} }
+1
View File
@@ -93,6 +93,7 @@ class UserRepository {
'first_aid_permission' => $request->firstAidPermission, 'first_aid_permission' => $request->firstAidPermission,
'bank_account_owner' => $request->bankAccountOwner, 'bank_account_owner' => $request->bankAccountOwner,
'bank_account_iban' => $request->bankAccountIban, 'bank_account_iban' => $request->bankAccountIban,
'birthday' => $request->birthday,
]); ]);
} }
} }
+14 -3
View File
@@ -2,6 +2,8 @@
namespace App\Tasks; namespace App\Tasks;
use App\Domains\CostUnit\Actions\ChangeCostUnitState\ChangeCostUnitStateCommand;
use App\Domains\CostUnit\Actions\ChangeCostUnitState\ChangeCostUnitStateRequest;
use App\Models\CostUnit; use App\Models\CostUnit;
use App\Repositories\CostUnitRepository; use App\Repositories\CostUnitRepository;
@@ -12,9 +14,18 @@ class CloseCostUnit implements CronTask {
$costUnitRepository = new CostUnitRepository(); $costUnitRepository = new CostUnitRepository();
/** @var CostUnit $costUnit */ /** @var CostUnit $costUnit */
foreach ($costUnitRepository->getCurrentEvents() as $costUnit) { foreach ($costUnitRepository->getCurrentEvents() as $costUnit) {
if (\DateTime::createFromFormat('Y-m-d', $costUnit->billing_deadline) < $now ) { $billingEndTime = \DateTime::createFromFormat('Y-m-d H:i:s', $costUnit['billing_deadline']);
$costUnit->allow_new = false; $billingEndTime->setTime(0,0,0);
$costUnit->save(); $billingEndTime->add(new \DateInterval('P1D'));
$now = now();
if ($billingEndTime < $now) {
new ChangeCostUnitStateCommand(
new ChangeCostUnitStateRequest(
CostUnit::where('id', $costUnit['id'])->first(),false, false
)
)->execute();
} }
} }
} }
+1 -1
View File
@@ -12,7 +12,7 @@ class CloseEvent implements CronTask {
$eventRepository = new EventRepository(); $eventRepository = new EventRepository();
/** @var Event $event */ /** @var Event $event */
foreach ($eventRepository->getAvailable(false) as $event) { foreach ($eventRepository->getAvailable(false) as $event) {
if ($event->registration_final_end < $now ) { if ($event->registration_final_end <= $now ) {
$event->registration_allowed = false; $event->registration_allowed = false;
$event->save(); $event->save();
} }
+5 -3
View File
@@ -16,16 +16,17 @@ use Psr\Log\LoggerInterface;
class NotifyTeam implements CronTask { class NotifyTeam implements CronTask {
public function handle(): void public function handle(): void
{ {
if (date('w') !== 0) { if ((int)date('w') !== 0) {
// return; return;
} }
$eventRepository = new EventRepository(); $eventRepository = new EventRepository();
/** @var Event $event */ /** @var Event $event */
foreach ($eventRepository->getAvailable(false) as $event) { foreach ($eventRepository->getAvailable(false) as $event) {
foreach (Tenant::all() as $tenant) { foreach (Tenant::all() as $tenant) {
echo $tenant->slug . PHP_EOL;
$participants = $event->participants()->where('local_group', $tenant->slug)->whereNull('unregistered_at')->get(); $participants = $event->participants()->where('local_group', $tenant->slug)->whereNull('unregistered_at')->get();
}
if ($participants->isEmpty()) { if ($participants->isEmpty()) {
continue; continue;
@@ -39,3 +40,4 @@ class NotifyTeam implements CronTask {
} }
} }
} }
}
+1 -1
View File
@@ -21,7 +21,7 @@
</div> </div>
</div> </div>
<div class="modal-body"> <div class="modal-body" style="max-height: 600px; overflow: auto;">
<slot /> <slot />
</div> </div>
</div> </div>
+2 -2
View File
@@ -12,6 +12,8 @@ install -d -m 0755 -o $USER -g $GROUPNAME \
storage/app/private \ storage/app/private \
storage/logs storage/logs
php artisan migrate --force
rm .dockerignore rm .dockerignore
rm .editorconfig rm .editorconfig
rm .env.example rm .env.example
@@ -31,6 +33,4 @@ rm -rf docker
rm -rf database rm -rf database
rm -rf app/Installer rm -rf app/Installer
php artisan migrate --force
exec "php-fpm" exec "php-fpm"
+1 -1
View File
@@ -1 +1 @@
4.1.0 4.2.0