feat(product): Add AssignCrossReference and RemoveCrossReference methods in ProductController, Service, and Repository
This commit is contained in:
parent
e0a5af65a1
commit
e1440e317c
|
|
@ -20,6 +20,8 @@ type (
|
|||
Delete(ctx *gin.Context)
|
||||
GetById(ctx *gin.Context)
|
||||
GetAll(ctx *gin.Context)
|
||||
AssignCrossReference(ctx *gin.Context)
|
||||
RemoveCrossReference(ctx *gin.Context)
|
||||
}
|
||||
|
||||
productController struct {
|
||||
|
|
@ -115,3 +117,39 @@ func (c *productController) GetAll(ctx *gin.Context) {
|
|||
response := utils.BuildResponseSuccessWithPagination(http.StatusOK, dto.MESSAGE_SUCCESS_GET_PRODUCT, products, paginationResponse)
|
||||
ctx.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// AssignCrossReference implements ProductController.
|
||||
func (c *productController) AssignCrossReference(ctx *gin.Context) {
|
||||
id := ctx.Param("id")
|
||||
var req dto.CrossReferenceRequest
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil)
|
||||
ctx.JSON(http.StatusBadRequest, res)
|
||||
return
|
||||
}
|
||||
if err := c.productService.AssignCrossReference(ctx, id, req.VendorIDs); err != nil {
|
||||
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_ASSIGN_CROSS_REF, dto.ErrAssignCrossRef.Error(), nil)
|
||||
ctx.JSON(http.StatusInternalServerError, res)
|
||||
return
|
||||
}
|
||||
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_ASSIGN_CROSS_REF, nil)
|
||||
ctx.JSON(http.StatusOK, res)
|
||||
}
|
||||
|
||||
// RemoveCrossReference implements ProductController.
|
||||
func (c *productController) RemoveCrossReference(ctx *gin.Context) {
|
||||
id := ctx.Param("id")
|
||||
var req dto.CrossReferenceRequest
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil)
|
||||
ctx.JSON(http.StatusBadRequest, res)
|
||||
return
|
||||
}
|
||||
if err := c.productService.RemoveCrossReference(ctx, id, req.VendorIDs); err != nil {
|
||||
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_REMOVE_CROSS_REF, dto.ErrRemoveCrossRef.Error(), nil)
|
||||
ctx.JSON(http.StatusInternalServerError, res)
|
||||
return
|
||||
}
|
||||
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_REMOVE_CROSS_REF, nil)
|
||||
ctx.JSON(http.StatusOK, res)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ const (
|
|||
MESSAGE_FAILED_DELETE_PRODUCT = "failed delete product"
|
||||
MESSAGE_SUCCESS_DELETE_PRODUCT = "success delete product"
|
||||
MESSAGE_FAILED_GET_DATA_FROM_BODY = "failed get data from body"
|
||||
MESSAGE_FAILED_ASSIGN_CROSS_REF = "failed assign cross reference"
|
||||
MESSAGE_SUCCESS_ASSIGN_CROSS_REF = "success assign cross reference"
|
||||
MESSAGE_FAILED_REMOVE_CROSS_REF = "failed remove cross reference"
|
||||
MESSAGE_SUCCESS_REMOVE_CROSS_REF = "success remove cross reference"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -23,6 +27,8 @@ var (
|
|||
ErrGetProductById = errors.New("failed to get product by id")
|
||||
ErrUpdateProduct = errors.New("failed to update product")
|
||||
ErrDeleteProduct = errors.New("failed to delete product")
|
||||
ErrAssignCrossRef = errors.New("failed to assign cross reference")
|
||||
ErrRemoveCrossRef = errors.New("failed to remove cross reference")
|
||||
)
|
||||
|
||||
type (
|
||||
|
|
@ -97,38 +103,50 @@ type (
|
|||
}
|
||||
|
||||
ProductResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
RefNumber string `json:"ref_number"`
|
||||
SKU string `json:"sku"`
|
||||
Description string `json:"description"`
|
||||
Status string `json:"status"`
|
||||
IsReturnable bool `json:"is_returnable"`
|
||||
DimLength float64 `json:"dim_length"`
|
||||
DimWidth float64 `json:"dim_width"`
|
||||
DimHeight float64 `json:"dim_height"`
|
||||
Weight float64 `json:"weight"`
|
||||
Volume float64 `json:"volume"`
|
||||
MaxStackHeight int `json:"max_stack_height"`
|
||||
Temperature string `json:"temperature"`
|
||||
IsHazardous bool `json:"is_hazardous"`
|
||||
MinStock int `json:"min_stock"`
|
||||
MaxStock int `json:"max_stock"`
|
||||
ReplenishType string `json:"replenish_type"`
|
||||
CycleCount string `json:"cycle_count"`
|
||||
LotRules string `json:"lot_rules"`
|
||||
LeadTime int `json:"lead_time"`
|
||||
MultiplyRate string `json:"multiply_rate"`
|
||||
DivideRate float64 `json:"divide_rate"`
|
||||
Client pkgdto.IdNameResponse `json:"client"`
|
||||
Category pkgdto.IdNameResponse `json:"category"`
|
||||
Uom pkgdto.IdNameResponse `json:"uom"`
|
||||
DimUom pkgdto.IdNameResponse `json:"dim_uom"`
|
||||
WeightUom pkgdto.IdNameResponse `json:"weight_uom"`
|
||||
VolumeUom pkgdto.IdNameResponse `json:"volume_uom"`
|
||||
MinStockUom pkgdto.IdNameResponse `json:"min_stock_uom"`
|
||||
MaxStockUom pkgdto.IdNameResponse `json:"max_stock_uom"`
|
||||
LeadTimeUom pkgdto.IdNameResponse `json:"lead_time_uom"`
|
||||
UomToUom pkgdto.IdNameResponse `json:"uom_to_uom"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
RefNumber string `json:"ref_number"`
|
||||
SKU string `json:"sku"`
|
||||
Description string `json:"description"`
|
||||
Status string `json:"status"`
|
||||
IsReturnable bool `json:"is_returnable"`
|
||||
DimLength float64 `json:"dim_length"`
|
||||
DimWidth float64 `json:"dim_width"`
|
||||
DimHeight float64 `json:"dim_height"`
|
||||
Weight float64 `json:"weight"`
|
||||
Volume float64 `json:"volume"`
|
||||
MaxStackHeight int `json:"max_stack_height"`
|
||||
Temperature string `json:"temperature"`
|
||||
IsHazardous bool `json:"is_hazardous"`
|
||||
MinStock int `json:"min_stock"`
|
||||
MaxStock int `json:"max_stock"`
|
||||
ReplenishType string `json:"replenish_type"`
|
||||
CycleCount string `json:"cycle_count"`
|
||||
LotRules string `json:"lot_rules"`
|
||||
LeadTime int `json:"lead_time"`
|
||||
MultiplyRate string `json:"multiply_rate"`
|
||||
DivideRate float64 `json:"divide_rate"`
|
||||
Client pkgdto.IdNameResponse `json:"client"`
|
||||
Category pkgdto.IdNameResponse `json:"category"`
|
||||
Uom pkgdto.IdNameResponse `json:"uom"`
|
||||
DimUom pkgdto.IdNameResponse `json:"dim_uom"`
|
||||
WeightUom pkgdto.IdNameResponse `json:"weight_uom"`
|
||||
VolumeUom pkgdto.IdNameResponse `json:"volume_uom"`
|
||||
MinStockUom pkgdto.IdNameResponse `json:"min_stock_uom"`
|
||||
MaxStockUom pkgdto.IdNameResponse `json:"max_stock_uom"`
|
||||
LeadTimeUom pkgdto.IdNameResponse `json:"lead_time_uom"`
|
||||
UomToUom pkgdto.IdNameResponse `json:"uom_to_uom"`
|
||||
CrossReferences []ProductVendorResponse `json:"cross_references"`
|
||||
}
|
||||
|
||||
CrossReferenceRequest struct {
|
||||
VendorIDs []string `json:"vendor_ids" binding:"required"`
|
||||
}
|
||||
|
||||
ProductVendorResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
ContactPerson string `json:"contact_person"`
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import (
|
|||
|
||||
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
|
||||
"github.com/Caknoooo/go-gin-clean-starter/modules/product/query"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type ProductRepository interface {
|
||||
|
|
@ -14,12 +16,74 @@ type ProductRepository interface {
|
|||
GetAll(ctx context.Context, filter query.ProductFilter) ([]entities.MProductEntity, int64, error)
|
||||
Update(ctx context.Context, tx *gorm.DB, product entities.MProductEntity) (entities.MProductEntity, error)
|
||||
Delete(ctx context.Context, tx *gorm.DB, productId 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
|
||||
}
|
||||
|
||||
type productRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func (r *productRepository) AssignCrossReference(ctx context.Context, tx *gorm.DB, productId string, vendorIds []string) error {
|
||||
if tx == nil {
|
||||
tx = r.db
|
||||
}
|
||||
|
||||
productUUID, err := uuid.Parse(productId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var crossRefs []entities.MCrossReferenceEntity
|
||||
for _, vendorId := range vendorIds {
|
||||
vendorUUID, err := uuid.Parse(vendorId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
crossRefs = append(crossRefs, entities.MCrossReferenceEntity{
|
||||
ProductID: productUUID,
|
||||
VendorID: vendorUUID,
|
||||
})
|
||||
}
|
||||
|
||||
if err := tx.WithContext(ctx).
|
||||
Model(&entities.MCrossReferenceEntity{}).
|
||||
Clauses(clause.OnConflict{DoNothing: true}).
|
||||
Create(&crossRefs).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *productRepository) RemoveCrossReference(ctx context.Context, tx *gorm.DB, productId string, vendorIds []string) error {
|
||||
if tx == nil {
|
||||
tx = r.db
|
||||
}
|
||||
|
||||
productUUID, err := uuid.Parse(productId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var vendorUUIDs []uuid.UUID
|
||||
for _, vendorId := range vendorIds {
|
||||
vendorUUID, err := uuid.Parse(vendorId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vendorUUIDs = append(vendorUUIDs, vendorUUID)
|
||||
}
|
||||
|
||||
if err := tx.WithContext(ctx).
|
||||
Where("product_id = ? AND vendor_id IN ?", productUUID, vendorUUIDs).
|
||||
Delete(&entities.MCrossReferenceEntity{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewProductRepository(db *gorm.DB) ProductRepository {
|
||||
return &productRepository{db: db}
|
||||
}
|
||||
|
|
@ -50,6 +114,8 @@ func (r *productRepository) GetById(ctx context.Context, tx *gorm.DB, productId
|
|||
Preload("MaxStockUom").
|
||||
Preload("LeadTimeUom").
|
||||
Preload("UomToUom").
|
||||
Preload("CrossReferences").
|
||||
Preload("CrossReferences.Vendor").
|
||||
First(&product, "id = ?", productId).Error; err != nil {
|
||||
return product, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,5 +20,7 @@ func RegisterRoutes(server *gin.Engine, injector *do.Injector) {
|
|||
productRoutes.PUT("/:id", middlewares.Authenticate(jwtService), productController.Update)
|
||||
productRoutes.DELETE("/:id", middlewares.Authenticate(jwtService), productController.Delete)
|
||||
productRoutes.GET("", middlewares.Authenticate(jwtService), productController.GetAll)
|
||||
productRoutes.POST("/:id/assign-cross-reference", middlewares.Authenticate(jwtService), productController.AssignCrossReference)
|
||||
productRoutes.POST("/:id/remove-cross-reference", middlewares.Authenticate(jwtService), productController.RemoveCrossReference)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ type ProductService interface {
|
|||
GetAll(ctx context.Context, filter query.ProductFilter) ([]dto.ProductResponse, int64, error)
|
||||
Update(ctx context.Context, req dto.ProductUpdateRequest, productId string) (dto.ProductResponse, error)
|
||||
Delete(ctx context.Context, productId string) error
|
||||
AssignCrossReference(ctx context.Context, productId string, vendorIds []string) error
|
||||
RemoveCrossReference(ctx context.Context, productId string, vendorIds []string) error
|
||||
}
|
||||
|
||||
type productService struct {
|
||||
|
|
@ -25,6 +27,46 @@ type productService struct {
|
|||
productRepo repository.ProductRepository
|
||||
}
|
||||
|
||||
// AssignCrossReference implements ProductService.
|
||||
func (s *productService) AssignCrossReference(ctx context.Context, productId string, vendorIds []string) error {
|
||||
tx := s.db.Begin()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
tx.Rollback()
|
||||
}
|
||||
}()
|
||||
err := s.productRepo.AssignCrossReference(ctx, tx, productId, vendorIds)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveCrossReference implements ProductService.
|
||||
func (s *productService) RemoveCrossReference(ctx context.Context, productId string, vendorIds []string) error {
|
||||
tx := s.db.Begin()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
tx.Rollback()
|
||||
}
|
||||
}()
|
||||
err := s.productRepo.RemoveCrossReference(ctx, tx, productId, vendorIds)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewProductService(productRepo repository.ProductRepository, db *gorm.DB) ProductService {
|
||||
return &productService{
|
||||
productRepo: productRepo,
|
||||
|
|
@ -263,6 +305,16 @@ func parseUUID(id string) uuid.UUID {
|
|||
}
|
||||
|
||||
func mapProductToResponse(product entities.MProductEntity) dto.ProductResponse {
|
||||
crossRefs := make([]dto.ProductVendorResponse, 0, len(product.CrossReferences))
|
||||
for _, v := range product.CrossReferences {
|
||||
crossRefs = append(crossRefs, dto.ProductVendorResponse{
|
||||
ID: v.Vendor.ID.String(),
|
||||
Name: v.Vendor.Name,
|
||||
Address: v.Vendor.Address,
|
||||
ContactPerson: v.Vendor.ContactPerson,
|
||||
})
|
||||
}
|
||||
|
||||
return dto.ProductResponse{
|
||||
ID: product.ID.String(),
|
||||
Name: product.Name,
|
||||
|
|
@ -327,5 +379,6 @@ func mapProductToResponse(product entities.MProductEntity) dto.ProductResponse {
|
|||
ID: product.UomToUom.ID.String(),
|
||||
Name: product.UomToUom.Name,
|
||||
},
|
||||
CrossReferences: crossRefs,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue