mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-25 07:45:44 +00:00
Feat[BE-222,223,224]: creating So create delete patch update get getall approval API
This commit is contained in:
@@ -2,8 +2,11 @@ package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
productWarehouseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||
@@ -11,7 +14,9 @@ import (
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/sales-orders/validations"
|
||||
customerRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/repositories"
|
||||
kandangRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
|
||||
userRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
@@ -20,11 +25,12 @@ import (
|
||||
)
|
||||
|
||||
type SalesOrdersService interface {
|
||||
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.SalesOrders, int64, error)
|
||||
GetOne(ctx *fiber.Ctx, id uint) (*entity.SalesOrders, error)
|
||||
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.SalesOrders, error)
|
||||
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.SalesOrders, error)
|
||||
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.Marketing, int64, error)
|
||||
GetOne(ctx *fiber.Ctx, id uint) (*entity.Marketing, error)
|
||||
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.Marketing, error)
|
||||
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.Marketing, error)
|
||||
DeleteOne(ctx *fiber.Ctx, id uint) error
|
||||
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.Marketing, error)
|
||||
}
|
||||
|
||||
type salesOrdersService struct {
|
||||
@@ -35,9 +41,11 @@ type salesOrdersService struct {
|
||||
KandangRepo kandangRepo.KandangRepository
|
||||
ProductWarehouseRepo productWarehouseRepo.ProductWarehouseRepository
|
||||
MarketingRepo repository.MarketingRepository
|
||||
UserRepo userRepo.UserRepository
|
||||
ApprovalSvc commonSvc.ApprovalService
|
||||
}
|
||||
|
||||
func NewSalesOrdersService(repo repository.SalesOrdersRepository, customerRepo customerRepo.CustomerRepository, kandangRepo kandangRepo.KandangRepository, productWarehouseRepo productWarehouseRepo.ProductWarehouseRepository, marketingRepo repository.MarketingRepository, validate *validator.Validate) SalesOrdersService {
|
||||
func NewSalesOrdersService(repo repository.SalesOrdersRepository, customerRepo customerRepo.CustomerRepository, kandangRepo kandangRepo.KandangRepository, productWarehouseRepo productWarehouseRepo.ProductWarehouseRepository, marketingRepo repository.MarketingRepository, userRepo userRepo.UserRepository, approvalSvc commonSvc.ApprovalService, validate *validator.Validate) SalesOrdersService {
|
||||
return &salesOrdersService{
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
@@ -46,48 +54,94 @@ func NewSalesOrdersService(repo repository.SalesOrdersRepository, customerRepo c
|
||||
KandangRepo: kandangRepo,
|
||||
ProductWarehouseRepo: productWarehouseRepo,
|
||||
MarketingRepo: marketingRepo,
|
||||
UserRepo: userRepo,
|
||||
ApprovalSvc: approvalSvc,
|
||||
}
|
||||
}
|
||||
|
||||
func (s salesOrdersService) withRelations(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("CreatedUser")
|
||||
return db.Preload("CreatedUser").
|
||||
Preload("Customer").
|
||||
Preload("SalesPerson").
|
||||
Preload("Products.ProductWarehouse.Product").
|
||||
Preload("Products.ProductWarehouse.Warehouse").
|
||||
Preload("Products.DeliveryProducts")
|
||||
}
|
||||
|
||||
func (s salesOrdersService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.SalesOrders, int64, error) {
|
||||
func (s salesOrdersService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.Marketing, int64, error) {
|
||||
if err := s.Validate.Struct(params); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
offset := (params.Page - 1) * params.Limit
|
||||
|
||||
salesOrderss, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||
marketings, total, err := s.MarketingRepo.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||
db = s.withRelations(db)
|
||||
if params.Search != "" {
|
||||
return db.Where("name LIKE ?", "%"+params.Search+"%")
|
||||
return db.Where("so_number LIKE ?", "%"+params.Search+"%")
|
||||
}
|
||||
return db.Order("created_at DESC").Order("updated_at DESC")
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to get salesOrderss: %+v", err)
|
||||
return nil, 0, err
|
||||
s.Log.Errorf("Failed to get marketings: %+v", err)
|
||||
return nil, 0, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch sales orders")
|
||||
}
|
||||
return salesOrderss, total, nil
|
||||
|
||||
if s.ApprovalSvc != nil && len(marketings) > 0 {
|
||||
ids := make([]uint, len(marketings))
|
||||
for i, item := range marketings {
|
||||
ids[i] = item.Id
|
||||
}
|
||||
|
||||
latestMap, err := s.ApprovalSvc.LatestByTargets(c.Context(), utils.ApprovalWorkflowMarketing, ids, func(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("ActionUser")
|
||||
})
|
||||
if err != nil {
|
||||
s.Log.Warnf("Unable to load latest approvals for marketings: %+v", err)
|
||||
} else if len(latestMap) > 0 {
|
||||
for i := range marketings {
|
||||
if approval, ok := latestMap[marketings[i].Id]; ok {
|
||||
marketings[i].LatestApproval = approval
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return marketings, total, nil
|
||||
}
|
||||
|
||||
func (s salesOrdersService) GetOne(c *fiber.Ctx, id uint) (*entity.SalesOrders, error) {
|
||||
salesOrders, err := s.Repository.GetByID(c.Context(), id, s.withRelations)
|
||||
func (s salesOrdersService) GetOne(c *fiber.Ctx, id uint) (*entity.Marketing, error) {
|
||||
|
||||
marketing, err := s.MarketingRepo.GetByID(c.Context(), id, s.withRelations)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "SalesOrders not found")
|
||||
}
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed get salesOrders by id: %+v", err)
|
||||
return nil, err
|
||||
s.Log.Errorf("Failed get marketing by id: %+v", err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch sales order")
|
||||
}
|
||||
return salesOrders, nil
|
||||
|
||||
if s.ApprovalSvc != nil {
|
||||
approvals, err := s.ApprovalSvc.ListByTarget(c.Context(), utils.ApprovalWorkflowMarketing, id, func(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("ActionUser")
|
||||
})
|
||||
if err != nil {
|
||||
s.Log.Warnf("Unable to load approvals for marketing %d: %+v", id, err)
|
||||
} else if len(approvals) > 0 {
|
||||
if marketing.LatestApproval == nil {
|
||||
latest := approvals[len(approvals)-1]
|
||||
marketing.LatestApproval = &latest
|
||||
}
|
||||
} else {
|
||||
marketing.LatestApproval = nil
|
||||
}
|
||||
}
|
||||
|
||||
return marketing, nil
|
||||
}
|
||||
|
||||
func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entity.SalesOrders, error) {
|
||||
func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entity.Marketing, error) {
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -107,25 +161,26 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
||||
}
|
||||
}
|
||||
|
||||
// parse date
|
||||
soDate, err := utils.ParseDateString(req.Date)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid date format")
|
||||
}
|
||||
|
||||
// generate SO number
|
||||
soNumber := "SO-" + time.Now().Format("20060102150405")
|
||||
|
||||
var marketing *entity.Marketing
|
||||
|
||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
// create marketing directly (tanpa create SalesOrders)
|
||||
|
||||
marketingRepoTx := repository.NewMarketingRepository(dbTransaction)
|
||||
marketingProductRepoTx := repository.NewMarketingProductRepository(dbTransaction)
|
||||
marketingDeliveryProductRepoTx := repository.NewMarketingDeliveryProductRepository(dbTransaction)
|
||||
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||
|
||||
marketing = &entity.Marketing{
|
||||
CustomerId: req.CustomerId,
|
||||
SoNumber: soNumber,
|
||||
SoDate: soDate,
|
||||
SalesPersonId: 1,
|
||||
SalesPersonId: req.SalesPersonId,
|
||||
Notes: req.Notes,
|
||||
CreatedBy: 1,
|
||||
}
|
||||
@@ -134,7 +189,6 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
||||
return err
|
||||
}
|
||||
|
||||
// Create MarketingProducts and MarketingDeliveryProducts
|
||||
if len(req.MarketingProducts) > 0 {
|
||||
for _, product := range req.MarketingProducts {
|
||||
marketingProduct := &entity.MarketingProduct{
|
||||
@@ -146,12 +200,11 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
||||
TotalWeight: product.TotalWeight,
|
||||
TotalPrice: product.TotalPrice,
|
||||
}
|
||||
if err := dbTransaction.Create(marketingProduct).Error; err != nil {
|
||||
if err := marketingProductRepoTx.CreateOne(c.Context(), marketingProduct, nil); err != nil {
|
||||
s.Log.Errorf("Failed to create marketing product: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// create delivery product with zeroed numeric fields and nil delivery date
|
||||
marketingDeliveryProduct := &entity.MarketingDeliveryProduct{
|
||||
MarketingProductId: marketingProduct.Id,
|
||||
Qty: 0,
|
||||
@@ -162,13 +215,37 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
||||
DeliveryDate: nil,
|
||||
VehicleNumber: product.VehicleNumber,
|
||||
}
|
||||
if err := dbTransaction.Create(marketingDeliveryProduct).Error; err != nil {
|
||||
if err := marketingDeliveryProductRepoTx.CreateOne(c.Context(), marketingDeliveryProduct, nil); err != nil {
|
||||
s.Log.Errorf("Failed to create marketing delivery product: %+v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actorID := uint(1) // TODO: ambil dari auth context
|
||||
if err := approvalSvcTx.RegisterWorkflowSteps(
|
||||
utils.ApprovalWorkflowMarketing,
|
||||
utils.MarketingApprovalSteps,
|
||||
); err != nil {
|
||||
s.Log.Errorf("Failed to register workflow steps: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
approvalAction := entity.ApprovalActionCreated
|
||||
if _, err := approvalSvcTx.CreateApproval(
|
||||
c.Context(),
|
||||
utils.ApprovalWorkflowMarketing,
|
||||
marketing.Id,
|
||||
utils.MarketingStepPengajuan,
|
||||
&approvalAction,
|
||||
actorID,
|
||||
nil); err != nil {
|
||||
if !errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||
s.Log.Errorf("Failed to create approval: %+v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
@@ -178,51 +255,320 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create salesOrders")
|
||||
}
|
||||
|
||||
createdMarketing, err := s.MarketingRepo.GetByID(c.Context(), marketing.Id, nil)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch created marketing: %+v", err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch created marketing")
|
||||
}
|
||||
|
||||
return &entity.SalesOrders{
|
||||
Id: createdMarketing.Id,
|
||||
Name: createdMarketing.SoNumber,
|
||||
}, nil
|
||||
return s.GetOne(c, marketing.Id)
|
||||
}
|
||||
|
||||
func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.SalesOrders, error) {
|
||||
func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.Marketing, error) {
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updateBody := make(map[string]any)
|
||||
|
||||
if req.Name != nil {
|
||||
updateBody["name"] = *req.Name
|
||||
}
|
||||
|
||||
if len(updateBody) == 0 {
|
||||
return s.GetOne(c, id)
|
||||
}
|
||||
|
||||
if err := s.Repository.PatchOne(c.Context(), id, updateBody, nil); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "SalesOrders not found")
|
||||
}
|
||||
s.Log.Errorf("Failed to update salesOrders: %+v", err)
|
||||
if err := commonSvc.EnsureRelations(c.Context(),
|
||||
commonSvc.RelationCheck{Name: "Marketing", ID: &id, Exists: s.MarketingRepo.IdExists},
|
||||
commonSvc.RelationCheck{Name: "Customer", ID: &req.CustomerId, Exists: s.CustomerRepo.IdExists},
|
||||
commonSvc.RelationCheck{Name: "SalesPerson", ID: &req.SalesPersonId, Exists: s.UserRepo.IdExists},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
latestApproval, err := s.ApprovalSvc.LatestByTarget(c.Context(), utils.ApprovalWorkflowMarketing, id, nil)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to check approval status: %+v", err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check approval status")
|
||||
}
|
||||
if latestApproval != nil && latestApproval.StepNumber >= 3 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Cannot update sales order after delivery order approval")
|
||||
}
|
||||
|
||||
if len(req.MarketingProducts) > 0 {
|
||||
for _, item := range req.MarketingProducts {
|
||||
if err := commonSvc.EnsureRelations(c.Context(),
|
||||
commonSvc.RelationCheck{Name: "ProductWarehouse", ID: &item.ProductWarehouseId, Exists: s.ProductWarehouseRepo.IdExists},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
|
||||
marketingRepoTx := repository.NewMarketingRepository(dbTransaction)
|
||||
marketingProductRepoTx := repository.NewMarketingProductRepository(dbTransaction)
|
||||
marketingDeliveryProductRepoTx := repository.NewMarketingDeliveryProductRepository(dbTransaction)
|
||||
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||
|
||||
updateBody := make(map[string]any)
|
||||
if req.CustomerId != 0 {
|
||||
updateBody["customer_id"] = req.CustomerId
|
||||
}
|
||||
if req.SalesPersonId != 0 {
|
||||
updateBody["sales_person_id"] = req.SalesPersonId
|
||||
}
|
||||
if req.Date != "" {
|
||||
soDate, err := utils.ParseDateString(req.Date)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid date format")
|
||||
}
|
||||
updateBody["so_date"] = soDate
|
||||
}
|
||||
if req.Notes != "" {
|
||||
updateBody["notes"] = req.Notes
|
||||
}
|
||||
|
||||
if len(updateBody) > 0 {
|
||||
if err := marketingRepoTx.PatchOne(c.Context(), id, updateBody, nil); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update sales order")
|
||||
}
|
||||
}
|
||||
|
||||
if len(req.MarketingProducts) > 0 {
|
||||
|
||||
oldProducts, err := marketingProductRepoTx.GetByMarketingID(c.Context(), id)
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
s.Log.Errorf("Failed to fetch old marketing products: %+v", err)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update products")
|
||||
}
|
||||
|
||||
for _, oldProduct := range oldProducts {
|
||||
if err := marketingDeliveryProductRepoTx.DeleteMany(c.Context(), func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("marketing_product_id = ?", oldProduct.Id)
|
||||
}); err != nil && err != gorm.ErrRecordNotFound {
|
||||
s.Log.Errorf("Failed to delete delivery products: %+v", err)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update products")
|
||||
}
|
||||
}
|
||||
|
||||
if err := marketingProductRepoTx.DeleteMany(c.Context(), func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("marketing_id = ?", id)
|
||||
}); err != nil && err != gorm.ErrRecordNotFound {
|
||||
s.Log.Errorf("Failed to delete marketing products: %+v", err)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update products")
|
||||
}
|
||||
|
||||
for _, product := range req.MarketingProducts {
|
||||
marketingProduct := &entity.MarketingProduct{
|
||||
MarketingId: id,
|
||||
ProductWarehouseId: product.ProductWarehouseId,
|
||||
Qty: product.Qty,
|
||||
UnitPrice: product.UnitPrice,
|
||||
AvgWeight: product.AvgWeight,
|
||||
TotalWeight: product.TotalWeight,
|
||||
TotalPrice: product.TotalPrice,
|
||||
}
|
||||
if err := marketingProductRepoTx.CreateOne(c.Context(), marketingProduct, nil); err != nil {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
marketingDeliveryProduct := &entity.MarketingDeliveryProduct{
|
||||
MarketingProductId: marketingProduct.Id,
|
||||
Qty: 0,
|
||||
UnitPrice: 0,
|
||||
TotalWeight: 0,
|
||||
AvgWeight: 0,
|
||||
TotalPrice: 0,
|
||||
DeliveryDate: nil,
|
||||
VehicleNumber: product.VehicleNumber,
|
||||
}
|
||||
if err := marketingDeliveryProductRepoTx.CreateOne(c.Context(), marketingDeliveryProduct, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if latestApproval != nil && latestApproval.StepNumber == 2 {
|
||||
actorID := uint(1) // todo: ambil dari auth context
|
||||
resetNote := ""
|
||||
action := entity.ApprovalActionApproved
|
||||
_, err := approvalSvcTx.CreateApproval(
|
||||
c.Context(),
|
||||
utils.ApprovalWorkflowMarketing,
|
||||
id,
|
||||
utils.MarketingStepPengajuan,
|
||||
&action,
|
||||
actorID,
|
||||
&resetNote)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to create reset approval: %+v", err)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to reset approval status")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if fiberErr, ok := err.(*fiber.Error); ok {
|
||||
return nil, fiberErr
|
||||
}
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update sales order")
|
||||
}
|
||||
|
||||
return s.GetOne(c, id)
|
||||
}
|
||||
|
||||
func (s salesOrdersService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||
if err := s.Repository.DeleteOne(c.Context(), id); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "SalesOrders not found")
|
||||
}
|
||||
s.Log.Errorf("Failed to delete salesOrders: %+v", err)
|
||||
return err
|
||||
marketing, err := s.MarketingRepo.GetByID(c.Context(), id, s.withRelations)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "SalesOrders not found")
|
||||
}
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch marketing %d before delete: %+v", id, err)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch sales order")
|
||||
}
|
||||
|
||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
|
||||
marketingProductRepoTx := repository.NewMarketingProductRepository(dbTransaction)
|
||||
marketingDeliveryProductRepoTx := repository.NewMarketingDeliveryProductRepository(dbTransaction)
|
||||
marketingRepoTx := repository.NewMarketingRepository(dbTransaction)
|
||||
|
||||
if len(marketing.Products) > 0 {
|
||||
for _, product := range marketing.Products {
|
||||
if err := marketingDeliveryProductRepoTx.DeleteMany(c.Context(), func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("marketing_product_id = ?", product.Id)
|
||||
}); err != nil && err != gorm.ErrRecordNotFound {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete sales order products")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := marketingProductRepoTx.DeleteMany(c.Context(), func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("marketing_id = ?", id)
|
||||
}); err != nil && err != gorm.ErrRecordNotFound {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete sales order products")
|
||||
}
|
||||
|
||||
if err := marketingRepoTx.DeleteOne(c.Context(), id); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete sales order")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if fiberErr, ok := err.(*fiber.Error); ok {
|
||||
return fiberErr
|
||||
}
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete sales order")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s salesOrdersService) Approval(c *fiber.Ctx, req *validation.Approve) ([]entity.Marketing, error) {
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
approvalSvc := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(s.MarketingRepo.DB()))
|
||||
|
||||
var action entity.ApprovalAction
|
||||
switch strings.ToUpper(strings.TrimSpace(req.Action)) {
|
||||
case string(entity.ApprovalActionRejected):
|
||||
action = entity.ApprovalActionRejected
|
||||
case string(entity.ApprovalActionApproved):
|
||||
action = entity.ApprovalActionApproved
|
||||
default:
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "action must be APPROVED or REJECTED")
|
||||
}
|
||||
|
||||
approvableIDs := utils.UniqueUintSlice(req.ApprovableIds)
|
||||
if len(approvableIDs) == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "approvable_ids must contain at least one id")
|
||||
}
|
||||
|
||||
for _, id := range approvableIDs {
|
||||
if err := commonSvc.EnsureRelations(c.Context(),
|
||||
commonSvc.RelationCheck{Name: "Marketing", ID: &id, Exists: s.MarketingRepo.IdExists},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
latestApproval, err := approvalSvc.LatestByTarget(c.Context(), utils.ApprovalWorkflowMarketing, id, nil)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check approval status")
|
||||
}
|
||||
|
||||
if latestApproval == nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("No approval found for Marketing %d - sales orders must be created first", id))
|
||||
}
|
||||
|
||||
if action == entity.ApprovalActionApproved {
|
||||
switch latestApproval.StepNumber {
|
||||
case uint16(utils.MarketingStepPengajuan):
|
||||
case uint16(utils.MarketingStepSalesOrder):
|
||||
default:
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest,
|
||||
fmt.Sprintf("Marketing %d cannot be approved - current step is %d", id, latestApproval.StepNumber))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := s.MarketingRepo.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
approvalSvc := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||
|
||||
for _, approvableID := range approvableIDs {
|
||||
|
||||
latestApproval, err := approvalSvc.LatestByTarget(c.Context(), utils.ApprovalWorkflowMarketing, approvableID, nil)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to get latest approval for %d: %+v", approvableID, err)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to check current approval step")
|
||||
}
|
||||
|
||||
if latestApproval == nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("No approval found for Marketing %d", approvableID))
|
||||
}
|
||||
|
||||
var nextStep approvalutils.ApprovalStep
|
||||
currentStep := latestApproval.StepNumber
|
||||
|
||||
if action == entity.ApprovalActionApproved {
|
||||
|
||||
if currentStep == uint16(utils.MarketingStepPengajuan) {
|
||||
nextStep = utils.MarketingStepSalesOrder
|
||||
} else if currentStep == uint16(utils.MarketingStepSalesOrder) {
|
||||
nextStep = utils.MarketingDeliveryOrder
|
||||
} else {
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Marketing %d already completed all approval steps", approvableID))
|
||||
}
|
||||
} else {
|
||||
|
||||
nextStep = approvalutils.ApprovalStep(currentStep)
|
||||
}
|
||||
|
||||
actorID := uint(1) // todo ambil dari auth context
|
||||
if _, err := approvalSvc.CreateApproval(
|
||||
c.Context(),
|
||||
utils.ApprovalWorkflowMarketing,
|
||||
approvableID,
|
||||
nextStep,
|
||||
&action,
|
||||
actorID,
|
||||
req.Notes,
|
||||
); err != nil {
|
||||
s.Log.Errorf("Failed to create approval for %d: %+v", approvableID, err)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create approval")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if fiberErr, ok := err.(*fiber.Error); ok {
|
||||
return nil, fiberErr
|
||||
}
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to record approval")
|
||||
}
|
||||
|
||||
updated := make([]entity.Marketing, 0, len(approvableIDs))
|
||||
for _, id := range approvableIDs {
|
||||
marketing, err := s.GetOne(c, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
updated = append(updated, *marketing)
|
||||
}
|
||||
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user