material_manage_system/internal/service/inbound.go
2026-09-16 23:30:25 +08:00

240 lines
5.2 KiB
Go

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
}