feat(payments): Block Zahlungsvorgang im Bestell-Editor, Storno-Felder bei automatischem Storno

- Bestell-Editor rechts: Zahlungsvorgang (Status, Grund, Transaktions-ID,
  Referenz, Betraege, Zeiten, Verlauf aus PG payment_log), anbieterneutral
  ueber payment_transactions (Payments\Overview, Log::forOrder)
- markCancelled() fuellt Stornodatum, Stornostatus, Stornogrund und
  Storno-Nachricht der Bestellung (nur leere Felder)
- neuer Stornogrund 'Zahlung beim Anbieter nicht abgeschlossen' (install.sql)
- reconcile: 404-Grund lesbar mit Alter des Vorgangs
This commit is contained in:
Tom Bartelt
2026-09-22 09:30:52 +02:00
parent fe868641d2
commit 85bc1d8c9a
8 changed files with 240 additions and 4 deletions
+25 -2
View File
@@ -275,13 +275,36 @@ abstract class AbstractProvider implements ProviderInterface
}
$this->setTransactionStatus($reference, 'cancelled', $why);
$orderId = (int) $tx->order_id;
// 6 = storniert (wie v1-PayPal im Cancel-Zweig)
$this->db->query("UPDATE orders SET order_status=6 WHERE id=$orderId AND payment_status<>2");
// 6 = storniert (wie v1-PayPal im Cancel-Zweig).
// 22.09.2026 -- dazu die Storno-Felder der Bestellung fuellen, damit im Bestell-Editor
// steht, WARUM: bisher stand da nur "Storniert", der Grund lag allein in
// payment_transactions.detail. Vorhandene Eintraege werden nicht ueberschrieben.
$msg = $this->db->real_escape_string(
$this->label() . ': ' . $why . '. Zahlung nicht abgeschlossen, kein Geld geflossen. '
. 'Automatisch storniert (' . $source . ', ' . \date('d.m.Y H:i') . ').'
);
$reason = $this->cancellationReasonId();
$this->db->query(
"UPDATE orders SET order_status=6,
cancellation_date = COALESCE(cancellation_date, CURDATE()),
cancellation_status = IF(cancellation_status IS NULL OR cancellation_status=0, 2, cancellation_status),
cancellation_reason_id = " . ($reason > 0 ? "COALESCE(NULLIF(cancellation_reason_id,0), $reason)" : 'cancellation_reason_id') . ",
cancellation_info = IF(cancellation_info IS NULL OR cancellation_info='', '$msg', CONCAT(cancellation_info, '\n', '$msg'))
WHERE id=$orderId AND payment_status<>2"
);
$this->pendingClear();
$this->event('cancelled', $orderId, (string) $tx->order_number, (float) $tx->expected_amount, $reference, null, (int) $tx->customer_id, $why . ' (' . $source . ')');
Log::write($this->module(), 'cancel', $orderId, (string) $tx->order_number, $reference, 'cancelled', null, null, $why);
}
/** Stornogrund "Zahlung beim Anbieter nicht abgeschlossen" (install.sql), 0 wenn nicht angelegt. */
private function cancellationReasonId(): int
{
$res = $this->db->query("SELECT id FROM cancellation_reasons WHERE name='Zahlung beim Anbieter nicht abgeschlossen' LIMIT 1");
$r = $res ? $res->fetch_object() : null;
return $r ? (int) $r->id : 0;
}
protected function amountMatches(string $expected, string $paid, string $expCur, string $paidCur): bool
{
return \number_format((float) $expected, 2, '.', '') === \number_format((float) $paid, 2, '.', '')