feat: Add OnComplete functionality to Inventory Receipt
- Implemented OnComplete method in InventoryReceiptController to handle completion of inventory receipts. - Added success and failure messages in dto for completing inventory receipts. - Updated routes to include the new OnComplete endpoint. - Enhanced InventoryReceiptService to manage the completion logic, including updating inventory storage. - Introduced new methods in InventoryStorageRepository for bulk creation and retrieval of inventory storages. - Added methods in ProductService to fetch inventory storages and transactions by product and client. - Updated ProductController to handle new endpoints for inventory storages and transactions. - Enhanced Product DTO to include inventory storage and transaction responses. - Refactored ProductRepository to retrieve cross-references and preloaded relationships for inventory storages and transactions. - Updated core dependency injection to include new repositories and services.
This commit is contained in:
parent
e22c76abe3
commit
2ef6052a2c
|
|
@ -22,6 +22,7 @@ type InventoryReceiptController interface {
|
||||||
CreateLine(ctx *gin.Context)
|
CreateLine(ctx *gin.Context)
|
||||||
UpdateLine(ctx *gin.Context)
|
UpdateLine(ctx *gin.Context)
|
||||||
DeleteLine(ctx *gin.Context)
|
DeleteLine(ctx *gin.Context)
|
||||||
|
OnComplete(ctx *gin.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
type inventoryReceiptController struct {
|
type inventoryReceiptController struct {
|
||||||
|
|
@ -29,6 +30,19 @@ type inventoryReceiptController struct {
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnComplete implements InventoryReceiptController.
|
||||||
|
func (c *inventoryReceiptController) OnComplete(ctx *gin.Context) {
|
||||||
|
id := ctx.Param("id")
|
||||||
|
updated, err := c.receiptService.OnComplete(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_COMPLETE_INVENTORY_RECEIPT, err.Error(), nil)
|
||||||
|
ctx.JSON(http.StatusInternalServerError, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_COMPLETE_INVENTORY_RECEIPT, updated)
|
||||||
|
ctx.JSON(http.StatusOK, res)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteLine implements InventoryReceiptController.
|
// DeleteLine implements InventoryReceiptController.
|
||||||
func (c *inventoryReceiptController) DeleteLine(ctx *gin.Context) {
|
func (c *inventoryReceiptController) DeleteLine(ctx *gin.Context) {
|
||||||
id := ctx.Param("id")
|
id := ctx.Param("id")
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ const (
|
||||||
MESSAGE_SUCCESS_DELETE_INVENTORY_RECEIPT = "success delete inventory receipt"
|
MESSAGE_SUCCESS_DELETE_INVENTORY_RECEIPT = "success delete inventory receipt"
|
||||||
MESSAGE_SUCCESS_DELETE_INVENTORY_RECEIPT_LINE = "success delete inventory receipt line"
|
MESSAGE_SUCCESS_DELETE_INVENTORY_RECEIPT_LINE = "success delete inventory receipt line"
|
||||||
MESSAGE_FAILED_GET_DATA_FROM_BODY = "failed get data from body"
|
MESSAGE_FAILED_GET_DATA_FROM_BODY = "failed get data from body"
|
||||||
|
MESSAGE_SUCCESS_COMPLETE_INVENTORY_RECEIPT = "success complete inventory receipt"
|
||||||
|
MESSAGE_FAILED_COMPLETE_INVENTORY_RECEIPT = "failed complete inventory receipt"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InventoryReceiptCreateRequest struct {
|
type InventoryReceiptCreateRequest struct {
|
||||||
|
|
@ -34,10 +36,10 @@ type InventoryReceiptLineCreateRequest struct {
|
||||||
Quantity float64 `json:"quantity"`
|
Quantity float64 `json:"quantity"`
|
||||||
BatchNumber string `json:"batch_number"`
|
BatchNumber string `json:"batch_number"`
|
||||||
RepackingSuggestion string `json:"repacking_suggestion"`
|
RepackingSuggestion string `json:"repacking_suggestion"`
|
||||||
RepackUomID string `json:"repack_uom_id"`
|
RepackUomID string `json:"repack_uom_id"`
|
||||||
RepackUomCode string `json:"repack_uom_code"`
|
RepackUomCode string `json:"repack_uom_code"`
|
||||||
ProductID string `json:"product_id"`
|
ProductID string `json:"product_id"`
|
||||||
ProductCode string `json:"product_code"`
|
ProductCode string `json:"product_code"`
|
||||||
ClientID string `json:"client_id"`
|
ClientID string `json:"client_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,5 +23,6 @@ func RegisterRoutes(server *gin.Engine, injector *do.Injector) {
|
||||||
receiptRoutes.POST(":id/lines", middlewares.Authenticate(jwtService), receiptController.CreateLine)
|
receiptRoutes.POST(":id/lines", middlewares.Authenticate(jwtService), receiptController.CreateLine)
|
||||||
receiptRoutes.PUT("lines/:id", middlewares.Authenticate(jwtService), receiptController.UpdateLine)
|
receiptRoutes.PUT("lines/:id", middlewares.Authenticate(jwtService), receiptController.UpdateLine)
|
||||||
receiptRoutes.DELETE("lines/:id", middlewares.Authenticate(jwtService), receiptController.DeleteLine)
|
receiptRoutes.DELETE("lines/:id", middlewares.Authenticate(jwtService), receiptController.DeleteLine)
|
||||||
|
receiptRoutes.POST(":id/complete", middlewares.Authenticate(jwtService), receiptController.OnComplete)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,16 @@ package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
|
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
|
||||||
dtodomain "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_receipt/dto"
|
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/query"
|
||||||
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_receipt/repository"
|
"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"
|
productrepository "github.com/Caknoooo/go-gin-clean-starter/modules/product/repository"
|
||||||
uomrepository "github.com/Caknoooo/go-gin-clean-starter/modules/uom/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"
|
pkgdto "github.com/Caknoooo/go-gin-clean-starter/pkg/dto"
|
||||||
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
|
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
@ -24,14 +27,75 @@ type InventoryReceiptService interface {
|
||||||
CreateLine(ctx context.Context, receiptId string, req dtodomain.InventoryReceiptLineCreateRequest) (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)
|
UpdateLine(ctx context.Context, lineId string, req dtodomain.InventoryReceiptLineUpdateRequest) (dtodomain.InventoryReceiptLineResponse, error)
|
||||||
DeleteLine(ctx context.Context, lineId string) error
|
DeleteLine(ctx context.Context, lineId string) error
|
||||||
|
OnComplete(ctx context.Context, id string) (dtodomain.InventoryReceiptResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type inventoryReceiptService struct {
|
type inventoryReceiptService struct {
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
receiptRepo repository.InventoryReceiptRepository
|
receiptRepo repository.InventoryReceiptRepository
|
||||||
receiptLineRepo repository.InventoryReceiptLineRepository
|
receiptLineRepo repository.InventoryReceiptLineRepository
|
||||||
productRepo productrepository.ProductRepository
|
productRepo productrepository.ProductRepository
|
||||||
uomRepo uomrepository.UomRepository
|
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.
|
// DeleteLine implements InventoryReceiptService.
|
||||||
|
|
@ -235,6 +299,7 @@ func (s *inventoryReceiptService) Create(ctx context.Context, req dtodomain.Inve
|
||||||
}
|
}
|
||||||
// Bulk create lines
|
// Bulk create lines
|
||||||
var lines []entities.TInventoryReceiptLineEntity
|
var lines []entities.TInventoryReceiptLineEntity
|
||||||
|
// var invStorages []entities.InventoryStorageEntity
|
||||||
for _, lineReq := range req.ReceiptLines {
|
for _, lineReq := range req.ReceiptLines {
|
||||||
var productUUID uuid.UUID
|
var productUUID uuid.UUID
|
||||||
if lineReq.ProductID != "" {
|
if lineReq.ProductID != "" {
|
||||||
|
|
@ -285,6 +350,21 @@ func (s *inventoryReceiptService) Create(ctx context.Context, req dtodomain.Inve
|
||||||
ProductID: productUUID,
|
ProductID: productUUID,
|
||||||
ClientID: clientLineUUID,
|
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 {
|
if len(lines) > 0 {
|
||||||
err = s.receiptLineRepo.BulkCreate(ctx, tx, lines)
|
err = s.receiptLineRepo.BulkCreate(ctx, tx, lines)
|
||||||
|
|
@ -293,6 +373,13 @@ func (s *inventoryReceiptService) Create(ctx context.Context, req dtodomain.Inve
|
||||||
return dtodomain.InventoryReceiptResponse{}, err
|
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()
|
tx.Commit()
|
||||||
result, err := s.receiptRepo.GetById(ctx, nil, created.ID.String())
|
result, err := s.receiptRepo.GetById(ctx, nil, created.ID.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -453,12 +540,14 @@ func NewInventoryReceiptService(db *gorm.DB,
|
||||||
receiptRepo repository.InventoryReceiptRepository,
|
receiptRepo repository.InventoryReceiptRepository,
|
||||||
receiptLineRepo repository.InventoryReceiptLineRepository,
|
receiptLineRepo repository.InventoryReceiptLineRepository,
|
||||||
productRepo productrepository.ProductRepository,
|
productRepo productrepository.ProductRepository,
|
||||||
uomRepo uomrepository.UomRepository) InventoryReceiptService {
|
uomRepo uomrepository.UomRepository,
|
||||||
|
invStorageRepository invstoragerepository.InventoryStorageRepository) InventoryReceiptService {
|
||||||
return &inventoryReceiptService{
|
return &inventoryReceiptService{
|
||||||
db: db,
|
db: db,
|
||||||
receiptRepo: receiptRepo,
|
receiptRepo: receiptRepo,
|
||||||
receiptLineRepo: receiptLineRepo,
|
receiptLineRepo: receiptLineRepo,
|
||||||
productRepo: productRepo,
|
productRepo: productRepo,
|
||||||
uomRepo: uomRepo,
|
uomRepo: uomRepo,
|
||||||
|
invStorageRepository: invStorageRepository,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,10 +75,11 @@ type AssignmentUserResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type InventoryRequestLineResponse struct {
|
type InventoryRequestLineResponse struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Quantity float64 `json:"quantity"`
|
Quantity float64 `json:"quantity"`
|
||||||
Product InventoryRequestLineProductResponse `json:"product"`
|
CurrentStock float64 `json:"current_stock"`
|
||||||
ClientID string `json:"client_id"`
|
Product InventoryRequestLineProductResponse `json:"product"`
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InventoryRequestLineProductResponse struct {
|
type InventoryRequestLineProductResponse struct {
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ func toInventoryRequestResponse(e entities.TInventoryRequestEntity) dtodomain.In
|
||||||
lines = append(lines, dtodomain.InventoryRequestLineResponse{
|
lines = append(lines, dtodomain.InventoryRequestLineResponse{
|
||||||
ID: line.ID.String(),
|
ID: line.ID.String(),
|
||||||
Quantity: line.Quantity,
|
Quantity: line.Quantity,
|
||||||
|
// CurrentStock: line.Product.CurrentStock,
|
||||||
Product: product,
|
Product: product,
|
||||||
ClientID: line.ClientID.String(),
|
ClientID: line.ClientID.String(),
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ type InventoryStorageCreateRequest struct {
|
||||||
ClientID string `json:"client_id" binding:"required"`
|
ClientID string `json:"client_id" binding:"required"`
|
||||||
OnHandQuantity float64 `json:"on_hand_quantity"`
|
OnHandQuantity float64 `json:"on_hand_quantity"`
|
||||||
AvailableQuantity float64 `json:"available_quantity"`
|
AvailableQuantity float64 `json:"available_quantity"`
|
||||||
InvRequestID string `json:"inv_request_id"`
|
InvRequestID *string `json:"inv_request_id"`
|
||||||
InvReceiptID string `json:"inv_receipt_id"`
|
InvReceiptID *string `json:"inv_receipt_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InventoryStorageUpdateRequest struct {
|
type InventoryStorageUpdateRequest struct {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
|
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
|
||||||
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_storage/query"
|
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_storage/query"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InventoryStorageRepository interface {
|
type InventoryStorageRepository interface {
|
||||||
|
|
@ -14,12 +15,66 @@ type InventoryStorageRepository interface {
|
||||||
GetAll(ctx context.Context, filter query.InventoryStorageFilter) ([]entities.InventoryStorageEntity, int64, error)
|
GetAll(ctx context.Context, filter query.InventoryStorageFilter) ([]entities.InventoryStorageEntity, int64, error)
|
||||||
Update(ctx context.Context, tx *gorm.DB, inventoryStorage entities.InventoryStorageEntity) (entities.InventoryStorageEntity, error)
|
Update(ctx context.Context, tx *gorm.DB, inventoryStorage entities.InventoryStorageEntity) (entities.InventoryStorageEntity, error)
|
||||||
Delete(ctx context.Context, tx *gorm.DB, inventoryStorageId string) error
|
Delete(ctx context.Context, tx *gorm.DB, inventoryStorageId string) error
|
||||||
|
BulkCreate(ctx context.Context, tx *gorm.DB, inventoryStorages []entities.InventoryStorageEntity) error
|
||||||
|
GetByProductAndClient(ctx context.Context, tx *gorm.DB, productId string, clientId string) (entities.InventoryStorageEntity, error)
|
||||||
|
GetStoragesByProductAndClient(ctx context.Context, tx *gorm.DB, productId string, clientId string) ([]entities.InventoryStorageEntity, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type inventoryStorageRepository struct {
|
type inventoryStorageRepository struct {
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStoragesByProductAndClient implements InventoryStorageRepository.
|
||||||
|
func (r *inventoryStorageRepository) GetStoragesByProductAndClient(ctx context.Context, tx *gorm.DB, productId string, clientId string) ([]entities.InventoryStorageEntity, error) {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
var inventoryStorages []entities.InventoryStorageEntity
|
||||||
|
if err := tx.WithContext(ctx).
|
||||||
|
Preload("Product").
|
||||||
|
Preload("Aisle").
|
||||||
|
Preload("Uom").
|
||||||
|
Preload("Client").
|
||||||
|
Preload("InvRequest").
|
||||||
|
Preload("InvReceipt").
|
||||||
|
Where("product_id = ? AND client_id = ?", productId, clientId).
|
||||||
|
Find(&inventoryStorages).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return inventoryStorages, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByProductAndClient implements InventoryStorageRepository.
|
||||||
|
func (r *inventoryStorageRepository) GetByProductAndClient(ctx context.Context, tx *gorm.DB, productId string, clientId string) (entities.InventoryStorageEntity, error) {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
var inventoryStorage entities.InventoryStorageEntity
|
||||||
|
if err := tx.WithContext(ctx).
|
||||||
|
Preload("Product").
|
||||||
|
Preload("Aisle").
|
||||||
|
Preload("Uom").
|
||||||
|
Preload("Client").
|
||||||
|
Preload("InvRequest").
|
||||||
|
Preload("InvReceipt").
|
||||||
|
Order("created_at DESC").
|
||||||
|
First(&inventoryStorage, "product_id = ? AND client_id = ?", productId, clientId).Error; err != nil {
|
||||||
|
return inventoryStorage, err
|
||||||
|
}
|
||||||
|
return inventoryStorage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BulkCreate implements InventoryStorageRepository.
|
||||||
|
func (r *inventoryStorageRepository) BulkCreate(ctx context.Context, tx *gorm.DB, inventoryStorages []entities.InventoryStorageEntity) error {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
if err := tx.WithContext(ctx).Clauses(clause.OnConflict{DoNothing: true}).Create(&inventoryStorages).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewInventoryStorageRepository(db *gorm.DB) InventoryStorageRepository {
|
func NewInventoryStorageRepository(db *gorm.DB) InventoryStorageRepository {
|
||||||
return &inventoryStorageRepository{db: db}
|
return &inventoryStorageRepository{db: db}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,16 +51,26 @@ func (s *inventoryStorageService) Create(ctx context.Context, req dtodomain.Inve
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return dtodomain.InventoryStorageResponse{}, err
|
return dtodomain.InventoryStorageResponse{}, err
|
||||||
}
|
}
|
||||||
InvRequestID, err := uuid.Parse(req.InvRequestID)
|
var InvRequestID, InvReceiptID uuid.UUID
|
||||||
if err != nil {
|
if req.InvRequestID != nil && *req.InvRequestID != "" {
|
||||||
tx.Rollback()
|
InvRequestID, err = uuid.Parse(*req.InvRequestID)
|
||||||
return dtodomain.InventoryStorageResponse{}, err
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return dtodomain.InventoryStorageResponse{}, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
InvRequestID = uuid.Nil
|
||||||
}
|
}
|
||||||
InvReceiptID, err := uuid.Parse(req.InvReceiptID)
|
if req.InvReceiptID != nil && *req.InvReceiptID != "" {
|
||||||
if err != nil {
|
InvReceiptID, err = uuid.Parse(*req.InvReceiptID)
|
||||||
tx.Rollback()
|
if err != nil {
|
||||||
return dtodomain.InventoryStorageResponse{}, err
|
tx.Rollback()
|
||||||
|
return dtodomain.InventoryStorageResponse{}, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
InvReceiptID = uuid.Nil
|
||||||
}
|
}
|
||||||
|
|
||||||
inventoryStorage := entities.InventoryStorageEntity{
|
inventoryStorage := entities.InventoryStorageEntity{
|
||||||
ProductID: productID,
|
ProductID: productID,
|
||||||
AisleID: aisleID,
|
AisleID: aisleID,
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,33 @@ type InventoryTransactionRepository interface {
|
||||||
GetAll(ctx context.Context, filter query.InventoryTransactionFilter) ([]entities.InventoryTransactionEntity, int64, error)
|
GetAll(ctx context.Context, filter query.InventoryTransactionFilter) ([]entities.InventoryTransactionEntity, int64, error)
|
||||||
Update(ctx context.Context, tx *gorm.DB, inventoryTransaction entities.InventoryTransactionEntity) (entities.InventoryTransactionEntity, error)
|
Update(ctx context.Context, tx *gorm.DB, inventoryTransaction entities.InventoryTransactionEntity) (entities.InventoryTransactionEntity, error)
|
||||||
Delete(ctx context.Context, tx *gorm.DB, inventoryTransactionId string) error
|
Delete(ctx context.Context, tx *gorm.DB, inventoryTransactionId string) error
|
||||||
|
GetByProductAndClient(ctx context.Context, tx *gorm.DB, productId string, clientId string) ([]entities.InventoryTransactionEntity, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type inventoryTransactionRepository struct {
|
type inventoryTransactionRepository struct {
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetByProductAndClient implements InventoryTransactionRepository.
|
||||||
|
func (r *inventoryTransactionRepository) GetByProductAndClient(ctx context.Context, tx *gorm.DB, productId string, clientId string) ([]entities.InventoryTransactionEntity, error) {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
var inventoryTransactions []entities.InventoryTransactionEntity
|
||||||
|
if err := tx.WithContext(ctx).
|
||||||
|
Preload("Client").
|
||||||
|
Preload("Product").
|
||||||
|
Preload("Aisle").
|
||||||
|
Preload("InvReceipt").
|
||||||
|
Preload("InvIssue").
|
||||||
|
Preload("InvMove").
|
||||||
|
Where("product_id = ? AND client_id = ?", productId, clientId).
|
||||||
|
Find(&inventoryTransactions).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return inventoryTransactions, nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewInventoryTransactionRepository(db *gorm.DB) InventoryTransactionRepository {
|
func NewInventoryTransactionRepository(db *gorm.DB) InventoryTransactionRepository {
|
||||||
return &inventoryTransactionRepository{db: db}
|
return &inventoryTransactionRepository{db: db}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@ type (
|
||||||
GetAll(ctx *gin.Context)
|
GetAll(ctx *gin.Context)
|
||||||
AssignCrossReference(ctx *gin.Context)
|
AssignCrossReference(ctx *gin.Context)
|
||||||
RemoveCrossReference(ctx *gin.Context)
|
RemoveCrossReference(ctx *gin.Context)
|
||||||
|
GetInvStoragesByProductAndClient(ctx *gin.Context)
|
||||||
|
GetInvTransactionsByProductAndClient(ctx *gin.Context)
|
||||||
|
GetCrossReferencesByProductAndClient(ctx *gin.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
productController struct {
|
productController struct {
|
||||||
|
|
@ -30,6 +33,49 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetCrossReferencesByProductAndClient implements ProductController.
|
||||||
|
func (c *productController) GetCrossReferencesByProductAndClient(ctx *gin.Context) {
|
||||||
|
id := ctx.Param("id")
|
||||||
|
crossReferences, err := c.productService.GetCrossReferencesByProduct(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_CROSS_REFERENCES, err.Error(), nil)
|
||||||
|
ctx.JSON(http.StatusInternalServerError, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_CROSS_REFERENCES, crossReferences)
|
||||||
|
ctx.JSON(http.StatusOK, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInvStoragesByProductAndClient implements ProductController.
|
||||||
|
func (c *productController) GetInvStoragesByProductAndClient(ctx *gin.Context) {
|
||||||
|
id := ctx.Param("id")
|
||||||
|
clientId := ctx.MustGet("client_id").(string)
|
||||||
|
|
||||||
|
invStorages, err := c.productService.GetInvStoragesByProductAndClient(ctx, id, clientId)
|
||||||
|
if err != nil {
|
||||||
|
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_INV_STORAGES, err.Error(), nil)
|
||||||
|
ctx.JSON(http.StatusInternalServerError, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_INV_STORAGES, invStorages)
|
||||||
|
ctx.JSON(http.StatusOK, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInvTransactionsByProductAndClient implements ProductController.
|
||||||
|
func (c *productController) GetInvTransactionsByProductAndClient(ctx *gin.Context) {
|
||||||
|
id := ctx.Param("id")
|
||||||
|
clientId := ctx.MustGet("client_id").(string)
|
||||||
|
|
||||||
|
invTransactions, err := c.productService.GetInvTransactionsByProductAndClient(ctx, id, clientId)
|
||||||
|
if err != nil {
|
||||||
|
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_INV_TRANSACTIONS, err.Error(), nil)
|
||||||
|
ctx.JSON(http.StatusInternalServerError, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_INV_TRANSACTIONS, invTransactions)
|
||||||
|
ctx.JSON(http.StatusOK, res)
|
||||||
|
}
|
||||||
|
|
||||||
func NewProductController(i *do.Injector, productService service.ProductService) ProductController {
|
func NewProductController(i *do.Injector, productService service.ProductService) ProductController {
|
||||||
db := do.MustInvokeNamed[*gorm.DB](i, constants.DB)
|
db := do.MustInvokeNamed[*gorm.DB](i, constants.DB)
|
||||||
return &productController{
|
return &productController{
|
||||||
|
|
|
||||||
|
|
@ -11,19 +11,25 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MESSAGE_FAILED_CREATE_PRODUCT = "failed create product"
|
MESSAGE_FAILED_CREATE_PRODUCT = "failed create product"
|
||||||
MESSAGE_SUCCESS_CREATE_PRODUCT = "success create product"
|
MESSAGE_SUCCESS_CREATE_PRODUCT = "success create product"
|
||||||
MESSAGE_FAILED_GET_PRODUCT = "failed get product"
|
MESSAGE_FAILED_GET_PRODUCT = "failed get product"
|
||||||
MESSAGE_SUCCESS_GET_PRODUCT = "success get product"
|
MESSAGE_SUCCESS_GET_PRODUCT = "success get product"
|
||||||
MESSAGE_FAILED_UPDATE_PRODUCT = "failed update product"
|
MESSAGE_FAILED_UPDATE_PRODUCT = "failed update product"
|
||||||
MESSAGE_SUCCESS_UPDATE_PRODUCT = "success update product"
|
MESSAGE_SUCCESS_UPDATE_PRODUCT = "success update product"
|
||||||
MESSAGE_FAILED_DELETE_PRODUCT = "failed delete product"
|
MESSAGE_FAILED_DELETE_PRODUCT = "failed delete product"
|
||||||
MESSAGE_SUCCESS_DELETE_PRODUCT = "success delete product"
|
MESSAGE_SUCCESS_DELETE_PRODUCT = "success delete product"
|
||||||
MESSAGE_FAILED_GET_DATA_FROM_BODY = "failed get data from body"
|
MESSAGE_FAILED_GET_DATA_FROM_BODY = "failed get data from body"
|
||||||
MESSAGE_FAILED_ASSIGN_CROSS_REF = "failed assign cross reference"
|
MESSAGE_FAILED_ASSIGN_CROSS_REF = "failed assign cross reference"
|
||||||
MESSAGE_SUCCESS_ASSIGN_CROSS_REF = "success assign cross reference"
|
MESSAGE_SUCCESS_ASSIGN_CROSS_REF = "success assign cross reference"
|
||||||
MESSAGE_FAILED_REMOVE_CROSS_REF = "failed remove cross reference"
|
MESSAGE_FAILED_REMOVE_CROSS_REF = "failed remove cross reference"
|
||||||
MESSAGE_SUCCESS_REMOVE_CROSS_REF = "success remove cross reference"
|
MESSAGE_SUCCESS_REMOVE_CROSS_REF = "success remove cross reference"
|
||||||
|
MESSAGE_FAILED_GET_INV_STORAGES = "failed get inventory storages by product and client"
|
||||||
|
MESSAGE_SUCCESS_GET_INV_STORAGES = "success get inventory storages by product and client"
|
||||||
|
MESSAGE_FAILED_GET_INV_TRANSACTIONS = "failed get inventory transactions by product and client"
|
||||||
|
MESSAGE_SUCCESS_GET_INV_TRANSACTIONS = "success get inventory transactions by product and client"
|
||||||
|
MESSAGE_SUCCESS_GET_CROSS_REFERENCES = "success get cross references by product and client"
|
||||||
|
MESSAGE_FAILED_GET_CROSS_REFERENCES = "failed get cross references by product and client"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -107,41 +113,42 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
ProductResponse struct {
|
ProductResponse struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
RefNumber string `json:"ref_number"`
|
RefNumber string `json:"ref_number"`
|
||||||
SKU string `json:"sku"`
|
SKU string `json:"sku"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
IsReturnable bool `json:"is_returnable"`
|
IsReturnable bool `json:"is_returnable"`
|
||||||
DimLength float64 `json:"dim_length"`
|
DimLength float64 `json:"dim_length"`
|
||||||
DimWidth float64 `json:"dim_width"`
|
DimWidth float64 `json:"dim_width"`
|
||||||
DimHeight float64 `json:"dim_height"`
|
DimHeight float64 `json:"dim_height"`
|
||||||
Weight float64 `json:"weight"`
|
Weight float64 `json:"weight"`
|
||||||
Volume float64 `json:"volume"`
|
Volume float64 `json:"volume"`
|
||||||
MaxStackHeight int `json:"max_stack_height"`
|
MaxStackHeight int `json:"max_stack_height"`
|
||||||
Temperature string `json:"temperature"`
|
Temperature string `json:"temperature"`
|
||||||
IsHazardous bool `json:"is_hazardous"`
|
IsHazardous bool `json:"is_hazardous"`
|
||||||
MinStock int `json:"min_stock"`
|
MinStock int `json:"min_stock"`
|
||||||
MaxStock int `json:"max_stock"`
|
MaxStock int `json:"max_stock"`
|
||||||
ReplenishType string `json:"replenish_type"`
|
ReplenishType string `json:"replenish_type"`
|
||||||
CycleCount string `json:"cycle_count"`
|
CycleCount string `json:"cycle_count"`
|
||||||
LotRules string `json:"lot_rules"`
|
LotRules string `json:"lot_rules"`
|
||||||
LeadTime int `json:"lead_time"`
|
LeadTime int `json:"lead_time"`
|
||||||
MultiplyRate string `json:"multiply_rate"`
|
MultiplyRate string `json:"multiply_rate"`
|
||||||
DivideRate float64 `json:"divide_rate"`
|
DivideRate float64 `json:"divide_rate"`
|
||||||
Client pkgdto.IdNameResponse `json:"client"`
|
Client pkgdto.IdNameResponse `json:"client"`
|
||||||
Category pkgdto.IdNameResponse `json:"category"`
|
Category pkgdto.IdNameResponse `json:"category"`
|
||||||
Uom pkgdto.IdNameResponse `json:"uom"`
|
Uom pkgdto.IdNameResponse `json:"uom"`
|
||||||
DimUom pkgdto.IdNameResponse `json:"dim_uom"`
|
DimUom pkgdto.IdNameResponse `json:"dim_uom"`
|
||||||
WeightUom pkgdto.IdNameResponse `json:"weight_uom"`
|
WeightUom pkgdto.IdNameResponse `json:"weight_uom"`
|
||||||
VolumeUom pkgdto.IdNameResponse `json:"volume_uom"`
|
VolumeUom pkgdto.IdNameResponse `json:"volume_uom"`
|
||||||
MinStockUom pkgdto.IdNameResponse `json:"min_stock_uom"`
|
MinStockUom pkgdto.IdNameResponse `json:"min_stock_uom"`
|
||||||
MaxStockUom pkgdto.IdNameResponse `json:"max_stock_uom"`
|
MaxStockUom pkgdto.IdNameResponse `json:"max_stock_uom"`
|
||||||
LeadTimeUom pkgdto.IdNameResponse `json:"lead_time_uom"`
|
LeadTimeUom pkgdto.IdNameResponse `json:"lead_time_uom"`
|
||||||
UomToUom pkgdto.IdNameResponse `json:"uom_to_uom"`
|
UomToUom pkgdto.IdNameResponse `json:"uom_to_uom"`
|
||||||
CrossReferences []ProductVendorResponse `json:"cross_references"`
|
// CrossReferences []ProductVendorResponse `json:"cross_references"`
|
||||||
InvTransactions []ProductInventoryTransactionResponse `json:"inv_transactions"`
|
// InvTransactions []ProductInventoryTransactionResponse `json:"inv_transactions"`
|
||||||
|
// InvStorages []ProductInventoryStorageResponse `json:"inv_storages"`
|
||||||
}
|
}
|
||||||
|
|
||||||
CrossReferenceRequest struct {
|
CrossReferenceRequest struct {
|
||||||
|
|
@ -167,82 +174,21 @@ type (
|
||||||
InvIssueRef string `json:"inv_issue_ref,omitempty"`
|
InvIssueRef string `json:"inv_issue_ref,omitempty"`
|
||||||
InvMoveRef string `json:"inv_move_ref,omitempty"`
|
InvMoveRef string `json:"inv_move_ref,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProductInventoryStorageResponse struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Locater string `json:"locater"`
|
||||||
|
Lot string `json:"lot"`
|
||||||
|
OnHandQuantity float64 `json:"on_hand_quantity"`
|
||||||
|
AvailableQuantity float64 `json:"available_quantity"`
|
||||||
|
AisleID pkgdto.IdNameResponse `json:"aisle_id"`
|
||||||
|
Uom pkgdto.IdNameResponse `json:"uom"`
|
||||||
|
InvReceipt pkgdto.IdNameResponse `json:"inv_receipt"`
|
||||||
|
InvRequest pkgdto.IdNameResponse `json:"inv_request"`
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func MapProductToResponse(product entities.MProductEntity) ProductResponse {
|
func MapProductToResponse(product entities.MProductEntity) ProductResponse {
|
||||||
crossRefs := make([]ProductVendorResponse, 0, len(product.CrossReferences))
|
|
||||||
for _, v := range product.CrossReferences {
|
|
||||||
crossRefs = append(crossRefs, ProductVendorResponse{
|
|
||||||
ID: v.Vendor.ID.String(),
|
|
||||||
Name: v.Vendor.Name,
|
|
||||||
Address: v.Vendor.Address,
|
|
||||||
ContactPerson: v.Vendor.ContactPerson,
|
|
||||||
SearchKey: v.Vendor.SearchKey,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
invTransactions := make([]ProductInventoryTransactionResponse, 0, len(product.InventoryTransactions))
|
|
||||||
for _, it := range product.InventoryTransactions {
|
|
||||||
var transactionQuantity float64
|
|
||||||
var lot, locater, invReceiptRef, invIssueRef, invMoveRef string
|
|
||||||
var transactionDate string
|
|
||||||
valid := false
|
|
||||||
|
|
||||||
// Receipt
|
|
||||||
if it.InvReceipt.ID != uuid.Nil && it.InvReceipt.Status == constants.COMPLETED {
|
|
||||||
invReceiptRef = it.InvReceipt.ReferenceNumber
|
|
||||||
transactionDate = utils.DateTimeToString(it.TransactionDate)
|
|
||||||
for _, line := range it.InvReceipt.ReceiptLines {
|
|
||||||
if line.ProductID == it.ProductID {
|
|
||||||
transactionQuantity = line.Quantity
|
|
||||||
lot = line.BatchNumber
|
|
||||||
valid = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue
|
|
||||||
if it.InvIssue.ID != uuid.Nil && it.InvIssue.Status == constants.COMPLETED {
|
|
||||||
invIssueRef = it.InvIssue.DocumentNumber
|
|
||||||
transactionDate = utils.DateTimeToString(it.TransactionDate)
|
|
||||||
for _, line := range it.InvIssue.IssueLines {
|
|
||||||
if line.ProductID == it.ProductID {
|
|
||||||
transactionQuantity = line.IssuedQuantity
|
|
||||||
valid = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move
|
|
||||||
if it.InvMove.ID != uuid.Nil && it.InvMove.Status == constants.COMPLETED {
|
|
||||||
invMoveRef = it.InvMove.MovementNumber
|
|
||||||
transactionDate = utils.DateTimeToString(it.TransactionDate)
|
|
||||||
for _, line := range it.InvMove.MovementLines {
|
|
||||||
if line.ProductID == it.ProductID {
|
|
||||||
transactionQuantity = line.MovedQuantity
|
|
||||||
valid = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if valid {
|
|
||||||
invTransactions = append(invTransactions, ProductInventoryTransactionResponse{
|
|
||||||
ID: it.ID.String(),
|
|
||||||
TransactionDate: transactionDate,
|
|
||||||
TransactionType: it.TransactionType,
|
|
||||||
TransactionQuantity: transactionQuantity,
|
|
||||||
Lot: lot,
|
|
||||||
Locater: locater,
|
|
||||||
InvReceiptRef: invReceiptRef,
|
|
||||||
InvIssueRef: invIssueRef,
|
|
||||||
InvMoveRef: invMoveRef,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ProductResponse{
|
return ProductResponse{
|
||||||
ID: product.ID.String(),
|
ID: product.ID.String(),
|
||||||
Name: product.Name,
|
Name: product.Name,
|
||||||
|
|
@ -307,7 +253,111 @@ func MapProductToResponse(product entities.MProductEntity) ProductResponse {
|
||||||
ID: product.UomToUom.ID.String(),
|
ID: product.UomToUom.ID.String(),
|
||||||
Name: product.UomToUom.Name,
|
Name: product.UomToUom.Name,
|
||||||
},
|
},
|
||||||
CrossReferences: crossRefs,
|
|
||||||
InvTransactions: invTransactions,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MapInventoryTransactionsToResponses(transactions []entities.InventoryTransactionEntity) []ProductInventoryTransactionResponse {
|
||||||
|
responses := make([]ProductInventoryTransactionResponse, 0, len(transactions))
|
||||||
|
for _, t := range transactions {
|
||||||
|
resp := MapInventoryTransactionToProductInventoryTransactionResponse(t)
|
||||||
|
if resp.ID != "" { // hanya tambahkan jika mapping valid
|
||||||
|
responses = append(responses, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return responses
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapInventoryTransactionToProductInventoryTransactionResponse(transaction entities.InventoryTransactionEntity) ProductInventoryTransactionResponse {
|
||||||
|
var transactionQuantity float64
|
||||||
|
var lot, locater, invReceiptRef, invIssueRef, invMoveRef string
|
||||||
|
var transactionDate string
|
||||||
|
valid := false
|
||||||
|
|
||||||
|
if transaction.InvReceipt.ID != uuid.Nil && transaction.InvReceipt.Status == constants.COMPLETED {
|
||||||
|
invReceiptRef = transaction.InvReceipt.ReferenceNumber
|
||||||
|
transactionDate = utils.DateTimeToString(transaction.TransactionDate)
|
||||||
|
for _, line := range transaction.InvReceipt.ReceiptLines {
|
||||||
|
if line.ProductID == transaction.ProductID {
|
||||||
|
transactionQuantity = line.Quantity
|
||||||
|
lot = line.BatchNumber
|
||||||
|
valid = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if transaction.InvIssue.ID != uuid.Nil && transaction.InvIssue.Status == constants.COMPLETED {
|
||||||
|
invIssueRef = transaction.InvIssue.DocumentNumber
|
||||||
|
transactionDate = utils.DateTimeToString(transaction.TransactionDate)
|
||||||
|
for _, line := range transaction.InvIssue.IssueLines {
|
||||||
|
if line.ProductID == transaction.ProductID {
|
||||||
|
transactionQuantity = line.IssuedQuantity
|
||||||
|
valid = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if transaction.InvMove.ID != uuid.Nil && transaction.InvMove.Status == constants.COMPLETED {
|
||||||
|
invMoveRef = transaction.InvMove.MovementNumber
|
||||||
|
transactionDate = utils.DateTimeToString(transaction.TransactionDate)
|
||||||
|
for _, line := range transaction.InvMove.MovementLines {
|
||||||
|
if line.ProductID == transaction.ProductID {
|
||||||
|
transactionQuantity = line.MovedQuantity
|
||||||
|
valid = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
return ProductInventoryTransactionResponse{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProductInventoryTransactionResponse{
|
||||||
|
ID: transaction.ID.String(),
|
||||||
|
TransactionDate: transactionDate,
|
||||||
|
TransactionType: transaction.TransactionType,
|
||||||
|
TransactionQuantity: transactionQuantity,
|
||||||
|
Lot: lot,
|
||||||
|
Locater: locater,
|
||||||
|
InvReceiptRef: invReceiptRef,
|
||||||
|
InvIssueRef: invIssueRef,
|
||||||
|
InvMoveRef: invMoveRef,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapInventoryStorageToProductInventoryStorageResponse(storage entities.InventoryStorageEntity) ProductInventoryStorageResponse {
|
||||||
|
return ProductInventoryStorageResponse{
|
||||||
|
ID: storage.ID.String(),
|
||||||
|
// Locater: storage.Locater,
|
||||||
|
// Lot: storage.BatchNumber,
|
||||||
|
OnHandQuantity: storage.OnHandQuantity,
|
||||||
|
AvailableQuantity: storage.AvailableQuantity,
|
||||||
|
AisleID: pkgdto.IdNameResponse{
|
||||||
|
ID: storage.Aisle.ID.String(),
|
||||||
|
Name: storage.Aisle.Name,
|
||||||
|
},
|
||||||
|
Uom: pkgdto.IdNameResponse{
|
||||||
|
ID: storage.Uom.ID.String(),
|
||||||
|
Name: storage.Uom.Name,
|
||||||
|
},
|
||||||
|
InvReceipt: pkgdto.IdNameResponse{
|
||||||
|
ID: storage.InvReceipt.ID.String(),
|
||||||
|
Name: storage.InvReceipt.ReferenceNumber,
|
||||||
|
},
|
||||||
|
InvRequest: pkgdto.IdNameResponse{
|
||||||
|
ID: storage.InvRequest.ID.String(),
|
||||||
|
Name: storage.InvRequest.ReferenceNumber,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapCrossReferenceToProductVendorResponse(crossRef entities.MCrossReferenceEntity) ProductVendorResponse {
|
||||||
|
return ProductVendorResponse{
|
||||||
|
ID: crossRef.Vendor.ID.String(),
|
||||||
|
Name: crossRef.Vendor.Name,
|
||||||
|
ContactPerson: crossRef.Vendor.ContactPerson,
|
||||||
|
SearchKey: crossRef.Vendor.SearchKey,
|
||||||
|
Address: crossRef.Vendor.Address}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,27 @@ type ProductRepository interface {
|
||||||
Delete(ctx context.Context, tx *gorm.DB, productId string) error
|
Delete(ctx context.Context, tx *gorm.DB, productId string) error
|
||||||
AssignCrossReference(ctx context.Context, tx *gorm.DB, productId string, vendorIds []string) error
|
AssignCrossReference(ctx context.Context, tx *gorm.DB, productId string, vendorIds []string) error
|
||||||
RemoveCrossReference(ctx context.Context, tx *gorm.DB, productId string, vendorIds []string) error
|
RemoveCrossReference(ctx context.Context, tx *gorm.DB, productId string, vendorIds []string) error
|
||||||
|
GetCrossReferencesByProduct(ctx context.Context, tx *gorm.DB, productId string) ([]entities.MCrossReferenceEntity, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type productRepository struct {
|
type productRepository struct {
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCrossReferencesByProduct implements ProductRepository.
|
||||||
|
func (r *productRepository) GetCrossReferencesByProduct(ctx context.Context, tx *gorm.DB, productId string) ([]entities.MCrossReferenceEntity, error) {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
var crossReferences []entities.MCrossReferenceEntity
|
||||||
|
if err := tx.WithContext(ctx).
|
||||||
|
Where("product_id = ?", productId).
|
||||||
|
Find(&crossReferences).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return crossReferences, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetByCode implements ProductRepository.
|
// GetByCode implements ProductRepository.
|
||||||
func (r *productRepository) GetByCode(ctx context.Context, tx *gorm.DB, productCode string, clientId string) (entities.MProductEntity, error) {
|
func (r *productRepository) GetByCode(ctx context.Context, tx *gorm.DB, productCode string, clientId string) (entities.MProductEntity, error) {
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
|
|
@ -156,6 +171,12 @@ func (r *productRepository) GetById(ctx context.Context, tx *gorm.DB, productId
|
||||||
Preload("CrossReferences").
|
Preload("CrossReferences").
|
||||||
Preload("CrossReferences.Vendor").
|
Preload("CrossReferences.Vendor").
|
||||||
Preload("InventoryStorages").
|
Preload("InventoryStorages").
|
||||||
|
Preload("InventoryStorages.Client").
|
||||||
|
Preload("InventoryStorages.Product").
|
||||||
|
Preload("InventoryStorages.Aisle").
|
||||||
|
Preload("InventoryStorages.Uom").
|
||||||
|
Preload("InventoryStorages.InvReceipt").
|
||||||
|
Preload("InventoryStorages.InvRequest").
|
||||||
Preload("InventoryTransactions").
|
Preload("InventoryTransactions").
|
||||||
Preload("InventoryTransactions.Client").
|
Preload("InventoryTransactions.Client").
|
||||||
Preload("InventoryTransactions.Aisle").
|
Preload("InventoryTransactions.Aisle").
|
||||||
|
|
|
||||||
|
|
@ -22,5 +22,8 @@ func RegisterRoutes(server *gin.Engine, injector *do.Injector) {
|
||||||
productRoutes.GET("", middlewares.Authenticate(jwtService), productController.GetAll)
|
productRoutes.GET("", middlewares.Authenticate(jwtService), productController.GetAll)
|
||||||
productRoutes.POST("/:id/assign-cross-reference", middlewares.Authenticate(jwtService), productController.AssignCrossReference)
|
productRoutes.POST("/:id/assign-cross-reference", middlewares.Authenticate(jwtService), productController.AssignCrossReference)
|
||||||
productRoutes.POST("/:id/remove-cross-reference", middlewares.Authenticate(jwtService), productController.RemoveCrossReference)
|
productRoutes.POST("/:id/remove-cross-reference", middlewares.Authenticate(jwtService), productController.RemoveCrossReference)
|
||||||
|
productRoutes.GET("/:id/inv-storages", middlewares.Authenticate(jwtService), productController.GetInvStoragesByProductAndClient)
|
||||||
|
productRoutes.GET("/:id/inv-transactions", middlewares.Authenticate(jwtService), productController.GetInvTransactionsByProductAndClient)
|
||||||
|
productRoutes.GET("/:id/cross-references", middlewares.Authenticate(jwtService), productController.GetCrossReferencesByProductAndClient)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
|
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
|
||||||
|
invstoragerepo "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_storage/repository"
|
||||||
|
invtransactionrepo "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_transaction/repository"
|
||||||
"github.com/Caknoooo/go-gin-clean-starter/modules/product/dto"
|
"github.com/Caknoooo/go-gin-clean-starter/modules/product/dto"
|
||||||
"github.com/Caknoooo/go-gin-clean-starter/modules/product/query"
|
"github.com/Caknoooo/go-gin-clean-starter/modules/product/query"
|
||||||
"github.com/Caknoooo/go-gin-clean-starter/modules/product/repository"
|
"github.com/Caknoooo/go-gin-clean-starter/modules/product/repository"
|
||||||
|
|
@ -19,11 +21,55 @@ type ProductService interface {
|
||||||
Delete(ctx context.Context, productId string) error
|
Delete(ctx context.Context, productId string) error
|
||||||
AssignCrossReference(ctx context.Context, productId string, vendorIds []string) error
|
AssignCrossReference(ctx context.Context, productId string, vendorIds []string) error
|
||||||
RemoveCrossReference(ctx context.Context, productId string, vendorIds []string) error
|
RemoveCrossReference(ctx context.Context, productId string, vendorIds []string) error
|
||||||
|
GetInvTransactionsByProductAndClient(ctx context.Context, productId string, clientId string) ([]dto.ProductInventoryTransactionResponse, error)
|
||||||
|
GetInvStoragesByProductAndClient(ctx context.Context, productId string, clientId string) ([]dto.ProductInventoryStorageResponse, error)
|
||||||
|
GetCrossReferencesByProduct(ctx context.Context, productId string) ([]dto.ProductVendorResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type productService struct {
|
type productService struct {
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
productRepo repository.ProductRepository
|
productRepo repository.ProductRepository
|
||||||
|
inventoryTransactionRepo invtransactionrepo.InventoryTransactionRepository
|
||||||
|
inventoryStorageRepo invstoragerepo.InventoryStorageRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCrossReferencesByProductAndClient implements ProductService.
|
||||||
|
func (s *productService) GetCrossReferencesByProduct(ctx context.Context, productId string) ([]dto.ProductVendorResponse, error) {
|
||||||
|
crossReferences, err := s.productRepo.GetCrossReferencesByProduct(ctx, nil, productId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var responses []dto.ProductVendorResponse
|
||||||
|
for _, cr := range crossReferences {
|
||||||
|
responses = append(responses, dto.MapCrossReferenceToProductVendorResponse(cr))
|
||||||
|
}
|
||||||
|
if len(responses) == 0 {
|
||||||
|
responses = []dto.ProductVendorResponse{} // pastikan slice kosong, bukan nil
|
||||||
|
}
|
||||||
|
return responses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInvStoragesByProductAndClient implements ProductService.
|
||||||
|
func (s *productService) GetInvStoragesByProductAndClient(ctx context.Context, productId string, clientId string) ([]dto.ProductInventoryStorageResponse, error) {
|
||||||
|
invStorages, err := s.inventoryStorageRepo.GetStoragesByProductAndClient(ctx, nil, productId, clientId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var responses []dto.ProductInventoryStorageResponse
|
||||||
|
for _, invStorage := range invStorages {
|
||||||
|
responses = append(responses, dto.MapInventoryStorageToProductInventoryStorageResponse(invStorage))
|
||||||
|
}
|
||||||
|
return responses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInvTransactionsByProductAndClient implements ProductService.
|
||||||
|
func (s *productService) GetInvTransactionsByProductAndClient(ctx context.Context, productId string, clientId string) ([]dto.ProductInventoryTransactionResponse, error) {
|
||||||
|
invTransactions, err := s.inventoryTransactionRepo.GetByProductAndClient(ctx, nil, productId, clientId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
responses := dto.MapInventoryTransactionsToResponses(invTransactions)
|
||||||
|
return responses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssignCrossReference implements ProductService.
|
// AssignCrossReference implements ProductService.
|
||||||
|
|
@ -66,10 +112,12 @@ func (s *productService) RemoveCrossReference(ctx context.Context, productId str
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProductService(productRepo repository.ProductRepository, db *gorm.DB) ProductService {
|
func NewProductService(productRepo repository.ProductRepository, db *gorm.DB, inventoryTransactionRepo invtransactionrepo.InventoryTransactionRepository, inventoryStorageRepo invstoragerepo.InventoryStorageRepository) ProductService {
|
||||||
return &productService{
|
return &productService{
|
||||||
productRepo: productRepo,
|
productRepo: productRepo,
|
||||||
db: db,
|
db: db,
|
||||||
|
inventoryTransactionRepo: inventoryTransactionRepo,
|
||||||
|
inventoryStorageRepo: inventoryStorageRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,7 @@ func RegisterDependencies(injector *do.Injector) {
|
||||||
|
|
||||||
// Service
|
// Service
|
||||||
userServ := userService.NewUserService(userRepository, roleRepository, warehouseRepository, clientRepository, refreshTokenRepository, jwtService, db)
|
userServ := userService.NewUserService(userRepository, roleRepository, warehouseRepository, clientRepository, refreshTokenRepository, jwtService, db)
|
||||||
productService := productService.NewProductService(productRepository, db)
|
productService := productService.NewProductService(productRepository, db, inventoryTransactionRepository, inventoryStorageRepository)
|
||||||
roleServ := roleService.NewRoleService(roleRepository, refreshTokenRepository, jwtService, userServ, db)
|
roleServ := roleService.NewRoleService(roleRepository, refreshTokenRepository, jwtService, userServ, db)
|
||||||
menuSvc := menuService.NewMenuService(menuRepository, jwtService, db)
|
menuSvc := menuService.NewMenuService(menuRepository, jwtService, db)
|
||||||
maintenanceGroupServ := maintGroupService.NewMaintenanceGroupService(maintenanceGroupRepository, maintenanceGroupRoleRepository, maintenanceGroupRoleUserRepository, db)
|
maintenanceGroupServ := maintGroupService.NewMaintenanceGroupService(maintenanceGroupRepository, maintenanceGroupRoleRepository, maintenanceGroupRoleUserRepository, db)
|
||||||
|
|
@ -174,7 +174,7 @@ func RegisterDependencies(injector *do.Injector) {
|
||||||
warehouseServ := warehouseService.NewWarehouseService(warehouseRepository, db)
|
warehouseServ := warehouseService.NewWarehouseService(warehouseRepository, db)
|
||||||
zonaServ := zonaService.NewZonaService(zonaRepository, db)
|
zonaServ := zonaService.NewZonaService(zonaRepository, db)
|
||||||
aisleServ := aisleService.NewAisleService(aisleRepository, db)
|
aisleServ := aisleService.NewAisleService(aisleRepository, db)
|
||||||
inventoryReceiptServ := inventoryReceiptService.NewInventoryReceiptService(db, inventoryReceiptRepository, inventoryReceiptLineRepository, productRepository, uomRepository)
|
inventoryReceiptServ := inventoryReceiptService.NewInventoryReceiptService(db, inventoryReceiptRepository, inventoryReceiptLineRepository, productRepository, uomRepository, inventoryStorageRepository)
|
||||||
assignmentServ := assignmentService.NewAssignmentService(db, assignmentRepository, assignmentUserRepository)
|
assignmentServ := assignmentService.NewAssignmentService(db, assignmentRepository, assignmentUserRepository)
|
||||||
inventoryRequestServ := inventoryRequestService.NewInventoryRequestService(db, inventoryRequestRepository, inventoryRequestLineRepository)
|
inventoryRequestServ := inventoryRequestService.NewInventoryRequestService(db, inventoryRequestRepository, inventoryRequestLineRepository)
|
||||||
inventoryIssueServ := inventoryIssueService.NewInventoryIssueService(db, inventoryIssueRepository, inventoryIssueLineRepository)
|
inventoryIssueServ := inventoryIssueService.NewInventoryIssueService(db, inventoryIssueRepository, inventoryIssueLineRepository)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue