From ecac9275837b55ef91872ce0a05346c30d1645a8 Mon Sep 17 00:00:00 2001 From: giovanni Date: Fri, 8 May 2026 11:26:26 +0700 Subject: [PATCH] adjust export recording jumlah sapronak --- .../controllers/recording.export.go | 201 +++++++++++------- 1 file changed, 127 insertions(+), 74 deletions(-) diff --git a/internal/modules/production/recordings/controllers/recording.export.go b/internal/modules/production/recordings/controllers/recording.export.go index 55fd2f61..fc514fbd 100644 --- a/internal/modules/production/recordings/controllers/recording.export.go +++ b/internal/modules/production/recordings/controllers/recording.export.go @@ -79,8 +79,6 @@ func setRecordingExportColumns(file *excelize.File, sheet string) error { "AB": 18, "AC": 24, "AD": 18, - "AE": 18, - "AF": 18, } for col, width := range columnWidths { @@ -100,7 +98,7 @@ func setRecordingExportColumns(file *excelize.File, sheet string) error { } func setRecordingExportHeaders(file *excelize.File, sheet string) error { - verticalHeaderCols := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "Y", "Z", "AA", "AB", "AC", "AD", "AE", "AF"} + verticalHeaderCols := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "Y", "Z", "AA", "AB", "AC", "AD"} for _, col := range verticalHeaderCols { if err := file.MergeCell(sheet, col+"1", col+"2"); err != nil { return err @@ -121,10 +119,8 @@ func setRecordingExportHeaders(file *excelize.File, sheet string) error { "Z1": "Catatan Approval", "AA1": "Dibuat Oleh", "AB1": "Tanggal Submit", - "AC1": "Nama Pakan", - "AD1": "Jumlah Input Pakan", - "AE1": "Jumlah Penggunaan", - "AF1": "Pending Qty", + "AC1": "Nama Sapronak", + "AD1": "Jumlah Input Sapronak", } for cell, value := range headerValues { if err := file.SetCellValue(sheet, cell, value); err != nil { @@ -238,7 +234,7 @@ func setRecordingExportHeaders(file *excelize.File, sheet string) error { return err } - return file.SetCellStyle(sheet, "A1", "AF2", headerStyle) + return file.SetCellStyle(sheet, "A1", "AD2", headerStyle) } func setRecordingExportRows(file *excelize.File, sheet string, items []dto.RecordingListDTO) error { @@ -249,12 +245,14 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor columns := []string{ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB", - "AC", "AD", "AE", "AF", + "AC", "AD", } - for i, item := range items { - rowNumber := i + 3 + currentRow := 3 + type rowRange struct{ start, end int } + itemRanges := make([]rowRange, 0, len(items)) + for i, item := range items { fcrStd := 0.0 if item.ProjectFlock.Fcr != nil { fcrStd = item.ProjectFlock.Fcr.FcrStd @@ -292,73 +290,79 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor createdBy = safeExportText(item.Approval.ActionBy.Name) } - // Build feed usage columns — concatenate multiple feeds with newline - feedNames := make([]string, 0, len(item.FeedUsage)) - usageAmounts := make([]string, 0, len(item.FeedUsage)) - pendingQtys := make([]string, 0, len(item.FeedUsage)) - inputQtys := make([]string, 0, len(item.FeedUsage)) - for _, fu := range item.FeedUsage { - feedNames = append(feedNames, safeExportText(fu.ProductName)) - usageAmounts = append(usageAmounts, formatNumberID(fu.UsageAmount, 2, true)) - pendingQtys = append(pendingQtys, formatNumberID(fu.PendingQty, 2, true)) - inputQtys = append(inputQtys, formatNumberID(fu.UsageAmount+fu.PendingQty, 2, true)) + // Expand recordings into one row per sapronak + type sapronakRow struct { + name string + input string } - - feedNameCol := "-" - usageCol := "-" - pendingCol := "-" - inputCol := "-" - if len(feedNames) > 0 { - feedNameCol = strings.Join(feedNames, "\n") - usageCol = strings.Join(usageAmounts, "\n") - pendingCol = strings.Join(pendingQtys, "\n") - inputCol = strings.Join(inputQtys, "\n") - } - - rowValues := []interface{}{ - i + 1, - locationName, - safeExportText(item.ProjectFlock.FlockName), - kandangName, - item.ProjectFlock.Period, - formatCategoryLabel(item.ProjectFlock.ProjectFlockCategory), - formatAgeLabel(item), - formatDateIndonesian(item.RecordDatetime), - formatNumberID(item.ProjectFlock.TotalChickQty, 0, false), - formatNumberID(item.FcrValue, 2, true), - formatNumberID(fcrStd, 2, true), - formatNumberID(item.FeedIntake, 2, true), - formatNumberID(feedIntakeStd, 2, true), - formatPercentID(item.CumDepletionRate, 2), - formatPercentID(maxDepletionStd, 2), - formatNumberID(item.TotalDepletionQty, 2, true), - formatNumberID(item.EggMass, 2, true), - formatNumberID(eggMassStd, 2, true), - formatNumberID(item.EggWeight, 2, true), - formatNumberID(eggWeightStd, 2, true), - formatPercentID(item.HenDay, 2), - formatPercentID(henDayStd, 2), - formatPercentID(item.HenHouse, 2), - formatPercentID(henHouseStd, 2), - formatApprovalStatus(item), - safeExportText(pointerString(item.Approval.Notes)), - createdBy, - formatDateIndonesian(item.CreatedAt), - feedNameCol, // AC - inputCol, // AD - Jumlah Input Pakan - usageCol, // AE - Jumlah Penggunaan - pendingCol, // AF - Pending Qty - } - - for idx, col := range columns { - cell := fmt.Sprintf("%s%d", col, rowNumber) - if err := file.SetCellValue(sheet, cell, rowValues[idx]); err != nil { - return err + sapronaks := make([]sapronakRow, 0) + if len(item.FeedUsage) > 0 { + for _, fu := range item.FeedUsage { + sapronaks = append(sapronaks, sapronakRow{ + name: safeExportText(fu.ProductName), + input: formatNumberID(fu.UsageAmount+fu.PendingQty, 2, true), + }) } + } else { + sapronaks = append(sapronaks, sapronakRow{name: "-", input: "-"}) } + + groupStart := currentRow + + for sIdx, s := range sapronaks { + if sIdx == 0 { + rowValues := []interface{}{ + i + 1, // A + locationName, // B + safeExportText(item.ProjectFlock.FlockName), // C + kandangName, // D + item.ProjectFlock.Period, // E + formatCategoryLabel(item.ProjectFlock.ProjectFlockCategory), // F + formatAgeLabel(item), // G + formatDateIndonesian(item.RecordDatetime), // H + formatNumberID(item.ProjectFlock.TotalChickQty, 0, false), // I + formatNumberID(item.FcrValue, 2, true), // J + formatNumberID(fcrStd, 2, true), // K + formatNumberID(item.FeedIntake, 2, true), // L + formatNumberID(feedIntakeStd, 2, true), // M + formatPercentID(item.CumDepletionRate, 2), // N + formatPercentID(maxDepletionStd, 2), // O + formatNumberID(item.TotalDepletionQty, 2, true), // P + formatNumberID(item.EggMass, 2, true), // Q + formatNumberID(eggMassStd, 2, true), // R + formatNumberID(item.EggWeight, 2, true), // S + formatNumberID(eggWeightStd, 2, true), // T + formatPercentID(item.HenDay, 2), // U + formatPercentID(henDayStd, 2), // V + formatPercentID(item.HenHouse, 2), // W + formatPercentID(henHouseStd, 2), // X + formatApprovalStatus(item), // Y + safeExportText(pointerString(item.Approval.Notes)), // Z + createdBy, // AA + formatDateIndonesian(item.CreatedAt), // AB + s.name, // AC + s.input, // AD + } + + for idx, col := range columns { + cell := fmt.Sprintf("%s%d", col, currentRow) + if err := file.SetCellValue(sheet, cell, rowValues[idx]); err != nil { + return err + } + } + } else { + file.SetCellValue(sheet, fmt.Sprintf("AC%d", currentRow), s.name) + file.SetCellValue(sheet, fmt.Sprintf("AD%d", currentRow), s.input) + } + + currentRow++ + } + + itemRanges = append(itemRanges, rowRange{groupStart, currentRow - 1}) } - lastRow := len(items) + 2 + lastRow := currentRow - 1 + dataCenterStyle, err := file.NewStyle(&excelize.Style{ Alignment: &excelize.Alignment{ Horizontal: "center", @@ -375,7 +379,7 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor if err != nil { return err } - if err := file.SetCellStyle(sheet, "A3", fmt.Sprintf("AF%d", lastRow), dataCenterStyle); err != nil { + if err := file.SetCellStyle(sheet, "A3", fmt.Sprintf("AD%d", lastRow), dataCenterStyle); err != nil { return err } @@ -403,6 +407,55 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor } } + // Apply bottom border on the last sapronak row of each recording group + // Separate styles to preserve alignment (AC=left, AD=center) and thin borders + borderBottomLeftStyle, err := file.NewStyle(&excelize.Style{ + Alignment: &excelize.Alignment{ + Horizontal: "left", + Vertical: "center", + WrapText: true, + }, + Border: []excelize.Border{ + {Type: "left", Color: "E6E6E6", Style: 1}, + {Type: "top", Color: "E6E6E6", Style: 1}, + {Type: "bottom", Color: "999999", Style: 2}, + {Type: "right", Color: "E6E6E6", Style: 1}, + }, + }) + if err != nil { + return err + } + + borderBottomCenterStyle, err := file.NewStyle(&excelize.Style{ + Alignment: &excelize.Alignment{ + Horizontal: "center", + Vertical: "center", + WrapText: true, + }, + Border: []excelize.Border{ + {Type: "left", Color: "E6E6E6", Style: 1}, + {Type: "top", Color: "E6E6E6", Style: 1}, + {Type: "bottom", Color: "999999", Style: 2}, + {Type: "right", Color: "E6E6E6", Style: 1}, + }, + }) + if err != nil { + return err + } + + mergeCols := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", + "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB", + } + for _, rng := range itemRanges { + if rng.end > rng.start { + for _, col := range mergeCols { + file.MergeCell(sheet, fmt.Sprintf("%s%d", col, rng.start), fmt.Sprintf("%s%d", col, rng.end)) + } + } + file.SetCellStyle(sheet, fmt.Sprintf("AC%d", rng.end), fmt.Sprintf("AC%d", rng.end), borderBottomLeftStyle) + file.SetCellStyle(sheet, fmt.Sprintf("AD%d", rng.end), fmt.Sprintf("AD%d", rng.end), borderBottomCenterStyle) + } + return nil }