Merge branch 'feat/BE/Sprint-6' into 'dev/teguh' - merge all closing methods

This commit is contained in:
aguhh18
2025-12-08 08:39:17 +07:00
6 changed files with 164 additions and 15 deletions
@@ -53,15 +53,15 @@ func (u *ClosingController) GetAll(c *fiber.Ctx) error {
}) })
} }
func (u *ClosingController) GetOne(c *fiber.Ctx) error { func (u *ClosingController) GetClosingSummary(c *fiber.Ctx) error {
param := c.Params("id") param := c.Params("projectFlockId")
id, err := strconv.Atoi(param) id, err := strconv.Atoi(param)
if err != nil { if err != nil || id <= 0 {
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id") return fiber.NewError(fiber.StatusBadRequest, "Invalid projectFlockId")
} }
result, err := u.ClosingService.GetOne(c, uint(id)) result, err := u.ClosingService.GetClosingSummary(c, uint(id))
if err != nil { if err != nil {
return err return err
} }
@@ -70,8 +70,8 @@ func (u *ClosingController) GetOne(c *fiber.Ctx) error {
JSON(response.Success{ JSON(response.Success{
Code: fiber.StatusOK, Code: fiber.StatusOK,
Status: "success", Status: "success",
Message: "Get closing successfully", Message: "Retrieved project information successfully",
Data: dto.ToClosingListDTO(*result), Data: result,
}) })
} }
@@ -1,6 +1,7 @@
package dto package dto
import ( import (
"fmt"
"time" "time"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
@@ -26,6 +27,67 @@ type ClosingDetailDTO struct {
ClosingListDTO ClosingListDTO
} }
type ClosingSummaryDTO struct {
LocationID uint `json:"location_id"`
Periode int `json:"periode"`
JenisProduk string `json:"jenis_produk"`
LabelPopulasi string `json:"label_populasi"`
JumlahPopulasi int `json:"jumlah_populasi"`
JumlahPopulasiFormatted string `json:"jumlah_populasi_formatted"`
JenisProject string `json:"jenis_project"`
KandangAktif int `json:"kandang_aktif"`
KandangAktifFormatted string `json:"kandang_aktif_formatted"`
StatusPembayaranPenjualan string `json:"status_pembayaran_penjualan"`
StatusPembayaranMitra string `json:"status_pembayaran_mitra"`
StatusProject string `json:"status_project"`
StatusClosing string `json:"status_closing"`
}
func ToClosingSummaryDTO(project entity.ProjectFlock, statusProject, statusClosing string) ClosingSummaryDTO {
history := project.KandangHistory
period := maxPeriod(history)
kandangCount := len(history)
population := sumPopulation(history)
populationInt := int(population)
return ClosingSummaryDTO{
LocationID: project.LocationId,
Periode: period,
JenisProduk: project.Category,
LabelPopulasi: "",
JumlahPopulasi: populationInt,
JumlahPopulasiFormatted: fmt.Sprintf("%d Ekor", populationInt),
JenisProject: "",
KandangAktif: kandangCount,
KandangAktifFormatted: fmt.Sprintf("%d Kandang", kandangCount),
StatusPembayaranPenjualan: "Tempo",
StatusPembayaranMitra: "",
StatusProject: statusProject,
StatusClosing: statusClosing,
}
}
func maxPeriod(history []entity.ProjectFlockKandang) int {
max := 0
for _, h := range history {
if h.Period > max {
max = h.Period
}
}
return max
}
func sumPopulation(history []entity.ProjectFlockKandang) float64 {
var total float64
for _, h := range history {
for _, chickin := range h.Chickins {
total += chickin.UsageQty + chickin.PendingUsageQty
}
}
return total
}
// === Mapper Functions === // === Mapper Functions ===
func ToClosingRelationDTO(e entity.ProjectFlock) ClosingRelationDTO { func ToClosingRelationDTO(e entity.ProjectFlock) ClosingRelationDTO {
+6 -2
View File
@@ -5,10 +5,11 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"gorm.io/gorm" "gorm.io/gorm"
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
rClosing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/repositories" rClosing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/repositories"
sClosing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/services" sClosing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/services"
rMarketings "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/sales-orders/repositories" rMarketings "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/sales-orders/repositories"
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories" rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
) )
@@ -21,7 +22,10 @@ func (ClosingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
marketingRepo := rMarketings.NewMarketingRepository(db) marketingRepo := rMarketings.NewMarketingRepository(db)
marketingDeliveryProductRepo := rMarketings.NewMarketingDeliveryProductRepository(db) marketingDeliveryProductRepo := rMarketings.NewMarketingDeliveryProductRepository(db)
closingService := sClosing.NewClosingService(closingRepo, marketingRepo, marketingDeliveryProductRepo, validate) approvalRepo := commonRepo.NewApprovalRepository(db)
approvalService := commonSvc.NewApprovalService(approvalRepo)
closingService := sClosing.NewClosingService(closingRepo, marketingRepo, marketingDeliveryProductRepo, approvalService, validate)
userService := sUser.NewUserService(userRepo, validate) userService := sUser.NewUserService(userRepo, validate)
ClosingRoutes(router, userService, closingService) ClosingRoutes(router, userService, closingService)
+2 -2
View File
@@ -12,7 +12,7 @@ import (
func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService) { func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService) {
ctrl := controller.NewClosingController(s) ctrl := controller.NewClosingController(s)
route := v1.Group("/closings") route := v1.Group("/closing")
// route.Get("/", m.Auth(u), ctrl.GetAll) // route.Get("/", m.Auth(u), ctrl.GetAll)
// route.Post("/", m.Auth(u), ctrl.CreateOne) // route.Post("/", m.Auth(u), ctrl.CreateOne)
@@ -22,6 +22,6 @@ func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService
route.Get("/", ctrl.GetAll) route.Get("/", ctrl.GetAll)
route.Get("/:id", ctrl.GetOne) route.Get("/:id", ctrl.GetOne)
route.Get("/:projectFlockId", ctrl.GetClosingSummary)
route.Get("/:project_flock_id/penjualan", ctrl.GetPenjualan) route.Get("/:project_flock_id/penjualan", ctrl.GetPenjualan)
} }
@@ -1,14 +1,18 @@
package service package service
import ( import (
"context"
"errors" "errors"
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"gitlab.com/mbugroup/lti-api.git/internal/modules/closings/dto"
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/repositories" repository "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/repositories"
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations"
marketingDeliveryProductRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/sales-orders/repositories" marketingDeliveryProductRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/sales-orders/repositories"
marketingRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/sales-orders/repositories" marketingRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/sales-orders/repositories"
"gitlab.com/mbugroup/lti-api.git/internal/utils" "gitlab.com/mbugroup/lti-api.git/internal/utils"
approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
@@ -20,6 +24,7 @@ type ClosingService interface {
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error) GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error)
GetOne(ctx *fiber.Ctx, id uint) (*entity.ProjectFlock, error) GetOne(ctx *fiber.Ctx, id uint) (*entity.ProjectFlock, error)
GetPenjualan(ctx *fiber.Ctx, projectFlockID uint) ([]entity.MarketingDeliveryProduct, error) GetPenjualan(ctx *fiber.Ctx, projectFlockID uint) ([]entity.MarketingDeliveryProduct, error)
GetClosingSummary(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingSummaryDTO, error)
} }
type closingService struct { type closingService struct {
@@ -28,15 +33,17 @@ type closingService struct {
Repository repository.ClosingRepository Repository repository.ClosingRepository
MarketingRepo marketingRepository.MarketingRepository MarketingRepo marketingRepository.MarketingRepository
MarketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository MarketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository
ApprovalSvc commonSvc.ApprovalService
} }
func NewClosingService(repo repository.ClosingRepository, marketingRepo marketingRepository.MarketingRepository, marketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository, validate *validator.Validate) ClosingService { func NewClosingService(repo repository.ClosingRepository, marketingRepo marketingRepository.MarketingRepository, marketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository, approvalSvc commonSvc.ApprovalService, validate *validator.Validate) ClosingService {
return &closingService{ return &closingService{
Log: utils.Log, Log: utils.Log,
Validate: validate, Validate: validate,
Repository: repo, Repository: repo,
MarketingRepo: marketingRepo, MarketingRepo: marketingRepo,
MarketingDeliveryProductRepo: marketingDeliveryProductRepo, MarketingDeliveryProductRepo: marketingDeliveryProductRepo,
ApprovalSvc: approvalSvc,
} }
} }
@@ -44,6 +51,12 @@ func (s closingService) withRelations(db *gorm.DB) *gorm.DB {
return db.Preload("CreatedUser") return db.Preload("CreatedUser")
} }
func (s closingService) withClosingRelations(db *gorm.DB) *gorm.DB {
return s.withRelations(db).
Preload("KandangHistory").
Preload("KandangHistory.Chickins")
}
func (s closingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error) { func (s closingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error) {
if err := s.Validate.Struct(params); err != nil { if err := s.Validate.Struct(params); err != nil {
return nil, 0, err return nil, 0, err
@@ -103,3 +116,73 @@ func (s closingService) GetPenjualan(c *fiber.Ctx, projectFlockID uint) ([]entit
} }
return realisasi, nil return realisasi, nil
} }
func (s closingService) GetClosingSummary(c *fiber.Ctx, projectFlockID uint) (*dto.ClosingSummaryDTO, error) {
if projectFlockID == 0 {
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project flock id")
}
project, err := s.Repository.GetByID(c.Context(), projectFlockID, s.withClosingRelations)
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fiber.NewError(fiber.StatusNotFound, "Project flock not found")
}
if err != nil {
s.Log.Errorf("Failed get project flock %d for closing summary: %+v", projectFlockID, err)
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock")
}
statusProject, statusClosing, err := s.getApprovalStatuses(c.Context(), projectFlockID)
if err != nil {
s.Log.Errorf("Failed to retrieve approval statuses for project flock %d: %+v", projectFlockID, err)
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch approval status")
}
summary := dto.ToClosingSummaryDTO(*project, statusProject, statusClosing)
return &summary, nil
}
func (s closingService) getApprovalStatuses(ctx context.Context, projectFlockID uint) (string, string, error) {
if s.ApprovalSvc == nil {
return "", "Belum Selesai", nil
}
records, _, err := s.ApprovalSvc.List(ctx, utils.ApprovalWorkflowProjectFlock.String(), &projectFlockID, 1, 1000, "")
if err != nil {
return "", "", err
}
var (
minStep uint16
statusProject string
completed int
)
for _, rec := range records {
if minStep == 0 || rec.StepNumber < minStep {
minStep = rec.StepNumber
statusProject = rec.StepName
}
if rec.StepNumber == uint16(utils.ProjectFlockStepSelesai) {
completed++
}
}
if statusProject == "" && minStep > 0 {
if label, ok := approvalutils.ApprovalStepName(utils.ApprovalWorkflowProjectFlock, approvalutils.ApprovalStep(minStep)); ok {
statusProject = label
}
}
statusClosing := "Belum Selesai"
switch {
case len(records) == 0 || completed == 0:
statusClosing = "Belum Selesai"
case completed < len(records):
statusClosing = "Sebagian"
default:
statusClosing = "Selesai"
}
return statusProject, statusClosing, nil
}
@@ -151,7 +151,7 @@ func (r *ProductWarehouseRepositoryImpl) AdjustQuantities(ctx context.Context, d
} }
if err := base.Model(&entity.ProductWarehouse{}). if err := base.Model(&entity.ProductWarehouse{}).
Where("id = ?", id). Where("id = ?", id).
Update("quantity", gorm.Expr("COALESCE(quantity,0) + ?", delta)).Error; err != nil { Update("qty", gorm.Expr("COALESCE(qty,0) + ?", delta)).Error; err != nil {
return err return err
} }
} }
@@ -171,7 +171,7 @@ func (r *ProductWarehouseRepositoryImpl) CleanupEmpty(ctx context.Context, affec
var emptyIDs []uint var emptyIDs []uint
if err := r.DB().WithContext(ctx). if err := r.DB().WithContext(ctx).
Model(&entity.ProductWarehouse{}). Model(&entity.ProductWarehouse{}).
Where("id IN ? AND COALESCE(quantity,0) <= 0", ids). Where("id IN ? AND COALESCE(qty,0) <= 0", ids).
Pluck("id", &emptyIDs).Error; err != nil { Pluck("id", &emptyIDs).Error; err != nil {
return err return err
} }
@@ -257,7 +257,7 @@ func (r *ProductWarehouseRepositoryImpl) GetByFlagAndWarehouseID(ctx context.Con
Joins("JOIN products ON products.id = product_warehouses.product_id"). Joins("JOIN products ON products.id = product_warehouses.product_id").
Joins("JOIN flags ON flags.flagable_id = products.id AND flags.flagable_type = 'products'"). Joins("JOIN flags ON flags.flagable_id = products.id AND flags.flagable_type = 'products'").
Where("flags.name = ? AND product_warehouses.warehouse_id = ?", flagName, warehouseId). Where("flags.name = ? AND product_warehouses.warehouse_id = ?", flagName, warehouseId).
Order("product_warehouses.created_at DESC"). Order("product_warehouses.id DESC").
Preload("Product").Preload("Warehouse"). Preload("Product").Preload("Warehouse").
Find(&productWarehouses).Error Find(&productWarehouses).Error
if err != nil { if err != nil {