Files
QuoteForge/bible-local/decisions/2026-07-10-world-pricelist-fallback.md
T
Mikhail ChusavitinandClaude Opus 4.8 70a3ff255f feat: world-прайслист как заглушка для отсутствующих цен
Там, где для LOT нет цены в estimate/warehouse/competitor, подставляется
цена из world-прайслиста. Такие ячейки в таблицах «Цена покупки»/«Цена продажи»
подсвечиваются (amber), участвуют в «Итого» и убирают красную «*».
В CSV-экспорте добавлена колонка «Заглушка (world)» с перечнем столбцов,
где сработал фолбэк.

Добавлены Tx-версии GetLatestLocalPricelistBySource/GetLocalPricesForLots,
чтобы резолв прайслистов внутри транзакции не дедлочил single-connection пул.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:25:46 +03:00

2.5 KiB

Decision: world pricelist as a fallback stub for missing prices

Date: 2026-07-10 Status: active

Context

Four pricelist source values sync from the server: estimate, warehouse, competitor, world. Only the first three were ever read — world synced into local_pricelists / local_pricelist_items but nothing consumed it, so LOTs with no warehouse/competitor price showed as "—" in the "Ценообразование" tab, which also broke the "Итого" coverage (red "*").

Decision

world is a fallback stub, applied per-column, only when the native price for that column is missing or ≤ 0:

  • Resolution order per column: native source price → world price → still missing.
  • Applies to all three Buy columns (Estimate, Склад, Конкуренты), the Sale table (same unit prices, multiplied by the existing sale coefficients), and CSV export.
  • A world-fallback price counts toward "Итого" like a native price (closes the red "*" gap).
  • UI marks only the specific cell that used a fallback, with bg-amber-50 text-amber-700 (amber was chosen because applyCustomPrice() strips gray/green/red/blue text-color classes via regex on vendor-price cells — amber survives that).
  • CSV keeps price values untouched (no suffixes) and adds one trailing comment column, "Заглушка (world)", listing which columns used the fallback per row (e.g. world: Stock, Конкуренты), always present regardless of export options so column counts stay aligned across header/rows/summary.
  • world is resolved independently in each call site (quote.go CalculatePriceLevels, export.go resolvePricingTotals) via "explicit ID → latest active → latest local", same pattern as the other three sources. It is intentionally NOT added to levelBySource / ResolvedPricelistIDs as a fourth on-screen level.
  • NormalizePricelistSource("world") returns PricelistSourceWorld, not estimate — it must not collapse into the estimate source.

Consequences

  • PriceLevelsItem (API POST /api/quote/price-levels) carries three new bools: estimate_from_world, warehouse_from_world, competitor_from_world.
  • price_missing on that endpoint only lists a source when BOTH the native and the world price are unavailable.
  • No new config-level column (e.g. world_pricelist_id) — world always resolves to "latest active world pricelist," not a per-configuration pin.
  • If no world pricelist exists locally, behavior is unchanged (missing prices stay "—", no error).