New Penner Management page (/penner-management) with sales analytics, article KPIs, per-article price/sales insights, and a dedicated Omni API in service_omni.
Price-wave reconstruction fixed: adaptation dates are sorted chronologically and each wave applies a consistent 2% step from the reconstructed original price.
Prod β local DB sync scripts for selected tables (repricer, orders, shopping list) via SSH.
Frontend caching for Penner API responses (10-minute localStorage TTL) and a global dark mode with per-user preference persistence.
GET /penner/sales?period=β¦ β attributed Amazon/Plenty order lines after the first price reduction; periods: yesterday, 7d, 30d, Q1βQ4.
GET /penner/articles β penner articles with stock, 30d sales, capital bound, channel prices (original vs current), reduction waves, and sortable columns.
GET /penner/articles/{sku}/insights β price timeline per wave/channel and sales segmented before/after each adaptation (365-day window).
Joins repricer_articles (+ channel detail tables) with amazon_orders, plenty_orders, and shopping_table.
Penner Management (frontend):
Sales tab: period chips, gross-revenue summary, order-line table, and an info alert explaining attribution rules.
Articles tab: expandable channel prices, Bestandsanalyse (days since last sale with colour buckets), sort chips, and chart icon for per-article insights.
Article insights dialog: stacked bar chart of sales quantity (Stk.) by phase (before/after each wave), price step chart, and chronological wave list.
Navigation entry Penner Management enabled in the dashboard sidebar.
Local DB sync (scripts/db-sync/):
sync-from-prod.ps1 / sync-from-prod.sh β SSH dump from prod Postgres, truncate local tables, restore; configurable via config.local.env.
Price waves: unsorted price_adaptation_dates no longer map to the wrong wave index; waves are ordered by date and prices decrease monotonically (2% per step).
Price ratios: fixed _remaining_wave_multiplier slice so explicit per-wave ratios (e.g. [0.98, 0.98, 0.98]) no longer swap intermediate wave prices.
Penner filtering: scalar or invalid price_adaptation_dates values are skipped in Python instead of causing jsonb_array_length errors.
Penner recalc guards: zero ratios values no longer cause division-by-zero in wave history reconstruction.
DB sync (PowerShell): fixed SSH remote-command invocation ($Target parameter) and Unicode dash characters that broke script parsing on Windows.