Automatischer Zahlungsparser

This commit is contained in:
2026-09-10 09:50:14 +02:00
parent 025035190d
commit 6301c342e4
24 changed files with 1692 additions and 83 deletions
@@ -2,8 +2,11 @@
namespace App\EventPaymentModules\Modules;
use App\Enumerations\RefundAccountSource;
use App\EventPaymentModules\AbstractEventPaymentModule;
use App\EventPaymentModules\DTO\CreateInvoiceRequest;
use App\EventPaymentModules\DTO\GetRefundDataRequest;
use App\EventPaymentModules\DTO\GetRefundDataResponse;
use App\EventPaymentModules\DTO\RegistrationRenderContext;
use App\EventPaymentModules\DTO\RegistrationSummaryRequest;
use App\EventPaymentModules\DTO\RegistrationSummaryResponse;
@@ -108,6 +111,41 @@ class AccountTransferPaymentModule extends AbstractEventPaymentModule implements
return BankStatementRuleset::fromConfiguration(is_array($override) ? $override : null);
}
/**
* Erstattet wird auf das Konto, von dem der Beitrag kam -- und genau das steht seit dem
* Zahlungsimport in den Teilnehmer-Optionen.
*
* Gelesen wird ausschließlich aus `payment_options`, nicht aus dem Kennzeichen `refund_data`:
* Das ist ein abgeleitetes Merkmal für Listen und Abfragen. Zwei Quellen für dieselbe Wahrheit
* driften früher oder später auseinander, und die falsche gewänne dann eine Auszahlung.
*
* Eine IBAN, die die Prüfziffer nicht besteht, wird nicht gemeldet: Sie würde ungeprüft
* übernommen und das Geld ginge im Zweifel an eine fremde Person. Lieber wie bisher nachfragen.
*/
public function getRefundData(GetRefundDataRequest $request): GetRefundDataResponse
{
$response = new GetRefundDataResponse();
$options = $request->participant->payment_options ?? [];
$iban = Iban::normalize((string) ($options[self::OPTION_PAYER_IBAN] ?? ''));
$owner = trim((string) ($options[self::OPTION_PAYER_ACCOUNT_OWNER] ?? ''));
if ($iban === '' || $owner === '' || !Iban::isValid($iban)) {
return $response;
}
$response->source = RefundAccountSource::Known;
$response->accountOwner = $owner;
$response->accountIban = $iban;
$paidOn = $request->participant->last_payment_date?->format('d.m.Y');
$response->sourceNote = $paidOn === null
? 'Zahlungseingang'
: 'Zahlungseingang vom ' . $paidOn;
return $response;
}
/** Bei der Überweisung zählen Gutschriften -- Belastungen sind Ausgaben der Aktion. */
public function isRelevantTransaction(BankTransaction $transaction): bool
{
@@ -2,8 +2,11 @@
namespace App\EventPaymentModules\Modules;
use App\Enumerations\RefundAccountSource;
use App\EventPaymentModules\AbstractEventPaymentModule;
use App\EventPaymentModules\DTO\CreateInvoiceRequest;
use App\EventPaymentModules\DTO\GetRefundDataRequest;
use App\EventPaymentModules\DTO\GetRefundDataResponse;
use App\EventPaymentModules\DTO\RegistrationRenderContext;
use App\EventPaymentModules\DTO\RegistrationSummaryRequest;
use App\EventPaymentModules\DTO\RegistrationSummaryResponse;
@@ -57,6 +60,25 @@ class UndefinedPaymentModule extends AbstractEventPaymentModule
return $response;
}
/**
* Bar gezahlt heißt: Es gab nie ein Konto, von dem der Beitrag kam.
*
* Damit greift die sonst geltende Kontrolle „zurück nur auf das Ursprungskonto" nicht -- die Frage
* danach wäre für den Teili sinnlos und die Erklärung, es sei dasselbe Konto, schlicht unwahr. An
* ihre Stelle tritt die Erklärung, dass das angegebene Konto auf seinen Namen läuft.
*
* Zurückgezahlt wird trotzdem per Überweisung: Der Kontoauszug belegt die Zahlung, während eine
* Barauszahlung an einer Unterschrift hinge und die Barkasse berührte. Rechtlich spricht nichts
* dagegen -- eine Regel „bar rein, bar raus" gibt es nicht.
*/
public function getRefundData(GetRefundDataRequest $request): GetRefundDataResponse
{
$response = new GetRefundDataResponse();
$response->source = RefundAccountSource::None;
return $response;
}
protected function invoiceClosingStatement(CreateInvoiceRequest $request): string
{
return sprintf(