feat: implement inventory movement management with CRUD operations and routing

This commit is contained in:
Habib Fatkhul Rohman 2025-11-20 13:14:34 +07:00
parent 59333e5144
commit 5773c34131
11 changed files with 745 additions and 2 deletions

View File

@ -15,6 +15,7 @@ import (
"github.com/Caknoooo/go-gin-clean-starter/modules/category" "github.com/Caknoooo/go-gin-clean-starter/modules/category"
"github.com/Caknoooo/go-gin-clean-starter/modules/client" "github.com/Caknoooo/go-gin-clean-starter/modules/client"
inventoryissue "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue" inventoryissue "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue"
inventorymovement "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement"
inventoryreceipt "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_receipt" inventoryreceipt "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_receipt"
inventoryrequest "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_request" inventoryrequest "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_request"
inventoryreturn "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_return" inventoryreturn "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_return"
@ -171,6 +172,7 @@ func main() {
inventoryrequest.RegisterRoutes(server, injector) inventoryrequest.RegisterRoutes(server, injector)
inventoryissue.RegisterRoutes(server, injector) inventoryissue.RegisterRoutes(server, injector)
inventoryreturn.RegisterRoutes(server, injector) inventoryreturn.RegisterRoutes(server, injector)
inventorymovement.RegisterRoutes(server, injector)
// register swagger route // register swagger route
server.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) server.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

View File

@ -20,7 +20,10 @@ type TInventoryMovementEntity struct {
SourceLocationID uuid.UUID `gorm:"type:uuid;index;" json:"source_location_id"` SourceLocationID uuid.UUID `gorm:"type:uuid;index;" json:"source_location_id"`
DestinationLocationID uuid.UUID `gorm:"type:uuid;index;" json:"destination_location_id"` DestinationLocationID uuid.UUID `gorm:"type:uuid;index;" json:"destination_location_id"`
Client M_Client `gorm:"foreignKey:ClientID;references:ID"` MovementLines []TInventoryMovementLineEntity `gorm:"foreignKey:InventoryMovementID;references:ID"`
SourceLocation MWarehouseEntity `gorm:"foreignKey:SourceLocationID;references:ID"`
DestinationLocation MWarehouseEntity `gorm:"foreignKey:DestinationLocationID;references:ID"`
Client M_Client `gorm:"foreignKey:ClientID;references:ID"`
FullAuditTrail FullAuditTrail
} }

View File

