Skip to content

1.8.0

⚙️ Patch 1.8.0 — Price Alerts (Qogita + Keepa watchlist pipeline)

🚀 Summary

  • New Price Alerts area (/price-alerts) with Qogita and Keepa integrations for wholesale watchlist monitoring and margin-based buy targets.
  • Keepa pipeline on qogita_watchlist_items: finder → product fetch → EK analysis (10% margin) → push to Qogita — no separate ASIN queue table.
  • Scheduled jobs: Qogita watchlist sync (stock/prices, Discord on targets met) and Keepa selling-price refresh with auto re-analyze + export when values change.
  • Watchlist UI: Qogita-first status display, status filters, refresh button, react-cron-generator, localStorage prefs, merged Target EK column.

✨ Improvements

Price Alerts — Qogita

  • Credentials CRUD + login test (POST /api/v1/omni/qogita/credentials/…).
  • Watchlist sync from Qogita API — stock, price, areTargetsMet, placeholder cleanup.
  • Watchlist CRUD — add / patch / delete by GTIN on Qogita + local DB.
  • UTC-aware token expiry (fixes naive/aware datetime compare on refresh).
  • Sync removes only broken placeholder rows (name === "Product name unavailable", no qid); out-of-stock items with real names are kept.
  • DELETE /watchlist/items/{gtin}/ URL fixed (was wrong path/body).

Price Alerts — Keepa

  • Keepa credentials (API key only) under same page.
  • Pipeline steps (callable individually or combined):
  • POST /keepa/sync-asins — finder query → ASIN stubs on watchlist (ASIN:{asin}).
  • POST /keepa/fetch-products — product API → real EAN, metrics columns.
  • POST /keepa/analyze-products — fees + Target EK at 10% margin.
  • POST /keepa/push-to-qogita — create/update Qogita watchlist targets.
  • POST /keepa/scan — all steps in one call.
  • GET /keepa/queue/status — pending fetch / analyze / analyzed counts from watchlist.
  • Keepa metrics stored in dedicated columns (avg90 prices, fees, EK, etc.); raw keepa_product_json removed.
  • analyze_from_stored_row for cron re-analysis without persisting full Keepa payloads.

Price Alerts — Crons (/api/v1/omni/price-alerts/cron/…)

  • qogita_watchlist_sync — sync Qogita → Discord webhook when are_targets_met newly true → re-analyze + push changed EK to Qogita.
  • keepa_price_sync — refresh Keepa avg90 selling prices → re-analyze → export to Qogita when EK/target changed.
  • Start/stop/status/run endpoints; schedules persisted in cron_jobs (Berlin TZ).
  • Registered on service-omni startup via CronJobManager.

Push → Qogita

  • After successful push, upserts Qogita API response then runs full watchlist sync so qid, stock, and Qogita price appear immediately.
  • Push response includes qogita_sync stats.

Frontend (QogitaWatchlistTab)

  • Sidebar: Rituals → Price Alerts; tabs: Overview, Outlet Monitor, Qogita Watchlist.
  • Pipeline buttons: Sync ASINs → Fetch Products → Analyze → Push → Sync from Qogita.
  • Refresh — reload table + Keepa queue (no API sync).
  • Qogita status filter — all, on Qogita, targets met, in/out of stock, awaiting push, Keepa only (qogita_status query param).
  • Status chips: Qogita rows show stock/targets met; Keepa-only rows show pipeline state.
  • Qogita price/stock columns only when qid present.
  • Target EK (10%) — single column (calculated_ek_price ?? target_price).
  • Cron UIreact-cron-generator + Discord webhook field; prefs in localStorage (qogitaWatchlist.prefs.v1).

Tests

  • test_keepa_workflow_integration.py — live pipeline for first 100 ASINs (RUN_KEEPA_INTEGRATION=1).

