From fc49cef781f82d019821a181eaae9ebe60038f03 Mon Sep 17 00:00:00 2001 From: ragilap Date: Sun, 14 Dec 2025 23:15:30 +0700 Subject: [PATCH 1/3] add counting hpp-expedition by project --- .../controllers/closing.controller.go | 32 ++++++++++++ .../closings/dto/closingExpedition.dto.go | 18 +++++++ .../repositories/closing.repository.go | 49 +++++++++++++++++++ internal/modules/closings/route.go | 1 + .../closings/services/closing.service.go | 43 ++++++++++++++++ 5 files changed, 143 insertions(+) create mode 100644 internal/modules/closings/dto/closingExpedition.dto.go diff --git a/internal/modules/closings/controllers/closing.controller.go b/internal/modules/closings/controllers/closing.controller.go index dc39a666..1f61a775 100644 --- a/internal/modules/closings/controllers/closing.controller.go +++ b/internal/modules/closings/controllers/closing.controller.go @@ -188,3 +188,35 @@ func (u *ClosingController) GetClosingSapronak(c *fiber.Ctx) error { Data: result, }) } + +func (u *ClosingController) GetExpeditionHPP(c *fiber.Ctx) error { + param := c.Params("project_flock_id") + + projectFlockID, err := strconv.Atoi(param) + if err != nil || projectFlockID <= 0 { + return fiber.NewError(fiber.StatusBadRequest, "Invalid Project Flock Id") + } + + var projectFlockKandangID *uint + if raw := c.Query("project_flock_kandang_id"); raw != "" { + idInt, convErr := strconv.Atoi(raw) + if convErr != nil || idInt <= 0 { + return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_kandang_id") + } + idUint := uint(idInt) + projectFlockKandangID = &idUint + } + + result, err := u.ClosingService.GetExpeditionHPP(c, uint(projectFlockID), projectFlockKandangID) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.Success{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get expedition HPP successfully", + Data: result, + }) +} diff --git a/internal/modules/closings/dto/closingExpedition.dto.go b/internal/modules/closings/dto/closingExpedition.dto.go new file mode 100644 index 00000000..f1b8628b --- /dev/null +++ b/internal/modules/closings/dto/closingExpedition.dto.go @@ -0,0 +1,18 @@ +package dto + +// ExpeditionCostItemDTO merepresentasikan biaya ekspedisi per vendor. +type ExpeditionCostItemDTO struct { + Id uint64 `json:"id"` + ExpeditionVendorID uint64 `json:"expedition_vendor_id"` + ExpeditionVendorName string `json:"expedition_vendor_name"` + Qty float64 `json:"qty"` + UnitPrice float64 `json:"unit_price"` + HPPAmount float64 `json:"hpp_amount"` +} + +// ExpeditionHPPDTO adalah struktur response utama untuk HPP Ekspedisi. +type ExpeditionHPPDTO struct { + ExpeditionCosts []ExpeditionCostItemDTO `json:"expedition_costs"` + TotalHPPAmount float64 `json:"total_hpp_amount"` +} + diff --git a/internal/modules/closings/repositories/closing.repository.go b/internal/modules/closings/repositories/closing.repository.go index fe555378..ecdfd125 100644 --- a/internal/modules/closings/repositories/closing.repository.go +++ b/internal/modules/closings/repositories/closing.repository.go @@ -9,12 +9,14 @@ import ( "gitlab.com/mbugroup/lti-api.git/internal/common/repository" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations" + "gitlab.com/mbugroup/lti-api.git/internal/utils" "gorm.io/gorm" ) type ClosingRepository interface { repository.BaseRepository[entity.ProjectFlock] GetSapronak(ctx context.Context, params SapronakQueryParams) ([]SapronakRow, int64, error) + GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error) } type ClosingRepositoryImpl struct { @@ -44,6 +46,13 @@ type SapronakRow struct { Notes string `gorm:"column:notes"` } +type ExpeditionHPPRow struct { + SupplierID uint64 `gorm:"column:supplier_id"` + SupplierName string `gorm:"column:supplier_name"` + Qty float64 `gorm:"column:qty"` + TotalAmount float64 `gorm:"column:total_amount"` +} + type SapronakQueryParams struct { Type string WarehouseIDs []uint @@ -102,6 +111,46 @@ func (r *ClosingRepositoryImpl) GetSapronak(ctx context.Context, params Sapronak return rows, totalResults, nil } +func (r *ClosingRepositoryImpl) GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error) { + db := r.DB().WithContext(ctx) + + if projectFlockID == 0 { + return nil, fmt.Errorf("invalid project flock id") + } + + query := db. + Table("expense_realizations AS er"). + Joins("JOIN expense_nonstocks ens ON ens.id = er.expense_nonstock_id"). + Joins("JOIN expenses e ON e.id = ens.expense_id"). + Joins("JOIN project_flock_kandangs pfk ON pfk.id = ens.project_flock_kandang_id"). + Joins("JOIN nonstocks n ON n.id = ens.nonstock_id"). + Joins("JOIN flags f ON f.flagable_id = n.id AND f.flagable_type = ?", entity.FlagableTypeNonstock). + Joins("JOIN suppliers s ON s.id = e.supplier_id"). + Where("pfk.project_flock_id = ?", projectFlockID). + Where("e.category = ?", "BOP"). + Where("UPPER(f.name) = ?", strings.ToUpper(string(utils.FlagEkspedisi))) + + if projectFlockKandangID != nil && *projectFlockKandangID != 0 { + query = query.Where("pfk.id = ?", *projectFlockKandangID) + } + + var rows []ExpeditionHPPRow + err := query. + Select( + "e.supplier_id AS supplier_id, " + + "s.name AS supplier_name, " + + "SUM(er.qty) AS qty, " + + "SUM(er.qty * er.price) AS total_amount", + ). + Group("e.supplier_id, s.name"). + Scan(&rows).Error + if err != nil { + return nil, err + } + + return rows, nil +} + const ( sapronakIncomingPurchasesSQL = ` SELECT diff --git a/internal/modules/closings/route.go b/internal/modules/closings/route.go index 4d142f44..6a35ba06 100644 --- a/internal/modules/closings/route.go +++ b/internal/modules/closings/route.go @@ -25,4 +25,5 @@ func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService route.Get("/:project_flock_id/overhead", ctrl.GetOverhead) route.Get("/:projectFlockId", ctrl.GetClosingSummary) route.Get("/:projectFlockId/sapronak", ctrl.GetClosingSapronak) + route.Get("/:project_flock_id/expedition-hpp", ctrl.GetExpeditionHPP) } diff --git a/internal/modules/closings/services/closing.service.go b/internal/modules/closings/services/closing.service.go index cfc22948..12357e46 100644 --- a/internal/modules/closings/services/closing.service.go +++ b/internal/modules/closings/services/closing.service.go @@ -31,6 +31,7 @@ type ClosingService interface { GetClosingSummary(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingSummaryDTO, error) GetOverhead(ctx *fiber.Ctx, projectFlockID uint) (*dto.OverheadListDTO, error) GetClosingSapronak(ctx *fiber.Ctx, projectFlockID uint, params *validation.SapronakQuery) ([]dto.ClosingSapronakItemDTO, int64, error) + GetExpeditionHPP(ctx *fiber.Ctx, projectFlockID uint, projectFlockKandangID *uint) (*dto.ExpeditionHPPDTO, error) } type closingService struct { @@ -379,3 +380,45 @@ func (s closingService) GetOverhead(c *fiber.Ctx, projectFlockID uint) (*dto.Ove return &result, nil } + +// GetExpeditionHPP menghitung HPP ekspedisi per vendor untuk sebuah project flock. +// Jika projectFlockKandangID tidak nil, maka hanya data untuk kandang tersebut yang dihitung. +func (s closingService) GetExpeditionHPP(c *fiber.Ctx, projectFlockID uint, projectFlockKandangID *uint) (*dto.ExpeditionHPPDTO, error) { + if projectFlockID == 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project flock id") + } + + rows, err := s.Repository.GetExpeditionHPP(c.Context(), projectFlockID, projectFlockKandangID) + if err != nil { + s.Log.Errorf("Failed to get expedition HPP for project flock %d: %+v", projectFlockID, err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch expedition HPP") + } + + expeditionCosts := make([]dto.ExpeditionCostItemDTO, 0, len(rows)) + var totalHPP float64 + + for idx, row := range rows { + unitPrice := 0.0 + if row.Qty > 0 { + unitPrice = row.TotalAmount / row.Qty + } + + expeditionCosts = append(expeditionCosts, dto.ExpeditionCostItemDTO{ + Id: uint64(idx + 1), + ExpeditionVendorID: row.SupplierID, + ExpeditionVendorName: row.SupplierName, + Qty: row.Qty, + UnitPrice: unitPrice, + HPPAmount: row.TotalAmount, + }) + + totalHPP += row.TotalAmount + } + + result := &dto.ExpeditionHPPDTO{ + ExpeditionCosts: expeditionCosts, + TotalHPPAmount: totalHPP, + } + + return result, nil +} From 3bfc401206eed1e05a6fff56057dbd7c40966856 Mon Sep 17 00:00:00 2001 From: ragilap Date: Wed, 17 Dec 2025 13:56:51 +0700 Subject: [PATCH 2/3] Feat(BE-334): make reporting closing hpp for project_flock_kandang --- .../controllers/closing.controller.go | 32 ++++++++++++++++++- internal/modules/closings/route.go | 1 + 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/internal/modules/closings/controllers/closing.controller.go b/internal/modules/closings/controllers/closing.controller.go index 2b29429e..113aa667 100644 --- a/internal/modules/closings/controllers/closing.controller.go +++ b/internal/modules/closings/controllers/closing.controller.go @@ -251,7 +251,7 @@ func (u *ClosingController) GetExpeditionHPP(c *fiber.Ctx) error { projectFlockID, err := strconv.Atoi(param) if err != nil || projectFlockID <= 0 { - return fiber.NewError(fiber.StatusBadRequest, "Invalid Project Flock Id") + return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id") } var projectFlockKandangID *uint @@ -277,3 +277,33 @@ func (u *ClosingController) GetExpeditionHPP(c *fiber.Ctx) error { Data: result, }) } + +func (u *ClosingController) GetExpeditionHPPByKandang(c *fiber.Ctx) error { + projectParam := c.Params("project_flock_id") + kandangParam := c.Params("project_flock_kandang_id") + + projectFlockID, err := strconv.Atoi(projectParam) + if err != nil || projectFlockID <= 0 { + return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id") + } + + pfkID, err := strconv.Atoi(kandangParam) + if err != nil || pfkID <= 0 { + return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_kandang_id") + } + + kandangID := uint(pfkID) + + result, err := u.ClosingService.GetExpeditionHPP(c, uint(projectFlockID), &kandangID) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.Success{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get expedition HPP successfully", + Data: result, + }) +} diff --git a/internal/modules/closings/route.go b/internal/modules/closings/route.go index 526ab3b9..8a155bd0 100644 --- a/internal/modules/closings/route.go +++ b/internal/modules/closings/route.go @@ -28,4 +28,5 @@ func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService route.Get("/:projectFlockId", ctrl.GetClosingSummary) route.Get("/:projectFlockId/sapronak", ctrl.GetClosingSapronak) route.Get("/:project_flock_id/expedition-hpp", ctrl.GetExpeditionHPP) + route.Get("/:project_flock_id/:project_flock_kandang_id/expedition-hpp", ctrl.GetExpeditionHPPByKandang) } From 14a4d9e944374478eb6e74708734069b0a029763 Mon Sep 17 00:00:00 2001 From: ragilap Date: Thu, 18 Dec 2025 16:02:57 +0700 Subject: [PATCH 3/3] Feat(BE-334):Fixing dto closing hpp expedisi --- internal/modules/closings/dto/closingExpedition.dto.go | 4 ---- .../dto/{sapronak.dto.go => closingSapronak.dto.go} | 0 .../modules/closings/repositories/closing.repository.go | 5 +---- internal/modules/closings/services/closing.service.go | 8 -------- 4 files changed, 1 insertion(+), 16 deletions(-) rename internal/modules/closings/dto/{sapronak.dto.go => closingSapronak.dto.go} (100%) diff --git a/internal/modules/closings/dto/closingExpedition.dto.go b/internal/modules/closings/dto/closingExpedition.dto.go index f1b8628b..5f8a09d4 100644 --- a/internal/modules/closings/dto/closingExpedition.dto.go +++ b/internal/modules/closings/dto/closingExpedition.dto.go @@ -3,10 +3,7 @@ package dto // ExpeditionCostItemDTO merepresentasikan biaya ekspedisi per vendor. type ExpeditionCostItemDTO struct { Id uint64 `json:"id"` - ExpeditionVendorID uint64 `json:"expedition_vendor_id"` ExpeditionVendorName string `json:"expedition_vendor_name"` - Qty float64 `json:"qty"` - UnitPrice float64 `json:"unit_price"` HPPAmount float64 `json:"hpp_amount"` } @@ -15,4 +12,3 @@ type ExpeditionHPPDTO struct { ExpeditionCosts []ExpeditionCostItemDTO `json:"expedition_costs"` TotalHPPAmount float64 `json:"total_hpp_amount"` } - diff --git a/internal/modules/closings/dto/sapronak.dto.go b/internal/modules/closings/dto/closingSapronak.dto.go similarity index 100% rename from internal/modules/closings/dto/sapronak.dto.go rename to internal/modules/closings/dto/closingSapronak.dto.go diff --git a/internal/modules/closings/repositories/closing.repository.go b/internal/modules/closings/repositories/closing.repository.go index 14854430..0214d739 100644 --- a/internal/modules/closings/repositories/closing.repository.go +++ b/internal/modules/closings/repositories/closing.repository.go @@ -55,9 +55,7 @@ type SapronakRow struct { } type ExpeditionHPPRow struct { - SupplierID uint64 `gorm:"column:supplier_id"` SupplierName string `gorm:"column:supplier_name"` - Qty float64 `gorm:"column:qty"` TotalAmount float64 `gorm:"column:total_amount"` } @@ -147,7 +145,6 @@ func (r *ClosingRepositoryImpl) GetExpeditionHPP(ctx context.Context, projectFlo Select( "e.supplier_id AS supplier_id, " + "s.name AS supplier_name, " + - "SUM(er.qty) AS qty, " + "SUM(er.qty * er.price) AS total_amount", ). Group("e.supplier_id, s.name"). @@ -645,4 +642,4 @@ func (r *ClosingRepositoryImpl) FetchSapronakTransfers(ctx context.Context, kand return fmt.Sprintf("TRF-%d", row.ID) }) return in, out, nil -} \ No newline at end of file +} diff --git a/internal/modules/closings/services/closing.service.go b/internal/modules/closings/services/closing.service.go index d6e24e7f..afba0a9d 100644 --- a/internal/modules/closings/services/closing.service.go +++ b/internal/modules/closings/services/closing.service.go @@ -398,17 +398,9 @@ func (s closingService) GetExpeditionHPP(c *fiber.Ctx, projectFlockID uint, proj var totalHPP float64 for idx, row := range rows { - unitPrice := 0.0 - if row.Qty > 0 { - unitPrice = row.TotalAmount / row.Qty - } - expeditionCosts = append(expeditionCosts, dto.ExpeditionCostItemDTO{ Id: uint64(idx + 1), - ExpeditionVendorID: row.SupplierID, ExpeditionVendorName: row.SupplierName, - Qty: row.Qty, - UnitPrice: unitPrice, HPPAmount: row.TotalAmount, })