diff --git a/custom/apps/mining-checker/assets/js/app.js b/custom/apps/mining-checker/assets/js/app.js index eb3aa874..d8f4e642 100644 --- a/custom/apps/mining-checker/assets/js/app.js +++ b/custom/apps/mining-checker/assets/js/app.js @@ -2226,6 +2226,7 @@ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(raw), }); + invalidateProjectBootstrapCache(projectKey); setMessage(successMessage); setOcrPreview(null); await reloadBootstrapAfterMutation(successMessage); @@ -2459,7 +2460,7 @@ return; } - setMessage('OCR-Ergebnis geladen. Bei Bedarf direkt speichern.'); + setMessage('OCR-Ergebnis geladen, aber noch nicht gespeichert. Bitte den Vorschlag pruefen und anschliessend speichern.'); } catch (err) { setError(err.message); } finally { diff --git a/custom/apps/mining-checker/docs/README.md b/custom/apps/mining-checker/docs/README.md index 6fcc2188..14062164 100644 --- a/custom/apps/mining-checker/docs/README.md +++ b/custom/apps/mining-checker/docs/README.md @@ -91,6 +91,8 @@ Empfohlene Umgebungsvariablen: Laut OCR.space-Doku wird `POST https://api.ocr.space/parse/image` mit `file`, Header-`apikey`, optional `language`, `scale`, `detectOrientation`, `isTable` und `OCREngine` verwendet. Der Modulparser wertet die OCR.space-Felder `ParsedResults`, `ParsedText`, `IsErroredOnProcessing`, `ErrorMessage` und `OCRExitCode` aus. Quellen: https://ocr.space/ocrapi +Bei Wallet-Screenshots gilt der erkannte positive Asset-Bestand als Snapshot. Sichtbare negative Transaktionsbetraege, etwa die letzte Abbuchung oberhalb der Asset-Liste, duerfen nicht als positiver Wallet-Bestand uebernommen werden. Ein fehlender oder abgeschnittener Gesamtsaldo verhindert die automatische Speicherung nicht, wenn Hauptbestand, Waehrung und Wallet-Kontext eindeutig erkannt wurden. Niedriger bewertete OCR-Ergebnisse werden ausdruecklich als noch nicht gespeichert gekennzeichnet und muessen bestaetigt werden. + ## Wechselkurse und Waehrungen Der Mining-Checker speichert keine eigenen FX-Snapshots mehr, sondern referenziert die `fetch_id` aus `fx-rates`. diff --git a/custom/apps/mining-checker/src/Api/Router.php b/custom/apps/mining-checker/src/Api/Router.php index 43976660..b04dbf76 100644 --- a/custom/apps/mining-checker/src/Api/Router.php +++ b/custom/apps/mining-checker/src/Api/Router.php @@ -1563,7 +1563,7 @@ final class Router 'payouts' => true, 'wallet_withdrawals' => true, 'miner_offers' => false, - 'purchased_miners' => false, + 'purchased_miners' => true, ], 'dashboards' => [ 'cost_plans' => false, diff --git a/custom/apps/mining-checker/src/Domain/OcrService.php b/custom/apps/mining-checker/src/Domain/OcrService.php index df586675..9fb5c351 100644 --- a/custom/apps/mining-checker/src/Domain/OcrService.php +++ b/custom/apps/mining-checker/src/Domain/OcrService.php @@ -507,20 +507,28 @@ final class OcrService $assetRows = []; foreach ($lines as $lineIndex => $line) { - if (!preg_match('/(\d+(?:[.,]\d+)?)\s*([A-Z]{2,10})\b/u', $line, $match)) { - continue; - } - $amount = round((float) str_replace(',', '.', $match[1]), 10); - $currency = strtoupper((string) $match[2]); - if ($amount < 0 || $currency === '' || in_array($currency, ['USD', 'EUR'], true)) { + if (!preg_match_all('/(? $lineIndex, - 'currency' => $currency, - 'balance' => $amount, - ]; + foreach ($matches as $match) { + $sign = trim((string) $match[1]); + $amount = round((float) str_replace(',', '.', $match[2]), 10); + $currency = strtoupper((string) $match[3]); + if (in_array($sign, ['-', "\u{2212}", "\u{2013}"], true)) { + $flags[] = 'wallet_transaction_amount_ignored'; + continue; + } + if ($currency === '' || in_array($currency, ['USD', 'EUR'], true)) { + continue; + } + + $assetRows[] = [ + 'index' => $lineIndex, + 'currency' => $currency, + 'balance' => $amount, + ]; + } } foreach ($assetRows as $assetIndex => $assetRow) { @@ -596,14 +604,23 @@ final class OcrService } } - $matchedFields = 0; - foreach ([$totalValueAmount, $walletBalance, $walletCurrency] as $field) { - if ($field !== null && $field !== '') { - $matchedFields++; - } - } + $hasWalletBalance = $walletBalance !== null; + $hasWalletCurrency = $walletCurrency !== ''; + $hasTotalValue = $totalValueAmount !== null && $totalValueCurrency !== null; + $matchedFields = (int) $hasWalletBalance + (int) $hasWalletCurrency + (int) $hasTotalValue; $score = $matchedFields + ($walletIndicators * 2); - $confidence = max(0.05, min(0.99, ($matchedFields / 3) + (min(3, $walletIndicators) * 0.12) - (count($flags) * 0.03))); + // Der Hauptbestand ist fuer einen autoritativen Wallet-Snapshot ausreichend. + // Ein im Screenshot abgeschnittener Gesamtsaldo ist dagegen kein Grund, einen + // sauber erkannten Asset-Bestand nur als ungespeicherte Vorschau zu behandeln. + $confidence = 0.05 + + ($hasWalletBalance ? 0.60 : 0.0) + + ($hasWalletCurrency ? 0.10 : 0.0) + + ($hasTotalValue ? 0.10 : 0.0) + + (min(3, $walletIndicators) * 0.10); + if (!$hasWalletBalance) { + $confidence -= 0.15; + } + $confidence = max(0.05, min(0.99, $confidence)); ksort($balances); diff --git a/docs/ANLEITUNG.md b/docs/ANLEITUNG.md index 2b8daedc..01063258 100644 --- a/docs/ANLEITUNG.md +++ b/docs/ANLEITUNG.md @@ -100,6 +100,13 @@ Der `Waehrungs-Checker` verwaltet gespeicherte Wechselkurse, Kurs-Historie, Umre - mit `force` kann ein Abruf bewusst erzwungen werden - das zugehoerige Widget nutzt dieselbe Regel wie App und API +## Mining-Checker Wallet-Screenshots + +- Ein eindeutig erkannter Wallet-Screenshot wird nach dem Upload als autoritativer Wallet-Snapshot gespeichert und die Wallet-Anzeige danach neu geladen. +- Negative Betraege aus einer eingeblendeten letzten Transaktion werden nicht als Wallet-Bestand uebernommen. +- Fehlt der Gesamtsaldo im Bild, koennen die eindeutig erkannten einzelnen Asset-Bestaende trotzdem gespeichert werden. +- Wird ein OCR-Ergebnis nicht automatisch gespeichert, weist die Upload-Ansicht ausdruecklich darauf hin; der Vorschlag muss dann geprueft und manuell gespeichert werden. + ## Benutzerdaten Aktuell gelten folgende Regeln: diff --git a/docs/CONTENT.md b/docs/CONTENT.md index 2b9c2e9a..b914db4e 100644 --- a/docs/CONTENT.md +++ b/docs/CONTENT.md @@ -137,6 +137,7 @@ Aus [custom/apps/README.md](/home/lars/Schreibtisch/Projekte/desktop.kusche.berl - `Mining-Checker` ist eine Desktop-App aus `custom/apps/mining-checker/` - Historische Mining-Nachtraege verwenden ihren belegten Zeitpunkt; fuer feste Krypto-Buchungen ist ein zeitnaher, referenzierter `fx-rates`-Snapshot Pflicht und ein aktueller Kurs darf keinen historischen Snapshot ersetzen. - Der Bereich `Nachtragen` im Mining-Checker speichert zusammenhaengende historische Miner-, Transfer-, Wallet- und Mining-Daten in chronologischer Reihenfolge und legt den belegten FX-Snapshot davor an. +- Wallet-Screenshots uebernehmen positive Asset-Bestaende autoritativ; eingeblendete negative Transaktionen werden nicht als Wallet-Bestand interpretiert. Eindeutige Wallet-Erkennungen koennen auch ohne sichtbaren Gesamtsaldo automatisch gespeichert werden. - `Bisher verdient` trennt variable Coin-Bestaende von fixen, bereits ausgezahlten und reinvestierten Coin-Werten; Krypto-Mieten bleiben dabei zugleich historische Ausgaben. - `Waehrungs-Checker` ist eine Desktop-App aus `custom/apps/fx-rates/` - die Desktop-Shell kann Moduldefinitionen zentral erkennen und als `App` bereitstellen