@ -74,7 +74,7 @@ func MigrateFresh(db *gorm.DB) error {
// &entities.MWarehouseEntity{}, // &entities.MWarehouseEntity{},
// &entities.MZonaEntity{}, // &entities.MZonaEntity{},
// &entities.MAisleEntity{}, // &entities.MAisleEntity{},
&entities.MUserWarehouseEntity{}, // &entities.MUserWarehouseEntity{},
// &entities.TAssignmentEntity{}, // &entities.TAssignmentEntity{},
// &entities.TAssignmentUserEntity{}, // &entities.TAssignmentUserEntity{},
// &entities.TInventoryReceiptEntity{}, // &entities.TInventoryReceiptEntity{},

View File

@ -0,0 +1,174 @@
package controller
import (
"net/http"
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/dto"
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/query"
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/service"
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
"github.com/gin-gonic/gin"
"github.com/samber/do"
)
type InventoryMovementController interface {
Create(ctx *gin.Context)
Update(ctx *gin.Context)
Delete(ctx *gin.Context)
GetById(ctx *gin.Context)
GetAll(ctx *gin.Context)
CreateLine(ctx *gin.Context)
UpdateLine(ctx *gin.Context)
DeleteLine(ctx *gin.Context)
}
type inventoryMovementController struct {
movementService service.InventoryMovementService
}
func (c *inventoryMovementController) Create(ctx *gin.Context) {
var req dto.InventoryMovementCreateRequest
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
}
result, err := c.movementService.Create(ctx, req)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_INVENTORY_MOVEMENT, err.Error(), nil)
ctx.JSON(http.StatusInternalServerError, res)
return
}
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_CREATE_INVENTORY_MOVEMENT, result)
ctx.JSON(http.StatusOK, res)
}
func (c *inventoryMovementController) Update(ctx *gin.Context) {
id := ctx.Param("id")
var req dto.InventoryMovementUpdateRequest
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
}
result, err := c.movementService.Update(ctx, req, id)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_UPDATE_INVENTORY_MOVEMENT, err.Error(), nil)
ctx.JSON(http.StatusInternalServerError, res)
return
}
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_UPDATE_INVENTORY_MOVEMENT, result)
ctx.JSON(http.StatusOK, res)
}
func (c *inventoryMovementController) Delete(ctx *gin.Context) {
id := ctx.Param("id")
if err := c.movementService.Delete(ctx, id); err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_DELETE_INVENTORY_MOVEMENT, err.Error(), nil)
ctx.JSON(http.StatusInternalServerError, res)
return
}
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_DELETE_INVENTORY_MOVEMENT, nil)
ctx.JSON(http.StatusOK, res)
}
func (c *inventoryMovementController) GetById(ctx *gin.Context) {
id := ctx.Param("id")
result, err := c.movementService.GetById(ctx, id)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_INVENTORY_MOVEMENT, err.Error(), nil)
ctx.JSON(http.StatusInternalServerError, res)
return
}
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_INVENTORY_MOVEMENT, result)
ctx.JSON(http.StatusOK, res)
}
func (c *inventoryMovementController) GetAll(ctx *gin.Context) {
clientId := ctx.DefaultQuery("client_id", "")
var filter query.InventoryMovementFilter
filter.ClientID = clientId
if err := ctx.ShouldBindQuery(&filter); err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_INVENTORY_MOVEMENT, err.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}
getAll := ctx.Query("get_all")
if getAll != "" {
movements, _, err := c.movementService.GetAll(ctx, filter)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_INVENTORY_MOVEMENT, err.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}
response := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_INVENTORY_MOVEMENT, movements)
ctx.JSON(http.StatusOK, response)
return
}
perPage := utils.ParseInt(ctx.DefaultQuery("per_page", "10"))
page := utils.ParseInt(ctx.DefaultQuery("page", "1"))
filter.PerPage = perPage
filter.Page = (page - 1) * perPage
movements, total, err := c.movementService.GetAll(ctx, filter)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_INVENTORY_MOVEMENT, err.Error(), nil)
ctx.JSON(http.StatusInternalServerError, res)
return
}
paginationResponse := utils.BuildPaginationResponse(perPage, page, total)
res := utils.BuildResponseSuccessWithPagination(http.StatusOK, dto.MESSAGE_SUCCESS_GET_INVENTORY_MOVEMENT, movements, paginationResponse)
ctx.JSON(http.StatusOK, res)
}
func (c *inventoryMovementController) CreateLine(ctx *gin.Context) {
id := ctx.Param("id")
var req dto.InventoryMovementLineCreateRequest
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
}
created, err := c.movementService.CreateLine(ctx, id, req)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_INVENTORY_MOVEMENT_LINE, err.Error(), nil)
ctx.JSON(http.StatusInternalServerError, res)
return
}
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_CREATE_INVENTORY_MOVEMENT_LINE, created)
ctx.JSON(http.StatusOK, res)
}
func (c *inventoryMovementController) UpdateLine(ctx *gin.Context) {
id := ctx.Param("id")
var req dto.InventoryMovementLineUpdateRequest
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
}
updated, err := c.movementService.UpdateLine(ctx, id, req)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_UPDATE_INVENTORY_MOVEMENT_LINE, err.Error(), nil)
ctx.JSON(http.StatusInternalServerError, res)
return
}
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_UPDATE_INVENTORY_MOVEMENT_LINE, updated)
ctx.JSON(http.StatusOK, res)
}
func (c *inventoryMovementController) DeleteLine(ctx *gin.Context) {
id := ctx.Param("id")
if err := c.movementService.DeleteLine(ctx, id); err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_DELETE_INVENTORY_MOVEMENT_LINE, err.Error(), nil)
ctx.JSON(http.StatusInternalServerError, res)
return
}
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_DELETE_INVENTORY_MOVEMENT_LINE, nil)
ctx.JSON(http.StatusOK, res)
}
func NewInventoryMovementController(i *do.Injector, movementService service.InventoryMovementService) InventoryMovementController {
return &inventoryMovementController{
movementService: movementService,
}
}

