From 4e9be2b87a1f4bb074aa34114e9be2de16f4e0da Mon Sep 17 00:00:00 2001 From: Habib Fatkhul Rohman Date: Fri, 21 Nov 2025 11:28:02 +0700 Subject: [PATCH] feat: implement inventory transaction management with CRUD operations and routing --- cmd/main.go | 2 + .../entities/inventory_transaction_entity.go | 23 +- database/migration.go | 72 +++---- .../inventory_transaction_controller.go | 107 ++++++++++ .../dto/inventory_transaction_dto.go | 100 +++++++++ .../query/inventory_transaction_query.go | 42 ++++ .../inventory_transaction_repository.go | 83 ++++++++ modules/inventory_transaction/routes.go | 24 +++ .../service/inventory_transaction_service.go | 199 ++++++++++++++++++ pkg/dto/response.go | 5 + providers/core.go | 12 ++ 11 files changed, 622 insertions(+), 47 deletions(-) create mode 100644 modules/inventory_transaction/controller/inventory_transaction_controller.go create mode 100644 modules/inventory_transaction/dto/inventory_transaction_dto.go create mode 100644 modules/inventory_transaction/query/inventory_transaction_query.go create mode 100644 modules/inventory_transaction/repository/inventory_transaction_repository.go create mode 100644 modules/inventory_transaction/routes.go create mode 100644 modules/inventory_transaction/service/inventory_transaction_service.go diff --git a/cmd/main.go b/cmd/main.go index e8bc489..54e0b3f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -20,6 +20,7 @@ import ( inventoryrequest "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_request" inventoryreturn "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_return" inventorystorage "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_storage" + inventorytransaction "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_transaction" maintenancegroup "github.com/Caknoooo/go-gin-clean-starter/modules/maintenance_group" "github.com/Caknoooo/go-gin-clean-starter/modules/menu" "github.com/Caknoooo/go-gin-clean-starter/modules/monitoring" @@ -175,6 +176,7 @@ func main() { inventoryreturn.RegisterRoutes(server, injector) inventorymovement.RegisterRoutes(server, injector) inventorystorage.RegisterRoutes(server, injector) + inventorytransaction.RegisterRoutes(server, injector) // register swagger route server.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) diff --git a/database/entities/inventory_transaction_entity.go b/database/entities/inventory_transaction_entity.go index 6de3325..c4cec2c 100644 --- a/database/entities/inventory_transaction_entity.go +++ b/database/entities/inventory_transaction_entity.go @@ -11,18 +11,19 @@ type InventoryTransactionEntity struct { TransactionDate time.Time `gorm:"type:timestamp;" json:"transaction_date"` TransactionType string `gorm:"type:varchar(100);" json:"transaction_type"` - ProductID uuid.UUID `gorm:"type:uuid;index;" json:"product_id"` - AisleID uuid.UUID `gorm:"type:uuid;index;" json:"aisle_id"` - ClientID uuid.UUID `gorm:"type:uuid;index;" json:"client_id"` - InvReceiptID uuid.UUID `gorm:"type:uuid;index;" json:"inv_receipt_id"` - InvIssueID uuid.UUID `gorm:"type:uuid;index;" json:"inv_issue_id"` - InvMoveID uuid.UUID `gorm:"type:uuid;index;" json:"inv_move_id"` + ProductID uuid.UUID `gorm:"type:uuid;index;" json:"product_id"` + AisleID *uuid.UUID `gorm:"type:uuid;index;" json:"aisle_id"` + ClientID uuid.UUID `gorm:"type:uuid;index;" json:"client_id"` + InvReceiptID *uuid.UUID `gorm:"type:uuid;index;" json:"inv_receipt_id"` + InvIssueID *uuid.UUID `gorm:"type:uuid;index;" json:"inv_issue_id"` + InvMoveID *uuid.UUID `gorm:"type:uuid;index;" json:"inv_move_id"` - Product MProductEntity `gorm:"foreignKey:ProductID;references:ID"` - Aisle MAisleEntity `gorm:"foreignKey:AisleID;references:ID"` - Client M_Client `gorm:"foreignKey:ClientID;references:ID"` - InvReceipt TInventoryReceiptEntity `gorm:"foreignKey:InvReceiptID;references:ID"` - InvIssue TInventoryIssueEntity `gorm:"foreignKey:InvIssueID;references:ID"` + Product MProductEntity `gorm:"foreignKey:ProductID;references:ID"` + Aisle MAisleEntity `gorm:"foreignKey:AisleID;references:ID"` + Client M_Client `gorm:"foreignKey:ClientID;references:ID"` + InvReceipt TInventoryReceiptEntity `gorm:"foreignKey:InvReceiptID;references:ID"` + InvIssue TInventoryIssueEntity `gorm:"foreignKey:InvIssueID;references:ID"` + InvMove TInventoryMovementEntity `gorm:"foreignKey:InvMoveID;references:ID"` FullAuditTrail } diff --git a/database/migration.go b/database/migration.go index 9aaf785..6a896eb 100644 --- a/database/migration.go +++ b/database/migration.go @@ -53,42 +53,42 @@ func Migrate(db *gorm.DB) error { func MigrateFresh(db *gorm.DB) error { // Drop tables if err := db.Migrator().DropTable( - // &entities.M_Client{}, - // &entities.M_User{}, - // &entities.RefreshToken{}, - // &entities.M_Menu{}, - // &entities.M_Role{}, - // &entities.M_Permissions{}, - // &entities.M_User_Role{}, - // &entities.M_Role_Menu{}, - // &entities.M_Menu_Client{}, - // &entities.M_Role_Permission{}, - // &entities.M_MaintenanceGroup{}, - // &entities.M_MaintenanceGroupRole{}, - // &entities.M_MaintenanceGroupRoleUser{}, - // &entities.MCategoryEntity{}, - // &entities.MProductEntity{}, - // &entities.MUomEntity{}, - // &entities.MVendorEntity{}, - // &entities.MCrossReferenceEntity{}, - // &entities.MWarehouseEntity{}, - // &entities.MZonaEntity{}, - // &entities.MAisleEntity{}, - // &entities.MUserWarehouseEntity{}, - // &entities.TAssignmentEntity{}, - // &entities.TAssignmentUserEntity{}, - // &entities.TInventoryReceiptEntity{}, - // &entities.TInventoryReceiptLineEntity{}, - // &entities.TInventoryRequestEntity{}, - // &entities.TInventoryRequestLineEntity{}, - // &entities.TInventoryIssueEntity{}, - // &entities.TInventoryIssueLineEntity{}, - // &entities.TInventoryReturnEntity{}, - // &entities.TInventoryReturnLineEntity{}, - // &entities.InventoryTransactionEntity{}, - // &entities.InventoryStorageEntity{}, - // &entities.TInventoryMovementEntity{}, - // &entities.TInventoryMovementLineEntity{}, + // &entities.M_Client{}, + // &entities.M_User{}, + // &entities.RefreshToken{}, + // &entities.M_Menu{}, + // &entities.M_Role{}, + // &entities.M_Permissions{}, + // &entities.M_User_Role{}, + // &entities.M_Role_Menu{}, + // &entities.M_Menu_Client{}, + // &entities.M_Role_Permission{}, + // &entities.M_MaintenanceGroup{}, + // &entities.M_MaintenanceGroupRole{}, + // &entities.M_MaintenanceGroupRoleUser{}, + // &entities.MCategoryEntity{}, + // &entities.MProductEntity{}, + // &entities.MUomEntity{}, + // &entities.MVendorEntity{}, + // &entities.MCrossReferenceEntity{}, + // &entities.MWarehouseEntity{}, + // &entities.MZonaEntity{}, + // &entities.MAisleEntity{}, + // &entities.MUserWarehouseEntity{}, + // &entities.TAssignmentEntity{}, + // &entities.TAssignmentUserEntity{}, + // &entities.TInventoryReceiptEntity{}, + // &entities.TInventoryReceiptLineEntity{}, + // &entities.TInventoryRequestEntity{}, + // &entities.TInventoryRequestLineEntity{}, + // &entities.TInventoryIssueEntity{}, + // &entities.TInventoryIssueLineEntity{}, + // &entities.TInventoryReturnEntity{}, + // &entities.TInventoryReturnLineEntity{}, + // &entities.InventoryTransactionEntity{}, + // &entities.InventoryStorageEntity{}, + // &entities.TInventoryMovementEntity{}, + // &entities.TInventoryMovementLineEntity{}, ); err != nil { return err } diff --git a/modules/inventory_transaction/controller/inventory_transaction_controller.go b/modules/inventory_transaction/controller/inventory_transaction_controller.go new file mode 100644 index 0000000..392e806 --- /dev/null +++ b/modules/inventory_transaction/controller/inventory_transaction_controller.go @@ -0,0 +1,107 @@ +package controller + +import ( + "net/http" + + "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_transaction/dto" + "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_transaction/query" + "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_transaction/service" + "github.com/Caknoooo/go-gin-clean-starter/pkg/utils" + "github.com/gin-gonic/gin" + "github.com/samber/do" +) + +type InventoryTransactionController interface { + Create(ctx *gin.Context) + Update(ctx *gin.Context) + Delete(ctx *gin.Context) + GetById(ctx *gin.Context) + GetAll(ctx *gin.Context) +} + +type inventoryTransactionController struct { + inventoryTransactionService service.InventoryTransactionService +} + +func (c *inventoryTransactionController) Create(ctx *gin.Context) { + var req dto.InventoryTransactionCreateRequest + 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.inventoryTransactionService.Create(ctx, req) + if err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_INVENTORY_TRANSACTION, err.Error(), nil) + ctx.JSON(http.StatusInternalServerError, res) + return + } + res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_CREATE_INVENTORY_TRANSACTION, created) + ctx.JSON(http.StatusOK, res) +} + +func (c *inventoryTransactionController) Update(ctx *gin.Context) { + id := ctx.Param("id") + var req dto.InventoryTransactionUpdateRequest + 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.inventoryTransactionService.Update(ctx, req, id) + if err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_UPDATE_INVENTORY_TRANSACTION, err.Error(), nil) + ctx.JSON(http.StatusInternalServerError, res) + return + } + res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_UPDATE_INVENTORY_TRANSACTION, updated) + ctx.JSON(http.StatusOK, res) +} + +func (c *inventoryTransactionController) Delete(ctx *gin.Context) { + id := ctx.Param("id") + err := c.inventoryTransactionService.Delete(ctx, id) + if err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_DELETE_INVENTORY_TRANSACTION, err.Error(), nil) + ctx.JSON(http.StatusInternalServerError, res) + return + } + res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_DELETE_INVENTORY_TRANSACTION, nil) + ctx.JSON(http.StatusOK, res) +} + +func (c *inventoryTransactionController) GetById(ctx *gin.Context) { + id := ctx.Param("id") + result, err := c.inventoryTransactionService.GetById(ctx, id) + if err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_INVENTORY_TRANSACTION, err.Error(), nil) + ctx.JSON(http.StatusInternalServerError, res) + return + } + res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_INVENTORY_TRANSACTION, result) + ctx.JSON(http.StatusOK, res) +} + +func (c *inventoryTransactionController) GetAll(ctx *gin.Context) { + var filter query.InventoryTransactionFilter + if err := ctx.ShouldBindQuery(&filter); err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil) + ctx.JSON(http.StatusBadRequest, res) + return + } + results, total, err := c.inventoryTransactionService.GetAll(ctx, filter) + if err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_INVENTORY_TRANSACTION, err.Error(), nil) + ctx.JSON(http.StatusInternalServerError, res) + return + } + pagination := utils.BuildPaginationResponse(filter.PerPage, filter.Page, total) + res := utils.BuildResponseSuccessWithPagination(http.StatusOK, dto.MESSAGE_SUCCESS_GET_INVENTORY_TRANSACTION, results, pagination) + ctx.JSON(http.StatusOK, res) +} + +func NewInventoryTransactionController(injector *do.Injector, inventoryTransactionService service.InventoryTransactionService) InventoryTransactionController { + return &inventoryTransactionController{ + inventoryTransactionService: inventoryTransactionService, + } +} diff --git a/modules/inventory_transaction/dto/inventory_transaction_dto.go b/modules/inventory_transaction/dto/inventory_transaction_dto.go new file mode 100644 index 0000000..7025679 --- /dev/null +++ b/modules/inventory_transaction/dto/inventory_transaction_dto.go @@ -0,0 +1,100 @@ +package dto + +import ( + "errors" + + "github.com/Caknoooo/go-gin-clean-starter/database/entities" + pkgdto "github.com/Caknoooo/go-gin-clean-starter/pkg/dto" + "github.com/Caknoooo/go-gin-clean-starter/pkg/utils" +) + +const ( + MESSAGE_FAILED_CREATE_INVENTORY_TRANSACTION = "failed create inventory transaction" + MESSAGE_SUCCESS_CREATE_INVENTORY_TRANSACTION = "success create inventory transaction" + MESSAGE_FAILED_GET_INVENTORY_TRANSACTION = "failed get inventory transaction" + MESSAGE_SUCCESS_GET_INVENTORY_TRANSACTION = "success get inventory transaction" + MESSAGE_FAILED_UPDATE_INVENTORY_TRANSACTION = "failed update inventory transaction" + MESSAGE_SUCCESS_UPDATE_INVENTORY_TRANSACTION = "success update inventory transaction" + MESSAGE_FAILED_DELETE_INVENTORY_TRANSACTION = "failed delete inventory transaction" + MESSAGE_SUCCESS_DELETE_INVENTORY_TRANSACTION = "success delete inventory transaction" + MESSAGE_FAILED_GET_DATA_FROM_BODY = "failed get data from body" +) + +var ( + ErrCreateInventoryTransaction = errors.New("failed to create inventory transaction") + ErrGetInventoryTransactionById = errors.New("failed to get inventory transaction by id") + ErrUpdateInventoryTransaction = errors.New("failed to update inventory transaction") + ErrDeleteInventoryTransaction = errors.New("failed to delete inventory transaction") +) + +type InventoryTransactionCreateRequest struct { + TransactionType string `json:"transaction_type" binding:"required"` + TransactionDate string `json:"transaction_date" binding:"required"` + ProductID string `json:"product_id" binding:"required"` + AisleID string `json:"aisle_id"` + ClientID string `json:"client_id" binding:"required"` + InvReceiptID string `json:"inv_receipt_id"` + InvIssueID string `json:"inv_issue_id"` + InvMoveID string `json:"inv_move_id"` +} + +type InventoryTransactionUpdateRequest struct { + TransactionType string `json:"transaction_type"` + TransactionDate string `json:"transaction_date"` + ProductID string `json:"product_id"` + AisleID string `json:"aisle_id"` + InvReceiptID string `json:"inv_receipt_id"` + InvIssueID string `json:"inv_issue_id"` + InvMoveID string `json:"inv_move_id"` +} + +type InventoryTransactionResponse struct { + ID string `json:"id"` + TransactionType string `json:"transaction_type"` + TransactionDate string `json:"transaction_date"` + Product InvTransactionProductResponse `json:"product"` + Aisle pkgdto.IdNameResponse `json:"aisle"` + InvReceipt pkgdto.InventoryResponse `json:"inv_receipt"` + InvIssue pkgdto.InventoryResponse `json:"inv_issue"` + InvMove pkgdto.InventoryResponse `json:"inv_move"` + Client pkgdto.IdNameResponse `json:"client"` +} + +type InvTransactionProductResponse struct { + ID string `json:"id"` + RefNumber string `json:"ref_number"` + Name string `json:"name"` +} + +func ToInventoryTransactionResponse(e entities.InventoryTransactionEntity) InventoryTransactionResponse { + return InventoryTransactionResponse{ + ID: e.ID.String(), + TransactionType: e.TransactionType, + TransactionDate: utils.DateTimeToString(e.TransactionDate), + Product: InvTransactionProductResponse{ + ID: e.Product.ID.String(), + RefNumber: e.Product.RefNumber, + Name: e.Product.Name, + }, + Aisle: pkgdto.IdNameResponse{ + ID: e.Aisle.ID.String(), + Name: e.Aisle.Name, + }, + Client: pkgdto.IdNameResponse{ + ID: e.Client.ID.String(), + Name: e.Client.Name, + }, + InvReceipt: pkgdto.InventoryResponse{ + ID: e.InvReceipt.ID.String(), + DocumentNumber: e.InvReceipt.DocumentNumber, + }, + InvIssue: pkgdto.InventoryResponse{ + ID: e.InvIssue.ID.String(), + DocumentNumber: e.InvIssue.DocumentNumber, + }, + InvMove: pkgdto.InventoryResponse{ + ID: e.InvMove.ID.String(), + DocumentNumber: e.InvMove.MovementNumber, + }, + } +} diff --git a/modules/inventory_transaction/query/inventory_transaction_query.go b/modules/inventory_transaction/query/inventory_transaction_query.go new file mode 100644 index 0000000..04aa148 --- /dev/null +++ b/modules/inventory_transaction/query/inventory_transaction_query.go @@ -0,0 +1,42 @@ +package query + +import ( + "gorm.io/gorm" +) + +type InventoryTransactionFilter struct { + TransactionType string `form:"transaction_type"` + ProductID string `form:"product_id"` + AisleID string `form:"aisle_id"` + ClientID string `form:"client_id"` + InvReceiptID string `form:"inv_receipt_id"` + InvIssueID string `form:"inv_issue_id"` + InvMoveID string `form:"inv_move_id"` + PerPage int `form:"per_page"` + Page int `form:"page"` +} + +func ApplyInventoryTransactionFilters(db *gorm.DB, filter InventoryTransactionFilter) *gorm.DB { + if filter.TransactionType != "" { + db = db.Where("transaction_type = ?", filter.TransactionType) + } + if filter.ProductID != "" { + db = db.Where("product_id = ?", filter.ProductID) + } + if filter.AisleID != "" { + db = db.Where("aisle_id = ?", filter.AisleID) + } + if filter.ClientID != "" { + db = db.Where("client_id = ?", filter.ClientID) + } + if filter.InvReceiptID != "" { + db = db.Where("inv_receipt_id = ?", filter.InvReceiptID) + } + if filter.InvIssueID != "" { + db = db.Where("inv_issue_id = ?", filter.InvIssueID) + } + if filter.InvMoveID != "" { + db = db.Where("inv_move_id = ?", filter.InvMoveID) + } + return db +} diff --git a/modules/inventory_transaction/repository/inventory_transaction_repository.go b/modules/inventory_transaction/repository/inventory_transaction_repository.go new file mode 100644 index 0000000..3119955 --- /dev/null +++ b/modules/inventory_transaction/repository/inventory_transaction_repository.go @@ -0,0 +1,83 @@ +package repository + +import ( + "context" + + "github.com/Caknoooo/go-gin-clean-starter/database/entities" + "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_transaction/query" + "gorm.io/gorm" +) + +type InventoryTransactionRepository interface { + Create(ctx context.Context, tx *gorm.DB, inventoryTransaction entities.InventoryTransactionEntity) (entities.InventoryTransactionEntity, error) + GetById(ctx context.Context, tx *gorm.DB, inventoryTransactionId string) (entities.InventoryTransactionEntity, 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) + Delete(ctx context.Context, tx *gorm.DB, inventoryTransactionId string) error +} + +type inventoryTransactionRepository struct { + db *gorm.DB +} + +func NewInventoryTransactionRepository(db *gorm.DB) InventoryTransactionRepository { + return &inventoryTransactionRepository{db: db} +} + +func (r *inventoryTransactionRepository) Create(ctx context.Context, tx *gorm.DB, inventoryTransaction entities.InventoryTransactionEntity) (entities.InventoryTransactionEntity, error) { + if tx == nil { + tx = r.db + } + if err := tx.WithContext(ctx).Create(&inventoryTransaction).Error; err != nil { + return inventoryTransaction, err + } + return inventoryTransaction, nil +} + +func (r *inventoryTransactionRepository) GetById(ctx context.Context, tx *gorm.DB, inventoryTransactionId string) (entities.InventoryTransactionEntity, error) { + if tx == nil { + tx = r.db + } + var inventoryTransaction entities.InventoryTransactionEntity + if err := tx.WithContext(ctx). + Preload("Client"). + Preload("Product"). + Preload("Aisle"). + Preload("InvReceipt"). + Preload("InvIssue"). + Preload("InvMove"). + First(&inventoryTransaction, "id = ?", inventoryTransactionId).Error; err != nil { + return inventoryTransaction, err + } + return inventoryTransaction, nil +} + +func (r *inventoryTransactionRepository) GetAll(ctx context.Context, filter query.InventoryTransactionFilter) ([]entities.InventoryTransactionEntity, int64, error) { + db := query.ApplyInventoryTransactionFilters(r.db, filter) + var inventoryTransactions []entities.InventoryTransactionEntity + var total int64 + if err := db.WithContext(ctx).Find(&inventoryTransactions).Count(&total).Error; err != nil { + return nil, 0, err + } + return inventoryTransactions, total, nil +} + +func (r *inventoryTransactionRepository) Update(ctx context.Context, tx *gorm.DB, inventoryTransaction entities.InventoryTransactionEntity) (entities.InventoryTransactionEntity, error) { + if tx == nil { + tx = r.db + } + if err := tx.WithContext(ctx).Save(&inventoryTransaction).Error; err != nil { + return inventoryTransaction, err + } + return inventoryTransaction, nil +} + +func (r *inventoryTransactionRepository) Delete(ctx context.Context, tx *gorm.DB, inventoryTransactionId string) error { + if tx == nil { + tx = r.db + } + if err := tx.WithContext(ctx).Delete(&entities.InventoryTransactionEntity{}, "id = ?", inventoryTransactionId).Error; err != nil { + return err + } + return nil +} diff --git a/modules/inventory_transaction/routes.go b/modules/inventory_transaction/routes.go new file mode 100644 index 0000000..ff56927 --- /dev/null +++ b/modules/inventory_transaction/routes.go @@ -0,0 +1,24 @@ +package inventorytransaction + +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_transaction/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) { + inventoryTransactionController := do.MustInvoke[controller.InventoryTransactionController](injector) + jwtService := do.MustInvokeNamed[service.JWTService](injector, constants.JWTService) + + inventoryTransactionRoutes := server.Group("/api/v1/inventory-transactions") + { + inventoryTransactionRoutes.POST("", middlewares.Authenticate(jwtService), inventoryTransactionController.Create) + inventoryTransactionRoutes.GET("/:id", middlewares.Authenticate(jwtService), inventoryTransactionController.GetById) + inventoryTransactionRoutes.PUT("/:id", middlewares.Authenticate(jwtService), inventoryTransactionController.Update) + inventoryTransactionRoutes.DELETE("/:id", middlewares.Authenticate(jwtService), inventoryTransactionController.Delete) + inventoryTransactionRoutes.GET("", middlewares.Authenticate(jwtService), inventoryTransactionController.GetAll) + } +} diff --git a/modules/inventory_transaction/service/inventory_transaction_service.go b/modules/inventory_transaction/service/inventory_transaction_service.go new file mode 100644 index 0000000..64cbc83 --- /dev/null +++ b/modules/inventory_transaction/service/inventory_transaction_service.go @@ -0,0 +1,199 @@ +package service + +import ( + "context" + + "github.com/Caknoooo/go-gin-clean-starter/database/entities" + dtodomain "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_transaction/dto" + "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_transaction/query" + "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_transaction/repository" + "github.com/Caknoooo/go-gin-clean-starter/pkg/utils" + "github.com/google/uuid" + "gorm.io/gorm" +) + +type InventoryTransactionService interface { + Create(ctx context.Context, req dtodomain.InventoryTransactionCreateRequest) (dtodomain.InventoryTransactionResponse, error) + GetById(ctx context.Context, inventoryTransactionId string) (dtodomain.InventoryTransactionResponse, error) + GetAll(ctx context.Context, filter query.InventoryTransactionFilter) ([]dtodomain.InventoryTransactionResponse, int64, error) + Update(ctx context.Context, req dtodomain.InventoryTransactionUpdateRequest, inventoryTransactionId string) (dtodomain.InventoryTransactionResponse, error) + Delete(ctx context.Context, inventoryTransactionId string) error +} + +type inventoryTransactionService struct { + db *gorm.DB + inventoryTransactionRepo repository.InventoryTransactionRepository +} + +func (s *inventoryTransactionService) Create(ctx context.Context, req dtodomain.InventoryTransactionCreateRequest) (dtodomain.InventoryTransactionResponse, 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.InventoryTransactionResponse{}, err + } + productUUID, err := uuid.Parse(req.ProductID) + if err != nil { + tx.Rollback() + return dtodomain.InventoryTransactionResponse{}, err + } + var aisleUUID, invReceiptUUID, invIssueUUID, invMoveUUID *uuid.UUID + if req.AisleID != "" { + tmp, err := uuid.Parse(req.AisleID) + if err == nil { + aisleUUID = &tmp + } + } + if req.InvReceiptID != "" { + tmp, err := uuid.Parse(req.InvReceiptID) + if err == nil { + invReceiptUUID = &tmp + } + } + if req.InvIssueID != "" { + tmp, err := uuid.Parse(req.InvIssueID) + if err == nil { + invIssueUUID = &tmp + } + } + if req.InvMoveID != "" { + tmp, err := uuid.Parse(req.InvMoveID) + if err == nil { + invMoveUUID = &tmp + } + } + transactionDate := utils.StringToDateTime(req.TransactionDate) + entity := entities.InventoryTransactionEntity{ + TransactionType: req.TransactionType, + TransactionDate: transactionDate, + ProductID: productUUID, + AisleID: aisleUUID, + ClientID: clientUUID, + InvReceiptID: invReceiptUUID, + InvIssueID: invIssueUUID, + InvMoveID: invMoveUUID, + } + created, err := s.inventoryTransactionRepo.Create(ctx, tx, entity) + if err != nil { + tx.Rollback() + return dtodomain.InventoryTransactionResponse{}, err + } + tx.Commit() + result, err := s.inventoryTransactionRepo.GetById(ctx, nil, created.ID.String()) + if err != nil { + return dtodomain.InventoryTransactionResponse{}, err + } + return dtodomain.ToInventoryTransactionResponse(result), nil +} + +func (s *inventoryTransactionService) GetById(ctx context.Context, inventoryTransactionId string) (dtodomain.InventoryTransactionResponse, error) { + entity, err := s.inventoryTransactionRepo.GetById(ctx, nil, inventoryTransactionId) + if err != nil { + return dtodomain.InventoryTransactionResponse{}, err + } + return dtodomain.ToInventoryTransactionResponse(entity), nil +} + +func (s *inventoryTransactionService) GetAll(ctx context.Context, filter query.InventoryTransactionFilter) ([]dtodomain.InventoryTransactionResponse, int64, error) { + entities, total, err := s.inventoryTransactionRepo.GetAll(ctx, filter) + if err != nil { + return nil, 0, err + } + var responses []dtodomain.InventoryTransactionResponse + for _, e := range entities { + responses = append(responses, dtodomain.ToInventoryTransactionResponse(e)) + } + if responses == nil { + responses = make([]dtodomain.InventoryTransactionResponse, 0) + } + return responses, total, nil +} + +func (s *inventoryTransactionService) Update(ctx context.Context, req dtodomain.InventoryTransactionUpdateRequest, inventoryTransactionId string) (dtodomain.InventoryTransactionResponse, error) { + tx := s.db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + entity, err := s.inventoryTransactionRepo.GetById(ctx, tx, inventoryTransactionId) + if err != nil { + tx.Rollback() + return dtodomain.InventoryTransactionResponse{}, err + } + if req.TransactionType != "" { + entity.TransactionType = req.TransactionType + } + if req.TransactionDate != "" { + transactionDate := utils.StringToDateTime(req.TransactionDate) + entity.TransactionDate = transactionDate + } + if req.ProductID != "" { + productUUID, err := uuid.Parse(req.ProductID) + if err == nil { + entity.ProductID = productUUID + } + } + if req.AisleID != "" { + aisleUUID, err := uuid.Parse(req.AisleID) + if err == nil { + entity.AisleID = &aisleUUID + } + } + if req.InvReceiptID != "" { + invReceiptUUID, err := uuid.Parse(req.InvReceiptID) + if err == nil { + entity.InvReceiptID = &invReceiptUUID + } + } + if req.InvIssueID != "" { + invIssueUUID, err := uuid.Parse(req.InvIssueID) + if err == nil { + entity.InvIssueID = &invIssueUUID + } + } + if req.InvMoveID != "" { + invMoveUUID, err := uuid.Parse(req.InvMoveID) + if err == nil { + entity.InvMoveID = &invMoveUUID + } + } + updated, err := s.inventoryTransactionRepo.Update(ctx, tx, entity) + if err != nil { + tx.Rollback() + return dtodomain.InventoryTransactionResponse{}, err + } + tx.Commit() + result, err := s.inventoryTransactionRepo.GetById(ctx, nil, updated.ID.String()) + if err != nil { + return dtodomain.InventoryTransactionResponse{}, err + } + return dtodomain.ToInventoryTransactionResponse(result), nil +} + +func (s *inventoryTransactionService) Delete(ctx context.Context, inventoryTransactionId string) error { + tx := s.db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + if err := s.inventoryTransactionRepo.Delete(ctx, tx, inventoryTransactionId); err != nil { + tx.Rollback() + return err + } + tx.Commit() + return nil +} + +func NewInventoryTransactionService(db *gorm.DB, repo repository.InventoryTransactionRepository) InventoryTransactionService { + return &inventoryTransactionService{ + db: db, + inventoryTransactionRepo: repo, + } +} diff --git a/pkg/dto/response.go b/pkg/dto/response.go index 8ddbf4d..ad91519 100644 --- a/pkg/dto/response.go +++ b/pkg/dto/response.go @@ -30,4 +30,9 @@ type ( ID string `json:"id"` Name string `json:"name"` } + + InventoryResponse struct { + ID string `json:"id"` + DocumentNumber string `json:"document_number"` + } ) diff --git a/providers/core.go b/providers/core.go index eed4a71..94a0045 100644 --- a/providers/core.go +++ b/providers/core.go @@ -92,6 +92,11 @@ import ( inventoryStorageRepo "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_storage/repository" inventoryStorageService "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_storage/service" + inventoryTransactionController "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_transaction/controller" + inventoryTransactionRepo "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_transaction/repository" + inventoryTransactionService "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_transaction/service" + + "github.com/Caknoooo/go-gin-clean-starter/modules/user/controller" "github.com/Caknoooo/go-gin-clean-starter/modules/user/repository" userService "github.com/Caknoooo/go-gin-clean-starter/modules/user/service" @@ -154,6 +159,7 @@ func RegisterDependencies(injector *do.Injector) { inventoryMovementRepository := inventoryMovementRepo.NewInventoryMovementRepository(db) inventoryMovementLineRepository := inventoryMovementLineRepo.NewInventoryMovementLineRepository(db) inventoryStorageRepository := inventoryStorageRepo.NewInventoryStorageRepository(db) + inventoryTransactionRepository := inventoryTransactionRepo.NewInventoryTransactionRepository(db) // Service userServ := userService.NewUserService(userRepository, roleRepository, warehouseRepository, refreshTokenRepository, jwtService, db) @@ -176,6 +182,7 @@ func RegisterDependencies(injector *do.Injector) { inventoryReturnServ := inventoryReturnService.NewInventoryReturnService(db, inventoryReturnRepository, inventoryReturnLineRepository, inventoryIssueLineRepository, productRepository) inventoryMovementServ := inventoryMovementService.NewInventoryMovementService(db, inventoryMovementRepository, inventoryMovementLineRepository) inventoryStorageService := inventoryStorageService.NewInventoryStorageService(db, inventoryStorageRepository) + inventoryTransactionServ := inventoryTransactionService.NewInventoryTransactionService(db, inventoryTransactionRepository) // Controller do.Provide( @@ -288,4 +295,9 @@ func RegisterDependencies(injector *do.Injector) { return inventoryStorageController.NewInventoryStorageController(i, inventoryStorageService), nil }, ) + do.Provide( + injector, func(i *do.Injector) (inventoryTransactionController.InventoryTransactionController, error) { + return inventoryTransactionController.NewInventoryTransactionController(i, inventoryTransactionServ), nil + }, + ) }