diff --git a/internal/services/export.go b/internal/services/export.go index 7bef03f..1db4025 100644 --- a/internal/services/export.go +++ b/internal/services/export.go @@ -388,17 +388,37 @@ func (s *ExportService) buildPricingExportBlock(cfg *models.Configuration, opts description = componentDescriptions[rowMappings[0].LotName] } - pricingRow := ProjectPricingExportRow{ - LotDisplay: formatLotDisplay(rowMappings), - VendorPN: row.VendorPartnumber, - Description: description, - Quantity: exportPositiveInt(row.Quantity, 1), - BOMTotal: vendorRowTotal(row), - Estimate: computeMappingTotal(priceMap, rowMappings, row.Quantity, func(p pricingLevels) *float64 { return p.Estimate }), - Stock: computeMappingTotal(priceMap, rowMappings, row.Quantity, func(p pricingLevels) *float64 { return p.Stock }), - Competitor: computeMappingTotal(priceMap, rowMappings, row.Quantity, func(p pricingLevels) *float64 { return p.Competitor }), + if len(rowMappings) == 0 { + block.Rows = append(block.Rows, ProjectPricingExportRow{ + LotDisplay: "н/д", + VendorPN: row.VendorPartnumber, + Description: description, + Quantity: exportPositiveInt(row.Quantity, 1), + BOMTotal: vendorRowTotal(row), + }) + continue + } + + // One export row per LOT mapping so that bundles (1 PN → N LOTs) appear + // as separate lines, matching the frontend pricing table layout. + pnQty := exportPositiveInt(row.Quantity, 1) + for i, mapping := range rowMappings { + lotQty := pnQty * mapping.QuantityPerPN + var bomTotal *float64 + if i == 0 { + bomTotal = vendorRowTotal(row) + } + block.Rows = append(block.Rows, ProjectPricingExportRow{ + LotDisplay: mapping.LotName, + VendorPN: row.VendorPartnumber, + Description: description, + Quantity: lotQty, + BOMTotal: bomTotal, + Estimate: computeSingleLotTotal(priceMap, mapping.LotName, lotQty, func(p pricingLevels) *float64 { return p.Estimate }), + Stock: computeSingleLotTotal(priceMap, mapping.LotName, lotQty, func(p pricingLevels) *float64 { return p.Stock }), + Competitor: computeSingleLotTotal(priceMap, mapping.LotName, lotQty, func(p pricingLevels) *float64 { return p.Competitor }), + }) } - block.Rows = append(block.Rows, pricingRow) } for _, item := range cfg.Items { @@ -696,6 +716,14 @@ func computeMappingTotal(priceMap map[string]pricingLevels, mappings []localdb.V return floatPtr(total) } +func computeSingleLotTotal(priceMap map[string]pricingLevels, lotName string, qty int, selector func(pricingLevels) *float64) *float64 { + price := selector(priceMap[lotName]) + if price == nil || *price <= 0 { + return nil + } + return floatPtr(*price * float64(qty)) +} + func totalForUnitPrice(unitPrice *float64, quantity int) *float64 { if unitPrice == nil || *unitPrice <= 0 { return nil @@ -789,16 +817,6 @@ func pricingConfigSummaryRow(cfg ProjectPricingExportConfig, opts ProjectPricing return record } -func formatLotDisplay(mappings []localdb.VendorSpecLotMapping) string { - switch len(mappings) { - case 0: - return "н/д" - case 1: - return mappings[0].LotName - default: - return fmt.Sprintf("%s +%d", mappings[0].LotName, len(mappings)-1) - } -} func formatMoneyValue(value *float64) string { if value == nil {