View File

@ -0,0 +1,99 @@
package dto
import (
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
)
const (
MESSAGE_FAILED_CREATE_INVENTORY_MOVEMENT = "failed create inventory movement"
MESSAGE_FAILED_CREATE_INVENTORY_MOVEMENT_LINE = "failed create inventory movement line"
MESSAGE_SUCCESS_CREATE_INVENTORY_MOVEMENT = "success create inventory movement"
MESSAGE_SUCCESS_CREATE_INVENTORY_MOVEMENT_LINE = "success create inventory movement line"
MESSAGE_FAILED_GET_INVENTORY_MOVEMENT = "failed get inventory movement"
MESSAGE_SUCCESS_GET_INVENTORY_MOVEMENT = "success get inventory movement"
MESSAGE_FAILED_UPDATE_INVENTORY_MOVEMENT = "failed update inventory movement"
MESSAGE_FAILED_UPDATE_INVENTORY_MOVEMENT_LINE = "failed update inventory movement line"
MESSAGE_SUCCESS_UPDATE_INVENTORY_MOVEMENT = "success update inventory movement"
MESSAGE_SUCCESS_UPDATE_INVENTORY_MOVEMENT_LINE = "success update inventory movement line"
MESSAGE_FAILED_DELETE_INVENTORY_MOVEMENT = "failed delete inventory movement"
MESSAGE_FAILED_DELETE_INVENTORY_MOVEMENT_LINE = "failed delete inventory movement line"
MESSAGE_SUCCESS_DELETE_INVENTORY_MOVEMENT = "success delete inventory movement"
MESSAGE_SUCCESS_DELETE_INVENTORY_MOVEMENT_LINE = "success delete inventory movement line"
MESSAGE_FAILED_GET_DATA_FROM_BODY = "failed get data from body"
)
type InventoryMovementCreateRequest struct {
// MovementNumber string `json:"movement_number"`
MovementDate string `json:"movement_date"`
MovementType string `json:"movement_type"`
ClientID string `json:"client_id" binding:"required"`
Status string `json:"status" binding:"required"`
SourceLocationID string `json:"source_location_id"`
DestinationLocationID string `json:"destination_location_id"`
MovementLines []InventoryMovementLineCreateRequest `json:"movement_lines,omitempty" binding:"dive"`
}
type InventoryMovementLineCreateRequest struct {
MovedQuantity float64 `json:"moved_quantity"`
ProductID string `json:"product_id"`
StorageID string `json:"storage_id"`
ClientID string `json:"client_id"`
Status string `json:"status"`
}
type InventoryMovementUpdateRequest struct {
// MovementNumber string `json:"movement_number"`
MovementDate string `json:"movement_date"`
MovementType string `json:"movement_type"`
Status string `json:"status"`
}
type InventoryMovementLineUpdateRequest struct {
MovedQuantity *float64 `json:"moved_quantity"`
Status *string `json:"status"`
}
type InventoryMovementResponse struct {
ID string `json:"id"`
MovementNumber string `json:"movement_number"`
MovementDate string `json:"movement_date"`
MovementType string `json:"movement_type"`
Status string `json:"status"`
ClientID string `json:"client_id"`
SourceLocationID string `json:"source_location_id"`
DestinationLocationID string `json:"destination_location_id"`
}
type InventoryMovementLineResponse struct {
ID string `json:"id"`
MovedQuantity float64 `json:"moved_quantity"`
ProductID string `json:"product_id"`
StorageID string `json:"storage_id"`
ClientID string `json:"client_id"`
Status string `json:"status"`
}
func ToInventoryMovementResponse(e entities.TInventoryMovementEntity) InventoryMovementResponse {
return InventoryMovementResponse{
ID: e.ID.String(),
MovementNumber: e.MovementNumber,
MovementDate: utils.DateTimeToString(e.MovementDate),
MovementType: e.MovementType,
Status: e.Status,
ClientID: e.ClientID.String(),
SourceLocationID: e.SourceLocationID.String(),
DestinationLocationID: e.DestinationLocationID.String(),
}
}
func ToInventoryMovementLineResponse(e entities.TInventoryMovementLineEntity) InventoryMovementLineResponse {
return InventoryMovementLineResponse{
ID: e.ID.String(),
MovedQuantity: e.MovedQuantity,
ProductID: e.ProductID.String(),
StorageID: e.StorageID.String(),
ClientID: e.ClientID.String(),
Status: e.Status,
}
}

