mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
611 lines
21 KiB
Go
611 lines
21 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"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"
|
|
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
|
productWarehouseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
|
"gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/dto"
|
|
marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
|
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/validations"
|
|
rShared "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
|
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
|
|
|
"github.com/go-playground/validator/v10"
|
|
"github.com/gofiber/fiber/v2"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type DeliveryOrdersService interface {
|
|
GetAll(ctx *fiber.Ctx, params *validation.DeliveryOrderQuery) ([]dto.MarketingListDTO, int64, error)
|
|
GetOne(ctx *fiber.Ctx, id uint) (*dto.MarketingDetailDTO, error)
|
|
CreateOne(ctx *fiber.Ctx, req *validation.DeliveryOrderCreate) (*dto.MarketingDetailDTO, error)
|
|
UpdateOne(ctx *fiber.Ctx, req *validation.DeliveryOrderUpdate, id uint) (*dto.MarketingDetailDTO, error)
|
|
}
|
|
|
|
type deliveryOrdersService struct {
|
|
Validate *validator.Validate
|
|
MarketingRepo marketingRepo.MarketingRepository
|
|
MarketingProductRepo marketingRepo.MarketingProductRepository
|
|
MarketingDeliveryProductRepo marketingRepo.MarketingDeliveryProductRepository
|
|
StockLogRepo rShared.StockLogRepository
|
|
ApprovalSvc commonSvc.ApprovalService
|
|
FifoSvc commonSvc.FifoService
|
|
}
|
|
|
|
func NewDeliveryOrdersService(
|
|
marketingRepo marketingRepo.MarketingRepository,
|
|
marketingProductRepo marketingRepo.MarketingProductRepository,
|
|
marketingDeliveryProductRepo marketingRepo.MarketingDeliveryProductRepository,
|
|
stockLogRepo rShared.StockLogRepository,
|
|
approvalSvc commonSvc.ApprovalService,
|
|
fifoSvc commonSvc.FifoService,
|
|
validate *validator.Validate,
|
|
) DeliveryOrdersService {
|
|
return &deliveryOrdersService{
|
|
Validate: validate,
|
|
MarketingRepo: marketingRepo,
|
|
MarketingProductRepo: marketingProductRepo,
|
|
MarketingDeliveryProductRepo: marketingDeliveryProductRepo,
|
|
StockLogRepo: stockLogRepo,
|
|
ApprovalSvc: approvalSvc,
|
|
FifoSvc: fifoSvc,
|
|
}
|
|
}
|
|
|
|
func (s deliveryOrdersService) withRelations(db *gorm.DB) *gorm.DB {
|
|
return db.
|
|
Preload("CreatedUser").
|
|
Preload("Customer").
|
|
Preload("SalesPerson").
|
|
Preload("Products.ProductWarehouse.Product").
|
|
Preload("Products.ProductWarehouse.Warehouse").
|
|
Preload("Products.DeliveryProduct")
|
|
}
|
|
|
|
func (s deliveryOrdersService) getMarketingWithDeliveries(c *fiber.Ctx, marketingId uint) (*dto.MarketingDetailDTO, error) {
|
|
if err := m.EnsureMarketingAccess(c, s.MarketingRepo.DB(), marketingId); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
marketing, err := s.MarketingRepo.GetByID(c.Context(), marketingId, s.withRelations)
|
|
if err != nil {
|
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch marketing")
|
|
}
|
|
|
|
latestApproval, err := s.ApprovalSvc.LatestByTarget(c.Context(), utils.ApprovalWorkflowMarketing, marketingId, nil)
|
|
if err != nil {
|
|
}
|
|
marketing.LatestApproval = latestApproval
|
|
|
|
allDeliveryProducts, err := s.MarketingDeliveryProductRepo.GetByMarketingId(c.Context(), marketingId)
|
|
if err != nil {
|
|
allDeliveryProducts = []entity.MarketingDeliveryProduct{}
|
|
}
|
|
|
|
responseDTO := dto.ToMarketingDetailDTO(marketing, allDeliveryProducts)
|
|
return &responseDTO, nil
|
|
}
|
|
|
|
func (s deliveryOrdersService) GetAll(c *fiber.Ctx, params *validation.DeliveryOrderQuery) ([]dto.MarketingListDTO, int64, error) {
|
|
if err := s.Validate.Struct(params); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
scope, err := m.ResolveLocationScope(c, s.MarketingRepo.DB())
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
offset := (params.Page - 1) * params.Limit
|
|
|
|
marketings, total, err := s.MarketingRepo.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
|
db = db.
|
|
Preload("CreatedUser").
|
|
Preload("Customer").
|
|
Preload("SalesPerson").
|
|
Preload("Products.ProductWarehouse.Product").
|
|
Preload("Products.ProductWarehouse.Warehouse").
|
|
Preload("Products.DeliveryProduct")
|
|
|
|
if scope.Restrict {
|
|
if len(scope.IDs) == 0 {
|
|
return db.Where("1 = 0")
|
|
}
|
|
db = db.Where(
|
|
`EXISTS (
|
|
SELECT 1
|
|
FROM marketing_products mp
|
|
JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id
|
|
JOIN warehouses w ON w.id = pw.warehouse_id
|
|
WHERE mp.marketing_id = marketings.id
|
|
AND w.location_id IN ?
|
|
)`,
|
|
scope.IDs,
|
|
)
|
|
}
|
|
|
|
if params.MarketingId != 0 {
|
|
return db.Where("id = ?", params.MarketingId)
|
|
}
|
|
return db.Order("created_at DESC").Order("updated_at DESC")
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
for i := range marketings {
|
|
latestApproval, err := s.ApprovalSvc.LatestByTarget(c.Context(), utils.ApprovalWorkflowMarketing, marketings[i].Id, func(db *gorm.DB) *gorm.DB {
|
|
return db.Preload("ActionUser")
|
|
})
|
|
if err != nil {
|
|
continue
|
|
}
|
|
marketings[i].LatestApproval = latestApproval
|
|
}
|
|
|
|
result := make([]dto.MarketingListDTO, len(marketings))
|
|
for i, marketing := range marketings {
|
|
result[i] = dto.ToMarketingListDTO(&marketing, []entity.MarketingDeliveryProduct{})
|
|
}
|
|
|
|
return result, total, nil
|
|
}
|
|
|
|
func (s deliveryOrdersService) GetOne(c *fiber.Ctx, id uint) (*dto.MarketingDetailDTO, error) {
|
|
if err := m.EnsureMarketingAccess(c, s.MarketingRepo.DB(), id); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
marketing, err := s.MarketingRepo.GetByID(c.Context(), id, s.withRelations)
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, fiber.NewError(fiber.StatusNotFound, "Marketing not found")
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
allDeliveryProducts, err := s.MarketingDeliveryProductRepo.GetByMarketingId(c.Context(), id)
|
|
if err != nil {
|
|
allDeliveryProducts = []entity.MarketingDeliveryProduct{}
|
|
}
|
|
|
|
if s.ApprovalSvc != nil {
|
|
approvals, err := s.ApprovalSvc.ListByTarget(c.Context(), utils.ApprovalWorkflowMarketing, marketing.Id, func(db *gorm.DB) *gorm.DB {
|
|
return db.Preload("ActionUser")
|
|
})
|
|
if err != nil {
|
|
|
|
} else if len(approvals) > 0 {
|
|
if marketing.LatestApproval == nil {
|
|
latest := approvals[len(approvals)-1]
|
|
marketing.LatestApproval = &latest
|
|
}
|
|
} else {
|
|
marketing.LatestApproval = nil
|
|
}
|
|
}
|
|
|
|
responseDTO := dto.ToMarketingDetailDTO(marketing, allDeliveryProducts)
|
|
return &responseDTO, nil
|
|
}
|
|
|
|
func (s *deliveryOrdersService) CreateOne(c *fiber.Ctx, req *validation.DeliveryOrderCreate) (*dto.MarketingDetailDTO, error) {
|
|
if err := s.Validate.Struct(req); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := m.EnsureMarketingAccess(c, s.MarketingRepo.DB(), req.MarketingId); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := commonSvc.EnsureRelations(c.Context(),
|
|
commonSvc.RelationCheck{Name: "Marketing", ID: &req.MarketingId, Exists: s.MarketingRepo.IdExists},
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
actorID, err := m.ActorIDFromContext(c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
approvalSvc := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(s.MarketingRepo.DB()))
|
|
|
|
latestApproval, err := approvalSvc.LatestByTarget(c.Context(), utils.ApprovalWorkflowMarketing, req.MarketingId, nil)
|
|
if err != nil {
|
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check approval status")
|
|
}
|
|
if latestApproval == nil {
|
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Marketing has not been submitted for approval")
|
|
}
|
|
if latestApproval.StepNumber < uint16(utils.MarketingStepSalesOrder) {
|
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Marketing must be approved to Sales Order step before creating delivery order")
|
|
}
|
|
if latestApproval.StepNumber >= uint16(utils.MarketingDeliveryOrder) {
|
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Delivery order already exists for this marketing")
|
|
}
|
|
|
|
err = s.MarketingRepo.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
|
|
|
marketingProductRepositoryTx := marketingRepo.NewMarketingProductRepository(dbTransaction)
|
|
marketingDeliveryProductRepositoryTx := marketingRepo.NewMarketingDeliveryProductRepository(dbTransaction)
|
|
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
|
|
|
allMarketingProducts, err := marketingProductRepositoryTx.GetByMarketingID(c.Context(), req.MarketingId)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("No marketing products found for marketing %d", req.MarketingId))
|
|
}
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch marketing products")
|
|
}
|
|
|
|
for _, requestedProduct := range req.DeliveryProducts {
|
|
var foundMarketingProduct *entity.MarketingProduct
|
|
for i := range allMarketingProducts {
|
|
if allMarketingProducts[i].Id == requestedProduct.MarketingProductId {
|
|
foundMarketingProduct = &allMarketingProducts[i]
|
|
break
|
|
}
|
|
}
|
|
if foundMarketingProduct == nil {
|
|
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Marketing product %d not found for this marketing", requestedProduct.MarketingProductId))
|
|
}
|
|
|
|
if err := commonSvc.EnsureProjectFlockNotClosedForProductWarehouses(
|
|
c.Context(),
|
|
dbTransaction,
|
|
[]uint{foundMarketingProduct.ProductWarehouseId},
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
deliveryProduct, err := marketingDeliveryProductRepositoryTx.GetByMarketingProductID(c.Context(), foundMarketingProduct.Id)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Delivery product for marketing product %d not found", requestedProduct.MarketingProductId))
|
|
}
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch delivery product")
|
|
}
|
|
|
|
var itemDeliveryDate *time.Time
|
|
if requestedProduct.DeliveryDate != "" {
|
|
parsedDate, err := utils.ParseDateString(requestedProduct.DeliveryDate)
|
|
if err != nil {
|
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid delivery date format for product %d: %v", requestedProduct.MarketingProductId, err))
|
|
}
|
|
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 {
|
|
|
|
totalPrice = requestedProduct.Qty * requestedProduct.UnitPrice
|
|
} else {
|
|
|
|
totalPrice = totalWeight * requestedProduct.UnitPrice
|
|
}
|
|
|
|
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
|
|
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
|
|
deliveryProduct.AvgWeight = requestedProduct.AvgWeight
|
|
deliveryProduct.TotalWeight = totalWeight
|
|
deliveryProduct.TotalPrice = totalPrice
|
|
deliveryProduct.DeliveryDate = itemDeliveryDate
|
|
deliveryProduct.VehicleNumber = requestedProduct.VehicleNumber
|
|
|
|
if requestedProduct.Qty > 0 {
|
|
|
|
if err := s.consumeDeliveryStock(c.Context(), dbTransaction, deliveryProduct, foundMarketingProduct, requestedProduct.Qty, actorID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := marketingDeliveryProductRepositoryTx.UpdateOne(c.Context(), deliveryProduct.Id, deliveryProduct, nil); err != nil {
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update delivery product")
|
|
}
|
|
|
|
}
|
|
|
|
approvalAction := entity.ApprovalActionApproved
|
|
if _, err := approvalSvcTx.CreateApproval(
|
|
c.Context(),
|
|
utils.ApprovalWorkflowMarketing,
|
|
req.MarketingId,
|
|
utils.MarketingDeliveryOrder,
|
|
&approvalAction,
|
|
actorID,
|
|
nil); err != nil {
|
|
if !errors.Is(err, gorm.ErrDuplicatedKey) {
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create delivery order approval")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
if fiberErr, ok := err.(*fiber.Error); ok {
|
|
return nil, fiberErr
|
|
}
|
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create delivery order")
|
|
}
|
|
|
|
return s.getMarketingWithDeliveries(c, req.MarketingId)
|
|
}
|
|
|
|
func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryOrderUpdate, id uint) (*dto.MarketingDetailDTO, error) {
|
|
if err := s.Validate.Struct(req); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := m.EnsureMarketingAccess(c, s.MarketingRepo.DB(), id); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := commonSvc.EnsureRelations(c.Context(),
|
|
commonSvc.RelationCheck{Name: "Marketing", ID: &id, Exists: s.MarketingRepo.IdExists},
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
actorID, err := m.ActorIDFromContext(c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = s.MarketingRepo.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
|
|
|
marketingProductRepositoryTx := marketingRepo.NewMarketingProductRepository(dbTransaction)
|
|
marketingDeliveryProductRepositoryTx := marketingRepo.NewMarketingDeliveryProductRepository(dbTransaction)
|
|
|
|
allMarketingProducts, err := marketingProductRepositoryTx.GetByMarketingID(c.Context(), id)
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch marketing products")
|
|
}
|
|
|
|
if len(req.DeliveryProducts) > 0 {
|
|
for _, requestedProduct := range req.DeliveryProducts {
|
|
|
|
var foundMarketingProduct *entity.MarketingProduct
|
|
for i := range allMarketingProducts {
|
|
if allMarketingProducts[i].Id == requestedProduct.MarketingProductId {
|
|
foundMarketingProduct = &allMarketingProducts[i]
|
|
break
|
|
}
|
|
}
|
|
if foundMarketingProduct == nil {
|
|
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Marketing product %d not found for this marketing", requestedProduct.MarketingProductId))
|
|
}
|
|
if err := commonSvc.EnsureProjectFlockNotClosedForProductWarehouses(
|
|
c.Context(),
|
|
dbTransaction,
|
|
[]uint{foundMarketingProduct.ProductWarehouseId},
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
deliveryProduct, err := marketingDeliveryProductRepositoryTx.GetByMarketingProductID(c.Context(), foundMarketingProduct.Id)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Delivery product for marketing product %d not found", requestedProduct.MarketingProductId))
|
|
}
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch delivery product")
|
|
}
|
|
|
|
var itemDeliveryDate *time.Time
|
|
if requestedProduct.DeliveryDate != "" {
|
|
parsedDate, err := utils.ParseDateString(requestedProduct.DeliveryDate)
|
|
if err != nil {
|
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid delivery date format for product %d: %v", requestedProduct.MarketingProductId, err))
|
|
}
|
|
itemDeliveryDate = &parsedDate
|
|
} else if deliveryProduct.DeliveryDate != nil {
|
|
itemDeliveryDate = deliveryProduct.DeliveryDate
|
|
}
|
|
|
|
oldRequestedQty := deliveryProduct.UsageQty + deliveryProduct.PendingQty
|
|
|
|
// 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
|
|
}
|
|
}
|
|
}
|
|
|
|
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
|
|
var totalPrice float64
|
|
if isPakanOrOVK {
|
|
|
|
totalPrice = requestedProduct.Qty * requestedProduct.UnitPrice
|
|
} else {
|
|
|
|
totalPrice = totalWeight * requestedProduct.UnitPrice
|
|
}
|
|
|
|
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
|
|
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
|
|
deliveryProduct.AvgWeight = requestedProduct.AvgWeight
|
|
deliveryProduct.TotalWeight = totalWeight
|
|
deliveryProduct.TotalPrice = totalPrice
|
|
deliveryProduct.DeliveryDate = itemDeliveryDate
|
|
deliveryProduct.VehicleNumber = requestedProduct.VehicleNumber
|
|
|
|
if requestedProduct.Qty != oldRequestedQty {
|
|
|
|
if oldRequestedQty > 0 {
|
|
if err := s.releaseDeliveryStock(c.Context(), dbTransaction, deliveryProduct, foundMarketingProduct, actorID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if requestedProduct.Qty > 0 {
|
|
if err := s.consumeDeliveryStock(c.Context(), dbTransaction, deliveryProduct, foundMarketingProduct, requestedProduct.Qty, actorID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if err := marketingDeliveryProductRepositoryTx.UpdateOne(c.Context(), deliveryProduct.Id, deliveryProduct, nil); err != nil {
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update delivery product")
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
if fiberErr, ok := err.(*fiber.Error); ok {
|
|
return nil, fiberErr
|
|
}
|
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update delivery order")
|
|
}
|
|
|
|
return s.getMarketingWithDeliveries(c, id)
|
|
}
|
|
|
|
func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gorm.DB, deliveryProduct *entity.MarketingDeliveryProduct, marketingProduct *entity.MarketingProduct, requestedQty float64, actorID uint) error {
|
|
if marketingProduct == nil || marketingProduct.ProductWarehouseId == 0 {
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Product warehouse not found")
|
|
}
|
|
|
|
if deliveryProduct == nil || deliveryProduct.Id == 0 {
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Delivery product not found")
|
|
}
|
|
|
|
result, err := s.FifoSvc.Consume(ctx, commonSvc.StockConsumeRequest{
|
|
UsableKey: fifo.UsableKeyMarketingDelivery,
|
|
UsableID: deliveryProduct.Id,
|
|
ProductWarehouseID: marketingProduct.ProductWarehouseId,
|
|
Quantity: requestedQty,
|
|
AllowPending: false,
|
|
Tx: tx,
|
|
})
|
|
|
|
deliveryProductRepo := marketingRepo.NewMarketingDeliveryProductRepository(tx)
|
|
|
|
if err == nil && result.UsageQuantity > 0 {
|
|
if actorID > 0 {
|
|
decreaseLog := &entity.StockLog{
|
|
Decrease: result.UsageQuantity,
|
|
LoggableType: string(utils.StockLogTypeMarketing),
|
|
LoggableId: deliveryProduct.Id,
|
|
ProductWarehouseId: marketingProduct.ProductWarehouseId,
|
|
CreatedBy: actorID,
|
|
Notes: "",
|
|
}
|
|
s.StockLogRepo.WithTx(tx).CreateOne(ctx, decreaseLog, nil)
|
|
}
|
|
}
|
|
|
|
if err != nil {
|
|
pwRepo := productWarehouseRepo.NewProductWarehouseRepository(tx)
|
|
pw, err2 := pwRepo.GetByID(ctx, marketingProduct.ProductWarehouseId, nil)
|
|
if err2 != nil {
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to check product warehouse stock")
|
|
}
|
|
|
|
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))
|
|
}
|
|
|
|
if err := deliveryProductRepo.UpdateFifoFields(ctx, deliveryProduct.Id, requestedQty, 0); err != nil {
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update delivery product")
|
|
}
|
|
|
|
if actorID > 0 {
|
|
decreaseLog := &entity.StockLog{
|
|
Decrease: requestedQty,
|
|
LoggableType: string(utils.StockLogTypeMarketing),
|
|
LoggableId: deliveryProduct.Id,
|
|
ProductWarehouseId: marketingProduct.ProductWarehouseId,
|
|
CreatedBy: actorID,
|
|
Notes: "",
|
|
}
|
|
s.StockLogRepo.WithTx(tx).CreateOne(ctx, decreaseLog, nil)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
if err := deliveryProductRepo.UpdateFifoFields(ctx, deliveryProduct.Id, result.UsageQuantity, result.PendingQuantity); err != nil {
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update delivery product")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s deliveryOrdersService) releaseDeliveryStock(ctx context.Context, tx *gorm.DB, deliveryProduct *entity.MarketingDeliveryProduct, marketingProduct *entity.MarketingProduct, actorID uint) error {
|
|
if deliveryProduct == nil || deliveryProduct.Id == 0 {
|
|
return nil
|
|
}
|
|
|
|
if marketingProduct == nil || marketingProduct.ProductWarehouseId == 0 {
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Product warehouse not found")
|
|
}
|
|
|
|
deliveryProductRepo := marketingRepo.NewMarketingDeliveryProductRepository(tx)
|
|
currentUsage, err := deliveryProductRepo.GetUsageQty(ctx, deliveryProduct.Id)
|
|
if err != nil {
|
|
currentUsage = 0
|
|
}
|
|
|
|
if currentUsage == 0 {
|
|
return nil
|
|
}
|
|
|
|
if err := s.FifoSvc.ReleaseUsage(ctx, commonSvc.StockReleaseRequest{
|
|
UsableKey: fifo.UsableKeyMarketingDelivery,
|
|
UsableID: deliveryProduct.Id,
|
|
Tx: tx,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
if actorID > 0 && currentUsage > 0 {
|
|
increaseLog := &entity.StockLog{
|
|
Increase: currentUsage,
|
|
LoggableType: string(utils.StockLogTypeMarketing),
|
|
LoggableId: deliveryProduct.Id,
|
|
ProductWarehouseId: marketingProduct.ProductWarehouseId,
|
|
CreatedBy: actorID,
|
|
Notes: "",
|
|
}
|
|
s.StockLogRepo.WithTx(tx).CreateOne(ctx, increaseLog, nil)
|
|
}
|
|
|
|
if err := deliveryProductRepo.ResetFifoFields(ctx, deliveryProduct.Id); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|