mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
FEAT[BE] :add marketing type and conversion fields to marketing entities and services
This commit is contained in:
@@ -237,6 +237,12 @@ func (s *deliveryOrdersService) CreateOne(c *fiber.Ctx, req *validation.Delivery
|
||||
marketingProductRepositoryTx := marketingRepo.NewMarketingProductRepository(dbTransaction)
|
||||
marketingDeliveryProductRepositoryTx := marketingRepo.NewMarketingDeliveryProductRepository(dbTransaction)
|
||||
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||
marketingRepoTx := marketingRepo.NewMarketingRepository(dbTransaction)
|
||||
|
||||
marketing, err := marketingRepoTx.GetByID(c.Context(), req.MarketingId, nil)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch marketing")
|
||||
}
|
||||
|
||||
allMarketingProducts, err := marketingProductRepositoryTx.GetByMarketingID(c.Context(), req.MarketingId)
|
||||
if err != nil {
|
||||
@@ -283,23 +289,11 @@ func (s *deliveryOrdersService) CreateOne(c *fiber.Ctx, req *validation.Delivery
|
||||
itemDeliveryDate = &parsedDate
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
|
||||
var totalPrice float64
|
||||
if isPakanOrOVK {
|
||||
|
||||
if marketing.MarketingType == string(utils.MarketingTypeTrading) {
|
||||
totalPrice = requestedProduct.Qty * requestedProduct.UnitPrice
|
||||
} else {
|
||||
|
||||
totalPrice = totalWeight * requestedProduct.UnitPrice
|
||||
}
|
||||
|
||||
@@ -374,6 +368,12 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO
|
||||
|
||||
marketingProductRepositoryTx := marketingRepo.NewMarketingProductRepository(dbTransaction)
|
||||
marketingDeliveryProductRepositoryTx := marketingRepo.NewMarketingDeliveryProductRepository(dbTransaction)
|
||||
marketingRepoTx := marketingRepo.NewMarketingRepository(dbTransaction)
|
||||
|
||||
marketing, err := marketingRepoTx.GetByID(c.Context(), id, nil)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch marketing")
|
||||
}
|
||||
|
||||
allMarketingProducts, err := marketingProductRepositoryTx.GetByMarketingID(c.Context(), id)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
@@ -421,23 +421,11 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO
|
||||
itemDeliveryDate = deliveryProduct.DeliveryDate
|
||||
}
|
||||
|
||||
isPakanOrOVK := false
|
||||
if foundMarketingProduct.ProductWarehouse.Id != 0 && 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
|
||||
var totalPrice float64
|
||||
if isPakanOrOVK {
|
||||
|
||||
if marketing.MarketingType == string(utils.MarketingTypeTrading) {
|
||||
totalPrice = requestedProduct.Qty * requestedProduct.UnitPrice
|
||||
} else {
|
||||
|
||||
totalPrice = totalWeight * requestedProduct.UnitPrice
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,10 @@ 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")
|
||||
}
|
||||
|
||||
actorID, err := m.ActorIDFromContext(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -115,6 +119,9 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
||||
}
|
||||
|
||||
for _, item := range req.MarketingProducts {
|
||||
if item.ConvertionUnit != nil && !utils.IsValidConvertionUnit(*item.ConvertionUnit) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid convertion_unit. Must be one of: PETI, KG")
|
||||
}
|
||||
if err := m.EnsureProductWarehouseAccess(c, s.MarketingRepo.DB(), item.ProductWarehouseId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -149,6 +156,7 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
||||
SoDate: soDate,
|
||||
SalesPersonId: req.SalesPersonId,
|
||||
Notes: req.Notes,
|
||||
MarketingType: req.MarketingType,
|
||||
CreatedBy: actorID,
|
||||
}
|
||||
if err := marketingRepoTx.CreateOne(c.Context(), marketing, nil); err != nil {
|
||||
@@ -161,10 +169,9 @@ 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, product, marketingProductRepoTx, invDeliveryRepoTx); err != nil {
|
||||
if err := s.createMarketingProductWithDelivery(c.Context(), marketing.Id, marketing.MarketingType, product, marketingProductRepoTx, invDeliveryRepoTx); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create marketing product")
|
||||
}
|
||||
|
||||
}
|
||||
if err := commonSvc.EnsureProjectFlockNotClosedForProductWarehouses(c.Context(), s.MarketingRepo.DB(), pwIDs); err != nil {
|
||||
return err
|
||||
@@ -207,6 +214,10 @@ 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")
|
||||
}
|
||||
|
||||
if err := m.EnsureMarketingAccess(c, s.MarketingRepo.DB(), id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -234,6 +245,9 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
||||
|
||||
if len(req.MarketingProducts) > 0 {
|
||||
for _, item := range req.MarketingProducts {
|
||||
if item.ConvertionUnit != nil && !utils.IsValidConvertionUnit(*item.ConvertionUnit) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid convertion_unit. Must be one of: PETI, KG")
|
||||
}
|
||||
if err := m.EnsureProductWarehouseAccess(c, s.MarketingRepo.DB(), item.ProductWarehouseId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -281,6 +295,9 @@ 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(updateBody) > 0 {
|
||||
if err := marketingRepoTx.PatchOne(c.Context(), id, updateBody, nil); err != nil {
|
||||
@@ -306,31 +323,17 @@ 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 {
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
totalWeight := rp.Qty * rp.AvgWeight
|
||||
var totalPrice float64
|
||||
if isPakanOrOVK {
|
||||
if marketing.MarketingType == string(utils.MarketingTypeTrading) {
|
||||
totalPrice = rp.Qty * rp.UnitPrice
|
||||
} else {
|
||||
totalPrice = totalWeight * rp.UnitPrice
|
||||
@@ -340,7 +343,6 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to check delivery product")
|
||||
}
|
||||
|
||||
if err == nil && deliveryProduct.Id != 0 {
|
||||
oldQty := old.Qty
|
||||
newQty := rp.Qty
|
||||
@@ -363,12 +365,15 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
||||
}
|
||||
|
||||
updateBody := map[string]any{
|
||||
"product_warehouse_id": rp.ProductWarehouseId,
|
||||
"qty": rp.Qty,
|
||||
"unit_price": rp.UnitPrice,
|
||||
"avg_weight": rp.AvgWeight,
|
||||
"total_weight": totalWeight,
|
||||
"total_price": totalPrice,
|
||||
"product_warehouse_id": rp.ProductWarehouseId,
|
||||
"qty": rp.Qty,
|
||||
"unit_price": rp.UnitPrice,
|
||||
"avg_weight": rp.AvgWeight,
|
||||
"total_weight": totalWeight,
|
||||
"total_price": totalPrice,
|
||||
"convertion_unit": rp.ConvertionUnit,
|
||||
"weight_per_convertion": rp.WeightPerConvertion,
|
||||
"week": rp.Week,
|
||||
}
|
||||
if err := marketingProductRepoTx.PatchOne(c.Context(), old.Id, updateBody, nil); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update marketing product")
|
||||
@@ -391,7 +396,7 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := s.createMarketingProductWithDelivery(c.Context(), id, rp, marketingProductRepoTx, invDeliveryRepoTx); err != nil {
|
||||
if err := s.createMarketingProductWithDelivery(c.Context(), id, marketing.MarketingType, rp, marketingProductRepoTx, invDeliveryRepoTx); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create marketing product")
|
||||
}
|
||||
}
|
||||
@@ -399,7 +404,6 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
||||
|
||||
for _, old := range oldProducts {
|
||||
if _, ok := reqByPW[old.ProductWarehouseId]; !ok {
|
||||
|
||||
deliveryProduct, err := invDeliveryRepoTx.GetByMarketingProductID(c.Context(), old.Id)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to check existing delivery product")
|
||||
@@ -682,45 +686,27 @@ func (s salesOrdersService) Approval(c *fiber.Ctx, req *validation.Approve) ([]e
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
func (s *salesOrdersService) createMarketingProductWithDelivery(ctx context.Context, marketingId uint, rp validation.CreateMarketingProduct, marketingProductRepo repository.MarketingProductRepository, invDeliveryRepo repository.MarketingDeliveryProductRepository) error {
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
func (s *salesOrdersService) createMarketingProductWithDelivery(ctx context.Context, marketingId uint, marketingType string, rp validation.CreateMarketingProduct, marketingProductRepo repository.MarketingProductRepository, invDeliveryRepo repository.MarketingDeliveryProductRepository) error {
|
||||
|
||||
totalWeight := rp.Qty * rp.AvgWeight
|
||||
var totalPrice float64
|
||||
if isPakanOrOVK {
|
||||
// PAKAN atau OVK: qty × unit_price
|
||||
if marketingType == string(utils.MarketingTypeTrading) {
|
||||
totalPrice = rp.Qty * rp.UnitPrice
|
||||
} else {
|
||||
// Produk lain: total_weight × unit_price
|
||||
totalPrice = totalWeight * rp.UnitPrice
|
||||
}
|
||||
|
||||
marketingProduct := &entity.MarketingProduct{
|
||||
MarketingId: marketingId,
|
||||
ProductWarehouseId: rp.ProductWarehouseId,
|
||||
Qty: rp.Qty,
|
||||
UnitPrice: rp.UnitPrice,
|
||||
AvgWeight: rp.AvgWeight,
|
||||
TotalWeight: totalWeight,
|
||||
TotalPrice: totalPrice,
|
||||
MarketingId: marketingId,
|
||||
ProductWarehouseId: rp.ProductWarehouseId,
|
||||
Qty: rp.Qty,
|
||||
UnitPrice: rp.UnitPrice,
|
||||
AvgWeight: rp.AvgWeight,
|
||||
TotalWeight: totalWeight,
|
||||
TotalPrice: totalPrice,
|
||||
ConvertionUnit: rp.ConvertionUnit,
|
||||
WeightPerConvertion: rp.WeightPerConvertion,
|
||||
Week: rp.Week,
|
||||
}
|
||||
if err := marketingProductRepo.CreateOne(ctx, marketingProduct, nil); err != nil {
|
||||
return err
|
||||
|
||||
@@ -5,15 +5,19 @@ 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 {
|
||||
VehicleNumber string `json:"vehicle_number" validate:"required,min=1,max=50"`
|
||||
ProductWarehouseId uint `json:"product_warehouse_id" validate:"required,gt=0"`
|
||||
UnitPrice float64 `json:"unit_price" validate:"required,gt=0"`
|
||||
Qty float64 `json:"qty" validate:"required,gt=0"`
|
||||
AvgWeight float64 `json:"avg_weight" validate:"required,gt=0"`
|
||||
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"`
|
||||
Week *int `json:"week" validate:"omitempty,gt=0"`
|
||||
ProductWarehouseId uint `json:"product_warehouse_id" validate:"required,gt=0"`
|
||||
UnitPrice float64 `json:"unit_price" validate:"required,gt=0"`
|
||||
Qty float64 `json:"qty" validate:"required,gt=0"`
|
||||
AvgWeight float64 `json:"avg_weight" validate:"required,gt=0"`
|
||||
}
|
||||
|
||||
type Update struct {
|
||||
@@ -21,6 +25,7 @@ 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