Feature: PayPal-Import – STATUS-Fix, Auto-Regeln (Authorization ignorieren, Transfer/Bank-Gegenbuchung paaren)
This commit is contained in:
parent
e1c262c369
commit
308a826c3e
3 changed files with 156 additions and 3 deletions
|
|
@ -211,7 +211,12 @@ class BankController extends BaseController
|
||||||
View::redirect('/bank/upload', 'Import fehlgeschlagen: ' . $e->getMessage(), 'error');
|
View::redirect('/bank/upload', 'Import fehlgeschlagen: ' . $e->getMessage(), 'error');
|
||||||
}
|
}
|
||||||
|
|
||||||
View::redirect('/bank', count($rows) . ' Buchungen importiert.');
|
$paired = \Steuerkram\Services\PayPalNvpFetchService::applyBankImportRules($batchId);
|
||||||
|
$msg = count($rows) . ' Buchungen importiert.';
|
||||||
|
if ($paired > 0) {
|
||||||
|
$msg .= ' ' . $paired . ' PayPal-Gegenbuchung(en) automatisch ignoriert.';
|
||||||
|
}
|
||||||
|
View::redirect('/bank', $msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show(array $params): void
|
public function show(array $params): void
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,144 @@ class PayPalNvpFetchService
|
||||||
$this->updateLastFetched();
|
$this->updateLastFetched();
|
||||||
|
|
||||||
($this->log)(sprintf('Fertig. %d neu importiert, %d bereits vorhanden.', $totalNew, $totalSkip));
|
($this->log)(sprintf('Fertig. %d neu importiert, %d bereits vorhanden.', $totalNew, $totalSkip));
|
||||||
|
|
||||||
|
($this->log)('');
|
||||||
|
($this->log)('Automatische Regeln werden angewendet…');
|
||||||
|
$this->applyAutoRules();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wird nach einem Bank-CSV-Import aufgerufen.
|
||||||
|
* Findet Abbuchungen mit „paypal" im Verwendungszweck und setzt sie + den
|
||||||
|
* passenden PayPal-Transfer auf „ignoriert", wenn das Paar existiert.
|
||||||
|
*/
|
||||||
|
public static function applyBankImportRules(int $batchId): int
|
||||||
|
{
|
||||||
|
$debits = Database::fetchAll(
|
||||||
|
"SELECT bt.id, bt.buchungstag, bt.wert
|
||||||
|
FROM bank_transactions bt
|
||||||
|
WHERE bt.batch_id = ?
|
||||||
|
AND bt.wert < 0
|
||||||
|
AND LOWER(bt.buchungstext) LIKE '%paypal%'
|
||||||
|
AND bt.status = 'offen'",
|
||||||
|
[$batchId]
|
||||||
|
);
|
||||||
|
|
||||||
|
$paired = 0;
|
||||||
|
foreach ($debits as $debit) {
|
||||||
|
$dateFrom = date('Y-m-d', strtotime($debit['buchungstag'] . ' -7 days'));
|
||||||
|
$dateTo = date('Y-m-d', strtotime($debit['buchungstag'] . ' +2 days'));
|
||||||
|
$amount = abs((float)$debit['wert']);
|
||||||
|
|
||||||
|
// Passenden PayPal-Transfer suchen (positiver Betrag gleicher Höhe)
|
||||||
|
$transfer = Database::fetchOne(
|
||||||
|
"SELECT bt.id FROM bank_transactions bt
|
||||||
|
JOIN bank_import_batches bib ON bt.batch_id = bib.id
|
||||||
|
JOIN bank_sources bs ON bib.source_id = bs.id
|
||||||
|
WHERE bs.type = 'api'
|
||||||
|
AND bt.vorgang = 'Transfer'
|
||||||
|
AND bt.buchungstext = 'Bank Account (direct debit)'
|
||||||
|
AND bt.wert = ?
|
||||||
|
AND bt.buchungstag BETWEEN ? AND ?
|
||||||
|
AND bt.status != 'ignoriert'
|
||||||
|
LIMIT 1",
|
||||||
|
[$amount, $dateFrom, $dateTo]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$transfer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Database::execute(
|
||||||
|
"UPDATE bank_transactions SET status = 'ignoriert' WHERE id = ?",
|
||||||
|
[$debit['id']]
|
||||||
|
);
|
||||||
|
Database::execute(
|
||||||
|
"UPDATE bank_transactions SET status = 'ignoriert' WHERE id = ?",
|
||||||
|
[$transfer['id']]
|
||||||
|
);
|
||||||
|
$paired++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $paired;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyAutoRules(): void
|
||||||
|
{
|
||||||
|
// Regel 1: Authorization → ignoriert
|
||||||
|
$auths = Database::fetchAll(
|
||||||
|
"SELECT id FROM bank_transactions bt
|
||||||
|
JOIN bank_import_batches bib ON bt.batch_id = bib.id
|
||||||
|
WHERE bib.source_id = ? AND bt.vorgang = 'Authorization' AND bt.status = 'offen'",
|
||||||
|
[$this->sourceId]
|
||||||
|
);
|
||||||
|
foreach ($auths as $row) {
|
||||||
|
Database::execute(
|
||||||
|
"UPDATE bank_transactions SET status = 'ignoriert' WHERE id = ?",
|
||||||
|
[$row['id']]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($auths) {
|
||||||
|
($this->log)(count($auths) . ' Authorization-Buchung(en) auf „ignoriert" gesetzt.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regel 2: Transfer „Bank Account (direct debit)" + passende Bank-Gegenbuchung
|
||||||
|
// Suche alle noch offenen Transfers dieser PayPal-Quelle
|
||||||
|
$transfers = Database::fetchAll(
|
||||||
|
"SELECT bt.id, bt.buchungstag, bt.wert
|
||||||
|
FROM bank_transactions bt
|
||||||
|
JOIN bank_import_batches bib ON bt.batch_id = bib.id
|
||||||
|
WHERE bib.source_id = ?
|
||||||
|
AND bt.vorgang = 'Transfer'
|
||||||
|
AND bt.buchungstext = 'Bank Account (direct debit)'
|
||||||
|
AND bt.wert > 0
|
||||||
|
AND bt.status = 'offen'",
|
||||||
|
[$this->sourceId]
|
||||||
|
);
|
||||||
|
|
||||||
|
$pairedCount = 0;
|
||||||
|
foreach ($transfers as $transfer) {
|
||||||
|
// Passende Bank-Abbuchung in allen anderen Quellen suchen (+/- 7 Tage, gleicher Betrag negiert)
|
||||||
|
$dateFrom = date('Y-m-d', strtotime($transfer['buchungstag'] . ' -2 days'));
|
||||||
|
$dateTo = date('Y-m-d', strtotime($transfer['buchungstag'] . ' +7 days'));
|
||||||
|
$amount = -abs((float)$transfer['wert']);
|
||||||
|
|
||||||
|
$bankDebit = Database::fetchOne(
|
||||||
|
"SELECT bt.id FROM bank_transactions bt
|
||||||
|
JOIN bank_import_batches bib ON bt.batch_id = bib.id
|
||||||
|
WHERE bib.source_id != ?
|
||||||
|
AND bt.wert = ?
|
||||||
|
AND bt.buchungstag BETWEEN ? AND ?
|
||||||
|
AND LOWER(bt.buchungstext) LIKE '%paypal%'
|
||||||
|
AND bt.status != 'ignoriert'
|
||||||
|
LIMIT 1",
|
||||||
|
[$this->sourceId, $amount, $dateFrom, $dateTo]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$bankDebit) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Database::execute(
|
||||||
|
"UPDATE bank_transactions SET status = 'ignoriert' WHERE id = ?",
|
||||||
|
[$transfer['id']]
|
||||||
|
);
|
||||||
|
Database::execute(
|
||||||
|
"UPDATE bank_transactions SET status = 'ignoriert' WHERE id = ?",
|
||||||
|
[$bankDebit['id']]
|
||||||
|
);
|
||||||
|
($this->log)(sprintf(
|
||||||
|
'Gegenbuchung: PayPal-Transfer +%s€ (ID %d) + Bank-Abbuchung (ID %d) → ignoriert',
|
||||||
|
number_format((float)$transfer['wert'], 2, ',', '.'),
|
||||||
|
$transfer['id'],
|
||||||
|
$bankDebit['id']
|
||||||
|
));
|
||||||
|
$pairedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pairedCount === 0 && $transfers) {
|
||||||
|
($this->log)(count($transfers) . ' Transfer-Buchung(en) warten noch auf Bank-Gegenbuchung.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── NVP-API ──────────────────────────────────────────────────
|
// ── NVP-API ──────────────────────────────────────────────────
|
||||||
|
|
@ -183,7 +321,6 @@ class PayPalNvpFetchService
|
||||||
'METHOD' => 'TransactionSearch',
|
'METHOD' => 'TransactionSearch',
|
||||||
'STARTDATE' => $from,
|
'STARTDATE' => $from,
|
||||||
'ENDDATE' => $to,
|
'ENDDATE' => $to,
|
||||||
'STATUS' => 'All',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($result === null) {
|
if ($result === null) {
|
||||||
|
|
@ -193,9 +330,20 @@ class PayPalNvpFetchService
|
||||||
$ack = $result['ACK'] ?? '';
|
$ack = $result['ACK'] ?? '';
|
||||||
if (!in_array($ack, ['Success', 'SuccessWithWarning'], true)) {
|
if (!in_array($ack, ['Success', 'SuccessWithWarning'], true)) {
|
||||||
($this->log)(' API-Fehler: ' . ($result['L_LONGMESSAGE0'] ?? $ack));
|
($this->log)(' API-Fehler: ' . ($result['L_LONGMESSAGE0'] ?? $ack));
|
||||||
|
($this->log)(' ACK: ' . $ack);
|
||||||
|
($this->log)(' Felder in Antwort: ' . implode(', ', array_keys($result)));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debug: Anzahl gefundener Felder in der Antwort
|
||||||
|
$txCount = 0;
|
||||||
|
while (isset($result['L_TRANSACTIONID' . $txCount])) {
|
||||||
|
$txCount++;
|
||||||
|
}
|
||||||
|
if ($txCount === 0) {
|
||||||
|
($this->log)(' Debug: Antwort-Felder: ' . implode(', ', array_slice(array_keys($result), 0, 10)));
|
||||||
|
}
|
||||||
|
|
||||||
$rows = [];
|
$rows = [];
|
||||||
$i = 0;
|
$i = 0;
|
||||||
while (isset($result['L_TRANSACTIONID' . $i])) {
|
while (isset($result['L_TRANSACTIONID' . $i])) {
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1-260604
|
1-260608
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue