feat[BE]: create GetOverhead API, and fixing chickin use newest productwarehouse schema

This commit is contained in:
aguhh18
2025-12-09 15:32:11 +07:00
parent 89b23b0653
commit 511e5501bb
12 changed files with 309 additions and 21 deletions
@@ -123,3 +123,25 @@ func (u *ClosingController) GetPenjualan(c *fiber.Ctx) error {
Data: dto.ToPenjualanRealisasiResponseDTO(projectFlock.Category, uint(projectFlockID), result), Data: dto.ToPenjualanRealisasiResponseDTO(projectFlock.Category, uint(projectFlockID), result),
}) })
} }
func (u *ClosingController) GetOverhead(c *fiber.Ctx) error {
param := c.Params("project_flock_id")
projectFlockID, err := strconv.Atoi(param)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid Project Flock Id")
}
result, err := u.ClosingService.GetOverhead(c, uint(projectFlockID))
if err != nil {
return err
}
return c.Status(fiber.StatusOK).
JSON(response.Success{
Code: fiber.StatusOK,
Status: "success",
Message: "Get overhead successfully",
Data: result,
})
}
@@ -11,7 +11,6 @@ import (
) )
// === Response DTO === // === Response DTO ===
type SalesDTO struct { type SalesDTO struct {
Id uint `json:"id"` Id uint `json:"id"`
RealizationDate time.Time `json:"realization_date"` RealizationDate time.Time `json:"realization_date"`
@@ -0,0 +1,175 @@
package dto
import (
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
)
// === DTO Structs ===
type OverheadDTO struct {
ItemName string `json:"item_name"`
UOMName string `json:"uom_name"`
BudgetQuantity float64 `json:"budget_quantity"`
BudgetUnitPrice float64 `json:"budget_unit_price"`
BudgetTotalAmount float64 `json:"budget_total_amount"`
ActualDate string `json:"actual_date"`
ActualQuantity float64 `json:"actual_quantity"`
ActualUnitPrice float64 `json:"actual_unit_price"`
ActualTotalAmount float64 `json:"actual_total_amount"`
CostPerBird float64 `json:"cost_per_bird"`
}
type TotalDTO struct {
BudgetQuantity float64 `json:"budget_quantity"`
BudgetTotalAmount float64 `json:"budget_total_amount"`
ActualQuantity float64 `json:"actual_quantity"`
ActualTotalAmount float64 `json:"actual_total_amount"`
CostPerBird float64 `json:"cost_per_bird"`
}
type OverheadListDTO struct {
Total TotalDTO `json:"total"`
Overheads []OverheadDTO `json:"overheads"`
}
// === Mapper Functions ===
func ToOverheadDTO(budget *entity.ProjectBudget, realization *entity.ExpenseRealization) OverheadDTO {
if budget == nil && realization == nil {
return OverheadDTO{}
}
var itemName, itemUOM string
if budget != nil {
itemName, itemUOM = getItemInfo(budget.Nonstock)
}
if itemName == "" && realization != nil && realization.ExpenseNonstock != nil {
itemName, itemUOM = getItemInfo(realization.ExpenseNonstock.Nonstock)
}
dto := OverheadDTO{
ItemName: itemName,
UOMName: itemUOM,
}
if budget != nil {
dto.BudgetQuantity = budget.Qty
dto.BudgetUnitPrice = budget.Price
dto.BudgetTotalAmount = calculateTotal(budget.Qty, budget.Price)
}
if realization != nil {
dto.ActualQuantity = realization.Qty
dto.ActualUnitPrice = realization.Price
dto.ActualTotalAmount = calculateTotal(realization.Qty, realization.Price)
dto.ActualDate = formatRealizationDate(realization)
}
return dto
}
func ToOverheadListDTOs(budgets []entity.ProjectBudget, realizations []entity.ExpenseRealization, totalChickinQty float64) OverheadListDTO {
overheadsByNonstockID := make(map[uint]*OverheadDTO)
latestDateByNonstockID := make(map[uint]string)
for i := range budgets {
nonstockID := budgets[i].NonstockId
if overheadsByNonstockID[nonstockID] == nil {
overheadsByNonstockID[nonstockID] = &OverheadDTO{}
}
itemName, itemUOM := getItemInfo(budgets[i].Nonstock)
overheadsByNonstockID[nonstockID].ItemName = itemName
overheadsByNonstockID[nonstockID].UOMName = itemUOM
overheadsByNonstockID[nonstockID].BudgetQuantity = budgets[i].Qty
overheadsByNonstockID[nonstockID].BudgetUnitPrice = budgets[i].Price
overheadsByNonstockID[nonstockID].BudgetTotalAmount = calculateTotal(budgets[i].Qty, budgets[i].Price)
}
for i := range realizations {
if realizations[i].ExpenseNonstock == nil || realizations[i].ExpenseNonstock.NonstockId == nil {
continue
}
nonstockID := uint(*realizations[i].ExpenseNonstock.NonstockId)
if overheadsByNonstockID[nonstockID] == nil {
overheadsByNonstockID[nonstockID] = &OverheadDTO{}
}
overheadsByNonstockID[nonstockID].ActualQuantity += realizations[i].Qty
overheadsByNonstockID[nonstockID].ActualTotalAmount += calculateTotal(realizations[i].Qty, realizations[i].Price)
if overheadsByNonstockID[nonstockID].ItemName == "" {
itemName, itemUOM := getItemInfo(realizations[i].ExpenseNonstock.Nonstock)
overheadsByNonstockID[nonstockID].ItemName = itemName
overheadsByNonstockID[nonstockID].UOMName = itemUOM
}
realizationDateStr := formatRealizationDate(&realizations[i])
if realizationDateStr != "" {
if latestDateByNonstockID[nonstockID] == "" || realizationDateStr > latestDateByNonstockID[nonstockID] {
latestDateByNonstockID[nonstockID] = realizationDateStr
}
}
}
var totalBudgetQuantity, totalBudgetAmount, totalActualQuantity, totalActualAmount float64
overheadItems := make([]OverheadDTO, 0, len(overheadsByNonstockID))
for nonstockID, overhead := range overheadsByNonstockID {
overhead.ActualDate = latestDateByNonstockID[nonstockID]
overhead.CostPerBird = calculateCostPerBird(overhead.ActualTotalAmount, totalChickinQty)
if overhead.ActualQuantity > 0 {
overhead.ActualUnitPrice = overhead.ActualTotalAmount / overhead.ActualQuantity
}
totalBudgetQuantity += overhead.BudgetQuantity
totalBudgetAmount += overhead.BudgetTotalAmount
totalActualQuantity += overhead.ActualQuantity
totalActualAmount += overhead.ActualTotalAmount
overheadItems = append(overheadItems, *overhead)
}
return OverheadListDTO{
Total: TotalDTO{
BudgetQuantity: totalBudgetQuantity,
BudgetTotalAmount: totalBudgetAmount,
ActualQuantity: totalActualQuantity,
ActualTotalAmount: totalActualAmount,
CostPerBird: calculateCostPerBird(totalActualAmount, totalChickinQty),
},
Overheads: overheadItems,
}
}
// === Helper Functions ===
func getItemInfo(nonstock *entity.Nonstock) (string, string) {
if nonstock != nil && nonstock.Id != 0 {
return nonstock.Name, nonstock.Uom.Name
}
return "", ""
}
func calculateTotal(qty, price float64) float64 {
return qty * price
}
func calculateCostPerBird(totalPrice, totalChickinQty float64) float64 {
if totalChickinQty > 0 {
return totalPrice / totalChickinQty
}
return 0
}
func formatRealizationDate(realization *entity.ExpenseRealization) string {
if realization != nil && realization.ExpenseNonstock != nil && realization.ExpenseNonstock.Expense != nil {
if !realization.ExpenseNonstock.Expense.RealizationDate.IsZero() {
return realization.ExpenseNonstock.Expense.RealizationDate.Format("2006-01-02T15:04:05Z07:00")
}
}
return ""
}
+6 -1
View File
@@ -9,7 +9,9 @@ import (
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service" 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"
rExpenseRealization "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
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"
rChickin "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories" rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories" rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
@@ -22,12 +24,15 @@ func (ClosingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
closingRepo := rClosing.NewClosingRepository(db) closingRepo := rClosing.NewClosingRepository(db)
userRepo := rUser.NewUserRepository(db) userRepo := rUser.NewUserRepository(db)
projectFlockRepo := rProjectFlock.NewProjectflockRepository(db) projectFlockRepo := rProjectFlock.NewProjectflockRepository(db)
projectBudgetRepo := rProjectFlock.NewProjectBudgetRepository(db)
marketingRepo := rMarketings.NewMarketingRepository(db) marketingRepo := rMarketings.NewMarketingRepository(db)
marketingDeliveryProductRepo := rMarketings.NewMarketingDeliveryProductRepository(db) marketingDeliveryProductRepo := rMarketings.NewMarketingDeliveryProductRepository(db)
expenseRealizationRepo := rExpenseRealization.NewExpenseRealizationRepository(db)
chickinRepo := rChickin.NewChickinRepository(db)
approvalRepo := commonRepo.NewApprovalRepository(db) approvalRepo := commonRepo.NewApprovalRepository(db)
approvalService := commonSvc.NewApprovalService(approvalRepo) approvalService := commonSvc.NewApprovalService(approvalRepo)
closingService := sClosing.NewClosingService(closingRepo, projectFlockRepo, marketingRepo, marketingDeliveryProductRepo, approvalService, validate) closingService := sClosing.NewClosingService(closingRepo, projectFlockRepo, marketingRepo, marketingDeliveryProductRepo, approvalService, expenseRealizationRepo, projectBudgetRepo, chickinRepo, validate)
userService := sUser.NewUserService(userRepo, validate) userService := sUser.NewUserService(userRepo, validate)
ClosingRoutes(router, userService, closingService) ClosingRoutes(router, userService, closingService)
+2
View File
@@ -22,5 +22,7 @@ func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService
route.Get("/", ctrl.GetAll) route.Get("/", ctrl.GetAll)
route.Get("/:project_flock_id/penjualan", ctrl.GetPenjualan) route.Get("/:project_flock_id/penjualan", ctrl.GetPenjualan)
route.Get("/:project_flock_id/overhead", ctrl.GetOverhead)
route.Get("/:projectFlockId", ctrl.GetClosingSummary) route.Get("/:projectFlockId", ctrl.GetClosingSummary)
} }
@@ -9,8 +9,10 @@ import (
"gitlab.com/mbugroup/lti-api.git/internal/modules/closings/dto" "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"
expenseRealizationRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
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"
chickinRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
projectflockRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories" projectflockRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/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" approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
@@ -26,6 +28,7 @@ type ClosingService interface {
GetProjectFlockByID(ctx *fiber.Ctx, id uint) (*entity.ProjectFlock, error) GetProjectFlockByID(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) GetClosingSummary(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingSummaryDTO, error)
GetOverhead(ctx *fiber.Ctx, projectFlockID uint) (*dto.OverheadListDTO, error)
} }
type closingService struct { type closingService struct {
@@ -36,9 +39,12 @@ type closingService struct {
MarketingRepo marketingRepository.MarketingRepository MarketingRepo marketingRepository.MarketingRepository
MarketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository MarketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository
ApprovalSvc commonSvc.ApprovalService ApprovalSvc commonSvc.ApprovalService
ExpenseRealizationRepo expenseRealizationRepository.ExpenseRealizationRepository
ProjectBudgetRepo projectflockRepository.ProjectBudgetRepository
ChickinRepo chickinRepository.ProjectChickinRepository
} }
func NewClosingService(repo repository.ClosingRepository, projectFlockRepo projectflockRepository.ProjectflockRepository, marketingRepo marketingRepository.MarketingRepository, marketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository, approvalSvc commonSvc.ApprovalService, validate *validator.Validate) ClosingService { func NewClosingService(repo repository.ClosingRepository, projectFlockRepo projectflockRepository.ProjectflockRepository, marketingRepo marketingRepository.MarketingRepository, marketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository, approvalSvc commonSvc.ApprovalService, expenseRealizationRepo expenseRealizationRepository.ExpenseRealizationRepository, projectBudgetRepo projectflockRepository.ProjectBudgetRepository, chickinRepo chickinRepository.ProjectChickinRepository, validate *validator.Validate) ClosingService {
return &closingService{ return &closingService{
Log: utils.Log, Log: utils.Log,
Validate: validate, Validate: validate,
@@ -47,6 +53,9 @@ func NewClosingService(repo repository.ClosingRepository, projectFlockRepo proje
MarketingRepo: marketingRepo, MarketingRepo: marketingRepo,
MarketingDeliveryProductRepo: marketingDeliveryProductRepo, MarketingDeliveryProductRepo: marketingDeliveryProductRepo,
ApprovalSvc: approvalSvc, ApprovalSvc: approvalSvc,
ExpenseRealizationRepo: expenseRealizationRepo,
ProjectBudgetRepo: projectBudgetRepo,
ChickinRepo: chickinRepo,
} }
} }
@@ -188,3 +197,29 @@ func (s closingService) getApprovalStatuses(ctx context.Context, projectFlockID
return statusProject, statusClosing, nil return statusProject, statusClosing, nil
} }
func (s closingService) GetOverhead(c *fiber.Ctx, projectFlockID uint) (*dto.OverheadListDTO, error) {
budgets, err := s.ProjectBudgetRepo.GetByProjectFlockID(c.Context(), projectFlockID)
if err != nil {
return nil, err
}
realizations, err := s.ExpenseRealizationRepo.GetByProjectFlockID(c.Context(), projectFlockID)
if err != nil {
return nil, err
}
chickins, err := s.ChickinRepo.GetByProjectFlockID(c.Context(), projectFlockID)
if err != nil {
return nil, err
}
var totalChickinQty float64
for _, chickin := range chickins {
totalChickinQty += chickin.UsageQty
}
result := dto.ToOverheadListDTOs(budgets, realizations, totalChickinQty)
return &result, nil
}
@@ -12,6 +12,7 @@ type ExpenseRealizationRepository interface {
repository.BaseRepository[entity.ExpenseRealization] repository.BaseRepository[entity.ExpenseRealization]
IdExists(ctx context.Context, id uint64) (bool, error) IdExists(ctx context.Context, id uint64) (bool, error)
GetByExpenseNonstockID(ctx context.Context, expenseNonstockID uint64) (*entity.ExpenseRealization, error) GetByExpenseNonstockID(ctx context.Context, expenseNonstockID uint64) (*entity.ExpenseRealization, error)
GetByProjectFlockID(ctx context.Context, projectFlockID uint) ([]entity.ExpenseRealization, error)
} }
type ExpenseRealizationRepositoryImpl struct { type ExpenseRealizationRepositoryImpl struct {
@@ -30,11 +31,22 @@ func (r *ExpenseRealizationRepositoryImpl) IdExists(ctx context.Context, id uint
func (r *ExpenseRealizationRepositoryImpl) GetByExpenseNonstockID(ctx context.Context, expenseNonstockID uint64) (*entity.ExpenseRealization, error) { func (r *ExpenseRealizationRepositoryImpl) GetByExpenseNonstockID(ctx context.Context, expenseNonstockID uint64) (*entity.ExpenseRealization, error) {
var realization entity.ExpenseRealization var realization entity.ExpenseRealization
err := r.DB().WithContext(ctx). err := r.DB().WithContext(ctx).Where("expense_nonstock_id = ?", expenseNonstockID).First(&realization).Error
Where("expense_nonstock_id = ?", expenseNonstockID). return &realization, err
First(&realization).Error }
if err != nil {
return nil, err func (r *ExpenseRealizationRepositoryImpl) GetByProjectFlockID(ctx context.Context, projectFlockID uint) ([]entity.ExpenseRealization, error) {
} var realizations []entity.ExpenseRealization
return &realization, nil err := r.DB().WithContext(ctx).
Preload("ExpenseNonstock").
Preload("ExpenseNonstock.Nonstock").
Preload("ExpenseNonstock.Nonstock.Uom").
Preload("ExpenseNonstock.Expense").
Joins("JOIN expense_nonstocks ON expense_nonstocks.id = expense_realizations.expense_nonstock_id").
Joins("JOIN project_flock_kandangs ON project_flock_kandangs.id = expense_nonstocks.project_flock_kandang_id").
Joins("JOIN expenses ON expenses.id = expense_nonstocks.expense_id").
Where("project_flock_kandangs.project_flock_id = ?", projectFlockID).
Where("expenses.category = ?", "BOP").
Find(&realizations).Error
return realizations, err
} }
@@ -11,6 +11,7 @@ import (
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository" commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service" 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"
middleware "gitlab.com/mbugroup/lti-api.git/internal/middleware"
expenseDto "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/dto" expenseDto "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/dto"
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories" repository "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/validations" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/validations"
@@ -188,7 +189,11 @@ func (s *expenseService) CreateOne(c *fiber.Ctx, req *validation.Create) (*expen
return fiber.NewError(fiber.StatusInternalServerError, "Failed to generate reference number") return fiber.NewError(fiber.StatusInternalServerError, "Failed to generate reference number")
} }
createdBy := uint64(1) //todo get from auth actorID, err := middleware.ActorIDFromContext(c)
if err != nil {
return fiber.NewError(fiber.StatusUnauthorized, "Failed to get actor ID from context")
}
createdBy := uint64(actorID)
expense = &entity.Expense{ expense = &entity.Expense{
ReferenceNumber: referenceNumber, ReferenceNumber: referenceNumber,
PoNumber: req.PoNumber, PoNumber: req.PoNumber,
@@ -496,7 +501,10 @@ func (s expenseService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint)
} }
} }
actorID := uint(1) // TODO: replace with authenticated user id actorID, err := middleware.ActorIDFromContext(c)
if err != nil {
return fiber.NewError(fiber.StatusUnauthorized, "Failed to get actor ID from context")
}
if *latestApproval.Action != entity.ApprovalActionUpdated { if *latestApproval.Action != entity.ApprovalActionUpdated {
approvalAction := entity.ApprovalActionUpdated approvalAction := entity.ApprovalActionUpdated
@@ -655,7 +663,10 @@ func (s *expenseService) CompleteExpense(c *fiber.Ctx, id uint, notes *string) (
return nil, err return nil, err
} }
actorID := uint(1) // TODO: replace with authenticated user id actorID, err := middleware.ActorIDFromContext(c)
if err != nil {
return nil, fiber.NewError(fiber.StatusUnauthorized, "Failed to get actor ID from context")
}
latestApproval, err := s.ApprovalSvc.LatestByTarget(c.Context(), utils.ApprovalWorkflowExpense, id, nil) latestApproval, err := s.ApprovalSvc.LatestByTarget(c.Context(), utils.ApprovalWorkflowExpense, id, nil)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
@@ -960,11 +971,14 @@ func (s *expenseService) Approval(c *fiber.Ctx, req *validation.ApprovalRequest,
return nil, fiber.NewError(fiber.StatusBadRequest, "No expense IDs provided") return nil, fiber.NewError(fiber.StatusBadRequest, "No expense IDs provided")
} }
actorID := uint(1) // TODO: replace with authenticated user id actorID, err := middleware.ActorIDFromContext(c)
if err != nil {
return nil, fiber.NewError(fiber.StatusUnauthorized, "Failed to get actor ID from context")
}
var results []expenseDto.ExpenseDetailDTO var results []expenseDto.ExpenseDetailDTO
err := s.Repository.DB().WithContext(c.Context()).Transaction(func(tx *gorm.DB) error { err = s.Repository.DB().WithContext(c.Context()).Transaction(func(tx *gorm.DB) error {
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(tx)) approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(tx))
expenseRepoTx := repository.NewExpenseRepository(tx) expenseRepoTx := repository.NewExpenseRepository(tx)
@@ -1,7 +1,7 @@
package adjustments package adjustments
import ( import (
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware" m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/controllers" controller "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/controllers"
adjustment "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/services" adjustment "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/services"
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
@@ -13,7 +13,7 @@ func AdjustmentRoutes(v1 fiber.Router, u user.UserService, s adjustment.Adjustme
ctrl := controller.NewAdjustmentController(s) ctrl := controller.NewAdjustmentController(s)
route := v1.Group("/adjustments") route := v1.Group("/adjustments")
route.Use(m.Auth(u))
// Standard CRUD routes following master data pattern // Standard CRUD routes following master data pattern
route.Get("/", ctrl.AdjustmentHistory) // Get all with pagination and filters route.Get("/", ctrl.AdjustmentHistory) // Get all with pagination and filters
route.Post("/", ctrl.Adjustment) // Create adjustment route.Post("/", ctrl.Adjustment) // Create adjustment
@@ -11,6 +11,7 @@ import (
type ProjectChickinRepository interface { type ProjectChickinRepository interface {
repository.BaseRepository[entity.ProjectChickin] repository.BaseRepository[entity.ProjectChickin]
GetFirstByProjectFlockID(ctx context.Context, projectFlockID uint) (*entity.ProjectChickin, error) GetFirstByProjectFlockID(ctx context.Context, projectFlockID uint) (*entity.ProjectChickin, error)
GetByProjectFlockID(ctx context.Context, projectFlockID uint) ([]entity.ProjectChickin, error)
GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error) GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error)
GetPendingByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error) GetPendingByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error)
GetTotalPendingUsageQtyByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (float64, error) GetTotalPendingUsageQtyByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (float64, error)
@@ -40,6 +41,16 @@ func (r *ChickinRepositoryImpl) GetFirstByProjectFlockID(ctx context.Context, pr
return &chickin, nil return &chickin, nil
} }
func (r *ChickinRepositoryImpl) GetByProjectFlockID(ctx context.Context, projectFlockID uint) ([]entity.ProjectChickin, error) {
var chickins []entity.ProjectChickin
err := r.db.WithContext(ctx).
Joins("JOIN project_flock_kandangs ON project_flock_kandangs.id = project_chickins.project_flock_kandang_id").
Where("project_flock_kandangs.project_flock_id = ?", projectFlockID).
Order("project_chickins.created_at DESC").
Find(&chickins).Error
return chickins, err
}
func (r *ChickinRepositoryImpl) GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error) { func (r *ChickinRepositoryImpl) GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error) {
var chickins []entity.ProjectChickin var chickins []entity.ProjectChickin
err := r.db.WithContext(ctx). err := r.db.WithContext(ctx).
@@ -197,7 +197,7 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti
if category == string(utils.ProjectFlockCategoryLaying) { if category == string(utils.ProjectFlockCategoryLaying) {
for _, chickin := range newChikins { for _, chickin := range newChikins {
updates := map[string]any{"quantity": gorm.Expr("quantity - ?", chickin.PendingUsageQty)} updates := map[string]any{"qty": gorm.Expr("qty - ?", chickin.PendingUsageQty)}
if err := productWarehouseTx.PatchOne(c.Context(), chickin.ProductWarehouseId, updates, nil); err != nil { if err := productWarehouseTx.PatchOne(c.Context(), chickin.ProductWarehouseId, updates, nil); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
@@ -498,7 +498,7 @@ func (s chickinService) Approval(c *fiber.Ctx, req *validation.Approve) ([]entit
for _, chickin := range chickins { for _, chickin := range chickins {
if categoryForRejection == string(utils.ProjectFlockCategoryGrowing) { if categoryForRejection == string(utils.ProjectFlockCategoryGrowing) {
updates := map[string]any{"quantity": gorm.Expr("quantity + ?", chickin.PendingUsageQty)} updates := map[string]any{"qty": gorm.Expr("qty + ?", chickin.PendingUsageQty)}
if err := productWarehouseTx.PatchOne(c.Context(), chickin.ProductWarehouseId, updates, nil); err != nil { if err := productWarehouseTx.PatchOne(c.Context(), chickin.ProductWarehouseId, updates, nil); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
@@ -600,7 +600,7 @@ func (s *chickinService) convertChickinsToTarget(ctx *fiber.Ctx, chickins []enti
if chickin.ProductWarehouseId != targetPW.Id { if chickin.ProductWarehouseId != targetPW.Id {
if err := productWarehouseTx.PatchOne(ctx.Context(), chickin.ProductWarehouseId, map[string]any{ if err := productWarehouseTx.PatchOne(ctx.Context(), chickin.ProductWarehouseId, map[string]any{
"quantity": gorm.Expr("quantity - ?", quantityToConvert), "qty": gorm.Expr("qty - ?", quantityToConvert),
}, nil); err != nil { }, nil); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Source product warehouse %d not found", chickin.ProductWarehouseId)) return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Source product warehouse %d not found", chickin.ProductWarehouseId))
@@ -610,7 +610,7 @@ func (s *chickinService) convertChickinsToTarget(ctx *fiber.Ctx, chickins []enti
} }
if err := productWarehouseTx.PatchOne(ctx.Context(), targetPW.Id, map[string]any{ if err := productWarehouseTx.PatchOne(ctx.Context(), targetPW.Id, map[string]any{
"quantity": gorm.Expr("quantity + ?", quantityToConvert), "qty": gorm.Expr("qty + ?", quantityToConvert),
}, nil); err != nil { }, nil); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Target product warehouse %d not found", targetPW.Id)) return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Target product warehouse %d not found", targetPW.Id))
@@ -1,6 +1,8 @@
package repository package repository
import ( import (
"context"
"gitlab.com/mbugroup/lti-api.git/internal/common/repository" "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"gorm.io/gorm" "gorm.io/gorm"
@@ -8,6 +10,7 @@ import (
type ProjectBudgetRepository interface { type ProjectBudgetRepository interface {
repository.BaseRepository[entity.ProjectBudget] repository.BaseRepository[entity.ProjectBudget]
GetByProjectFlockID(ctx context.Context, projectFlockID uint) ([]entity.ProjectBudget, error)
} }
type ProjectBudgetRepositoryImpl struct { type ProjectBudgetRepositoryImpl struct {
@@ -21,3 +24,13 @@ func NewProjectBudgetRepository(db *gorm.DB) ProjectBudgetRepository {
db: db, db: db,
} }
} }
func (r *ProjectBudgetRepositoryImpl) GetByProjectFlockID(ctx context.Context, projectFlockID uint) ([]entity.ProjectBudget, error) {
var budgets []entity.ProjectBudget
err := r.db.WithContext(ctx).
Where("project_flock_id = ?", projectFlockID).
Preload("Nonstock").
Preload("Nonstock.Uom").
Find(&budgets).Error
return budgets, err
}