package service import ( "context" "github.com/Caknoooo/go-gin-clean-starter/database/entities" dtodomain "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/dto" "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/query" "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/repository" sequenceservice "github.com/Caknoooo/go-gin-clean-starter/modules/sequence/service" "github.com/Caknoooo/go-gin-clean-starter/pkg/constants" "github.com/Caknoooo/go-gin-clean-starter/pkg/utils" "github.com/google/uuid" "github.com/sirupsen/logrus" "gorm.io/gorm" ) type InventoryMovementService interface { Create(ctx context.Context, req dtodomain.InventoryMovementCreateRequest) (dtodomain.InventoryMovementResponse, error) GetById(ctx context.Context, id string) (dtodomain.InventoryMovementResponse, error) GetAll(ctx context.Context, filter query.InventoryMovementFilter) ([]dtodomain.InventoryMovementResponse, int64, error) Update(ctx context.Context, req dtodomain.InventoryMovementUpdateRequest, id string) (dtodomain.InventoryMovementResponse, error) Delete(ctx context.Context, id string) error GetLinesByMovementId(ctx context.Context, movementId string) ([]dtodomain.InventoryMovementLineResponse, error) CreateLine(ctx context.Context, movementId string, req dtodomain.InventoryMovementLineCreateRequest) (dtodomain.InventoryMovementLineResponse, error) UpdateLine(ctx context.Context, lineId string, req dtodomain.InventoryMovementLineUpdateRequest) (dtodomain.InventoryMovementLineResponse, error) DeleteLine(ctx context.Context, lineId string) error } type inventoryMovementService struct { db *gorm.DB movementRepo repository.InventoryMovementRepository movementLineRepo repository.InventoryMovementLineRepository sequenceService sequenceservice.SequenceService log *logrus.Logger } // GetLinesByMovementId implements InventoryMovementService. func (s *inventoryMovementService) GetLinesByMovementId(ctx context.Context, movementId string) ([]dtodomain.InventoryMovementLineResponse, error) { lines, err := s.movementLineRepo.GetAllByMovementId(ctx, movementId) if err != nil { return nil, err } var responses []dtodomain.InventoryMovementLineResponse for _, e := range lines { responses = append(responses, dtodomain.ToInventoryMovementLineResponse(e)) } if responses == nil { responses = make([]dtodomain.InventoryMovementLineResponse, 0) } return responses, nil } func (s *inventoryMovementService) Create(ctx context.Context, req dtodomain.InventoryMovementCreateRequest) (dtodomain.InventoryMovementResponse, error) { tx := s.db.Begin() defer func() { if r := recover(); r != nil { tx.Rollback() } }() clientUUID, err := uuid.Parse(req.ClientID) if err != nil { tx.Rollback() return dtodomain.InventoryMovementResponse{}, err } movNumber, err := entities.GenerateDocumentNumberInvMovement(s.db, req.ClientID) if err != nil { tx.Rollback() return dtodomain.InventoryMovementResponse{}, err } movement := entities.TInventoryMovementEntity{ MovementNumber: movNumber, MovementDate: utils.StringToDateTime(req.MovementDate), MovementType: req.MovementType, ClientID: clientUUID, Status: req.Status, SourceLocationID: uuid.MustParse(req.SourceLocationID), DestinationLocationID: uuid.MustParse(req.DestinationLocationID), FullAuditTrail: utils.FillAuditTrail(ctx, constants.CREATE), } created, err := s.movementRepo.Create(ctx, tx, movement) if err != nil { tx.Rollback() return dtodomain.InventoryMovementResponse{}, err } // Bulk create lines var lines []entities.TInventoryMovementLineEntity for _, lineReq := range req.MovementLines { productUUID := uuid.Nil if lineReq.ProductID != "" { productUUID, err = uuid.Parse(lineReq.ProductID) if err != nil { tx.Rollback() return dtodomain.InventoryMovementResponse{}, err } } clientLineUUID, err := uuid.Parse(lineReq.ClientID) if err != nil { tx.Rollback() return dtodomain.InventoryMovementResponse{}, err } lines = append(lines, entities.TInventoryMovementLineEntity{ MovedQuantity: lineReq.MovedQuantity, InvMovementID: created.ID, ProductID: productUUID, StorageID: uuid.MustParse(lineReq.StorageID), ClientID: clientLineUUID, Status: lineReq.Status, }) } if len(lines) > 0 { err = s.movementLineRepo.BulkCreate(ctx, tx, lines) if err != nil { tx.Rollback() return dtodomain.InventoryMovementResponse{}, err } } tx.Commit() result, err := s.movementRepo.GetById(ctx, nil, created.ID.String()) if err != nil { return dtodomain.InventoryMovementResponse{}, err } s.log.WithFields(logrus.Fields{ "user_id": utils.GetUserID(ctx), "action": "create", "entity": "inventory_movement", "entity_id": created.ID.String(), }).Info("Inventory Movement created") return dtodomain.ToInventoryMovementResponse(result), nil } func (s *inventoryMovementService) GetById(ctx context.Context, id string) (dtodomain.InventoryMovementResponse, error) { movement, err := s.movementRepo.GetById(ctx, nil, id) if err != nil { return dtodomain.InventoryMovementResponse{}, err } return dtodomain.ToInventoryMovementResponse(movement), nil } func (s *inventoryMovementService) GetAll(ctx context.Context, filter query.InventoryMovementFilter) ([]dtodomain.InventoryMovementResponse, int64, error) { movements, total, err := s.movementRepo.GetAll(ctx, filter) if err != nil { return nil, 0, err } var responses []dtodomain.InventoryMovementResponse for _, e := range movements { responses = append(responses, dtodomain.ToInventoryMovementResponse(e)) } if responses == nil { responses = make([]dtodomain.InventoryMovementResponse, 0) } return responses, total, nil } func (s *inventoryMovementService) Update(ctx context.Context, req dtodomain.InventoryMovementUpdateRequest, id string) (dtodomain.InventoryMovementResponse, error) { tx := s.db.Begin() defer func() { if r := recover(); r != nil { tx.Rollback() } }() movement, err := s.movementRepo.GetById(ctx, tx, id) if err != nil { tx.Rollback() return dtodomain.InventoryMovementResponse{}, err } before := movement movement.MovementDate = utils.StringToDateTime(req.MovementDate) movement.MovementType = req.MovementType movement.Status = req.Status movement.FullAuditTrail = utils.FillAuditTrail(ctx, constants.UPDATE) updated, err := s.movementRepo.Update(ctx, tx, movement) if err != nil { tx.Rollback() return dtodomain.InventoryMovementResponse{}, err } tx.Commit() result, err := s.movementRepo.GetById(ctx, nil, updated.ID.String()) if err != nil { return dtodomain.InventoryMovementResponse{}, err } changes := utils.GetChangedFields(before, result) s.log.WithFields(logrus.Fields{ "user_id": utils.GetUserID(ctx), "action": "update", "entity": "inventory_movement", "entity_id": id, "changes": changes, }).Info("Inventory Movement updated") return dtodomain.ToInventoryMovementResponse(result), nil } func (s *inventoryMovementService) Delete(ctx context.Context, id string) error { tx := s.db.Begin() defer func() { if r := recover(); r != nil { tx.Rollback() } }() movement, err := s.movementRepo.GetById(ctx, tx, id) if err != nil { tx.Rollback() return err } movement.FullAuditTrail = utils.FillAuditTrail(ctx, constants.DELETE) if _, err := s.movementRepo.Update(ctx, tx, movement); err != nil { tx.Rollback() return err } if err := s.movementRepo.Delete(ctx, tx, id); err != nil { tx.Rollback() return err } tx.Commit() s.log.WithFields(logrus.Fields{ "user_id": utils.GetUserID(ctx), "action": "delete", "entity": "inventory_movement", "entity_id": id, }).Info("Inventory Movement deleted") return nil } func (s *inventoryMovementService) CreateLine(ctx context.Context, movementId string, req dtodomain.InventoryMovementLineCreateRequest) (dtodomain.InventoryMovementLineResponse, error) { movementUUID, err := uuid.Parse(movementId) if err != nil { return dtodomain.InventoryMovementLineResponse{}, err } productUUID := uuid.Nil if req.ProductID != "" { productUUID, err = uuid.Parse(req.ProductID) if err != nil { return dtodomain.InventoryMovementLineResponse{}, err } } clientLineUUID, err := uuid.Parse(req.ClientID) if err != nil { return dtodomain.InventoryMovementLineResponse{}, err } line := entities.TInventoryMovementLineEntity{ MovedQuantity: req.MovedQuantity, InvMovementID: movementUUID, ProductID: productUUID, StorageID: uuid.MustParse(req.StorageID), ClientID: clientLineUUID, Status: req.Status, FullAuditTrail: utils.FillAuditTrail(ctx, constants.CREATE), } created, err := s.movementLineRepo.Create(ctx, nil, line) if err != nil { return dtodomain.InventoryMovementLineResponse{}, err } s.log.WithFields(logrus.Fields{ "user_id": utils.GetUserID(ctx), "action": "create", "entity": "inventory_movement_line", "entity_id": created.ID.String(), }).Info("Inventory Movement Line created") return dtodomain.ToInventoryMovementLineResponse(created), nil } func (s *inventoryMovementService) UpdateLine(ctx context.Context, lineId string, req dtodomain.InventoryMovementLineUpdateRequest) (dtodomain.InventoryMovementLineResponse, error) { line, err := s.movementLineRepo.GetById(ctx, nil, lineId) if err != nil { return dtodomain.InventoryMovementLineResponse{}, err } before := line if req.MovedQuantity != nil { line.MovedQuantity = *req.MovedQuantity } if req.Status != nil { line.Status = *req.Status } updated, err := s.movementLineRepo.Update(ctx, nil, line) if err != nil { return dtodomain.InventoryMovementLineResponse{}, err } changes := utils.GetChangedFields(before, updated) s.log.WithFields(logrus.Fields{ "user_id": utils.GetUserID(ctx), "action": "update", "entity": "inventory_movement_line", "entity_id": lineId, "changes": changes, }).Info("Inventory Movement Line updated") return dtodomain.ToInventoryMovementLineResponse(updated), nil } func (s *inventoryMovementService) DeleteLine(ctx context.Context, lineId string) error { line, err := s.movementLineRepo.GetById(ctx, nil, lineId) if err != nil { return err } line.FullAuditTrail = utils.FillAuditTrail(ctx, constants.DELETE) if _, err := s.movementLineRepo.Update(ctx, nil, line); err != nil { return err } result := s.movementLineRepo.Delete(ctx, nil, lineId) if result != nil { return result } s.log.WithFields(logrus.Fields{ "user_id": utils.GetUserID(ctx), "action": "delete", "entity": "inventory_movement_line", "entity_id": lineId, }).Info("Inventory Movement Line deleted") return nil } func NewInventoryMovementService(db *gorm.DB, movementRepo repository.InventoryMovementRepository, movementLineRepo repository.InventoryMovementLineRepository, sequenceService sequenceservice.SequenceService, log *logrus.Logger) InventoryMovementService { return &inventoryMovementService{ db: db, movementRepo: movementRepo, movementLineRepo: movementLineRepo, sequenceService: sequenceService, log: log, } }