package service import ( "time" "github.com/shopspring/decimal" "github.com/wmms/wmms/internal/model" "github.com/wmms/wmms/internal/repository" ) type InboundService struct { inboundRepo *repository.InboundRepo materialSvc *MaterialService stockRepo *repository.StockRepo } func NewInboundService() *InboundService { return &InboundService{ inboundRepo: repository.NewInboundRepo(), materialSvc: NewMaterialService(), stockRepo: repository.NewStockRepo(), } } func (s *InboundService) Create(req *model.CreateInboundRequest) (*model.InboundOrder, error) { order := &model.InboundOrder{ InboundDate: time.Now(), Handler: req.Handler, Supplier: req.Supplier, Items: make([]model.InboundItem, len(req.Items)), } // Validate all items first (outside transaction) for i, item := range req.Items { exists, err := s.materialSvc.Exists(item.MaterialID) if err != nil { return nil, err } if !exists { return nil, ErrMaterialNotFound } order.Items[i] = model.InboundItem{ MaterialID: item.MaterialID, Quantity: item.Quantity, } } // Start transaction tx, err := repository.BeginTx() if err != nil { return nil, err } defer func() { if err != nil { tx.Rollback() } }() // Create inbound order within transaction err = s.inboundRepo.CreateTx(tx, order) if err != nil { return nil, err } // Update stock and create logs within transaction for i := range order.Items { item := &order.Items[i] material, err := s.materialSvc.GetByID(item.MaterialID) if err != nil { return nil, err } item.MaterialCode = material.MaterialCode item.MaterialName = material.Name err = s.stockRepo.UpdateStockTx(tx, item.MaterialID, decimal.NewFromInt(int64(item.Quantity))) if err != nil { return nil, err } stockLog := &model.StockLog{ MaterialID: item.MaterialID, Type: "in", Quantity: item.Quantity, RelatedOrderType: "inbound", RelatedOrderID: order.ID, } err = s.stockRepo.CreateLogTx(tx, stockLog) if err != nil { return nil, err } } // Commit transaction if err = tx.Commit(); err != nil { return nil, err } order.Status = "completed" return order, nil } func (s *InboundService) GetByID(id int64) (*model.InboundOrder, error) { return s.inboundRepo.GetByID(id) } func (s *InboundService) List(search string, limit, offset int) ([]model.InboundOrder, int64, error) { return s.inboundRepo.List(search, limit, offset) } func (s *InboundService) GetByMaterialID(materialID int64) ([]model.InboundRecord, error) { return s.inboundRepo.GetByMaterialID(materialID) } func (s *InboundService) Delete(id int64) error { // Get the inbound order with items order, err := s.inboundRepo.GetByID(id) if err != nil { return err } // Start transaction tx, err := repository.BeginTx() if err != nil { return err } defer func() { if err != nil { tx.Rollback() } }() // Reverse stock for each item within transaction for _, item := range order.Items { err = s.stockRepo.UpdateStockTx(tx, item.MaterialID, decimal.NewFromInt(int64(-item.Quantity))) if err != nil { return err } } // Delete stock logs within transaction err = s.stockRepo.DeleteLogsTx(tx, "inbound", id) if err != nil { return err } // Delete the inbound order (items will be cascade deleted) err = s.inboundRepo.DeleteTx(tx, id) if err != nil { return err } // Commit transaction if err = tx.Commit(); err != nil { return err } return nil } func (s *InboundService) Update(id int64, req *model.UpdateInboundRequest) (*model.InboundOrder, error) { // Get old order data oldOrder, err := s.inboundRepo.GetByID(id) if err != nil { return nil, err } // Start transaction tx, err := repository.BeginTx() if err != nil { return nil, err } defer func() { if err != nil { tx.Rollback() } }() // Reverse stock for old items (subtract what was previously added) for _, item := range oldOrder.Items { _, err = s.stockRepo.GetStockForUpdateTx(tx, item.MaterialID) if err != nil { return nil, err } err = s.stockRepo.UpdateStockTx(tx, item.MaterialID, decimal.NewFromInt(int64(-item.Quantity))) if err != nil { return nil, err } } // Delete old stock logs err = s.stockRepo.DeleteLogsTx(tx, "inbound", id) if err != nil { return nil, err } // Update order in repository err = s.inboundRepo.UpdateTx(tx, id, req) if err != nil { return nil, err } // Add stock for new items and create logs for _, item := range req.Items { _, err = s.stockRepo.GetStockForUpdateTx(tx, item.MaterialID) if err != nil { return nil, err } err = s.stockRepo.UpdateStockTx(tx, item.MaterialID, decimal.NewFromInt(int64(item.Quantity))) if err != nil { return nil, err } stockLog := &model.StockLog{ MaterialID: item.MaterialID, Type: "in", Quantity: item.Quantity, RelatedOrderType: "inbound", RelatedOrderID: id, } err = s.stockRepo.CreateLogTx(tx, stockLog) if err != nil { return nil, err } } // Commit transaction if err = tx.Commit(); err != nil { return nil, err } // Get updated order with material details updatedOrder, err := s.inboundRepo.GetByID(id) if err != nil { return nil, err } updatedOrder.Status = "completed" return updatedOrder, nil }