🐛 Bug Fixes

  • Missing DB columns keepa_product_fetched_at / pipeline fields — migration add_keepa_watchlist_pipeline.sql.
  • Qogita token check TypeError (offset-naive vs offset-aware datetimes).
  • Mass watchlist deletion on sync — unavailable items no longer treated as delete candidates.
  • 400 on push for GTINs not in Qogita catalog — expected; logged as warning (not in Qogita assortment).

📁 Files changed

Backend (service_omni)

  • app/api/v1/endpoints/qogita.py, keepa.py, price_alerts.py
  • app/services/qogita/qogita_client.py, watchlist_sync.py
  • app/services/keepa/keepa_client.py, scanner.py, pricing.py, storage_fees.py, constants.py, watchlist_keys.py
  • app/services/price_alerts/watchlist_cron.py, discord_notifications.py
  • app/crud/qogita.py, crud/keepa.py
  • app/schemas/qogita.py, app/models/qogita.py, app/models/keepa.py, app/models/price_alerts.py
  • app/main.py, app/api/v1/api.py
  • app/tests/test_keepa_workflow_integration.py, conftest.py

Frontend

  • src/pages/Rituals/index.jsx, QogitaWatchlistSection.jsx
  • src/pages/Statistics/Tabs/QogitaWatchlistTab.jsx, qogitaWatchlistPrefs.js
  • src/services/api/omni/qogita.js, keepa.js, priceAlerts.js
  • src/layouts/DashboardLayout — Price Alerts menu entry

Postgres

  • docker/postgres/tables/qogita_credentials.sql, qogita_watchlist_items.sql, keepa_credentials.sql
  • docker/postgres/migrations/add_qogita_schema.sql
  • docker/postgres/migrations/add_keepa_watchlist_columns.sql
  • docker/postgres/migrations/add_keepa_watchlist_pipeline.sql
  • docker/postgres/migrations/drop_keepa_product_json.sql

🔧 Database & SQL

Run on server before deploy (in order):

-- Qogita + Keepa credentials and watchlist base
\i docker/postgres/migrations/add_qogita_schema.sql

-- Keepa analysis columns on watchlist
\i docker/postgres/migrations/add_keepa_watchlist_columns.sql

-- Pipeline timestamp (fetch step)
\i docker/postgres/migrations/add_keepa_watchlist_pipeline.sql

-- Drop unused JSON blob (metrics are columnized)
\i docker/postgres/migrations/drop_keepa_product_json.sql

Or via Docker:

Get-Content docker\postgres\migrations\add_qogita_schema.sql | docker exec -i postgres psql -U <user> -d <db>
Get-Content docker\postgres\migrations\add_keepa_watchlist_columns.sql | docker exec -i postgres psql -U <user> -d <db>
Get-Content docker\postgres\migrations\add_keepa_watchlist_pipeline.sql | docker exec -i postgres psql -U <user> -d <db>
Get-Content docker\postgres\migrations\drop_keepa_product_json.sql | docker exec -i postgres psql -U <user> -d <db>

Restart service-omni after migrations. Rebuild frontend for UI changes.

Usage notes

Initial setup

  1. Run SQL migrations above.
  2. Price Alerts → Credentials — add Qogita login + Keepa API key.
  3. Sync ASINs → Fetch → Analyze → Push to Qogita (or use Scan API for all-in-one).

Crons

  • POST /api/v1/omni/price-alerts/cron/qogita/start — body: schedule, discord_webhook, margin_percent, target_quantity.
  • POST /api/v1/omni/price-alerts/cron/keepa/start — body: schedule, refresh_limit, reanalyze_limit.
  • Or use the Scheduled jobs panel in the watchlist tab (cron builder + Start/Stop).

Integration test (optional, consumes Keepa tokens):

RUN_KEEPA_INTEGRATION=1 pytest app/tests/test_keepa_workflow_integration.py -v

Inside service-omni container with credentials in DB or KEEPA_API_KEY set.

Watchlist filterGET /qogita/watchlist/?qogita_status=targets_met for rows where Qogita reports target price/qty met.

Push failures (400) — GTIN not in Qogita catalog; product may exist on Amazon but not on Qogita wholesale. Safe to skip.