Merge branch 'fix/record' into 'development'

[FIX][BE]: adjust export recording jumlah sapronak

See merge request mbugroup/lti-api!519
This commit is contained in:
Giovanni Gabriel Septriadi
2026-05-08 04:29:04 +00:00
@@ -79,8 +79,6 @@ func setRecordingExportColumns(file *excelize.File, sheet string) error {
"AB": 18, "AB": 18,
"AC": 24, "AC": 24,
"AD": 18, "AD": 18,
"AE": 18,
"AF": 18,
} }
for col, width := range columnWidths { 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 { 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 { for _, col := range verticalHeaderCols {
if err := file.MergeCell(sheet, col+"1", col+"2"); err != nil { if err := file.MergeCell(sheet, col+"1", col+"2"); err != nil {
return err return err
@@ -121,10 +119,8 @@ func setRecordingExportHeaders(file *excelize.File, sheet string) error {
"Z1": "Catatan Approval", "Z1": "Catatan Approval",
"AA1": "Dibuat Oleh", "AA1": "Dibuat Oleh",
"AB1": "Tanggal Submit", "AB1": "Tanggal Submit",
"AC1": "Nama Pakan", "AC1": "Nama Sapronak",
"AD1": "Jumlah Input Pakan", "AD1": "Jumlah Input Sapronak",
"AE1": "Jumlah Penggunaan",
"AF1": "Pending Qty",
} }
for cell, value := range headerValues { for cell, value := range headerValues {
if err := file.SetCellValue(sheet, cell, value); err != nil { if err := file.SetCellValue(sheet, cell, value); err != nil {
@@ -238,7 +234,7 @@ func setRecordingExportHeaders(file *excelize.File, sheet string) error {
return err 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 { 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{ columns := []string{
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "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", "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 { currentRow := 3
rowNumber := i + 3 type rowRange struct{ start, end int }
itemRanges := make([]rowRange, 0, len(items))
for i, item := range items {
fcrStd := 0.0 fcrStd := 0.0
if item.ProjectFlock.Fcr != nil { if item.ProjectFlock.Fcr != nil {
fcrStd = item.ProjectFlock.Fcr.FcrStd 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) createdBy = safeExportText(item.Approval.ActionBy.Name)
} }
// Build feed usage columns — concatenate multiple feeds with newline // Expand recordings into one row per sapronak
feedNames := make([]string, 0, len(item.FeedUsage)) type sapronakRow struct {
usageAmounts := make([]string, 0, len(item.FeedUsage)) name string
pendingQtys := make([]string, 0, len(item.FeedUsage)) input string
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))
} }
sapronaks := make([]sapronakRow, 0)
feedNameCol := "-" if len(item.FeedUsage) > 0 {
usageCol := "-" for _, fu := range item.FeedUsage {
pendingCol := "-" sapronaks = append(sapronaks, sapronakRow{
inputCol := "-" name: safeExportText(fu.ProductName),
if len(feedNames) > 0 { input: formatNumberID(fu.UsageAmount+fu.PendingQty, 2, true),
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
} }
} 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{ dataCenterStyle, err := file.NewStyle(&excelize.Style{
Alignment: &excelize.Alignment{ Alignment: &excelize.Alignment{
Horizontal: "center", Horizontal: "center",
@@ -375,7 +379,7 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor
if err != nil { if err != nil {
return err 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 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 return nil
} }