mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-24 07:15:43 +00:00
Merge branch 'development' of https://gitlab.com/mbugroup/lti-api into feat/BE/sso-adjustment
This commit is contained in:
@@ -41,7 +41,7 @@ type ClosingService interface {
|
||||
GetClosingDataProduksi(ctx *fiber.Ctx, projectFlockID uint, kandangID *uint) (*dto.ClosingProductionReportDTO, error)
|
||||
GetOverhead(ctx *fiber.Ctx, projectFlockID uint, projectFlockKandangID *uint) (*dto.OverheadListDTO, error)
|
||||
GetClosingSapronak(ctx *fiber.Ctx, projectFlockID uint, params *validation.ClosingSapronakQuery) ([]dto.ClosingSapronakItemDTO, int64, error)
|
||||
GetClosingKeuangan(ctx *fiber.Ctx, projectFlockID uint) (*dto.ReportResponse, error)
|
||||
GetClosingSapronakSummary(ctx *fiber.Ctx, projectFlockID uint, params *validation.ClosingSapronakQuery) ([]dto.ClosingSapronakSummaryItemDTO, error)
|
||||
GetExpeditionHPP(ctx *fiber.Ctx, projectFlockID uint, projectFlockKandangID *uint) (*dto.ExpeditionHPPDTO, error)
|
||||
}
|
||||
|
||||
@@ -99,16 +99,33 @@ func (s closingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]dto.Cl
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
scope, err := m.ResolveLocationScope(c, s.Repository.DB())
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
offset := (params.Page - 1) * params.Limit
|
||||
statusFilter := ""
|
||||
if params.ProjectStatus != nil {
|
||||
switch *params.ProjectStatus {
|
||||
case 1:
|
||||
statusFilter = "Pengajuan"
|
||||
case 2:
|
||||
statusFilter = "Aktif"
|
||||
}
|
||||
}
|
||||
|
||||
closings, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||
db = s.withClosingRelations(db)
|
||||
db = m.ApplyScopeFilter(db, scope, "project_flocks.location_id")
|
||||
if params.LocationID != nil {
|
||||
db = db.Where("location_id = ?", *params.LocationID)
|
||||
}
|
||||
if statusFilter != "" {
|
||||
latestApprovalSubQuery := s.Repository.DB().
|
||||
WithContext(c.Context()).
|
||||
Table("approvals").
|
||||
Select("DISTINCT ON (approvable_id) approvable_id, step_name, id").
|
||||
Where("approvable_type = ?", utils.ApprovalWorkflowProjectFlock.String()).
|
||||
Order("approvable_id, id DESC")
|
||||
db = db.Joins("JOIN (?) AS latest_approval ON latest_approval.approvable_id = project_flocks.id", latestApprovalSubQuery).
|
||||
Where("LOWER(latest_approval.step_name) = LOWER(?)", statusFilter)
|
||||
}
|
||||
if params.Search != "" {
|
||||
return db.Where("flock_name ILIKE ?", "%"+params.Search+"%")
|
||||
}
|
||||
@@ -347,8 +364,10 @@ func (s closingService) GetClosingSapronak(c *fiber.Ctx, projectFlockID uint, pa
|
||||
}
|
||||
|
||||
var projectFlockKandangIDs []uint
|
||||
if params.Type == validation.SapronakTypeOutgoing {
|
||||
projectFlockKandangIDs, err = s.getProjectFlockKandangIDs(c.Context(), projectFlockID, params.KandangID)
|
||||
if params.KandangID != nil && *params.KandangID > 0 {
|
||||
projectFlockKandangIDs = []uint{*params.KandangID}
|
||||
} else if params.Type == validation.SapronakTypeOutgoing {
|
||||
projectFlockKandangIDs, err = s.getProjectFlockKandangIDs(c.Context(), projectFlockID)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch project flock kandang IDs for project flock %d: %+v", projectFlockID, err)
|
||||
return nil, 0, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock kandang")
|
||||
@@ -362,6 +381,7 @@ func (s closingService) GetClosingSapronak(c *fiber.Ctx, projectFlockID uint, pa
|
||||
ProjectFlockKandangIDs: projectFlockKandangIDs,
|
||||
Limit: params.Limit,
|
||||
Offset: offset,
|
||||
Search: params.Search,
|
||||
})
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch sapronak %s for project flock %d: %+v", params.Type, projectFlockID, err)
|
||||
@@ -396,6 +416,74 @@ func (s closingService) GetClosingSapronak(c *fiber.Ctx, projectFlockID uint, pa
|
||||
return items, totalResults, nil
|
||||
}
|
||||
|
||||
func (s closingService) GetClosingSapronakSummary(c *fiber.Ctx, projectFlockID uint, params *validation.ClosingSapronakQuery) ([]dto.ClosingSapronakSummaryItemDTO, error) {
|
||||
if projectFlockID == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project flock id")
|
||||
}
|
||||
|
||||
if params == nil {
|
||||
params = &validation.ClosingSapronakQuery{}
|
||||
}
|
||||
|
||||
if err := s.Validate.Struct(params); err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
if params.Type != validation.SapronakTypeIncoming && params.Type != validation.SapronakTypeOutgoing {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "type must be either incoming or outgoing")
|
||||
}
|
||||
|
||||
if _, err := s.Repository.GetByID(c.Context(), projectFlockID, nil); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Project flock tidak ditemukan")
|
||||
}
|
||||
s.Log.Errorf("Failed get project flock %d for sapronak closing summary: %+v", projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock")
|
||||
}
|
||||
|
||||
warehouseIDs, err := s.getWarehouseIDsByProjectFlock(c.Context(), projectFlockID)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch warehouses for project flock %d: %+v", projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch warehouses for project flock")
|
||||
}
|
||||
|
||||
var projectFlockKandangIDs []uint
|
||||
if params.KandangID != nil && *params.KandangID > 0 {
|
||||
projectFlockKandangIDs = []uint{*params.KandangID}
|
||||
} else if params.Type == validation.SapronakTypeOutgoing {
|
||||
projectFlockKandangIDs, err = s.getProjectFlockKandangIDs(c.Context(), projectFlockID)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch project flock kandang IDs for project flock %d: %+v", projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock kandang")
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := s.Repository.GetSapronakSummary(c.Context(), repository.SapronakQueryParams{
|
||||
Type: params.Type,
|
||||
WarehouseIDs: warehouseIDs,
|
||||
ProjectFlockKandangIDs: projectFlockKandangIDs,
|
||||
Search: params.Search,
|
||||
})
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch sapronak %s summary for project flock %d: %+v", params.Type, projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch sapronak summary data")
|
||||
}
|
||||
|
||||
items := make([]dto.ClosingSapronakSummaryItemDTO, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
items = append(items, dto.ClosingSapronakSummaryItemDTO{
|
||||
Category: row.Category,
|
||||
TotalQty: row.TotalQty,
|
||||
Uom: dto.UomSummaryDTO{
|
||||
ID: row.UomID,
|
||||
Name: row.UomName,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (s closingService) getWarehouseIDsByProjectFlock(ctx context.Context, projectFlockID uint) ([]uint, error) {
|
||||
var kandangIDs []uint
|
||||
db := s.Repository.DB().WithContext(ctx)
|
||||
@@ -428,14 +516,11 @@ func (s closingService) getWarehouseIDsByProjectFlock(ctx context.Context, proje
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (s closingService) getProjectFlockKandangIDs(ctx context.Context, projectFlockID uint, kandangID *uint) ([]uint, error) {
|
||||
func (s closingService) getProjectFlockKandangIDs(ctx context.Context, projectFlockID uint) ([]uint, error) {
|
||||
var ids []uint
|
||||
query := s.Repository.DB().WithContext(ctx).
|
||||
Model(&entity.ProjectFlockKandang{}).
|
||||
Where("project_flock_id = ?", projectFlockID)
|
||||
if kandangID != nil {
|
||||
query = query.Where("kandang_id = ?", *kandangID)
|
||||
}
|
||||
err := query.Order("id ASC").Pluck("id", &ids).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -595,85 +680,6 @@ func (s closingService) GetOverhead(c *fiber.Ctx, projectFlockID uint, projectFl
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (s closingService) GetClosingKeuangan(c *fiber.Ctx, projectFlockID uint) (*dto.ReportResponse, error) {
|
||||
if err := m.EnsureProjectFlockAccess(c, s.Repository.DB(), projectFlockID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := commonSvc.EnsureRelations(c.Context(),
|
||||
commonSvc.RelationCheck{Name: "Project Flock", ID: &projectFlockID, Exists: s.ProjectFlockRepo.IdExists},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
projectFlock, err := s.ProjectFlockRepo.GetByID(c.Context(), projectFlockID, nil)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock")
|
||||
}
|
||||
|
||||
budgets, err := s.ProjectBudgetRepo.GetByProjectFlockID(c.Context(), projectFlockID)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch budgets")
|
||||
}
|
||||
|
||||
actualUsageRows, err := s.Repository.GetActualUsageCostByProjectFlockID(c.Context(), projectFlockID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch actual usage cost")
|
||||
}
|
||||
|
||||
purchaseItems := s.convertActualUsageToPurchaseItems(c.Context(), actualUsageRows)
|
||||
|
||||
realizations, err := s.ExpenseRealizationRepo.GetByProjectFlockID(c.Context(), projectFlockID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch realizations")
|
||||
}
|
||||
|
||||
deliveryProducts, err := s.MarketingDeliveryProductRepo.GetDeliveryProductsByProjectFlockID(c.Context(), projectFlockID, func(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("MarketingProduct").
|
||||
Preload("MarketingProduct.ProductWarehouse").
|
||||
Preload("MarketingProduct.ProductWarehouse.Product")
|
||||
})
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch delivery products")
|
||||
}
|
||||
|
||||
chickins, err := s.ChickinRepo.GetByProjectFlockID(c.Context(), projectFlockID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch chickins")
|
||||
}
|
||||
|
||||
totalWeightProduced, _, err := s.RecordingRepo.GetProductionWeightAndQtyByProjectFlockID(c.Context(), projectFlockID)
|
||||
if err != nil {
|
||||
s.Log.Warnf("GetProductionWeightAndQtyByProjectFlockID error: %v", err)
|
||||
}
|
||||
|
||||
totalEggWeightKg, err := s.RecordingRepo.GetTotalEggProductionWeightByProjectFlockID(c.Context(), projectFlockID)
|
||||
if err != nil {
|
||||
s.Log.Warnf("GetTotalEggProductionWeightByProjectFlockID error: %v", err)
|
||||
}
|
||||
|
||||
totalDepletion, err := s.RecordingRepo.GetTotalDepletionByProjectFlockID(c.Context(), projectFlockID)
|
||||
if err != nil {
|
||||
s.Log.Warnf("GetTotalDepletionByProjectFlockID error: %v", err)
|
||||
}
|
||||
|
||||
input := dto.ClosingKeuanganInput{
|
||||
ProjectFlockCategory: projectFlock.Category,
|
||||
PurchaseItems: purchaseItems,
|
||||
Budgets: budgets,
|
||||
Realizations: realizations,
|
||||
DeliveryProducts: deliveryProducts,
|
||||
Chickins: chickins,
|
||||
TotalWeightProduced: totalWeightProduced,
|
||||
TotalEggWeightKg: totalEggWeightKg,
|
||||
TotalDepletion: totalDepletion,
|
||||
}
|
||||
|
||||
report := dto.ToClosingKeuanganReport(input)
|
||||
|
||||
return &report, nil
|
||||
}
|
||||
|
||||
func (s closingService) GetExpeditionHPP(c *fiber.Ctx, projectFlockID uint, projectFlockKandangID *uint) (*dto.ExpeditionHPPDTO, error) {
|
||||
if projectFlockKandangID != nil {
|
||||
if err := m.EnsureProjectFlockKandangAccess(c, s.Repository.DB(), projectFlockID, *projectFlockKandangID); err != nil {
|
||||
@@ -711,14 +717,20 @@ func (s closingService) GetExpeditionHPP(c *fiber.Ctx, projectFlockID uint, proj
|
||||
}
|
||||
|
||||
func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint, kandangID *uint) (*dto.ClosingProductionReportDTO, error) {
|
||||
if err := m.EnsureProjectFlockAccess(c, s.Repository.DB(), projectFlockID); err != nil {
|
||||
return nil, err
|
||||
if projectFlockID == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project flock id")
|
||||
}
|
||||
|
||||
projectFlockKandangIDs, err := s.getProjectFlockKandangIDs(c.Context(), projectFlockID, kandangID)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch project flock kandangs for %d: %+v", projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock kandangs")
|
||||
var projectFlockKandangIDs []uint
|
||||
if kandangID != nil && *kandangID > 0 {
|
||||
projectFlockKandangIDs = []uint{*kandangID}
|
||||
} else {
|
||||
var err error
|
||||
projectFlockKandangIDs, err = s.getProjectFlockKandangIDs(c.Context(), projectFlockID)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch project flock kandangs for %d: %+v", projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock kandangs")
|
||||
}
|
||||
}
|
||||
|
||||
if len(projectFlockKandangIDs) == 0 {
|
||||
@@ -748,6 +760,10 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to determine production week")
|
||||
}
|
||||
|
||||
if !isGrowing && currentWeek != 0 {
|
||||
currentWeek = currentWeek + 17
|
||||
}
|
||||
|
||||
targetAverages, err := s.RecordingRepo.GetAverageTargetMetricsByProjectFlockKandangID(c.Context(), projectFlockKandangIDs[0], !isGrowing)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to calculate target metrics for project flock %d: %+v", projectFlockID, err)
|
||||
@@ -957,19 +973,19 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
||||
if !isGrowing {
|
||||
if targetAverages.HenDayCount > 0 {
|
||||
henDayAct := targetAverages.HenDayAvg
|
||||
performance.HenDayAct = &henDayAct
|
||||
performance.HenDayAct = henDayAct
|
||||
}
|
||||
if targetAverages.HenHouseCount > 0 {
|
||||
henHouseAct := targetAverages.HenHouseAvg
|
||||
performance.HenHouseAct = &henHouseAct
|
||||
performance.HenHouseAct = henHouseAct
|
||||
}
|
||||
if targetAverages.EggWeightCount > 0 {
|
||||
eggWeight := targetAverages.EggWeightAvg
|
||||
performance.EggWeight = &eggWeight
|
||||
performance.EggWeight = eggWeight
|
||||
}
|
||||
if targetAverages.EggMassCount > 0 {
|
||||
eggMass := targetAverages.EggMassAvg
|
||||
performance.EggMass = &eggMass
|
||||
performance.EggMass = eggMass
|
||||
}
|
||||
}
|
||||
performance.DeffFcr = performance.FcrStd - performance.FcrAct
|
||||
@@ -979,16 +995,16 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
||||
}
|
||||
if !isGrowing {
|
||||
if productionStandardDetail.TargetHenDayProduction != nil {
|
||||
performance.HendayStd = productionStandardDetail.TargetHenDayProduction
|
||||
performance.HendayStd = *productionStandardDetail.TargetHenDayProduction
|
||||
}
|
||||
if productionStandardDetail.TargetHenHouseProduction != nil {
|
||||
performance.HenHouseStd = productionStandardDetail.TargetHenHouseProduction
|
||||
performance.HenHouseStd = *productionStandardDetail.TargetHenHouseProduction
|
||||
}
|
||||
if productionStandardDetail.TargetEggWeight != nil {
|
||||
performance.EggWeightStd = productionStandardDetail.TargetEggWeight
|
||||
performance.EggWeightStd = *productionStandardDetail.TargetEggWeight
|
||||
}
|
||||
if productionStandardDetail.TargetEggMass != nil {
|
||||
performance.EggMassStd = productionStandardDetail.TargetEggMass
|
||||
performance.EggMassStd = *productionStandardDetail.TargetEggMass
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1127,53 +1143,3 @@ func closestFcrValues(standards []entity.FcrStandard, averageWeight float64) (fl
|
||||
|
||||
return closest.Mortality, closest.FcrNumber
|
||||
}
|
||||
|
||||
func (s closingService) convertActualUsageToPurchaseItems(ctx context.Context, actualUsageRows []repository.ActualUsageCostRow) []entity.PurchaseItem {
|
||||
if len(actualUsageRows) == 0 {
|
||||
return []entity.PurchaseItem{}
|
||||
}
|
||||
|
||||
// Collect all product IDs
|
||||
productIDs := make([]uint, len(actualUsageRows))
|
||||
for i, row := range actualUsageRows {
|
||||
productIDs[i] = row.ProductID
|
||||
}
|
||||
|
||||
// Fetch products with flags from repository
|
||||
products, err := s.Repository.GetProductsWithFlagsByIDs(ctx, productIDs)
|
||||
if err != nil {
|
||||
s.Log.Warnf("Failed to fetch products for actual usage: %v", err)
|
||||
products = []entity.Product{}
|
||||
}
|
||||
|
||||
// Create product map
|
||||
productMap := make(map[uint]*entity.Product)
|
||||
for i := range products {
|
||||
productMap[products[i].Id] = &products[i]
|
||||
}
|
||||
|
||||
// Convert to pseudo purchase items
|
||||
purchaseItems := make([]entity.PurchaseItem, 0, len(actualUsageRows))
|
||||
for _, row := range actualUsageRows {
|
||||
product := productMap[row.ProductID]
|
||||
|
||||
// Skip if product not found
|
||||
if product == nil {
|
||||
s.Log.Warnf("Product ID %d not found for actual usage", row.ProductID)
|
||||
continue
|
||||
}
|
||||
|
||||
purchaseItem := entity.PurchaseItem{
|
||||
Id: 0, // Pseudo item, no ID
|
||||
ProductId: row.ProductID,
|
||||
TotalQty: row.TotalQty,
|
||||
TotalPrice: row.TotalPrice,
|
||||
Price: row.AveragePrice,
|
||||
Product: product,
|
||||
}
|
||||
|
||||
purchaseItems = append(purchaseItems, purchaseItem)
|
||||
}
|
||||
|
||||
return purchaseItems
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user