Replace competitor discount with price_uplift; stock pricelist detail UI
- Drop `expected_discount_pct`, add `price_uplift DECIMAL(8,4) DEFAULT 1.3` to `qt_competitors` (migration 040); formula: effective_price = price / uplift - Extend `LoadLotMetrics` to return per-PN qty map (`pnQtysByLot`) - Add virtual fields `CompetitorNames`, `PriceSpreadPct`, `PartnumberQtys` to `PricelistItem`; populate via `enrichWarehouseItems` / `enrichCompetitorItems` - Competitor quotes filtered to qty > 0 before lot resolution - New "stock layout" on pricelist detail page for warehouse/competitor: Partnumbers column (PN + qty, only qty>0), Поставщик column, no Настройки/Доступно - Spread badge ±N% shown next to price for competitor rows - Bible updated: pricelist.md, history.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -545,6 +545,63 @@ Warehouse and competitor pricelist items now store explicit, meaningful values i
|
||||
|
||||
---
|
||||
|
||||
## 2026-03-13: Price Uplift Replaces Competitor Discount; Stock Pricelist Detail UI
|
||||
|
||||
### Decision
|
||||
|
||||
1. Replaced `expected_discount_pct` with `price_uplift` in competitor model.
|
||||
2. Completely reworked pricelist detail page for warehouse and competitor sources into a compact "stock layout".
|
||||
|
||||
### What changed
|
||||
|
||||
**Competitor uplift**:
|
||||
- `qt_competitors.expected_discount_pct` dropped; `price_uplift DECIMAL(8,4) DEFAULT 1.3` added (migration `040_competitor_uplift.sql`).
|
||||
- Formula at pricelist build time: `effective_price = quote_price / price_uplift`.
|
||||
- `internal/models/competitor.go`: `PriceUplift float64`.
|
||||
- `internal/handlers/competitor.go`: Create/Update endpoints use `price_uplift`; default 1.3 when ≤0.
|
||||
- `internal/services/competitor_import.go`: `RebuildPricelist` and `buildCompetitorPricelistItems` use uplift divisor.
|
||||
- `web/templates/competitors.html`, `web/static/js/competitors.js`: UI field renamed "Аплифт", uses `price_uplift`.
|
||||
|
||||
**Per-lot enrichment**:
|
||||
- `internal/warehouse/snapshot.go` `LoadLotMetrics` returns 3 maps: `qtyByLot`, `partnumbersByLot`, `pnQtysByLot` (PN → qty).
|
||||
- `internal/models/pricelist.go` `PricelistItem` added virtual fields: `CompetitorNames []string`, `PriceSpreadPct *float64`, `PartnumberQtys map[string]float64`.
|
||||
- `internal/repository/pricelist.go` added `enrichWarehouseItems` (fills PartnumberQtys from stock_log) and `enrichCompetitorItems` (fills CompetitorNames, Partnumbers from quotes with qty>0, PriceSpreadPct).
|
||||
- `internal/repository/competitor.go` `GetLatestQuotesByPN`: added `AND qty > 0` filter.
|
||||
- `internal/handlers/pricing_lots.go`: updated to 4-return `LoadLotMetrics` call.
|
||||
|
||||
**Pricelist detail UI**:
|
||||
- Two layouts: `estimate` (with Настройки) and stock (`warehouse`/`competitor`, with Partnumbers + Поставщик columns, no Настройки/Доступно).
|
||||
- Partnumbers shown only if `partnumber_qtys[pn] > 0`; format `PN (qty шт.)`, max 4 + overflow badge.
|
||||
- Поставщик: blue competitor name badges (competitor) or grey "склад" text (warehouse).
|
||||
- Price spread `±N%` badge in amber next to price for competitor rows.
|
||||
- `web/static/js/pricelist_detail.js`: full rendering overhaul.
|
||||
- `web/templates/pricelist_detail.html`: column ids for JS toggle.
|
||||
|
||||
### Rationale
|
||||
|
||||
- "Discount %" was ambiguous and error-prone (mixed % vs ratio). Uplift as a divisor is explicit.
|
||||
- Default uplift 1.3 ≈ 23% margin, which matches the actual operational baseline.
|
||||
- Stock layout needed because warehouse/competitor pricelists have fundamentally different metadata (suppliers, per-PN stock) than estimate pricelists.
|
||||
|
||||
### Constraints
|
||||
|
||||
- `price_uplift` must be > 0; if omitted in API it defaults to 1.3.
|
||||
- Only PNs with `qty > 0` appear in Partnumbers column (no phantom entries from catalog).
|
||||
- `LoadLotMetrics` always returns 4 values; callers that don't need pnQtysByLot must use `_`.
|
||||
|
||||
### Files
|
||||
|
||||
- Migration: `migrations/040_competitor_uplift.sql`
|
||||
- Models: `internal/models/competitor.go`, `internal/models/pricelist.go`
|
||||
- Repository: `internal/repository/pricelist.go`, `internal/repository/competitor.go`
|
||||
- Warehouse: `internal/warehouse/snapshot.go`
|
||||
- Handlers: `internal/handlers/competitor.go`, `internal/handlers/pricing_lots.go`
|
||||
- Services: `internal/services/competitor_import.go`
|
||||
- UI: `web/templates/pricelist_detail.html`, `web/templates/competitors.html`, `web/static/js/pricelist_detail.js`, `web/static/js/competitors.js`
|
||||
- Docs: `bible-local/pricelist.md`
|
||||
|
||||
---
|
||||
|
||||
## Architecture Conventions
|
||||
|
||||
> All future architectural decisions must be documented here with:
|
||||
|
||||
Reference in New Issue
Block a user