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

554 lines
18 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"
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
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
}
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.GetByProductAndClient(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(),
})
}
assignment := dtodomain.AssignmentResponse{}
if e.Assignment.ID != uuid.Nil {
assignment = toAssignmentResponse(e.Assignment)
}
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 := entities.GenerateDocumentNumber(s.db, req.ClientID)
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) InventoryReceiptService {
return &inventoryReceiptService{
db: db,
receiptRepo: receiptRepo,
receiptLineRepo: receiptLineRepo,
productRepo: productRepo,
uomRepo: uomRepo,
invStorageRepository: invStorageRepository,
}
}