239 lines
5.1 KiB
Go
239 lines
5.1 KiB
Go
package service
|
|
|
|
import (
|
|
"time"
|
|
|
|
"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.materialSvc.UpdateStockTx(tx, item.MaterialID, 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.materialSvc.UpdateStockTx(tx, item.MaterialID, -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.materialSvc.GetStockForUpdateTx(tx, item.MaterialID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = s.materialSvc.UpdateStockTx(tx, item.MaterialID, -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.materialSvc.GetStockForUpdateTx(tx, item.MaterialID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = s.materialSvc.UpdateStockTx(tx, item.MaterialID, 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
|
|
}
|