wms-be/modules/inventory_receipt/service/inventory_receipt_service.go

578 lines
19 KiB
Go

package service
import (
"context"
"errors"
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
dtodomain "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_receipt/dto"
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_receipt/query"
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_receipt/repository"
invstoragerepository "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_storage/repository"
productrepository "github.com/Caknoooo/go-gin-clean-starter/modules/product/repository"
sequenceservice "github.com/Caknoooo/go-gin-clean-starter/modules/sequence/service"
uomrepository "github.com/Caknoooo/go-gin-clean-starter/modules/uom/repository"
"github.com/Caknoooo/go-gin-clean-starter/pkg/constants"
pkgdto "github.com/Caknoooo/go-gin-clean-starter/pkg/dto"
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
"github.com/google/uuid"
"gorm.io/gorm"
)
type InventoryReceiptService interface {
Create(ctx context.Context, req dtodomain.InventoryReceiptCreateRequest) (dtodomain.InventoryReceiptResponse, error)
GetById(ctx context.Context, id string) (dtodomain.InventoryReceiptResponse, error)
GetAll(ctx context.Context, filter query.InventoryReceiptFilter) ([]dtodomain.InventoryReceiptResponse, int64, error)
Update(ctx context.Context, req dtodomain.InventoryReceiptUpdateRequest, id string) (dtodomain.InventoryReceiptResponse, error)
Delete(ctx context.Context, id string) error
GetLinesByReceiptId(ctx context.Context, id string) ([]dtodomain.InventoryReceiptLineResponse, error)
CreateLine(ctx context.Context, receiptId string, req dtodomain.InventoryReceiptLineCreateRequest) (dtodomain.InventoryReceiptLineResponse, error)
UpdateLine(ctx context.Context, lineId string, req dtodomain.InventoryReceiptLineUpdateRequest) (dtodomain.InventoryReceiptLineResponse, error)
DeleteLine(ctx context.Context, lineId string) error
OnComplete(ctx context.Context, id string) (dtodomain.InventoryReceiptResponse, error)
}
type inventoryReceiptService struct {
db *gorm.DB
receiptRepo repository.InventoryReceiptRepository
receiptLineRepo repository.InventoryReceiptLineRepository
productRepo productrepository.ProductRepository
uomRepo uomrepository.UomRepository
invStorageRepository invstoragerepository.InventoryStorageRepository
sequenceService sequenceservice.SequenceService
}
func (s *inventoryReceiptService) GetLinesByReceiptId(ctx context.Context, id string) ([]dtodomain.InventoryReceiptLineResponse, error) {
lines, err := s.receiptLineRepo.GetAllByReceiptId(ctx, id)
if err != nil {
return nil, err
}
var responses []dtodomain.InventoryReceiptLineResponse
for _, line := range lines {
responses = append(responses, dtodomain.ToInventoryReceiptLineResponse(line))
}
return responses, nil
}
func (s *inventoryReceiptService) OnComplete(ctx context.Context, id string) (dtodomain.InventoryReceiptResponse, error) {
tx := s.db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
receipt, err := s.receiptRepo.GetById(ctx, tx, id)
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
receipt.Status = constants.COMPLETED
for _, line := range receipt.ReceiptLines {
product, err := s.productRepo.GetById(ctx, tx, line.ProductID.String())
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
existing, err := s.invStorageRepository.GetLatestByProductAndClient(ctx, tx, product.ID.String(), receipt.ClientID.String())
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
if existing.ID != uuid.Nil {
existing.OnHandQuantity += line.Quantity
existing.AvailableQuantity += line.Quantity
_, err = s.invStorageRepository.Update(ctx, tx, existing)
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
} else {
newStorage := entities.InventoryStorageEntity{
ProductID: line.ProductID,
UomID: *product.UomID,
OnHandQuantity: line.Quantity,
AvailableQuantity: line.Quantity,
InvReceiptID: receipt.ID,
ClientID: receipt.ClientID,
}
_, err = s.invStorageRepository.Create(ctx, tx, newStorage)
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
}
}
if _, err := s.receiptRepo.Update(ctx, tx, receipt); err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
tx.Commit()
return toInventoryReceiptResponse(receipt), nil
}
// DeleteLine implements InventoryReceiptService.
func (s *inventoryReceiptService) DeleteLine(ctx context.Context, lineId string) error {
return s.receiptLineRepo.Delete(ctx, nil, lineId)
}
// UpdateLine implements InventoryReceiptService.
func (s *inventoryReceiptService) UpdateLine(ctx context.Context, lineId string, req dtodomain.InventoryReceiptLineUpdateRequest) (dtodomain.InventoryReceiptLineResponse, error) {
line, err := s.receiptLineRepo.GetById(ctx, nil, lineId)
if err != nil {
return dtodomain.InventoryReceiptLineResponse{}, err
}
if req.Quantity != nil {
line.Quantity = *req.Quantity
}
if req.BatchNumber != nil {
line.BatchNumber = *req.BatchNumber
}
if req.RepackingSuggestion != nil {
line.RepackingSuggestion = *req.RepackingSuggestion
}
if req.RepackUomID != nil {
if *req.RepackUomID != "" {
tmp, err := uuid.Parse(*req.RepackUomID)
if err != nil {
return dtodomain.InventoryReceiptLineResponse{}, err
}
line.RepackUomID = &tmp
} else {
line.RepackUomID = nil
}
}
if req.ProductID != nil {
if *req.ProductID != "" {
tmp, err := uuid.Parse(*req.ProductID)
if err != nil {
return dtodomain.InventoryReceiptLineResponse{}, err
}
line.ProductID = tmp
} else {
line.ProductID = uuid.Nil
}
}
updated, err := s.receiptLineRepo.Update(ctx, nil, line)
if err != nil {
return dtodomain.InventoryReceiptLineResponse{}, err
}
var repackUomID *string
if updated.RepackUomID != nil {
tmp := updated.RepackUomID.String()
repackUomID = &tmp
}
product := dtodomain.InventoryReceiptLineProductResponse{}
if updated.Product.ID != uuid.Nil {
product = dtodomain.InventoryReceiptLineProductResponse{
ID: updated.Product.ID.String(),
Name: updated.Product.Name,
}
}
return dtodomain.InventoryReceiptLineResponse{
ID: updated.ID.String(),
Quantity: updated.Quantity,
BatchNumber: updated.BatchNumber,
RepackingSuggestion: updated.RepackingSuggestion,
RepackUomID: repackUomID,
Product: product,
ClientID: updated.ClientID.String(),
}, nil
}
func toAssignmentResponse(e entities.TAssignmentEntity) dtodomain.AssignmentResponse {
// client := pkgdto.IdNameResponse{}
// if e.Client.ID != uuid.Nil {
// client = pkgdto.IdNameResponse{
// ID: e.Client.ID.String(),
// Name: e.Client.Name,
// }
// }
users := make([]dtodomain.AssignmentUserResponse, 0)
for _, user := range e.AssignmentUsers {
userResp := dtodomain.AssignmentUserResponse{
ID: user.ID.String(),
TaskType: user.TaskType,
User: pkgdto.IdNameResponse{ID: user.User.ID.String(), Name: user.User.Name},
Role: pkgdto.IdNameResponse{ID: user.Role.ID.String(), Name: user.Role.Name},
// Client: pkgdto.IdNameResponse{ID: user.Client.ID.String(), Name: user.Client.Name},
}
users = append(users, userResp)
}
return dtodomain.AssignmentResponse{
ID: e.ID.String(),
DocumentType: e.DocumentType,
DocumentID: e.DocumentID.String(),
// Client: client,
AssignmentUsers: users,
}
}
func toInventoryReceiptResponse(e entities.TInventoryReceiptEntity) dtodomain.InventoryReceiptResponse {
client := pkgdto.IdNameResponse{}
if e.Client.ID != uuid.Nil {
client = pkgdto.IdNameResponse{
ID: e.Client.ID.String(),
Name: e.Client.Name,
}
}
// lines := make([]dtodomain.InventoryReceiptLineResponse, 0)
// for _, line := range e.ReceiptLines {
// product := dtodomain.InventoryReceiptLineProductResponse{}
// if line.Product.ID != uuid.Nil {
// product = dtodomain.InventoryReceiptLineProductResponse{
// ID: line.Product.ID.String(),
// Name: line.Product.Name,
// RefNumber: line.Product.RefNumber,
// Uom: pkgdto.IdNameResponse{
// ID: line.Product.Uom.ID.String(),
// Name: line.Product.Uom.Name,
// },
// DimLength: line.Product.DimLength,
// DimWidth: line.Product.DimWidth,
// DimHeight: line.Product.DimHeight,
// DimUom: pkgdto.IdNameResponse{
// ID: line.Product.DimUom.ID.String(),
// Name: line.Product.DimUom.Name,
// },
// }
// }
// var repackUomID *string
// if line.RepackUomID != nil {
// tmp := line.RepackUomID.String()
// repackUomID = &tmp
// } else {
// repackUomID = nil
// }
// lines = append(lines, dtodomain.InventoryReceiptLineResponse{
// ID: line.ID.String(),
// Quantity: line.Quantity,
// BatchNumber: line.BatchNumber,
// RepackingSuggestion: line.RepackingSuggestion,
// RepackUomID: repackUomID,
// Product: product,
// ClientID: line.ClientID.String(),
// })
// }
var assignment *dtodomain.AssignmentResponse
if e.Assignment.ID != uuid.Nil {
assignmentObj := toAssignmentResponse(e.Assignment)
assignment = &assignmentObj
} else {
assignment = nil
}
return dtodomain.InventoryReceiptResponse{
ID: e.ID.String(),
ReferenceNumber: e.ReferenceNumber,
DocumentNumber: e.DocumentNumber,
Status: e.Status,
DocumentDate: utils.DateTimeToString(e.DocumentDate),
Source: e.Source,
QrCodeFile: e.QrCodeFile,
// ClientID: e.ClientID.String(),
Client: client,
// LineCount: len(lines),
// ReceiptLines: lines,
Assignment: assignment,
}
}
func (s *inventoryReceiptService) Create(ctx context.Context, req dtodomain.InventoryReceiptCreateRequest) (dtodomain.InventoryReceiptResponse, 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.InventoryReceiptResponse{}, err
}
docNum, err := s.sequenceService.GenerateDocumentNumber(ctx, req.ClientID, "RCPT", pkgdto.SequenceConfig{
Prefix: "RCPT",
EntityType: "INV_RECEIPT",
Period: "",
})
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
receipt := entities.TInventoryReceiptEntity{
ReferenceNumber: req.ReferenceNumber,
DocumentNumber: docNum,
DocumentDate: utils.StringToDateTime(req.DocumentDate),
Source: req.Source,
QrCodeFile: req.QrCodeFile,
ClientID: clientUUID,
Status: req.Status,
}
created, err := s.receiptRepo.Create(ctx, tx, receipt)
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
// Bulk create lines
var lines []entities.TInventoryReceiptLineEntity
// var invStorages []entities.InventoryStorageEntity
for _, lineReq := range req.ReceiptLines {
var productUUID uuid.UUID
if lineReq.ProductID != "" {
productUUID, err = uuid.Parse(lineReq.ProductID)
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
} else if lineReq.ProductCode != "" {
product, err := s.productRepo.GetByCode(ctx, tx, lineReq.ProductCode, req.ClientID)
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
productUUID = product.ID
} else {
productUUID = uuid.Nil
}
var repackUomUUID *uuid.UUID
if lineReq.RepackUomID != "" {
tmp, err := uuid.Parse(lineReq.RepackUomID)
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
repackUomUUID = &tmp
} else if lineReq.RepackUomCode != "" {
uom, err := s.uomRepo.GetByCode(ctx, tx, lineReq.RepackUomCode, req.ClientID)
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
repackUomUUID = &uom.ID
} else {
repackUomUUID = nil
}
clientLineUUID, err := uuid.Parse(lineReq.ClientID)
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
lines = append(lines, entities.TInventoryReceiptLineEntity{
Quantity: lineReq.Quantity,
BatchNumber: lineReq.BatchNumber,
RepackingSuggestion: lineReq.RepackingSuggestion,
RepackUomID: repackUomUUID,
InvReceiptID: created.ID,
ProductID: productUUID,
ClientID: clientLineUUID,
})
// // Prepare inventory storage entity
// product, err := s.productRepo.GetById(ctx, tx, productUUID.String())
// if err != nil {
// tx.Rollback()
// return dtodomain.InventoryReceiptResponse{}, err
// }
// invStorages = append(invStorages, entities.InventoryStorageEntity{
// ProductID: productUUID,
// UomID: *product.UomID,
// OnHandQuantity: 0,
// AvailableQuantity: 0,
// InvReceiptID: created.ID,
// ClientID: clientUUID,
// })
}
if len(lines) > 0 {
err = s.receiptLineRepo.BulkCreate(ctx, tx, lines)
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
}
// if len(invStorages) > 0 {
// err = s.invStorageRepository.BulkCreate(ctx, tx, invStorages)
// if err != nil {
// tx.Rollback()
// return dtodomain.InventoryReceiptResponse{}, err
// }
// }
tx.Commit()
result, err := s.receiptRepo.GetById(ctx, nil, created.ID.String())
if err != nil {
return dtodomain.InventoryReceiptResponse{}, err
}
return toInventoryReceiptResponse(result), nil
}
func (s *inventoryReceiptService) GetById(ctx context.Context, id string) (dtodomain.InventoryReceiptResponse, error) {
receipt, err := s.receiptRepo.GetById(ctx, nil, id)
if err != nil {
return dtodomain.InventoryReceiptResponse{}, err
}
return toInventoryReceiptResponse(receipt), nil
}
func (s *inventoryReceiptService) GetAll(ctx context.Context, filter query.InventoryReceiptFilter) ([]dtodomain.InventoryReceiptResponse, int64, error) {
receipts, total, err := s.receiptRepo.GetAll(ctx, filter)
if err != nil {
return nil, 0, err
}
var responses []dtodomain.InventoryReceiptResponse
for _, e := range receipts {
responses = append(responses, toInventoryReceiptResponse(e))
}
if responses == nil {
responses = make([]dtodomain.InventoryReceiptResponse, 0)
}
return responses, total, nil
}
func (s *inventoryReceiptService) Update(ctx context.Context, req dtodomain.InventoryReceiptUpdateRequest, id string) (dtodomain.InventoryReceiptResponse, error) {
tx := s.db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
receipt, err := s.receiptRepo.GetById(ctx, tx, id)
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
if req.ReferenceNumber != "" {
receipt.ReferenceNumber = req.ReferenceNumber
}
receipt.DocumentDate = utils.StringToDateTime(req.DocumentDate)
receipt.Source = req.Source
receipt.QrCodeFile = req.QrCodeFile
receipt.Status = req.Status
updated, err := s.receiptRepo.Update(ctx, tx, receipt)
if err != nil {
tx.Rollback()
return dtodomain.InventoryReceiptResponse{}, err
}
tx.Commit()
result, err := s.receiptRepo.GetById(ctx, nil, updated.ID.String())
if err != nil {
return dtodomain.InventoryReceiptResponse{}, err
}
return toInventoryReceiptResponse(result), nil
}
func (s *inventoryReceiptService) Delete(ctx context.Context, id string) error {
tx := s.db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := s.receiptRepo.Delete(ctx, tx, id); err != nil {
tx.Rollback()
return err
}
tx.Commit()
return nil
}
func (s *inventoryReceiptService) CreateLine(ctx context.Context, receiptId string, req dtodomain.InventoryReceiptLineCreateRequest) (dtodomain.InventoryReceiptLineResponse, error) {
receiptUUID, err := uuid.Parse(receiptId)
if err != nil {
return dtodomain.InventoryReceiptLineResponse{}, err
}
var productUUID uuid.UUID
if req.ProductID != "" {
productUUID, err = uuid.Parse(req.ProductID)
if err != nil {
return dtodomain.InventoryReceiptLineResponse{}, err
}
} else if req.ProductCode != "" {
product, err := s.productRepo.GetByCode(ctx, nil, req.ProductCode, req.ClientID)
if err != nil {
return dtodomain.InventoryReceiptLineResponse{}, err
}
productUUID = product.ID
} else {
productUUID = uuid.Nil
}
var repackUomUUID *uuid.UUID
if req.RepackUomID != "" {
tmp, err := uuid.Parse(req.RepackUomID)
if err != nil {
return dtodomain.InventoryReceiptLineResponse{}, err
}
repackUomUUID = &tmp
} else if req.RepackUomCode != "" {
uom, err := s.uomRepo.GetByCode(ctx, nil, req.RepackUomCode, req.ClientID)
if err != nil {
return dtodomain.InventoryReceiptLineResponse{}, err
}
repackUomUUID = &uom.ID
} else {
repackUomUUID = nil
}
clientLineUUID, err := uuid.Parse(req.ClientID)
if err != nil {
return dtodomain.InventoryReceiptLineResponse{}, err
}
line := entities.TInventoryReceiptLineEntity{
Quantity: req.Quantity,
BatchNumber: req.BatchNumber,
RepackingSuggestion: req.RepackingSuggestion,
RepackUomID: repackUomUUID,
InvReceiptID: receiptUUID,
ProductID: productUUID,
ClientID: clientLineUUID,
}
created, err := s.receiptLineRepo.Create(ctx, nil, line)
if err != nil {
return dtodomain.InventoryReceiptLineResponse{}, err
}
var repackUomID *string
if created.RepackUomID != nil {
tmp := created.RepackUomID.String()
repackUomID = &tmp
}
product := dtodomain.InventoryReceiptLineProductResponse{}
if created.Product.ID != uuid.Nil {
product = dtodomain.InventoryReceiptLineProductResponse{
ID: created.Product.ID.String(),
Name: created.Product.Name,
}
}
return dtodomain.InventoryReceiptLineResponse{
ID: created.ID.String(),
Quantity: created.Quantity,
BatchNumber: created.BatchNumber,
RepackingSuggestion: created.RepackingSuggestion,
RepackUomID: repackUomID,
Product: product,
ClientID: created.ClientID.String(),
}, nil
}
func NewInventoryReceiptService(db *gorm.DB,
receiptRepo repository.InventoryReceiptRepository,
receiptLineRepo repository.InventoryReceiptLineRepository,
productRepo productrepository.ProductRepository,
uomRepo uomrepository.UomRepository,
invStorageRepository invstoragerepository.InventoryStorageRepository,
sequenceService sequenceservice.SequenceService) InventoryReceiptService {
return &inventoryReceiptService{
db: db,
receiptRepo: receiptRepo,
receiptLineRepo: receiptLineRepo,
productRepo: productRepo,
uomRepo: uomRepo,
invStorageRepository: invStorageRepository,
sequenceService: sequenceService,
}
}