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 }; }