From 4982adbe41eff18fe1aca74ef8f9f38e6d7c721b Mon Sep 17 00:00:00 2001 From: Mikhail Chusavitin Date: Thu, 18 Jun 2026 14:25:23 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D1=81=D0=BE=D1=80=D1=82=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D0=BA=D0=B0=D1=82=D0=B5=D0=B3=D0=BE=D1=80=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=B2=20pricing=20CSV=20=D0=B8=20=D0=B2=D0=BA=D0=BB?= =?UTF-8?q?=D0=B0=D0=B4=D0=BA=D0=B5=20=D0=A6=D0=B5=D0=BD=D0=BE=D0=BE=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20(no-BO?= =?UTF-8?q?M)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- internal/services/export.go | 25 +++++++++++++++++++++++++ web/templates/index.html | 7 ++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/internal/services/export.go b/internal/services/export.go index 365f084..d76b61d 100644 --- a/internal/services/export.go +++ b/internal/services/export.go @@ -450,7 +450,16 @@ func (s *ExportService) buildPricingExportBlock(cfg *models.Configuration, opts return block, nil } + catOrder := defaultCategoryOrder() + lotNames := make([]string, 0, len(cfg.Items)) for _, item := range cfg.Items { + if item.LotName != "" { + lotNames = append(lotNames, item.LotName) + } + } + itemCategories := s.resolveCategories(cfg.PricelistID, lotNames) + sortedItems := sortConfigItemsByCategoryMap(cfg.Items, catOrder, itemCategories) + for _, item := range sortedItems { if item.LotName == "" { continue } @@ -476,6 +485,22 @@ func (s *ExportService) buildPricingExportBlock(cfg *models.Configuration, opts return block, nil } +// sortConfigItemsByCategoryMap returns a copy of items sorted by category display order. +// categories maps lot_name → category code; catOrder maps category code → display order. +func sortConfigItemsByCategoryMap(items models.ConfigItems, catOrder map[string]int, categories map[string]string) models.ConfigItems { + sorted := make(models.ConfigItems, len(items)) + copy(sorted, items) + sort.SliceStable(sorted, func(i, j int) bool { + orderI, hasI := categoryDisplayOrder(catOrder, categories[sorted[i].LotName]) + orderJ, hasJ := categoryDisplayOrder(catOrder, categories[sorted[j].LotName]) + if hasI && hasJ { + return orderI < orderJ + } + return hasI && !hasJ + }) + return sorted +} + func applyDDPMarkup(rows []ProjectPricingExportRow, factor float64) { for i := range rows { rows[i].Estimate = scaleFloatPtr(rows[i].Estimate, factor) diff --git a/web/templates/index.html b/web/templates/index.html index d4a2181..f444d88 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -4048,7 +4048,12 @@ async function renderPricingTab() { }; if (!bomRows.length) { - cart.forEach(item => { _pushCartRow(item, false); coveredLots.add(item.lot_name); }); + const sortedByCategory = [...cart].sort((a, b) => { + const catA = (a.category || getCategoryFromLotName(a.lot_name)).toUpperCase(); + const catB = (b.category || getCategoryFromLotName(b.lot_name)).toUpperCase(); + return (categoryOrderMap[catA] || 9999) - (categoryOrderMap[catB] || 9999); + }); + sortedByCategory.forEach(item => { _pushCartRow(item, false); coveredLots.add(item.lot_name); }); return { result, coveredLots }; }