From 90ed035abdd78ff77ebfe4b126d65c25422eadb2 Mon Sep 17 00:00:00 2001 From: giovanni Date: Thu, 7 May 2026 10:56:30 +0700 Subject: [PATCH] add feed use at export excel recording --- .../controllers/recording.export.go | 70 ++++++++++++++----- .../recordings/dto/recording.dto.go | 48 +++++++++++-- .../repositories/recording.repository.go | 5 +- 3 files changed, 100 insertions(+), 23 deletions(-) diff --git a/internal/modules/production/recordings/controllers/recording.export.go b/internal/modules/production/recordings/controllers/recording.export.go index 3f5294ab..55fd2f61 100644 --- a/internal/modules/production/recordings/controllers/recording.export.go +++ b/internal/modules/production/recordings/controllers/recording.export.go @@ -77,6 +77,10 @@ func setRecordingExportColumns(file *excelize.File, sheet string) error { "Z": 22, "AA": 16, "AB": 18, + "AC": 24, + "AD": 18, + "AE": 18, + "AF": 18, } for col, width := range columnWidths { @@ -96,7 +100,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"} + verticalHeaderCols := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "Y", "Z", "AA", "AB", "AC", "AD", "AE", "AF"} for _, col := range verticalHeaderCols { if err := file.MergeCell(sheet, col+"1", col+"2"); err != nil { return err @@ -104,19 +108,23 @@ func setRecordingExportHeaders(file *excelize.File, sheet string) error { } headerValues := map[string]string{ - "A1": "No", - "B1": "Lokasi", - "C1": "Flock", - "D1": "Kandang", - "E1": "Periode", - "F1": "Kategori", - "G1": "Umur (hari)", - "H1": "Waktu Recording", - "I1": "Populasi Akhir", - "Y1": "Status Approval", - "Z1": "Catatan Approval", - "AA1": "Dibuat Oleh", - "AB1": "Tanggal Submit", + "A1": "No", + "B1": "Lokasi", + "C1": "Flock", + "D1": "Kandang", + "E1": "Periode", + "F1": "Kategori", + "G1": "Umur (hari)", + "H1": "Waktu Recording", + "I1": "Populasi Akhir", + "Y1": "Status Approval", + "Z1": "Catatan Approval", + "AA1": "Dibuat Oleh", + "AB1": "Tanggal Submit", + "AC1": "Nama Pakan", + "AD1": "Jumlah Input Pakan", + "AE1": "Jumlah Penggunaan", + "AF1": "Pending Qty", } for cell, value := range headerValues { if err := file.SetCellValue(sheet, cell, value); err != nil { @@ -230,7 +238,7 @@ func setRecordingExportHeaders(file *excelize.File, sheet string) error { return err } - return file.SetCellStyle(sheet, "A1", "AB2", headerStyle) + return file.SetCellStyle(sheet, "A1", "AF2", headerStyle) } func setRecordingExportRows(file *excelize.File, sheet string, items []dto.RecordingListDTO) error { @@ -241,6 +249,7 @@ 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", } for i, item := range items { @@ -283,6 +292,29 @@ 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)) + } + + 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, @@ -312,6 +344,10 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor 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 { @@ -339,7 +375,7 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor if err != nil { return err } - if err := file.SetCellStyle(sheet, "A3", fmt.Sprintf("AB%d", lastRow), dataCenterStyle); err != nil { + if err := file.SetCellStyle(sheet, "A3", fmt.Sprintf("AF%d", lastRow), dataCenterStyle); err != nil { return err } @@ -360,7 +396,7 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor return err } - leftColumns := []string{"B", "C", "D", "F", "G", "H", "Y", "Z", "AA", "AB"} + leftColumns := []string{"B", "C", "D", "F", "G", "H", "Y", "Z", "AA", "AB", "AC"} for _, col := range leftColumns { if err := file.SetCellStyle(sheet, col+"3", fmt.Sprintf("%s%d", col, lastRow), dataLeftStyle); err != nil { return err diff --git a/internal/modules/production/recordings/dto/recording.dto.go b/internal/modules/production/recordings/dto/recording.dto.go index b92d5a3c..a4e3ee47 100644 --- a/internal/modules/production/recordings/dto/recording.dto.go +++ b/internal/modules/production/recordings/dto/recording.dto.go @@ -92,13 +92,20 @@ type RecordingRelationDTO struct { Approval approvalDTO.ApprovalRelationDTO `json:"approval"` } +type RecordingFeedUsageDTO struct { + ProductName string `json:"product_name"` + UsageAmount float64 `json:"usage_amount"` + PendingQty float64 `json:"pending_qty"` +} + type RecordingListDTO struct { RecordingRelationDTO - CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Kandang *RecordingKandangDTO `json:"kandang,omitempty"` - Location *RecordingLocationDTO `json:"location,omitempty"` + CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Kandang *RecordingKandangDTO `json:"kandang,omitempty"` + Location *RecordingLocationDTO `json:"location,omitempty"` + FeedUsage []RecordingFeedUsageDTO `json:"feed_usage,omitempty"` } type RecordingDetailDTO struct { @@ -192,6 +199,36 @@ func ToRecordingStockDTOs(stocks []entity.RecordingStock) []RecordingStockDTO { return result } +func ToRecordingFeedUsageDTOs(stocks []entity.RecordingStock) []RecordingFeedUsageDTO { + return toRecordingFeedUsageDTOs(stocks) +} + +func toRecordingFeedUsageDTOs(stocks []entity.RecordingStock) []RecordingFeedUsageDTO { + result := make([]RecordingFeedUsageDTO, 0, len(stocks)) + for _, s := range stocks { + productName := "" + if s.ProductWarehouse.Product.Id != 0 { + productName = s.ProductWarehouse.Product.Name + } + + var usageAmount float64 + if s.UsageQty != nil { + usageAmount = *s.UsageQty + } + var pendingQty float64 + if s.PendingQty != nil { + pendingQty = *s.PendingQty + } + + result = append(result, RecordingFeedUsageDTO{ + ProductName: productName, + UsageAmount: usageAmount, + PendingQty: pendingQty, + }) + } + return result +} + func ToRecordingEggDTOs(eggs []entity.RecordingEgg) []RecordingEggDTO { result := make([]RecordingEggDTO, len(eggs)) for i, egg := range eggs { @@ -222,6 +259,7 @@ func toRecordingListDTO(e entity.Recording) RecordingListDTO { CreatedUser: createdUser, Kandang: recordingKandangDTO(e), Location: recordingKandangLocationDTO(e), + FeedUsage: toRecordingFeedUsageDTOs(e.Stocks), } } diff --git a/internal/modules/production/recordings/repositories/recording.repository.go b/internal/modules/production/recordings/repositories/recording.repository.go index ceb747ae..ca989359 100644 --- a/internal/modules/production/recordings/repositories/recording.repository.go +++ b/internal/modules/production/recordings/repositories/recording.repository.go @@ -147,7 +147,10 @@ func (r *RecordingRepositoryImpl) WithRelationsList(db *gorm.DB) *gorm.DB { Preload("ProjectFlockKandang.Kandang"). Preload("ProjectFlockKandang.Kandang.Location"). Preload("ProjectFlockKandang.ProjectFlock"). - Preload("ProjectFlockKandang.ProjectFlock.ProductionStandard") + Preload("ProjectFlockKandang.ProjectFlock.ProductionStandard"). + Preload("Stocks"). + Preload("Stocks.ProductWarehouse"). + Preload("Stocks.ProductWarehouse.Product") } func (r *RecordingRepositoryImpl) latestApprovalSubQuery(db *gorm.DB) *gorm.DB {