View File

@ -0,0 +1,26 @@
package query
import (
"gorm.io/gorm"
)
type InventoryMovementFilter struct {
ClientID string `form:"client_id"`
SourceLocation string `form:"source_location"`
DestinationLocation string `form:"destination_location"`
PerPage int `form:"per_page"`
Page int `form:"page"`
}
func ApplyInventoryMovementFilters(db *gorm.DB, filter InventoryMovementFilter) *gorm.DB {
if filter.ClientID != "" {
db = db.Where("client_id = ?", filter.ClientID)
}
if filter.SourceLocation != "" {
db = db.Where("source_location_id = ?", filter.SourceLocation)
}
if filter.DestinationLocation != "" {
db = db.Where("destination_location_id = ?", filter.DestinationLocation)
}
return db
}

View File

@ -0,0 +1,89 @@
package repository
import (
"context"
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
"gorm.io/gorm"
)
type InventoryMovementLineRepository interface {
Create(ctx context.Context, tx *gorm.DB, line entities.TInventoryMovementLineEntity) (entities.TInventoryMovementLineEntity, error)
GetById(ctx context.Context, tx *gorm.DB, id string) (entities.TInventoryMovementLineEntity, error)
Update(ctx context.Context, tx *gorm.DB, line entities.TInventoryMovementLineEntity) (entities.TInventoryMovementLineEntity, error)
Delete(ctx context.Context, tx *gorm.DB, id string) error
GetAllByMovementId(ctx context.Context, movementId string) ([]entities.TInventoryMovementLineEntity, error)
BulkCreate(ctx context.Context, tx *gorm.DB, lines []entities.TInventoryMovementLineEntity) error
DeleteByMovementId(ctx context.Context, tx *gorm.DB, movementId string) error
}
type inventoryMovementLineRepository struct {
db *gorm.DB
}
func NewInventoryMovementLineRepository(db *gorm.DB) InventoryMovementLineRepository {
return &inventoryMovementLineRepository{db: db}
}
func (r *inventoryMovementLineRepository) Create(ctx context.Context, tx *gorm.DB, line entities.TInventoryMovementLineEntity) (entities.TInventoryMovementLineEntity, error) {
if tx == nil {
tx = r.db
}
if err := tx.WithContext(ctx).Create(&line).Error; err != nil {
return line, err
}
return line, nil
}
func (r *inventoryMovementLineRepository) GetById(ctx context.Context, tx *gorm.DB, id string) (entities.TInventoryMovementLineEntity, error) {
if tx == nil {
tx = r.db
}
var line entities.TInventoryMovementLineEntity
if err := tx.WithContext(ctx).First(&line, "id = ?", id).Error; err != nil {
return line, err
}
return line, nil
}
func (r *inventoryMovementLineRepository) Update(ctx context.Context, tx *gorm.DB, line entities.TInventoryMovementLineEntity) (entities.TInventoryMovementLineEntity, error) {
if tx == nil {
tx = r.db
}
if err := tx.WithContext(ctx).Save(&line).Error; err != nil {
return line, err
}
return line, nil
}
func (r *inventoryMovementLineRepository) Delete(ctx context.Context, tx *gorm.DB, id string) error {
if tx == nil {
tx = r.db
}
if err := tx.WithContext(ctx).Delete(&entities.TInventoryMovementLineEntity{}, "id = ?", id).Error; err != nil {
return err
}
return nil
}
func (r *inventoryMovementLineRepository) GetAllByMovementId(ctx context.Context, movementId string) ([]entities.TInventoryMovementLineEntity, error) {
var lines []entities.TInventoryMovementLineEntity
if err := r.db.WithContext(ctx).Where("inv_movement_id = ?", movementId).Preload("Product").Preload("InvMovement").Preload("InvStorage").Preload("Client").Find(&lines).Error; err != nil {
return lines, err
}
return lines, nil
}
func (r *inventoryMovementLineRepository) BulkCreate(ctx context.Context, tx *gorm.DB, lines []entities.TInventoryMovementLineEntity) error {
if tx == nil {
tx = r.db
}
return tx.WithContext(ctx).Create(&lines).Error
}
func (r *inventoryMovementLineRepository) DeleteByMovementId(ctx context.Context, tx *gorm.DB, movementId string) error {
if tx == nil {
tx = r.db
}
return tx.WithContext(ctx).Where("inv_movement_id = ?", movementId).Delete(&entities.TInventoryMovementLineEntity{}).Error
}

