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,
"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
}