# Grocery Product Price Providers v1

Use this reference when extending the FinanceManager Grocery/Migros Optimizer from manual/reviewable matches into controlled online price-provider workflows.

## Non-negotiable boundaries

- Provider calls must be explicit user actions such as **Preise online suchen**. Normal page render, dashboard load, route navigation, or report display must not call live providers.
- No automatic ordering, no login scraping, no aggressive/broad catalog scraping, no API keys in frontend, no real reports/runtime DB/product data in Git.
- Prices may never be invented by LLMs or fallback code. LLM/heuristics may normalize names and score matches only; price fields must come from a source response or cache.
- Every product result needs retailer, product URL/source, fetched timestamp, cache/source marker, confidence, and quality flags. Unclear source/product/price => `needs_review`.
- Medical/Histamin/Behçet analysis remains inert placeholders only.

## Provider abstraction pattern

Create a class-level provider boundary such as `GroceryProductProvider` with methods:

- `search_products(query, retailer, locale='de-CH', use_cache=True, max_results=N, max_age_seconds=...)`
- `fetch_product_detail(url)` / controlled fetch helper
- `normalize_result(raw_result)`
- `get_cached_result(...)`
- `store_cache(...)`

Recommended result shape:

- `retailer`, `product_name`, `brand`, `product_url`, optional `image_url`
- `price_text`, `price_decimal_text`, `currency`
- `package_size`, `unit`, `unit_price_text`, `unit_price_decimal_text`
- `availability_status`, `promotion_text`
- `fetched_at`, `source`, `confidence`, `quality_flags`

Provider skeletons are acceptable for unstable sources. For skeleton retailers, return clear `future_status`/`needs_review` rows rather than silently failing or inventing candidates.

## Cache and provenance pitfalls

- Separate provider cache semantics from accepted/manual match semantics. If provider results and review matches share a table, ensure match upserts do **not** overwrite provider cache rows, source hashes, raw results, quality flags, or original `fetched_at`.
- Expose both `source_fetched_at`/cache timestamp and any `match_created_at`/stored-match timestamp if they differ. UI/report should show the source/cache timestamp for price provenance.
- Add a cache max-age/staleness policy. `use_cache=True` must not mean “trust arbitrarily old prices forever”; stale cache should refetch only after explicit user action or be marked stale/needs-review.
- Regression-test: first provider search fetches once; second search with valid cache does not fetch again and preserves source timestamp.

## Price safety and comparison rules

- Never coerce missing provider price to `0.00`. If no source price exists, return/display `no_price` + `needs_review`; do not persist it as a safe match.
- Prefer unit-price comparison when units are comparable.
- Fallback to package-price comparison only when package/quality flags do not make comparison unsafe; add `package_price_fallback`.
- Do not calculate savings for `needs_review`, `no_price`, non-CHF, different/uncertain units, different package/quality/organic mismatches unless explicitly handled as safe.
- A valid but **more expensive** alternative is not a savings suggestion. Add `not_cheaper`, keep it `needs_review` or informational, and exclude it from optimized totals/shopping lists.
- Do not clamp negative savings to `0.00` while still returning `can_calculate_savings=True`; that lets worse options become “safe”.

## Quality flags

Use durable review flags such as:

- `exact_match`
- `close_match`
- `cheaper_private_label`
- `different_pack_size`
- `different_quality`
- `organic_mismatch`
- `needs_review`
- `no_price`
- `no_unit_price`
- `package_price_fallback`
- `not_cheaper`

## UI workflow

1. User chooses a Bon.
2. User selects included products.
3. User clicks **Preise online suchen**.
4. User selects retailers and cache behavior.
5. Backend searches only selected products/retailers within bounded limits.
6. UI shows results with price/unit price/package/source link/fetched timestamp/confidence/quality flags.
7. Accept/reject/manual correction are review actions; final optimized shopping list uses only accepted/safe matches.

Add navigation tests for Desktop Sidebar and mobile/fallback menu. Browser sanity should cover page load, explicit online search, result display, analysis, report, and JS console.

## Verification additions

Beyond the normal FinanceManager gate, add provider-specific tests for:

- no provider call on render
- provider search mock for each implemented provider
- cache hit without refetch
- stale cache refetch or stale marker
- rate-limit/error fallback to `needs_review`
- missing price not persisted as `0.00`
- source timestamp distinct from match-created timestamp
- exact/close/different-pack/no-price/needs-review matching
- unit-price and package-price comparison
- unsafe or not-cheaper rows excluded from savings and shopping lists
