Merge branch 'HOTFIX/BE/marketing' into 'development'

Hotfix/be/marketing : hotfix perhitungan penjualan ovk dan pakan, overhead only bop

See merge request mbugroup/lti-api!235
This commit is contained in:
Hafizh A. Y.
2026-01-22 07:43:10 +00:00
5 changed files with 126 additions and 18 deletions
@@ -15,6 +15,7 @@ type SalesDTO struct {
Id uint `json:"id"`
RealizationDate time.Time `json:"realization_date"`
Age int `json:"age"`
Week int `json:"week"`
DoNumber string `json:"do_number"`
Product *productDTO.ProductRelationDTO `json:"product,omitempty"`
Customer *customerDTO.CustomerRelationDTO `json:"customer,omitempty"`
@@ -43,7 +44,7 @@ type PenjualanRealisasiResponseDTO struct {
func ToSalesDTO(e entity.MarketingDeliveryProduct) SalesDTO {
age := calculateAgeFromChickin(e.MarketingProduct.ProductWarehouse.ProjectFlockKandang, e.DeliveryDate)
ageInDay, ageInWeeks := calculateAgeFromChickin(e.MarketingProduct.ProductWarehouse.ProjectFlockKandang, e.DeliveryDate)
var product *productDTO.ProductRelationDTO
if e.MarketingProduct.ProductWarehouse.Product.Id != 0 {
@@ -73,7 +74,8 @@ func ToSalesDTO(e entity.MarketingDeliveryProduct) SalesDTO {
return SalesDTO{
Id: e.Id,
RealizationDate: realizationDate,
Age: age,
Age: ageInDay,
Week: ageInWeeks,
DoNumber: doNumber,
Product: product,
Customer: customer,
@@ -124,9 +126,9 @@ func ToPenjualanRealisasiResponseDTO(e []entity.MarketingDeliveryProduct) Penjua
}
}
func calculateAgeFromChickin(projectFlockKandang *entity.ProjectFlockKandang, deliveryDate *time.Time) int {
func calculateAgeFromChickin(projectFlockKandang *entity.ProjectFlockKandang, deliveryDate *time.Time) (int, int) {
if projectFlockKandang == nil || deliveryDate == nil || len(projectFlockKandang.Chickins) == 0 {
return 0
return 0, 0
}
earliestChickinDate := projectFlockKandang.Chickins[0].ChickInDate
@@ -136,7 +138,16 @@ func calculateAgeFromChickin(projectFlockKandang *entity.ProjectFlockKandang, de
}
}
ageInDays := int(deliveryDate.Sub(earliestChickinDate).Hours() / 24)
ageInWeeks := ageInDays / 7
return ageInWeeks
diff := deliveryDate.Sub(earliestChickinDate)
ageInDays := int(diff.Hours() / 24)
var ageInWeeks int
if ageInDays <= 0 {
ageInWeeks = 0
} else {
ageInWeeks = ((ageInDays - 1) / 7) + 1
}
return ageInDays, ageInWeeks
}
@@ -70,7 +70,8 @@ func (r *ExpenseRealizationRepositoryImpl) GetClosingOverhead(ctx context.Contex
Joins("JOIN expenses ON expenses.id = expense_nonstocks.expense_id").
Joins("LEFT JOIN project_flock_kandangs ON project_flock_kandangs.id = expense_nonstocks.project_flock_kandang_id").
Joins("LEFT JOIN kandangs ON kandangs.id = expense_nonstocks.kandang_id").
Where("expenses.realization_date IS NOT NULL")
Where("expenses.realization_date IS NOT NULL").
Where("expenses.category = ?", "BOP")
if projectFlockKandangID != nil {
db = db.Where(`(
@@ -26,7 +26,10 @@ func NewMarketingProductRepository(db *gorm.DB) MarketingProductRepository {
func (r *MarketingProductRepositoryImpl) GetByMarketingID(ctx context.Context, marketingID uint) ([]entity.MarketingProduct, error) {
var products []entity.MarketingProduct
if err := r.DB().WithContext(ctx).Where("marketing_id = ?", marketingID).Find(&products).Error; err != nil {
if err := r.DB().WithContext(ctx).
Preload("ProductWarehouse.Product.Flags").
Where("marketing_id = ?", marketingID).
Find(&products).Error; err != nil {
return nil, err
}
if len(products) == 0 {
@@ -247,9 +247,27 @@ func (s *deliveryOrdersService) CreateOne(c *fiber.Ctx, req *validation.Delivery
itemDeliveryDate = &parsedDate
}
// Hitung total_weight dan total_price otomatis
// Cek apakah product punya flag PAKAN atau OVK
isPakanOrOVK := false
if foundMarketingProduct.ProductWarehouse.Product.Id != 0 && len(foundMarketingProduct.ProductWarehouse.Product.Flags) > 0 {
for _, flag := range foundMarketingProduct.ProductWarehouse.Product.Flags {
if flag.Name == string(utils.FlagPakan) || flag.Name == string(utils.FlagOVK) {
isPakanOrOVK = true
break
}
}
}
// Hitung total_weight dan total_price berdasarkan flag
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
totalPrice := requestedProduct.UnitPrice * totalWeight
var totalPrice float64
if isPakanOrOVK {
// PAKAN atau OVK: qty × unit_price
totalPrice = requestedProduct.Qty * requestedProduct.UnitPrice
} else {
// Produk lain: total_weight × unit_price
totalPrice = totalWeight * requestedProduct.UnitPrice
}
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
@@ -361,9 +379,27 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO
oldRequestedQty := deliveryProduct.UsageQty + deliveryProduct.PendingQty
// Hitung total_weight dan total_price otomatis
// Cek apakah product punya flag PAKAN atau OVK
isPakanOrOVK := false
if foundMarketingProduct.ProductWarehouse.Product.Id != 0 && len(foundMarketingProduct.ProductWarehouse.Product.Flags) > 0 {
for _, flag := range foundMarketingProduct.ProductWarehouse.Product.Flags {
if flag.Name == string(utils.FlagPakan) || flag.Name == string(utils.FlagOVK) {
isPakanOrOVK = true
break
}
}
}
// Hitung total_weight dan total_price berdasarkan flag
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
totalPrice := requestedProduct.UnitPrice * totalWeight
var totalPrice float64
if isPakanOrOVK {
// PAKAN atau OVK: qty × unit_price
totalPrice = requestedProduct.Qty * requestedProduct.UnitPrice
} else {
// Produk lain: total_weight × unit_price
totalPrice = totalWeight * requestedProduct.UnitPrice
}
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
@@ -435,7 +471,13 @@ func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gor
}
if pw == nil || pw.Quantity < requestedQty {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Insufficient stock. Available: %.2f, Requested: %.2f", func() float64 { if pw != nil { return pw.Quantity } else { return 0 } }(), requestedQty))
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Insufficient stock. Available: %.2f, Requested: %.2f", func() float64 {
if pw != nil {
return pw.Quantity
} else {
return 0
}
}(), requestedQty))
}
if err := deliveryProductRepo.UpdateFifoFields(ctx, deliveryProduct.Id, requestedQty, 0); err != nil {
@@ -292,9 +292,35 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
for _, rp := range req.MarketingProducts {
if old, ok := oldByPW[rp.ProductWarehouseId]; ok {
// Hitung total_weight dan total_price otomatis
// Get product untuk cek flag PAKAN atau OVK
productWarehouse, err := s.ProductWarehouseRepo.GetByID(c.Context(), rp.ProductWarehouseId, func(db *gorm.DB) *gorm.DB {
return db.Preload("Product.Flags")
})
if err != nil {
return err
}
// Cek apakah product punya flag PAKAN atau OVK
isPakanOrOVK := false
if productWarehouse.Product.Id != 0 && len(productWarehouse.Product.Flags) > 0 {
for _, flag := range productWarehouse.Product.Flags {
if flag.Name == string(utils.FlagPakan) || flag.Name == string(utils.FlagOVK) {
isPakanOrOVK = true
break
}
}
}
// Hitung total_weight dan total_price berdasarkan flag
totalWeight := rp.Qty * rp.AvgWeight
totalPrice := rp.UnitPrice * totalWeight
var totalPrice float64
if isPakanOrOVK {
// PAKAN atau OVK: qty × unit_price
totalPrice = rp.Qty * rp.UnitPrice
} else {
// Produk lain: total_weight × unit_price
totalPrice = totalWeight * rp.UnitPrice
}
updateBody := map[string]any{
"product_warehouse_id": rp.ProductWarehouseId,
@@ -592,9 +618,34 @@ func (s salesOrdersService) Approval(c *fiber.Ctx, req *validation.Approve) ([]e
func (s *salesOrdersService) createMarketingProductWithDelivery(ctx context.Context, marketingId uint, rp validation.CreateMarketingProduct, marketingProductRepo repository.MarketingProductRepository, invDeliveryRepo repository.MarketingDeliveryProductRepository) error {
// Hitung total_weight dan total_price otomatis
// Get product untuk cek flag PAKAN atau OVK
productWarehouse, err := s.ProductWarehouseRepo.GetByID(ctx, rp.ProductWarehouseId, func(db *gorm.DB) *gorm.DB {
return db.Preload("Product.Flags")
})
if err != nil {
return err
}
// Cek apakah product punya flag PAKAN atau OVK
isPakanOrOVK := false
if productWarehouse.Product.Id != 0 && len(productWarehouse.Product.Flags) > 0 {
for _, flag := range productWarehouse.Product.Flags {
if flag.Name == string(utils.FlagPakan) || flag.Name == string(utils.FlagOVK) {
isPakanOrOVK = true
break
}
}
}
totalWeight := rp.Qty * rp.AvgWeight
totalPrice := rp.UnitPrice * totalWeight
var totalPrice float64
if isPakanOrOVK {
// PAKAN atau OVK: qty × unit_price
totalPrice = rp.Qty * rp.UnitPrice
} else {
// Produk lain: total_weight × unit_price
totalPrice = totalWeight * rp.UnitPrice
}
marketingProduct := &entity.MarketingProduct{
MarketingId: marketingId,