View File

@ -0,0 +1,79 @@
package repository
import (
"context"
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/query"
"gorm.io/gorm"
)
type InventoryMovementRepository interface {
Create(ctx context.Context, tx *gorm.DB, movement entities.TInventoryMovementEntity) (entities.TInventoryMovementEntity, error)
GetById(ctx context.Context, tx *gorm.DB, id string) (entities.TInventoryMovementEntity, error)
GetAll(ctx context.Context, filter query.InventoryMovementFilter) ([]entities.TInventoryMovementEntity, int64, error)
Update(ctx context.Context, tx *gorm.DB, movement entities.TInventoryMovementEntity) (entities.TInventoryMovementEntity, error)
Delete(ctx context.Context, tx *gorm.DB, id string) error
}
type inventoryMovementRepository struct {
db *gorm.DB
}
func NewInventoryMovementRepository(db *gorm.DB) InventoryMovementRepository {
return &inventoryMovementRepository{db: db}
}
func (r *inventoryMovementRepository) Create(ctx context.Context, tx *gorm.DB, movement entities.TInventoryMovementEntity) (entities.TInventoryMovementEntity, error) {
if tx == nil {
tx = r.db
}
if err := tx.WithContext(ctx).Create(&movement).Error; err != nil {
return movement, err
}
return movement, nil
}
func (r *inventoryMovementRepository) GetById(ctx context.Context, tx *gorm.DB, id string) (entities.TInventoryMovementEntity, error) {
if tx == nil {
tx = r.db
}
var movement entities.TInventoryMovementEntity
if err := tx.WithContext(ctx).
Preload("Client").
First(&movement, "id = ?", id).Error; err != nil {
return movement, err
}
return movement, nil
}
func (r *inventoryMovementRepository) GetAll(ctx context.Context, filter query.InventoryMovementFilter) ([]entities.TInventoryMovementEntity, int64, error) {
db := r.db.WithContext(ctx)
db = query.ApplyInventoryMovementFilters(db, filter)
var movements []entities.TInventoryMovementEntity
var total int64
if err := db.Find(&movements).Count(&total).Error; err != nil {
return nil, 0, err
}
return movements, total, nil
}
func (r *inventoryMovementRepository) Update(ctx context.Context, tx *gorm.DB, movement entities.TInventoryMovementEntity) (entities.TInventoryMovementEntity, error) {
if tx == nil {
tx = r.db
}
if err := tx.WithContext(ctx).Save(&movement).Error; err != nil {
return movement, err
}
return movement, nil
}
func (r *inventoryMovementRepository) Delete(ctx context.Context, tx *gorm.DB, id string) error {
if tx == nil {
tx = r.db
}
if err := tx.WithContext(ctx).Delete(&entities.TInventoryMovementEntity{}, "id = ?", id).Error; err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,27 @@
package inventorymovement
import (
"github.com/Caknoooo/go-gin-clean-starter/middlewares"
"github.com/Caknoooo/go-gin-clean-starter/modules/auth/service"
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/controller"
"github.com/Caknoooo/go-gin-clean-starter/pkg/constants"
"github.com/gin-gonic/gin"
"github.com/samber/do"
)
func RegisterRoutes(server *gin.Engine, injector *do.Injector) {
movementController := do.MustInvoke[controller.InventoryMovementController](injector)
jwtService := do.MustInvokeNamed[service.JWTService](injector, constants.JWTService)
movementRoutes := server.Group("/api/v1/inventory-movements")
{
movementRoutes.POST("", middlewares.Authenticate(jwtService), movementController.Create)
movementRoutes.GET(":id", middlewares.Authenticate(jwtService), movementController.GetById)
movementRoutes.PUT(":id", middlewares.Authenticate(jwtService), movementController.Update)
movementRoutes.DELETE(":id", middlewares.Authenticate(jwtService), movementController.Delete)
movementRoutes.GET("", middlewares.Authenticate(jwtService), movementController.GetAll)
movementRoutes.POST(":id/lines", middlewares.Authenticate(jwtService), movementController.CreateLine)
movementRoutes.PUT("lines/:id", middlewares.Authenticate(jwtService), movementController.UpdateLine)
movementRoutes.DELETE("lines/:id", middlewares.Authenticate(jwtService), movementController.DeleteLine)
}
}

View File

@ -0,0 +1,231 @@
package service
import (
"context"
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
dtodomain "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/dto"
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/query"
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/repository"
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
"github.com/google/uuid"
"gorm.io/gorm"
)
type InventoryMovementService interface {
Create(ctx context.Context, req dtodomain.InventoryMovementCreateRequest) (dtodomain.InventoryMovementResponse, error)
GetById(ctx context.Context, id string) (dtodomain.InventoryMovementResponse, error)
GetAll(ctx context.Context, filter query.InventoryMovementFilter) ([]dtodomain.InventoryMovementResponse, int64, error)
Update(ctx context.Context, req dtodomain.InventoryMovementUpdateRequest, id string) (dtodomain.InventoryMovementResponse, error)
Delete(ctx context.Context, id string) error
CreateLine(ctx context.Context, movementId string, req dtodomain.InventoryMovementLineCreateRequest) (dtodomain.InventoryMovementLineResponse, error)
UpdateLine(ctx context.Context, lineId string, req dtodomain.InventoryMovementLineUpdateRequest) (dtodomain.InventoryMovementLineResponse, error)
DeleteLine(ctx context.Context, lineId string) error
}
type inventoryMovementService struct {
db *gorm.DB
movementRepo repository.InventoryMovementRepository
movementLineRepo repository.InventoryMovementLineRepository
}
func (s *inventoryMovementService) Create(ctx context.Context, req dtodomain.InventoryMovementCreateRequest) (dtodomain.InventoryMovementResponse, 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.InventoryMovementResponse{}, err
}
movNumber, err := entities.GenerateDocumentNumberInvMovement(s.db, req.ClientID)
if err != nil {
tx.Rollback()
return dtodomain.InventoryMovementResponse{}, err
}
movement := entities.TInventoryMovementEntity{
MovementNumber: movNumber,
MovementDate: utils.StringToDateTime(req.MovementDate),
MovementType: req.MovementType,
ClientID: clientUUID,
Status: req.Status,
SourceLocationID: uuid.MustParse(req.SourceLocationID),
DestinationLocationID: uuid.MustParse(req.DestinationLocationID),
}
created, err := s.movementRepo.Create(ctx, tx, movement)
if err != nil {
tx.Rollback()
return dtodomain.InventoryMovementResponse{}, err
}
// Bulk create lines
var lines []entities.TInventoryMovementLineEntity
for _, lineReq := range req.MovementLines {
productUUID := uuid.Nil
if lineReq.ProductID != "" {
productUUID, err = uuid.Parse(lineReq.ProductID)
if err != nil {
tx.Rollback()
return dtodomain.InventoryMovementResponse{}, err
}
}
clientLineUUID, err := uuid.Parse(lineReq.ClientID)
if err != nil {
tx.Rollback()
return dtodomain.InventoryMovementResponse{}, err
}
lines = append(lines, entities.TInventoryMovementLineEntity{
MovedQuantity: lineReq.MovedQuantity,
InvMovementID: created.ID,
ProductID: productUUID,
StorageID: uuid.MustParse(lineReq.StorageID),
ClientID: clientLineUUID,
Status: lineReq.Status,
})
}
if len(lines) > 0 {
err = s.movementLineRepo.BulkCreate(ctx, tx, lines)
if err != nil {
tx.Rollback()
return dtodomain.InventoryMovementResponse{}, err
}
}
tx.Commit()
result, err := s.movementRepo.GetById(ctx, nil, created.ID.String())
if err != nil {
return dtodomain.InventoryMovementResponse{}, err
}
return dtodomain.ToInventoryMovementResponse(result), nil
}
func (s *inventoryMovementService) GetById(ctx context.Context, id string) (dtodomain.InventoryMovementResponse, error) {
movement, err := s.movementRepo.GetById(ctx, nil, id)
if err != nil {
return dtodomain.InventoryMovementResponse{}, err
}
return dtodomain.ToInventoryMovementResponse(movement), nil
}
func (s *inventoryMovementService) GetAll(ctx context.Context, filter query.InventoryMovementFilter) ([]dtodomain.InventoryMovementResponse, int64, error) {
movements, total, err := s.movementRepo.GetAll(ctx, filter)
if err != nil {
return nil, 0, err
}
var responses []dtodomain.InventoryMovementResponse
for _, e := range movements {
responses = append(responses, dtodomain.ToInventoryMovementResponse(e))
}
if responses == nil {
responses = make([]dtodomain.InventoryMovementResponse, 0)
}
return responses, total, nil
}
func (s *inventoryMovementService) Update(ctx context.Context, req dtodomain.InventoryMovementUpdateRequest, id string) (dtodomain.InventoryMovementResponse, error) {
tx := s.db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
movement, err := s.movementRepo.GetById(ctx, tx, id)
if err != nil {
tx.Rollback()
return dtodomain.InventoryMovementResponse{}, err
}
// if req.MovementNumber != "" {
// movement.MovementNumber = req.MovementNumber
// }
movement.MovementDate = utils.StringToDateTime(req.MovementDate)
movement.MovementType = req.MovementType
movement.Status = req.Status
updated, err := s.movementRepo.Update(ctx, tx, movement)
if err != nil {
tx.Rollback()
return dtodomain.InventoryMovementResponse{}, err
}
tx.Commit()
result, err := s.movementRepo.GetById(ctx, nil, updated.ID.String())
if err != nil {
return dtodomain.InventoryMovementResponse{}, err
}
return dtodomain.ToInventoryMovementResponse(result), nil
}
func (s *inventoryMovementService) Delete(ctx context.Context, id string) error {
tx := s.db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := s.movementRepo.Delete(ctx, tx, id); err != nil {
tx.Rollback()
return err
}
tx.Commit()
return nil
}
func (s *inventoryMovementService) CreateLine(ctx context.Context, movementId string, req dtodomain.InventoryMovementLineCreateRequest) (dtodomain.InventoryMovementLineResponse, error) {
movementUUID, err := uuid.Parse(movementId)
if err != nil {
return dtodomain.InventoryMovementLineResponse{}, err
}
productUUID := uuid.Nil
if req.ProductID != "" {
productUUID, err = uuid.Parse(req.ProductID)
if err != nil {
return dtodomain.InventoryMovementLineResponse{}, err
}
}
clientLineUUID, err := uuid.Parse(req.ClientID)
if err != nil {
return dtodomain.InventoryMovementLineResponse{}, err
}
line := entities.TInventoryMovementLineEntity{
MovedQuantity: req.MovedQuantity,
InvMovementID: movementUUID,
ProductID: productUUID,
StorageID: uuid.MustParse(req.StorageID),
ClientID: clientLineUUID,
Status: req.Status,
}
created, err := s.movementLineRepo.Create(ctx, nil, line)
if err != nil {
return dtodomain.InventoryMovementLineResponse{}, err
}
return dtodomain.ToInventoryMovementLineResponse(created), nil
}
func (s *inventoryMovementService) UpdateLine(ctx context.Context, lineId string, req dtodomain.InventoryMovementLineUpdateRequest) (dtodomain.InventoryMovementLineResponse, error) {
line, err := s.movementLineRepo.GetById(ctx, nil, lineId)
if err != nil {
return dtodomain.InventoryMovementLineResponse{}, err
}
if req.MovedQuantity != nil {
line.MovedQuantity = *req.MovedQuantity
}
if req.Status != nil {
line.Status = *req.Status
}
updated, err := s.movementLineRepo.Update(ctx, nil, line)
if err != nil {
return dtodomain.InventoryMovementLineResponse{}, err
}
return dtodomain.ToInventoryMovementLineResponse(updated), nil
}
func (s *inventoryMovementService) DeleteLine(ctx context.Context, lineId string) error {
return s.movementLineRepo.Delete(ctx, nil, lineId)
}
func NewInventoryMovementService(db *gorm.DB, movementRepo repository.InventoryMovementRepository, movementLineRepo repository.InventoryMovementLineRepository) InventoryMovementService {
return &inventoryMovementService{
db: db,
movementRepo: movementRepo,
movementLineRepo: movementLineRepo,
}
}

