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)
|
Delete(ctx *gin.Context)
|
||||||
GetById(ctx *gin.Context)
|
GetById(ctx *gin.Context)
|
||||||
GetAll(ctx *gin.Context)
|
GetAll(ctx *gin.Context)
|
||||||
|
AssignCrossReference(ctx *gin.Context)
|
||||||
|
RemoveCrossReference(ctx *gin.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
productController struct {
|
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)
|
response := utils.BuildResponseSuccessWithPagination(http.StatusOK, dto.MESSAGE_SUCCESS_GET_PRODUCT, products, paginationResponse)
|
||||||
ctx.JSON(http.StatusOK, response)
|
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_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_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 (
|
var (
|
||||||
|
|
@ -23,6 +27,8 @@ var (
|
||||||
ErrGetProductById = errors.New("failed to get product by id")
|
ErrGetProductById = errors.New("failed to get product by id")
|
||||||
ErrUpdateProduct = errors.New("failed to update product")
|
ErrUpdateProduct = errors.New("failed to update product")
|
||||||
ErrDeleteProduct = errors.New("failed to delete 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 (
|
type (
|
||||||
|
|
@ -97,38 +103,50 @@ 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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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/database/entities"
|
||||||
"github.com/Caknoooo/go-gin-clean-starter/modules/product/query"
|
"github.com/Caknoooo/go-gin-clean-starter/modules/product/query"
|
||||||
|
"github.com/google/uuid"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProductRepository interface {
|
type ProductRepository interface {
|
||||||
|
|
@ -14,12 +16,74 @@ type ProductRepository interface {
|
||||||
GetAll(ctx context.Context, filter query.ProductFilter) ([]entities.MProductEntity, int64, error)
|
GetAll(ctx context.Context, filter query.ProductFilter) ([]entities.MProductEntity, int64, error)
|
||||||
Update(ctx context.Context, tx *gorm.DB, product entities.MProductEntity) (entities.MProductEntity, error)
|
Update(ctx context.Context, tx *gorm.DB, product entities.MProductEntity) (entities.MProductEntity, error)
|
||||||
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
|
||||||
|
RemoveCrossReference(ctx context.Context, tx *gorm.DB, productId string, vendorIds []string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type productRepository struct {
|
type productRepository struct {
|
||||||
db *gorm.DB
|
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 {
|
func NewProductRepository(db *gorm.DB) ProductRepository {
|
||||||
return &productRepository{db: db}
|
return &productRepository{db: db}
|
||||||
}
|
}
|
||||||
|
|
@ -50,6 +114,8 @@ func (r *productRepository) GetById(ctx context.Context, tx *gorm.DB, productId
|
||||||
Preload("MaxStockUom").
|
Preload("MaxStockUom").
|
||||||
Preload("LeadTimeUom").
|
Preload("LeadTimeUom").
|
||||||
Preload("UomToUom").
|
Preload("UomToUom").
|
||||||
|
Preload("CrossReferences").
|
||||||
|
Preload("CrossReferences.Vendor").
|
||||||
First(&product, "id = ?", productId).Error; err != nil {
|
First(&product, "id = ?", productId).Error; err != nil {
|
||||||
return product, err
|
return product, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,5 +20,7 @@ func RegisterRoutes(server *gin.Engine, injector *do.Injector) {
|
||||||
productRoutes.PUT("/:id", middlewares.Authenticate(jwtService), productController.Update)
|
productRoutes.PUT("/:id", middlewares.Authenticate(jwtService), productController.Update)
|
||||||
productRoutes.DELETE("/:id", middlewares.Authenticate(jwtService), productController.Delete)
|
productRoutes.DELETE("/:id", middlewares.Authenticate(jwtService), productController.Delete)
|
||||||
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/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)
|
GetAll(ctx context.Context, filter query.ProductFilter) ([]dto.ProductResponse, int64, error)
|
||||||
Update(ctx context.Context, req dto.ProductUpdateRequest, productId string) (dto.ProductResponse, error)
|
Update(ctx context.Context, req dto.ProductUpdateRequest, productId string) (dto.ProductResponse, error)
|
||||||
Delete(ctx context.Context, productId string) 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 {
|
type productService struct {
|
||||||
|
|
@ -25,6 +27,46 @@ type productService struct {
|
||||||
productRepo repository.ProductRepository
|
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 {
|
func NewProductService(productRepo repository.ProductRepository, db *gorm.DB) ProductService {
|
||||||
return &productService{
|
return &productService{
|
||||||
productRepo: productRepo,
|
productRepo: productRepo,
|
||||||
|
|
@ -263,6 +305,16 @@ func parseUUID(id string) uuid.UUID {
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapProductToResponse(product entities.MProductEntity) dto.ProductResponse {
|
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{
|
return dto.ProductResponse{
|
||||||
ID: product.ID.String(),
|
ID: product.ID.String(),
|
||||||
Name: product.Name,
|
Name: product.Name,
|
||||||
|
|
@ -327,5 +379,6 @@ func mapProductToResponse(product entities.MProductEntity) dto.ProductResponse {
|
||||||
ID: product.UomToUom.ID.String(),
|
ID: product.UomToUom.ID.String(),
|
||||||
Name: product.UomToUom.Name,
|
Name: product.UomToUom.Name,
|
||||||
},
|
},
|
||||||
|
CrossReferences: crossRefs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue