From 5359ae6deda239ccd7dde9ddceb0b4ad7a92ea58 Mon Sep 17 00:00:00 2001 From: Mikhail Chusavitin Date: Wed, 17 Jun 2026 12:20:13 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20pricing-=D1=82=D0=B0=D0=B1=D0=BB=D0=B8?= =?UTF-8?q?=D1=86=D0=B0=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D1=83?= =?UTF-8?q?=D0=B5=D1=82=20qty=20=D0=B8=D0=B7=20=D0=BA=D0=BE=D1=80=D0=B7?= =?UTF-8?q?=D0=B8=D0=BD=D1=8B=20(source=20of=20truth)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ценообразование показывало неверное количество для LOT-ов с bundle (lot_qty_per_pn > 1) или устаревшим quantity_per_pn в vendor_spec. Итог Estimate расходился с Estimate-табом. Теперь qty берётся из корзины если LOT там присутствует; BOM-расчёт (row.quantity × lot_qty_per_pn) остаётся fallback для ещё не применённых строк. Co-Authored-By: Claude Sonnet 4.6 --- web/templates/index.html | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/web/templates/index.html b/web/templates/index.html index f22dfd3..d4a2181 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -3953,6 +3953,9 @@ async function renderPricingTab() { }; // Collect LOTs to price: from BOM rows (resolved) or from cart + // Use cart quantity when available (source of truth); fall back to BOM-computed quantity. + const _cartQtyMap = {}; + cart.forEach(item => { if (item?.lot_name) _cartQtyMap[item.lot_name] = item.quantity; }); let itemsForPriceLevels = []; if (bomRows.length) { const seen = new Set(); @@ -3961,13 +3964,13 @@ async function renderPricingTab() { const allocs = _getRowAllocations(row).filter(a => a.lot_name && _bomLotValid(a.lot_name) && a.quantity >= 1); if (baseLot && !seen.has(baseLot)) { seen.add(baseLot); - itemsForPriceLevels.push({ lot_name: baseLot, quantity: row.quantity * _getRowLotQtyPerPN(row) }); + itemsForPriceLevels.push({ lot_name: baseLot, quantity: _cartQtyMap[baseLot] ?? (row.quantity * _getRowLotQtyPerPN(row)) }); } if (allocs.length) { allocs.forEach(a => { if (!seen.has(a.lot_name)) { seen.add(a.lot_name); - itemsForPriceLevels.push({ lot_name: a.lot_name, quantity: row.quantity * a.quantity }); + itemsForPriceLevels.push({ lot_name: a.lot_name, quantity: _cartQtyMap[a.lot_name] ?? (row.quantity * a.quantity) }); } }); } @@ -4020,6 +4023,8 @@ async function renderPricingTab() { // ─── Build shared row data (unit prices for display, totals for math) ──── // Each BOM row is exploded into per-LOT sub-rows; grouped by vendor PN via groupStart/groupSize. + const cartQtyMap = {}; + cart.forEach(item => { if (item?.lot_name) cartQtyMap[item.lot_name] = item.quantity; }); const _buildRows = () => { const result = []; const coveredLots = new Set(); @@ -4064,7 +4069,7 @@ async function renderPricingTab() { if (baseLot) { const u = _getUnitPrices(priceMap[baseLot]); const lotQty = _getRowLotQtyPerPN(row); - const qty = row.quantity * lotQty; + const qty = cartQtyMap[baseLot] ?? (row.quantity * lotQty); subRows.push({ lotCell: escapeHtml(baseLot), lotText: baseLot, qty, estUnit: u.estUnit > 0 ? u.estUnit : 0, @@ -4076,7 +4081,7 @@ async function renderPricingTab() { } allocs.forEach(a => { const u = _getUnitPrices(priceMap[a.lot_name]); - const qty = row.quantity * a.quantity; + const qty = cartQtyMap[a.lot_name] ?? (row.quantity * a.quantity); subRows.push({ lotCell: escapeHtml(a.lot_name), lotText: a.lot_name, qty, estUnit: u.estUnit > 0 ? u.estUnit : 0,