# MVP Crypto Report Context & Export

Session-derived pattern for Finance System Tasks 28–29: build a safe local crypto inventory report layer without touching real finance data.

## Scope

Use this pattern when implementing or reviewing crypto inventory report context/builders/exporters for the Finance System.

## Required architecture

- Source data only from the local SQLite database; never call CoinGecko or any live market API during report context building or page/report rendering.
- Aggregate holdings from the crypto holdings calculator / initial snapshots + crypto transactions, not from legacy snapshot values.
- Keep crypto quantities, prices, fees and valuations as `Decimal`/text end-to-end. Do not use `float`, SQLite `REAL`, or scientific notation for crypto values.
- Treat CHF as the primary base currency. Prepare metadata for later USD/EUR views, but do not dilute the MVP with multi-currency valuation unless local prices exist.
- Export files only to the configured external runtime reports directory. Validate settings before export so repo-contained runtime/report paths are rejected.
- Never commit generated reports, PDFs, DBs or real portfolio files. Git-safety should block top-level `reports/` and `.pdf` files in the repo.

## Context shape

`build_crypto_report_context(conn, ...)` should return a renderer-neutral dict containing:

- Report metadata: id, type, generated_at, base currency, disclaimer, price_as_of, wallet_verification_as_of.
- Executive summary: total crypto CHF value, coin count, wallet/platform count, data quality status.
- Coin rows: coin name, symbol, coingecko_id, total quantity, local CHF price, total CHF value, portfolio share, price source, price timestamp, warning codes.
- Wallet rows: wallet/platform name, wallet type, provider, coin count, CHF value, last verification, verification status, warning codes.
- Detail rows by coin/wallet: wallet, coin, symbol, quantity, price, value CHF, last verification, notes/status.
- Data-quality warnings with stable codes and entity references.

## Data-quality warnings to include

- `missing_coingecko_id`: asset has no CoinGecko ID, so reliable scheduled valuation is blocked.
- `missing_price_chf`: no local CHF price exists; valuation for that coin/wallet/detail row is unknown.
- `stale_price_chf`: local price timestamp is older than the configured freshness threshold.
- `wallet_not_verified`: wallet verification status or last verification is missing/not verified.
- `negative_holding`: calculated holding quantity is negative; do not hide this.
- `legacy_snapshot_value_ignored`: legacy snapshot CHF/currency values exist but are deliberately ignored for current valuation.

## Export pattern

- Provide Markdown and HTML renderers from the same context.
- Prefer PDF only through an optional renderer dependency (e.g. WeasyPrint). If the renderer is unavailable or fails, fall back gracefully to HTML/Markdown and expose a clear warning in the result.
- Save a row in `reports` with `report_type='crypto_inventory'`, `generated_at`, `file_path`, `format`, `data_quality_status`, `summary_json`.
- Create an audit-log event with `action='report_generated'`, `entity_type='report'`, and the report id.

## Minimum tests

- Synthetic DB context builds without live APIs.
- Total quantity per coin is exact.
- Wallet aggregation is correct.
- Decimal strings remain exact; no float/scientific notation.
- Legacy snapshot values do not affect current valuation.
- Missing price and missing CoinGecko ID create warnings.
- Report file is written under external runtime reports dir, not repo.
- Report metadata row is persisted.
- `report_generated` audit event is created.
- PDF renderer can be mocked; fallback works when unavailable.
- Git-safety blocks PDFs/reports inside the repo.

## Pitfalls

- Do not reuse stale legacy snapshot values as current CHF values merely because they are present in the DB.
- Do not silently merge by symbol only; symbols are not globally unique.
- Do not make report generation mutate holdings/prices.
- Do not put generated demo reports in `tests/fixtures` unless they are tiny, synthetic, and explicitly required; prefer temporary runtime dirs in tests.
