upgrade
This commit is contained in:
@@ -3401,14 +3401,14 @@
|
||||
return h('div', { className: 'mc-stack' }, [
|
||||
h('div', { className: 'mc-two-col' }, [
|
||||
renderSharedOcrPanel(),
|
||||
panel('Mining manuell erfassen', 'Direkte Eingabe eines einzelnen Mining-Messpunkts mit serverseitiger Validierung.', h('form', {
|
||||
panel('Mining manuell erfassen', 'Direkte Eingabe eines einzelnen Mining-Messpunkts. Ein belegter historischer Zeitpunkt wird unveraendert gespeichert.', h('form', {
|
||||
className: 'mc-form',
|
||||
onSubmit: function (event) {
|
||||
event.preventDefault();
|
||||
submitMeasurement(false);
|
||||
},
|
||||
}, [
|
||||
displayField('Zeitpunkt', 'Wird beim Speichern automatisch auf den aktuellen Bestätigungszeitpunkt gesetzt.'),
|
||||
inputField('Zeitpunkt', 'datetime-local', measurementForm.measured_at, (value) => setMeasurementForm({ ...measurementForm, measured_at: value }), '1'),
|
||||
inputField('Coins total', 'number', measurementForm.coins_total, (value) => setMeasurementForm({ ...measurementForm, coins_total: value }), '0.000001'),
|
||||
inputField('Kurs', 'number', measurementForm.price_per_coin, (value) => setMeasurementForm({ ...measurementForm, price_per_coin: value }), '0.000001'),
|
||||
selectField('Waehrung', measurementForm.price_currency, [''].concat(selectableCurrencies.map((currency) => currency.code)), (value) => setMeasurementForm({ ...measurementForm, price_currency: value })),
|
||||
|
||||
@@ -112,7 +112,7 @@ Beispiel:
|
||||
|
||||
`currencyapi.net` wird ueber das Modul `fx-rates` abgefragt. Aus dem Response werden `base`, `rates` und `updated` uebernommen; `valid` muss `true` sein. Die eigentlichen Fetches und Raten liegen im Modul `fx-rates`.
|
||||
|
||||
Pro Abruf entsteht genau ein Datensatz in `fx-rates` mit Basiswaehrung, Provider und Stichtag. Neue Mining-Messpunkte pruefen beim Speichern, ob ein neuer FX-Fetch noetig ist; falls nicht, wird die letzte passende `fetch_id` wiederverwendet.
|
||||
Pro Abruf entsteht genau ein Datensatz in `fx-rates` mit Basiswaehrung, Provider und Stichtag. Neue Mining-Messpunkte pruefen beim Speichern, ob ein neuer FX-Fetch noetig ist; falls nicht, wird nur ein Snapshot innerhalb des zulaessigen Zeitfensters wiederverwendet. Ein historischer Messpunkt darf niemals auf einen heutigen oder sonst zeitlich fernen Fetch verweisen.
|
||||
|
||||
Falls noch historische Mining-Checker-Fetches in `miningcheck_fx_fetches` und `miningcheck_fx_rates` liegen, kann `POST /api/mining-checker/v1/projects/{projectKey}/legacy-fx-migrate` diese nach `fx-rates` ueberfuehren. Danach werden bestehende Messpunkte soweit moeglich auf die passende `fx_fetch_id` aktualisiert.
|
||||
|
||||
@@ -160,6 +160,19 @@ Beim Schema-Upgrade werden vorhandene Krypto-Miner einmalig bewertet:
|
||||
|
||||
Jede neu gespeicherte Buchung, die einen Kurs oder eine Waehrungsumrechnung benoetigt, muss die verwendete `fx-rates.fetch_id` gemeinsam mit Betrag, Quellwaehrung und Zielwaehrung speichern. Ein aktueller Kurs darf ausschliesslich fuer variable, noch gehaltene Bestaende verwendet werden. Fehlt fuer eine neue feste Buchung ein FX-Snapshot, muss der Speichervorgang fehlschlagen; ein stiller Fallback ist nicht erlaubt.
|
||||
|
||||
### Historische Nachtraege
|
||||
|
||||
Ein belegter, nachtraeglich erfasster Mining-Stand verwendet seinen tatsaechlichen Zeitpunkt, nicht den Speicherzeitpunkt. Der manuelle Messpunkt besitzt deshalb ein optionales Feld `Zeitpunkt`; bleibt es leer, wird der aktuelle Zeitpunkt verwendet.
|
||||
|
||||
Fuer eine unvollstaendige Upload-Phase werden die belegbaren Ereignisse einzeln und chronologisch erfasst:
|
||||
|
||||
1. Krypto-Miete mit Zeitpunkt, tatsaechlichem Coin-Preis, Laufzeit, Hashrate inklusive Bonus und dem historischen FX-Snapshot.
|
||||
2. Transfer aus dem Mining-Tool in das Wallet mit Zeitpunkt und Coin-Menge.
|
||||
3. Wallet-Snapshot mit dem tatsaechlichen Wallet-Bestand; dieser ist ab seinem Zeitpunkt autoritativ.
|
||||
4. Mining-Messpunkt mit Screenshot-Zeitpunkt, Miner-Bestand und dem im Screenshot angezeigten Kurs.
|
||||
|
||||
Fehlt ein exakter historischer FX-Snapshot, darf eine feste Krypto-Ausgabe nicht mit einem aktuellen Kurs gespeichert werden. Der fehlende historische Snapshot muss zuerst aus einer nachpruefbaren externen Quelle in `fx-rates` vorliegen; der Beleg oder Screenshot bleibt als Notiz am Nachtrag erhalten.
|
||||
|
||||
## Break-even
|
||||
|
||||
Der Gesamt-Break-even verwendet die Summe aus Fiat-Investitionen und `Reinvest fix` gegen aktuelle, noch gehaltene Miner- und Walletbestaende. Krypto-Reinvestitionen muessen immer den historischen festen Gegenwert aus dem zugeordneten `fx-rates`-Snapshot verwenden; eine Bewertung zum aktuellen Coin-Kurs ist unzulässig.
|
||||
|
||||
@@ -1475,8 +1475,10 @@ final class Router
|
||||
$projectTimezone = $this->projectTimezone($projectKey);
|
||||
$source = $this->enumValue($input['source'] ?? 'manual', ['manual', 'image_ocr', 'seed_import'], 'source');
|
||||
$payload = [
|
||||
'measured_at' => $source === 'seed_import'
|
||||
? $this->requiredDateTime($input['measured_at'] ?? null, 'measured_at', $projectTimezone)
|
||||
// A manual entry may document an older, verifiable upload. Only omit the
|
||||
// timestamp when the user intentionally records the current state.
|
||||
'measured_at' => trim((string) ($input['measured_at'] ?? '')) !== ''
|
||||
? $this->requiredDateTime($input['measured_at'], 'measured_at', $projectTimezone)
|
||||
: $this->currentTimestamp(),
|
||||
'coins_total' => $this->requiredDecimal($input['coins_total'] ?? null, 'coins_total'),
|
||||
'coin_currency' => $this->measurementCoinCurrency($projectKey, $input),
|
||||
@@ -2861,18 +2863,21 @@ final class Router
|
||||
}
|
||||
|
||||
if ($measuredAt !== '') {
|
||||
$nearest = $this->fx()->nearestSnapshot('USD', $measuredAt, null, null);
|
||||
// Never attach an unrelated current quote to a historical financial event.
|
||||
$nearest = $this->fx()->nearestSnapshot('USD', $measuredAt, null, (int) round($maxAgeHours * 60));
|
||||
if (is_array($nearest) && is_numeric($nearest['id'] ?? null)) {
|
||||
return (int) $nearest['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$latest = $this->fx()->latestSnapshot('USD', null);
|
||||
if (is_array($latest) && is_numeric($latest['id'] ?? null)) {
|
||||
return (int) $latest['id'];
|
||||
if ($measuredAt === '' || $this->isRecentTimestamp($measuredAt, $maxAgeHours)) {
|
||||
$latest = $this->fx()->latestSnapshot('USD', null);
|
||||
if (is_array($latest) && is_numeric($latest['id'] ?? null)) {
|
||||
return (int) $latest['id'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($allowRefresh) {
|
||||
if ($allowRefresh && ($measuredAt === '' || $this->isRecentTimestamp($measuredAt, $maxAgeHours))) {
|
||||
$fresh = $this->fx()->refreshLatestRates(null, 'USD');
|
||||
if (is_array($fresh) && is_numeric($fresh['fetch_id'] ?? null)) {
|
||||
return (int) $fresh['fetch_id'];
|
||||
|
||||
@@ -135,6 +135,7 @@ Aus [custom/apps/README.md](/home/lars/Schreibtisch/Projekte/desktop.kusche.berl
|
||||
### Aktueller Modulstand
|
||||
|
||||
- `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.
|
||||
- `Waehrungs-Checker` ist eine Desktop-App aus `custom/apps/fx-rates/`
|
||||
- die Desktop-Shell kann Moduldefinitionen zentral erkennen und als `App` bereitstellen
|
||||
- wiederverwendbare Modul-Helfer liegen im globalen Kern, die Fachlogik bleibt im Modul
|
||||
|
||||
Reference in New Issue
Block a user