View File

@ -83,6 +83,11 @@ import (
inventoryReturnRepo "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_return/repository" inventoryReturnRepo "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_return/repository"
inventoryReturnService "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_return/service" inventoryReturnService "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_return/service"
inventoryMovementController "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/controller"
inventoryMovementLineRepo "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/repository"
inventoryMovementRepo "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/repository"
inventoryMovementService "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_movement/service"
"github.com/Caknoooo/go-gin-clean-starter/modules/user/controller" "github.com/Caknoooo/go-gin-clean-starter/modules/user/controller"
"github.com/Caknoooo/go-gin-clean-starter/modules/user/repository" "github.com/Caknoooo/go-gin-clean-starter/modules/user/repository"
userService "github.com/Caknoooo/go-gin-clean-starter/modules/user/service" userService "github.com/Caknoooo/go-gin-clean-starter/modules/user/service"
@ -142,6 +147,8 @@ func RegisterDependencies(injector *do.Injector) {
inventoryIssueLineRepository := inventoryIssueLineRepo.NewInventoryIssueLineRepository(db) inventoryIssueLineRepository := inventoryIssueLineRepo.NewInventoryIssueLineRepository(db)
inventoryReturnRepository := inventoryReturnRepo.NewInventoryReturnRepository(db) inventoryReturnRepository := inventoryReturnRepo.NewInventoryReturnRepository(db)
inventoryReturnLineRepository := inventoryReturnLineRepo.NewInventoryReturnLineRepository(db) inventoryReturnLineRepository := inventoryReturnLineRepo.NewInventoryReturnLineRepository(db)
inventoryMovementRepository := inventoryMovementRepo.NewInventoryMovementRepository(db)
inventoryMovementLineRepository := inventoryMovementLineRepo.NewInventoryMovementLineRepository(db)
// Service // Service
userServ := userService.NewUserService(userRepository, roleRepository, warehouseRepository, refreshTokenRepository, jwtService, db) userServ := userService.NewUserService(userRepository, roleRepository, warehouseRepository, refreshTokenRepository, jwtService, db)
@ -162,6 +169,7 @@ func RegisterDependencies(injector *do.Injector) {
inventoryRequestServ := inventoryRequestService.NewInventoryRequestService(db, inventoryRequestRepository, inventoryRequestLineRepository) inventoryRequestServ := inventoryRequestService.NewInventoryRequestService(db, inventoryRequestRepository, inventoryRequestLineRepository)
inventoryIssueServ := inventoryIssueService.NewInventoryIssueService(db, inventoryIssueRepository, inventoryIssueLineRepository) inventoryIssueServ := inventoryIssueService.NewInventoryIssueService(db, inventoryIssueRepository, inventoryIssueLineRepository)
inventoryReturnServ := inventoryReturnService.NewInventoryReturnService(db, inventoryReturnRepository, inventoryReturnLineRepository, inventoryIssueLineRepository, productRepository) inventoryReturnServ := inventoryReturnService.NewInventoryReturnService(db, inventoryReturnRepository, inventoryReturnLineRepository, inventoryIssueLineRepository, productRepository)
inventoryMovementServ := inventoryMovementService.NewInventoryMovementService(db, inventoryMovementRepository, inventoryMovementLineRepository)
// Controller // Controller
do.Provide( do.Provide(
@ -264,4 +272,9 @@ func RegisterDependencies(injector *do.Injector) {
return inventoryReturnController.NewInventoryReturnController(i, inventoryReturnServ), nil return inventoryReturnController.NewInventoryReturnController(i, inventoryReturnServ), nil
}, },
) )
do.Provide(
injector, func(i *do.Injector) (inventoryMovementController.InventoryMovementController, error) {
return inventoryMovementController.NewInventoryMovementController(i, inventoryMovementServ), nil
},
)
} }