mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
FEAT[BE] :add marketing type field to delivery and sales order DTOs, enhance validation and service logic for consistent marketing type handling
This commit is contained in:
@@ -2,6 +2,7 @@ package dto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
@@ -79,6 +80,7 @@ type DeliveryMarketingProductDTO struct {
|
||||
Id uint `json:"id"`
|
||||
MarketingId uint `json:"marketing_id"`
|
||||
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||
MarketingType string `json:"marketing_type"`
|
||||
Qty float64 `json:"qty"`
|
||||
UnitPrice float64 `json:"unit_price"`
|
||||
AvgWeight float64 `json:"avg_weight"`
|
||||
@@ -111,7 +113,7 @@ func ToDeliveryMarketingProductDTO(e entity.MarketingProduct, marketingType stri
|
||||
// Calculate total_peti only for TELUR marketing type
|
||||
var totalPeti *float64
|
||||
if marketingType == "TELUR" && e.ConvertionUnit != nil && *e.ConvertionUnit == "PETI" && e.WeightPerConvertion != nil && *e.WeightPerConvertion > 0 {
|
||||
calculated := e.TotalWeight / *e.WeightPerConvertion
|
||||
calculated := math.Floor(e.TotalWeight / *e.WeightPerConvertion)
|
||||
totalPeti = &calculated
|
||||
}
|
||||
|
||||
@@ -119,6 +121,7 @@ func ToDeliveryMarketingProductDTO(e entity.MarketingProduct, marketingType stri
|
||||
Id: e.Id,
|
||||
MarketingId: e.MarketingId,
|
||||
ProductWarehouseId: e.ProductWarehouseId,
|
||||
MarketingType: marketingType,
|
||||
Qty: e.Qty,
|
||||
UnitPrice: e.UnitPrice,
|
||||
AvgWeight: e.AvgWeight,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
|
||||
type MarketingProductDTO struct {
|
||||
Id uint `json:"id"`
|
||||
MarketingType string `json:"marketing_type"`
|
||||
Qty float64 `json:"qty"`
|
||||
UnitPrice float64 `json:"unit_price"`
|
||||
AvgWeight float64 `json:"avg_weight"`
|
||||
@@ -44,12 +46,13 @@ func ToMarketingProductDTO(e entity.MarketingProduct, marketingType string) Mark
|
||||
// Calculate total_peti only for TELUR marketing type
|
||||
var totalPeti *float64
|
||||
if marketingType == "TELUR" && e.ConvertionUnit != nil && *e.ConvertionUnit == "PETI" && e.WeightPerConvertion != nil && *e.WeightPerConvertion > 0 {
|
||||
calculated := e.TotalWeight / *e.WeightPerConvertion
|
||||
calculated := math.Floor(e.TotalWeight / *e.WeightPerConvertion)
|
||||
totalPeti = &calculated
|
||||
}
|
||||
|
||||
return MarketingProductDTO{
|
||||
Id: e.Id,
|
||||
MarketingType: marketingType,
|
||||
Qty: e.Qty,
|
||||
UnitPrice: e.UnitPrice,
|
||||
AvgWeight: e.AvgWeight,
|
||||
|
||||
@@ -104,8 +104,23 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !utils.IsValidMarketingType(req.MarketingType) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid marketing_type. Must be one of: AYAM, TELUR, TRADING, AYAM_PULLET")
|
||||
// Validasi semua product harus punya marketing_type yang sama
|
||||
if len(req.MarketingProducts) == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "marketing_products is required")
|
||||
}
|
||||
|
||||
firstMarketingType := req.MarketingProducts[0].MarketingType
|
||||
if !utils.IsValidMarketingType(firstMarketingType) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Tipe penjualan tidak valid")
|
||||
}
|
||||
|
||||
for i, item := range req.MarketingProducts {
|
||||
if !utils.IsValidMarketingType(item.MarketingType) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Tipe penjualan tidak valid pada produk ke-%d", i+1))
|
||||
}
|
||||
if item.MarketingType != firstMarketingType {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Semua produk harus memiliki tipe penjualan yang sama")
|
||||
}
|
||||
}
|
||||
|
||||
actorID, err := m.ActorIDFromContext(c)
|
||||
@@ -120,11 +135,11 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
||||
}
|
||||
|
||||
for _, item := range req.MarketingProducts {
|
||||
if req.MarketingType != string(utils.MarketingTypeTrading) && item.AvgWeight == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "avg_weight is required for non-TRADING marketing type")
|
||||
if item.MarketingType != string(utils.MarketingTypeTrading) && item.AvgWeight == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Berat rata-rata harus diisi")
|
||||
}
|
||||
if item.ConvertionUnit != nil && !utils.IsValidConvertionUnit(*item.ConvertionUnit) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid convertion_unit. Must be one of: PETI, KG")
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Unit konversi tidak valid")
|
||||
}
|
||||
if err := m.EnsureProductWarehouseAccess(c, s.MarketingRepo.DB(), item.ProductWarehouseId); err != nil {
|
||||
return nil, err
|
||||
@@ -160,7 +175,7 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
||||
SoDate: soDate,
|
||||
SalesPersonId: req.SalesPersonId,
|
||||
Notes: req.Notes,
|
||||
MarketingType: req.MarketingType,
|
||||
MarketingType: firstMarketingType,
|
||||
CreatedBy: actorID,
|
||||
}
|
||||
if err := marketingRepoTx.CreateOne(c.Context(), marketing, nil); err != nil {
|
||||
@@ -173,7 +188,7 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
||||
if product.ProductWarehouseId != 0 {
|
||||
pwIDs = append(pwIDs, product.ProductWarehouseId)
|
||||
}
|
||||
if err := s.createMarketingProductWithDelivery(c.Context(), marketing.Id, marketing.MarketingType, product, marketingProductRepoTx, invDeliveryRepoTx); err != nil {
|
||||
if err := s.createMarketingProductWithDelivery(c.Context(), marketing.Id, product.MarketingType, product, marketingProductRepoTx, invDeliveryRepoTx); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create marketing product")
|
||||
}
|
||||
}
|
||||
@@ -218,8 +233,21 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.MarketingType != "" && !utils.IsValidMarketingType(req.MarketingType) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid marketing_type. Must be one of: AYAM, TELUR, TRADING, AYAM_PULLET")
|
||||
// Validasi semua product harus punya marketing_type yang sama
|
||||
if len(req.MarketingProducts) > 0 {
|
||||
firstMarketingType := req.MarketingProducts[0].MarketingType
|
||||
if !utils.IsValidMarketingType(firstMarketingType) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Tipe penjualan tidak valid")
|
||||
}
|
||||
|
||||
for i, item := range req.MarketingProducts {
|
||||
if !utils.IsValidMarketingType(item.MarketingType) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Tipe penjualan tidak valid pada produk ke-%d", i+1))
|
||||
}
|
||||
if item.MarketingType != firstMarketingType {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Semua produk harus memiliki tipe penjualan yang sama")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := m.EnsureMarketingAccess(c, s.MarketingRepo.DB(), id); err != nil {
|
||||
@@ -249,11 +277,11 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
||||
|
||||
if len(req.MarketingProducts) > 0 {
|
||||
for _, item := range req.MarketingProducts {
|
||||
if req.MarketingType != "" && req.MarketingType != string(utils.MarketingTypeTrading) && item.AvgWeight == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "avg_weight is required for non-TRADING marketing type")
|
||||
if item.MarketingType != string(utils.MarketingTypeTrading) && item.AvgWeight == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Berat rata-rata harus diisi")
|
||||
}
|
||||
if item.ConvertionUnit != nil && !utils.IsValidConvertionUnit(*item.ConvertionUnit) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid convertion_unit. Must be one of: PETI, KG")
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Unit konversi tidak valid")
|
||||
}
|
||||
if err := m.EnsureProductWarehouseAccess(c, s.MarketingRepo.DB(), item.ProductWarehouseId); err != nil {
|
||||
return nil, err
|
||||
@@ -302,8 +330,8 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
||||
if req.Notes != "" {
|
||||
updateBody["notes"] = req.Notes
|
||||
}
|
||||
if req.MarketingType != "" {
|
||||
updateBody["marketing_type"] = req.MarketingType
|
||||
if len(req.MarketingProducts) > 0 {
|
||||
updateBody["marketing_type"] = req.MarketingProducts[0].MarketingType
|
||||
}
|
||||
|
||||
if len(updateBody) > 0 {
|
||||
@@ -330,15 +358,10 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
||||
reqByPW[rp.ProductWarehouseId] = rp
|
||||
}
|
||||
|
||||
marketing, err := marketingRepoTx.GetByID(c.Context(), id, nil)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch marketing")
|
||||
}
|
||||
|
||||
for _, rp := range req.MarketingProducts {
|
||||
if old, ok := oldByPW[rp.ProductWarehouseId]; ok {
|
||||
|
||||
totalWeight, totalPrice := s.calculatePriceByMarketingType(marketing.MarketingType, rp.Qty, rp.AvgWeight, rp.UnitPrice, rp.Week)
|
||||
totalWeight, totalPrice := s.calculatePriceByMarketingType(rp.MarketingType, rp.Qty, rp.AvgWeight, rp.UnitPrice, rp.Week)
|
||||
|
||||
deliveryProduct, err := invDeliveryRepoTx.GetByMarketingProductID(c.Context(), old.Id)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
@@ -397,7 +420,7 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := s.createMarketingProductWithDelivery(c.Context(), id, marketing.MarketingType, rp, marketingProductRepoTx, invDeliveryRepoTx); err != nil {
|
||||
if err := s.createMarketingProductWithDelivery(c.Context(), id, rp.MarketingType, rp, marketingProductRepoTx, invDeliveryRepoTx); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create marketing product")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ type Create struct {
|
||||
SalesPersonId uint `json:"sales_person_id" validate:"required,gt=0"`
|
||||
Date string `json:"date" validate:"required,datetime=2006-01-02"`
|
||||
Notes string `json:"notes" validate:"omitempty,max=500"`
|
||||
MarketingType string `json:"marketing_type" validate:"required,min=1,max=50"`
|
||||
MarketingProducts []CreateMarketingProduct `json:"marketing_products" validate:"required,min=1,dive"`
|
||||
}
|
||||
|
||||
type CreateMarketingProduct struct {
|
||||
MarketingType string `json:"marketing_type" validate:"required,min=1,max=50"`
|
||||
VehicleNumber string `json:"vehicle_number" validate:"required,min=1,max=50"`
|
||||
ConvertionUnit *string `json:"convertion_unit" validate:"omitempty,min=1,max=20"`
|
||||
WeightPerConvertion *float64 `json:"weight_per_convertion" validate:"omitempty,gt=0"`
|
||||
@@ -25,7 +25,6 @@ type Update struct {
|
||||
SalesPersonId uint `json:"sales_person_id" validate:"omitempty,gt=0"`
|
||||
Date string `json:"date" validate:"omitempty,datetime=2006-01-02"`
|
||||
Notes string `json:"notes" validate:"omitempty,max=500"`
|
||||
MarketingType string `json:"marketing_type" validate:"omitempty,min=1,max=50"`
|
||||
MarketingProducts []CreateMarketingProduct `json:"marketing_products" validate:"omitempty,min=1,dive"`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user