diff --git a/cmd/main.go b/cmd/main.go index 9239d0f..e5fa521 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -14,6 +14,7 @@ import ( "github.com/Caknoooo/go-gin-clean-starter/modules/auth" "github.com/Caknoooo/go-gin-clean-starter/modules/category" "github.com/Caknoooo/go-gin-clean-starter/modules/client" + inventoryissue "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue" inventoryreceipt "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_receipt" inventoryrequest "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_request" maintenancegroup "github.com/Caknoooo/go-gin-clean-starter/modules/maintenance_group" @@ -167,6 +168,7 @@ func main() { inventoryreceipt.RegisterRoutes(server, injector) assignment.RegisterRoutes(server, injector) inventoryrequest.RegisterRoutes(server, injector) + inventoryissue.RegisterRoutes(server, injector) // register swagger route server.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) diff --git a/database/entities/t_inventory_issue_entity.go b/database/entities/t_inventory_issue_entity.go index 7fdcd72..30677ab 100644 --- a/database/entities/t_inventory_issue_entity.go +++ b/database/entities/t_inventory_issue_entity.go @@ -1,9 +1,12 @@ package entities import ( + "fmt" + "strings" "time" "github.com/google/uuid" + "gorm.io/gorm" ) type TInventoryIssueEntity struct { @@ -11,15 +14,17 @@ type TInventoryIssueEntity struct { DocumentNumber string `gorm:"type:varchar(100);" json:"document_number"` DocumentDate time.Time `gorm:"type:timestamp;" json:"document_date"` DueDate time.Time `gorm:"type:timestamp;" json:"due_date"` - Status string `gorm:"type:varchar(50);" json:"status"` + Status string `gorm:"type:varchar(50);default:'draft'" json:"status"` IssuerBy uuid.UUID `gorm:"type:uuid;index;" json:"issuer_by"` InvRequestID uuid.UUID `gorm:"type:uuid;index;" json:"inv_request_id"` ClientID uuid.UUID `gorm:"type:uuid;index;" json:"client_id"` - Issuer M_User `gorm:"foreignKey:IssuerBy;references:ID"` - InvRequest TInventoryRequestEntity `gorm:"foreignKey:InvRequestID;references:ID"` - Client M_Client `gorm:"foreignKey:ClientID;references:ID"` + Issuer M_User `gorm:"foreignKey:IssuerBy;references:ID"` + InvRequest TInventoryRequestEntity `gorm:"foreignKey:InvRequestID;references:ID"` + IssueLines []TInventoryIssueLineEntity `gorm:"foreignKey:InvIssueID;references:ID"` + Assignment TAssignmentEntity `gorm:"-"` + Client M_Client `gorm:"foreignKey:ClientID;references:ID"` FullAuditTrail } @@ -27,3 +32,44 @@ type TInventoryIssueEntity struct { func (TInventoryIssueEntity) TableName() string { return "t_inventory_issues" } + +// GenerateDocumentNumber generates a new document number for a client +func GenerateDocumentNumberInvIssue(db *gorm.DB, clientId string) (string, error) { + prefix := "ISSUE" + + // Ambil nama client berdasarkan clientId + var client struct { + Name string + } + if err := db.Table("m_clients").Select("name").Where("id = ?", clientId).First(&client).Error; err != nil { + return "", fmt.Errorf("client not found") + } + if client.Name == "" { + return "", fmt.Errorf("client name is empty") + } + words := strings.Fields(client.Name) + initials := "" + for _, w := range words { + if len(w) > 0 { + initials += strings.ToUpper(string(w[0])) + } + } + // Cari document number terakhir untuk client ini + var lastReceipt TInventoryIssueEntity + err := db. + Where("client_id = ?", clientId). + Order("document_number DESC"). + First(&lastReceipt).Error + + seq := 1 + if err == nil && lastReceipt.DocumentNumber != "" { + parts := strings.Split(lastReceipt.DocumentNumber, "-") + if len(parts) == 3 { + fmt.Sscanf(parts[2], "%d", &seq) + seq++ + } + } + + docNum := fmt.Sprintf("%s-%s-%04d", prefix, initials, seq) + return docNum, nil +} diff --git a/database/entities/t_inventory_issue_line_entity.go b/database/entities/t_inventory_issue_line_entity.go index a3e2c03..dba86dd 100644 --- a/database/entities/t_inventory_issue_line_entity.go +++ b/database/entities/t_inventory_issue_line_entity.go @@ -12,13 +12,13 @@ type TInventoryIssueLineEntity struct { IssuedQuantity float64 `gorm:"type:numeric;default:0" json:"issued_quantity"` Remarks string `gorm:"type:text;" json:"remarks"` - IssueID uuid.UUID `gorm:"type:uuid;index;" json:"issue_id"` + InvIssueID uuid.UUID `gorm:"type:uuid;index;" json:"inv_issue_id"` ProductID uuid.UUID `gorm:"type:uuid;index;" json:"product_id"` WarehouseID uuid.UUID `gorm:"type:uuid;index;" json:"warehouse_id"` ClientID uuid.UUID `gorm:"type:uuid;index;" json:"client_id"` Product MProductEntity `gorm:"foreignKey:ProductID;references:ID"` - Issue TInventoryIssueEntity `gorm:"foreignKey:IssueID;references:ID"` + InvIssue TInventoryIssueEntity `gorm:"foreignKey:InvIssueID;references:ID"` Warehouse MWarehouseEntity `gorm:"foreignKey:WarehouseID;references:ID"` Client M_Client `gorm:"foreignKey:ClientID;references:ID"` diff --git a/database/entities/t_inventory_receipt_entity.go b/database/entities/t_inventory_receipt_entity.go index 25bfdc4..26fcfec 100644 --- a/database/entities/t_inventory_receipt_entity.go +++ b/database/entities/t_inventory_receipt_entity.go @@ -22,7 +22,7 @@ type TInventoryReceiptEntity struct { ReceiptLines []TInventoryReceiptLineEntity `gorm:"foreignKey:InvReceiptID;references:ID"` Client M_Client `gorm:"foreignKey:ClientID;references:ID"` - Assignment TAssignmentEntity `gorm:"foreignKey:DocumentID;references:ID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` + Assignment TAssignmentEntity `gorm:"-"` FullAuditTrail } diff --git a/database/entities/t_inventory_request_entity.go b/database/entities/t_inventory_request_entity.go index 41613b0..ca783a2 100644 --- a/database/entities/t_inventory_request_entity.go +++ b/database/entities/t_inventory_request_entity.go @@ -21,7 +21,7 @@ type TInventoryRequestEntity struct { ClientID uuid.UUID `gorm:"type:uuid;index;" json:"client_id"` RequestLines []TInventoryRequestLineEntity `gorm:"foreignKey:InvRequestID;references:ID"` - Assignment TAssignmentEntity `gorm:"foreignKey:DocumentID;references:ID"` + Assignment TAssignmentEntity `gorm:"-"` Client M_Client `gorm:"foreignKey:ClientID;references:ID"` FullAuditTrail diff --git a/database/migration.go b/database/migration.go index 30f5616..2e70192 100644 --- a/database/migration.go +++ b/database/migration.go @@ -34,8 +34,8 @@ func Migrate(db *gorm.DB) error { &entities.TInventoryReceiptLineEntity{}, &entities.TInventoryRequestEntity{}, &entities.TInventoryRequestLineEntity{}, - // &entities.TInventoryIssueEntity{}, - // &entities.TInventoryIssueLineEntity{}, + &entities.TInventoryIssueEntity{}, + &entities.TInventoryIssueLineEntity{}, // &entities.InventoryTransactionEntity{}, // &entities.InventoryStorageEntity{}, ); err != nil { @@ -68,15 +68,15 @@ func MigrateFresh(db *gorm.DB) error { // &entities.MCrossReferenceEntity{}, // &entities.MWarehouseEntity{}, // &entities.MZonaEntity{}, - &entities.MAisleEntity{}, - // &entities.TAssignmentEntity{}, - // &entities.TAssignmentUserEntity{}, + // &entities.MAisleEntity{}, + &entities.TAssignmentEntity{}, + &entities.TAssignmentUserEntity{}, // &entities.TInventoryReceiptEntity{}, // &entities.TInventoryReceiptLineEntity{}, - &entities.TInventoryRequestEntity{}, - &entities.TInventoryRequestLineEntity{}, - // &entities.TInventoryIssueEntity{}, - // &entities.TInventoryIssueLineEntity{}, + // &entities.TInventoryRequestEntity{}, + // &entities.TInventoryRequestLineEntity{}, + &entities.TInventoryIssueEntity{}, + &entities.TInventoryIssueLineEntity{}, // &entities.InventoryTransactionEntity{}, // &entities.InventoryStorageEntity{}, ); err != nil { diff --git a/modules/inventory_issue/controller/inventory_issue_controller.go b/modules/inventory_issue/controller/inventory_issue_controller.go new file mode 100644 index 0000000..407bed5 --- /dev/null +++ b/modules/inventory_issue/controller/inventory_issue_controller.go @@ -0,0 +1,179 @@ +package controller + +import ( + "net/http" + + "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue/dto" + "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue/query" + "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue/service" + "github.com/Caknoooo/go-gin-clean-starter/pkg/constants" + "github.com/Caknoooo/go-gin-clean-starter/pkg/utils" + "github.com/gin-gonic/gin" + "github.com/samber/do" + "gorm.io/gorm" +) + +type InventoryIssueController 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 inventoryIssueController struct { + issueService service.InventoryIssueService + db *gorm.DB +} + +func NewInventoryIssueController(i *do.Injector, issueService service.InventoryIssueService) InventoryIssueController { + db := do.MustInvokeNamed[*gorm.DB](i, constants.DB) + return &inventoryIssueController{ + issueService: issueService, + db: db, + } +} + +func (c *inventoryIssueController) Create(ctx *gin.Context) { + var req dto.InventoryIssueCreateRequest + 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.issueService.Create(ctx, req) + if err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_INVENTORY_ISSUE, err.Error(), nil) + ctx.JSON(http.StatusInternalServerError, res) + return + } + res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_CREATE_INVENTORY_ISSUE, created) + ctx.JSON(http.StatusOK, res) +} + +func (c *inventoryIssueController) Update(ctx *gin.Context) { + id := ctx.Param("id") + var req dto.InventoryIssueUpdateRequest + 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.issueService.Update(ctx, req, id) + if err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_UPDATE_INVENTORY_ISSUE, err.Error(), nil) + ctx.JSON(http.StatusInternalServerError, res) + return + } + res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_UPDATE_INVENTORY_ISSUE, updated) + ctx.JSON(http.StatusOK, res) +} + +func (c *inventoryIssueController) Delete(ctx *gin.Context) { + id := ctx.Param("id") + if err := c.issueService.Delete(ctx, id); err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_DELETE_INVENTORY_ISSUE, err.Error(), nil) + ctx.JSON(http.StatusInternalServerError, res) + return + } + res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_DELETE_INVENTORY_ISSUE, nil) + ctx.JSON(http.StatusOK, res) +} + +func (c *inventoryIssueController) GetById(ctx *gin.Context) { + id := ctx.Param("id") + result, err := c.issueService.GetById(ctx, id) + if err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_INVENTORY_ISSUE, err.Error(), nil) + ctx.JSON(http.StatusInternalServerError, res) + return + } + res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_INVENTORY_ISSUE, result) + ctx.JSON(http.StatusOK, res) +} + +func (c *inventoryIssueController) GetAll(ctx *gin.Context) { + clientId := ctx.MustGet("client_id").(string) + var filter query.InventoryIssueFilter + filter.ClientID = clientId + if err := ctx.ShouldBindQuery(&filter); err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_INVENTORY_ISSUE, err.Error(), nil) + ctx.JSON(http.StatusBadRequest, res) + return + } + getAll := ctx.Query("get_all") + if getAll != "" { + issues, _, err := c.issueService.GetAll(ctx, filter) + if err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_INVENTORY_ISSUE, err.Error(), nil) + ctx.JSON(http.StatusBadRequest, res) + return + } + response := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_INVENTORY_ISSUE, issues) + 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 + issues, total, err := c.issueService.GetAll(ctx, filter) + if err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_INVENTORY_ISSUE, 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_ISSUE, issues, paginationResponse) + ctx.JSON(http.StatusOK, res) +} + +func (c *inventoryIssueController) CreateLine(ctx *gin.Context) { + issueId := ctx.Param("id") + var req dto.InventoryIssueLineCreateRequest + 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.issueService.CreateLine(ctx, issueId, req) + if err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_INVENTORY_ISSUE_LINE, err.Error(), nil) + ctx.JSON(http.StatusInternalServerError, res) + return + } + res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_CREATE_INVENTORY_ISSUE_LINE, created) + ctx.JSON(http.StatusOK, res) +} + +func (c *inventoryIssueController) UpdateLine(ctx *gin.Context) { + id := ctx.Param("id") + var req dto.InventoryIssueLineUpdateRequest + 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.issueService.UpdateLine(ctx, id, req) + if err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_UPDATE_INVENTORY_ISSUE_LINE, err.Error(), nil) + ctx.JSON(http.StatusInternalServerError, res) + return + } + res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_UPDATE_INVENTORY_ISSUE_LINE, updated) + ctx.JSON(http.StatusOK, res) +} + +func (c *inventoryIssueController) DeleteLine(ctx *gin.Context) { + id := ctx.Param("id") + if err := c.issueService.DeleteLine(ctx, id); err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_DELETE_INVENTORY_ISSUE_LINE, err.Error(), nil) + ctx.JSON(http.StatusInternalServerError, res) + return + } + res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_DELETE_INVENTORY_ISSUE_LINE, nil) + ctx.JSON(http.StatusOK, res) +} diff --git a/modules/inventory_issue/dto/inventory_issue_dto.go b/modules/inventory_issue/dto/inventory_issue_dto.go new file mode 100644 index 0000000..051c023 --- /dev/null +++ b/modules/inventory_issue/dto/inventory_issue_dto.go @@ -0,0 +1,107 @@ +package dto + +import pkgdto "github.com/Caknoooo/go-gin-clean-starter/pkg/dto" + +const ( + MESSAGE_FAILED_CREATE_INVENTORY_ISSUE = "failed create inventory issue" + MESSAGE_FAILED_CREATE_INVENTORY_ISSUE_LINE = "failed create inventory issue line" + MESSAGE_SUCCESS_CREATE_INVENTORY_ISSUE = "success create inventory issue" + MESSAGE_SUCCESS_CREATE_INVENTORY_ISSUE_LINE = "success create inventory issue line" + MESSAGE_FAILED_GET_INVENTORY_ISSUE = "failed get inventory issue" + MESSAGE_SUCCESS_GET_INVENTORY_ISSUE = "success get inventory issue" + MESSAGE_FAILED_UPDATE_INVENTORY_ISSUE = "failed update inventory issue" + MESSAGE_FAILED_UPDATE_INVENTORY_ISSUE_LINE = "failed update inventory issue line" + MESSAGE_SUCCESS_UPDATE_INVENTORY_ISSUE = "success update inventory issue" + MESSAGE_SUCCESS_UPDATE_INVENTORY_ISSUE_LINE = "success update inventory issue line" + MESSAGE_FAILED_DELETE_INVENTORY_ISSUE = "failed delete inventory issue" + MESSAGE_FAILED_DELETE_INVENTORY_ISSUE_LINE = "failed delete inventory issue line" + MESSAGE_SUCCESS_DELETE_INVENTORY_ISSUE = "success delete inventory issue" + MESSAGE_SUCCESS_DELETE_INVENTORY_ISSUE_LINE = "success delete inventory issue line" + MESSAGE_FAILED_GET_DATA_FROM_BODY = "failed get data from body" +) + +type InventoryIssueCreateRequest struct { + DocumentDate string `json:"document_date"` + DueDate string `json:"due_date"` + IssuerBy string `json:"issuer_by"` + InvRequestID string `json:"inv_request_id"` + ClientID string `json:"client_id" binding:"required"` + Status string `json:"status" binding:"required"` + IssueLines []InventoryIssueLineCreateRequest `json:"issue_lines,omitempty" binding:"dive"` +} + +type InventoryIssueLineCreateRequest struct { + CurrentStock float64 `json:"current_stock"` + MinStock float64 `json:"min_stock"` + RequestQuantity float64 `json:"request_quantity"` + IssuedQuantity float64 `json:"issued_quantity"` + Remarks string `json:"remarks"` + ProductID string `json:"product_id"` + WarehouseID string `json:"warehouse_id"` + ClientID string `json:"client_id"` +} + +type InventoryIssueUpdateRequest struct { + DocumentDate string `json:"document_date"` + DueDate string `json:"due_date"` + Status string `json:"status"` +} + +type InventoryIssueLineUpdateRequest struct { + CurrentStock *float64 `json:"current_stock"` + MinStock *float64 `json:"min_stock"` + RequestQuantity *float64 `json:"request_quantity"` + IssuedQuantity *float64 `json:"issued_quantity"` + Remarks *string `json:"remarks"` + ProductID *string `json:"product_id"` + WarehouseID *string `json:"warehouse_id"` + ClientID *string `json:"client_id"` +} + +type InventoryIssueResponse struct { + ID string `json:"id"` + DocumentNumber string `json:"document_number"` + DocumentDate string `json:"document_date"` + DueDate string `json:"due_date"` + Status string `json:"status"` + IssuerBy pkgdto.IdNameResponse `json:"issuer_by"` + InvRequest pkgdto.IdNameResponse `json:"inv_request"` + Client pkgdto.IdNameResponse `json:"client"` + LineCount int `json:"line_count"` + IssueLines []InventoryIssueLineResponse `json:"issue_lines"` + Assignment AssignmentResponse `json:"assignment,omitempty"` +} + +type InventoryIssueLineResponse struct { + ID string `json:"id"` + CurrentStock float64 `json:"current_stock"` + MinStock float64 `json:"min_stock"` + RequestQuantity float64 `json:"request_quantity"` + IssuedQuantity float64 `json:"issued_quantity"` + Remarks string `json:"remarks"` + Product InventoryIssueProductResponse `json:"product"` + Warehouse pkgdto.IdNameResponse `json:"warehouse"` + Client pkgdto.IdNameResponse `json:"client"` +} + +type InventoryIssueProductResponse struct { + ID string `json:"id"` + RefNumber string `json:"ref_number"` + Name string `json:"name"` +} + +type AssignmentResponse struct { + ID string `json:"id"` + DocumentType string `json:"document_type"` + DocumentID string `json:"document_id"` + // Client pkgdto.IdNameResponse `json:"client"` + AssignmentUsers []AssignmentUserResponse `json:"assignment_users"` +} + +type AssignmentUserResponse struct { + ID string `json:"id"` + TaskType string `json:"task_type"` + User pkgdto.IdNameResponse `json:"user"` + Role pkgdto.IdNameResponse `json:"role"` + // Client pkgdto.IdNameResponse `json:"client"` +} diff --git a/modules/inventory_issue/query/inventory_issue_query.go b/modules/inventory_issue/query/inventory_issue_query.go new file mode 100644 index 0000000..81e9c3f --- /dev/null +++ b/modules/inventory_issue/query/inventory_issue_query.go @@ -0,0 +1,22 @@ +package query + +import ( + "gorm.io/gorm" +) + +type InventoryIssueFilter struct { + ClientID string `form:"client_id"` + IssuerBy string `form:"issuer_by"` + PerPage int `form:"per_page"` + Page int `form:"page"` +} + +func ApplyInventoryIssueFilters(db *gorm.DB, filter InventoryIssueFilter) *gorm.DB { + if filter.ClientID != "" { + db = db.Where("client_id = ?", filter.ClientID) + } + if filter.IssuerBy != "" { + db = db.Where("issuer_by = ?", filter.IssuerBy) + } + return db +} diff --git a/modules/inventory_issue/repository/inventory_issue_line_repository.go b/modules/inventory_issue/repository/inventory_issue_line_repository.go new file mode 100644 index 0000000..4d96005 --- /dev/null +++ b/modules/inventory_issue/repository/inventory_issue_line_repository.go @@ -0,0 +1,89 @@ +package repository + +import ( + "context" + + "github.com/Caknoooo/go-gin-clean-starter/database/entities" + "gorm.io/gorm" +) + +type InventoryIssueLineRepository interface { + Create(ctx context.Context, tx *gorm.DB, line entities.TInventoryIssueLineEntity) (entities.TInventoryIssueLineEntity, error) + GetById(ctx context.Context, tx *gorm.DB, id string) (entities.TInventoryIssueLineEntity, error) + GetAllByIssueId(ctx context.Context, issueId string) ([]entities.TInventoryIssueLineEntity, error) + Update(ctx context.Context, tx *gorm.DB, line entities.TInventoryIssueLineEntity) (entities.TInventoryIssueLineEntity, error) + Delete(ctx context.Context, tx *gorm.DB, id string) error + BulkCreate(ctx context.Context, tx *gorm.DB, lines []entities.TInventoryIssueLineEntity) error + DeleteByIssueId(ctx context.Context, tx *gorm.DB, issueId string) error +} + +type inventoryIssueLineRepository struct { + db *gorm.DB +} + +func NewInventoryIssueLineRepository(db *gorm.DB) InventoryIssueLineRepository { + return &inventoryIssueLineRepository{db: db} +} + +func (r *inventoryIssueLineRepository) Create(ctx context.Context, tx *gorm.DB, line entities.TInventoryIssueLineEntity) (entities.TInventoryIssueLineEntity, 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 *inventoryIssueLineRepository) GetById(ctx context.Context, tx *gorm.DB, id string) (entities.TInventoryIssueLineEntity, error) { + if tx == nil { + tx = r.db + } + var line entities.TInventoryIssueLineEntity + if err := tx.WithContext(ctx).Preload("Product").Preload("Warehouse").Preload("Issue").Preload("Client").First(&line, "id = ?", id).Error; err != nil { + return line, err + } + return line, nil +} + +func (r *inventoryIssueLineRepository) GetAllByIssueId(ctx context.Context, issueId string) ([]entities.TInventoryIssueLineEntity, error) { + var lines []entities.TInventoryIssueLineEntity + if err := r.db.WithContext(ctx).Where("issue_id = ?", issueId).Preload("Product").Preload("Warehouse").Preload("Issue").Preload("Client").Find(&lines).Error; err != nil { + return lines, err + } + return lines, nil +} + +func (r *inventoryIssueLineRepository) Update(ctx context.Context, tx *gorm.DB, line entities.TInventoryIssueLineEntity) (entities.TInventoryIssueLineEntity, error) { + if tx == nil { + tx = r.db + } + if err := tx.WithContext(ctx).Model(&entities.TInventoryIssueLineEntity{}).Where("id = ?", line.ID).Select("*").Updates(&line).Error; err != nil { + return line, err + } + return line, nil +} + +func (r *inventoryIssueLineRepository) Delete(ctx context.Context, tx *gorm.DB, id string) error { + if tx == nil { + tx = r.db + } + if err := tx.WithContext(ctx).Delete(&entities.TInventoryIssueLineEntity{}, "id = ?", id).Error; err != nil { + return err + } + return nil +} + +func (r *inventoryIssueLineRepository) BulkCreate(ctx context.Context, tx *gorm.DB, lines []entities.TInventoryIssueLineEntity) error { + if tx == nil { + tx = r.db + } + return tx.WithContext(ctx).Create(&lines).Error +} + +func (r *inventoryIssueLineRepository) DeleteByIssueId(ctx context.Context, tx *gorm.DB, issueId string) error { + if tx == nil { + tx = r.db + } + return tx.WithContext(ctx).Where("issue_id = ?", issueId).Delete(&entities.TInventoryIssueLineEntity{}).Error +} diff --git a/modules/inventory_issue/repository/inventory_issue_repository.go b/modules/inventory_issue/repository/inventory_issue_repository.go new file mode 100644 index 0000000..3c0c9dc --- /dev/null +++ b/modules/inventory_issue/repository/inventory_issue_repository.go @@ -0,0 +1,100 @@ +package repository + +import ( + "context" + + "github.com/Caknoooo/go-gin-clean-starter/database/entities" + "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue/query" + "gorm.io/gorm" +) + +type InventoryIssueRepository interface { + Create(ctx context.Context, tx *gorm.DB, issue entities.TInventoryIssueEntity) (entities.TInventoryIssueEntity, error) + GetById(ctx context.Context, tx *gorm.DB, id string) (entities.TInventoryIssueEntity, error) + GetAll(ctx context.Context, filter query.InventoryIssueFilter) ([]entities.TInventoryIssueEntity, int64, error) + Update(ctx context.Context, tx *gorm.DB, issue entities.TInventoryIssueEntity) (entities.TInventoryIssueEntity, error) + Delete(ctx context.Context, tx *gorm.DB, id string) error +} + +type inventoryIssueRepository struct { + db *gorm.DB +} + +func NewInventoryIssueRepository(db *gorm.DB) InventoryIssueRepository { + return &inventoryIssueRepository{db: db} +} + +func (r *inventoryIssueRepository) Create(ctx context.Context, tx *gorm.DB, issue entities.TInventoryIssueEntity) (entities.TInventoryIssueEntity, error) { + if tx == nil { + tx = r.db + } + if err := tx.WithContext(ctx).Create(&issue).Error; err != nil { + return issue, err + } + return issue, nil +} + +func (r *inventoryIssueRepository) GetById(ctx context.Context, tx *gorm.DB, id string) (entities.TInventoryIssueEntity, error) { + if tx == nil { + tx = r.db + } + var issue entities.TInventoryIssueEntity + if err := tx.WithContext(ctx). + Preload("Client"). + Preload("IssueLines"). + Preload("IssueLines.Client"). + Preload("IssueLines.Product"). + Preload("IssueLines.Warehouse"). + Preload("Issuer"). + Preload("InvRequest"). + First(&issue, "id = ?", id).Error; err != nil { + return issue, err + } + + // Ambil assignment manual + var assignment entities.TAssignmentEntity + if err := tx.WithContext(ctx). + Preload("AssignmentUsers"). + Preload("AssignmentUsers.User"). + Preload("AssignmentUsers.Role"). + First(&assignment, "document_id = ? AND document_type = ?", issue.ID, "InventoryIssue").Error; err == nil { + issue.Assignment = assignment + } + return issue, nil +} + +func (r *inventoryIssueRepository) GetAll(ctx context.Context, filter query.InventoryIssueFilter) ([]entities.TInventoryIssueEntity, int64, error) { + var issues []entities.TInventoryIssueEntity + var total int64 + db := query.ApplyInventoryIssueFilters(r.db, filter) + db.Preload("Client"). + Preload("IssueLines"). + Preload("IssueLines.Client"). + Preload("IssueLines.Product"). + Preload("IssueLines.Warehouse"). + Preload("Issuer"). + Preload("InvRequest"). + Find(&issues). + Count(&total) + return issues, total, nil +} + +func (r *inventoryIssueRepository) Update(ctx context.Context, tx *gorm.DB, issue entities.TInventoryIssueEntity) (entities.TInventoryIssueEntity, error) { + if tx == nil { + tx = r.db + } + if err := tx.WithContext(ctx).Model(&entities.TInventoryIssueEntity{}).Where("id = ?", issue.ID).Select("*").Updates(&issue).Error; err != nil { + return issue, err + } + return issue, nil +} + +func (r *inventoryIssueRepository) Delete(ctx context.Context, tx *gorm.DB, id string) error { + if tx == nil { + tx = r.db + } + if err := tx.WithContext(ctx).Delete(&entities.TInventoryIssueEntity{}, "id = ?", id).Error; err != nil { + return err + } + return nil +} diff --git a/modules/inventory_issue/routes.go b/modules/inventory_issue/routes.go new file mode 100644 index 0000000..fceb732 --- /dev/null +++ b/modules/inventory_issue/routes.go @@ -0,0 +1,27 @@ +package inventoryissue + +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_issue/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) { + issueController := do.MustInvoke[controller.InventoryIssueController](injector) + jwtService := do.MustInvokeNamed[service.JWTService](injector, constants.JWTService) + + issueRoutes := server.Group("/api/v1/inventory-issues") + { + issueRoutes.POST("", middlewares.Authenticate(jwtService), issueController.Create) + issueRoutes.GET(":id", middlewares.Authenticate(jwtService), issueController.GetById) + issueRoutes.PUT(":id", middlewares.Authenticate(jwtService), issueController.Update) + issueRoutes.DELETE(":id", middlewares.Authenticate(jwtService), issueController.Delete) + issueRoutes.GET("", middlewares.Authenticate(jwtService), issueController.GetAll) + issueRoutes.POST(":id/lines", middlewares.Authenticate(jwtService), issueController.CreateLine) + issueRoutes.PUT("lines/:id", middlewares.Authenticate(jwtService), issueController.UpdateLine) + issueRoutes.DELETE("lines/:id", middlewares.Authenticate(jwtService), issueController.DeleteLine) + } +} diff --git a/modules/inventory_issue/service/inventory_issue_service.go b/modules/inventory_issue/service/inventory_issue_service.go new file mode 100644 index 0000000..1ffd562 --- /dev/null +++ b/modules/inventory_issue/service/inventory_issue_service.go @@ -0,0 +1,456 @@ +package service + +import ( + "context" + + "github.com/Caknoooo/go-gin-clean-starter/database/entities" + dtodomain "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue/dto" + "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue/query" + "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue/repository" + pkgdto "github.com/Caknoooo/go-gin-clean-starter/pkg/dto" + "github.com/Caknoooo/go-gin-clean-starter/pkg/utils" + "github.com/google/uuid" + "gorm.io/gorm" +) + +type InventoryIssueService interface { + Create(ctx context.Context, req dtodomain.InventoryIssueCreateRequest) (dtodomain.InventoryIssueResponse, error) + GetById(ctx context.Context, id string) (dtodomain.InventoryIssueResponse, error) + GetAll(ctx context.Context, filter query.InventoryIssueFilter) ([]dtodomain.InventoryIssueResponse, int64, error) + Update(ctx context.Context, req dtodomain.InventoryIssueUpdateRequest, id string) (dtodomain.InventoryIssueResponse, error) + Delete(ctx context.Context, id string) error + CreateLine(ctx context.Context, issueId string, req dtodomain.InventoryIssueLineCreateRequest) (dtodomain.InventoryIssueLineResponse, error) + UpdateLine(ctx context.Context, lineId string, req dtodomain.InventoryIssueLineUpdateRequest) (dtodomain.InventoryIssueLineResponse, error) + DeleteLine(ctx context.Context, lineId string) error +} + +type inventoryIssueService struct { + db *gorm.DB + issueRepo repository.InventoryIssueRepository + issueLineRepo repository.InventoryIssueLineRepository +} + +func NewInventoryIssueService(db *gorm.DB, issueRepo repository.InventoryIssueRepository, issueLineRepo repository.InventoryIssueLineRepository) InventoryIssueService { + return &inventoryIssueService{db: db, issueRepo: issueRepo, issueLineRepo: issueLineRepo} +} + +func toInventoryIssueResponse(e entities.TInventoryIssueEntity) dtodomain.InventoryIssueResponse { + issuer := pkgdto.IdNameResponse{} + if e.Issuer.ID != uuid.Nil { + issuer = pkgdto.IdNameResponse{ + ID: e.Issuer.ID.String(), + Name: e.Issuer.Name, + } + } + invRequest := pkgdto.IdNameResponse{} + if e.InvRequest.ID != uuid.Nil { + invRequest = pkgdto.IdNameResponse{ + ID: e.InvRequest.ID.String(), + Name: e.InvRequest.DocumentNumber, + } + } + client := pkgdto.IdNameResponse{} + if e.Client.ID != uuid.Nil { + client = pkgdto.IdNameResponse{ + ID: e.Client.ID.String(), + Name: e.Client.Name, + } + } + + assignment := dtodomain.AssignmentResponse{} + if e.Assignment.ID != uuid.Nil { + assignment = toAssignmentResponse(e.Assignment) + } + lines := make([]dtodomain.InventoryIssueLineResponse, 0) + for _, line := range e.IssueLines { + product := dtodomain.InventoryIssueProductResponse{} + if line.Product.ID != uuid.Nil { + product = dtodomain.InventoryIssueProductResponse{ + ID: line.Product.ID.String(), + RefNumber: line.Product.RefNumber, + Name: line.Product.Name, + } + } + warehouse := pkgdto.IdNameResponse{} + if line.Warehouse.ID != uuid.Nil { + warehouse = pkgdto.IdNameResponse{ + ID: line.Warehouse.ID.String(), + Name: line.Warehouse.Name, + } + } + clientLine := pkgdto.IdNameResponse{} + if line.Client.ID != uuid.Nil { + clientLine = pkgdto.IdNameResponse{ + ID: line.Client.ID.String(), + Name: line.Client.Name, + } + } + lines = append(lines, dtodomain.InventoryIssueLineResponse{ + ID: line.ID.String(), + CurrentStock: line.CurrentStock, + MinStock: line.MinStock, + RequestQuantity: line.RequestQuantity, + IssuedQuantity: line.IssuedQuantity, + Remarks: line.Remarks, + Product: product, + Warehouse: warehouse, + Client: clientLine, + }) + } + return dtodomain.InventoryIssueResponse{ + ID: e.ID.String(), + DocumentNumber: e.DocumentNumber, + DocumentDate: utils.DateTimeToString(e.DocumentDate), + DueDate: utils.DateTimeToString(e.DueDate), + Status: e.Status, + IssuerBy: issuer, + InvRequest: invRequest, + Client: client, + LineCount: len(lines), + IssueLines: lines, + Assignment: assignment, + } +} + +func toAssignmentResponse(e entities.TAssignmentEntity) dtodomain.AssignmentResponse { + // client := pkgdto.IdNameResponse{} + // if e.Client.ID != uuid.Nil { + // client = pkgdto.IdNameResponse{ + // ID: e.Client.ID.String(), + // Name: e.Client.Name, + // } + // } + users := make([]dtodomain.AssignmentUserResponse, 0) + for _, user := range e.AssignmentUsers { + userResp := dtodomain.AssignmentUserResponse{ + ID: user.ID.String(), + TaskType: user.TaskType, + User: pkgdto.IdNameResponse{ID: user.User.ID.String(), Name: user.User.Name}, + Role: pkgdto.IdNameResponse{ID: user.Role.ID.String(), Name: user.Role.Name}, + // Client: pkgdto.IdNameResponse{ID: user.Client.ID.String(), Name: user.Client.Name}, + } + users = append(users, userResp) + } + return dtodomain.AssignmentResponse{ + ID: e.ID.String(), + DocumentType: e.DocumentType, + DocumentID: e.DocumentID.String(), + // Client: client, + AssignmentUsers: users, + } +} + +func (s *inventoryIssueService) Create(ctx context.Context, req dtodomain.InventoryIssueCreateRequest) (dtodomain.InventoryIssueResponse, 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.InventoryIssueResponse{}, err + } + issuerUUID, err := uuid.Parse(req.IssuerBy) + if err != nil { + tx.Rollback() + return dtodomain.InventoryIssueResponse{}, err + } + invRequestUUID, err := uuid.Parse(req.InvRequestID) + if err != nil && req.InvRequestID != "" { + tx.Rollback() + return dtodomain.InventoryIssueResponse{}, err + } + docNum, err := entities.GenerateDocumentNumberInvIssue(s.db, req.ClientID) + if err != nil { + tx.Rollback() + return dtodomain.InventoryIssueResponse{}, err + } + issue := entities.TInventoryIssueEntity{ + DocumentNumber: docNum, + DocumentDate: utils.StringToDateTime(req.DocumentDate), + DueDate: utils.StringToDateTime(req.DueDate), + IssuerBy: issuerUUID, + InvRequestID: invRequestUUID, + ClientID: clientUUID, + Status: req.Status, + } + created, err := s.issueRepo.Create(ctx, tx, issue) + if err != nil { + tx.Rollback() + return dtodomain.InventoryIssueResponse{}, err + } + // Bulk create lines + var lines []entities.TInventoryIssueLineEntity + for _, lineReq := range req.IssueLines { + productUUID, err := uuid.Parse(lineReq.ProductID) + if err != nil && lineReq.ProductID != "" { + tx.Rollback() + return dtodomain.InventoryIssueResponse{}, err + } + warehouseUUID, err := uuid.Parse(lineReq.WarehouseID) + if err != nil && lineReq.WarehouseID != "" { + tx.Rollback() + return dtodomain.InventoryIssueResponse{}, err + } + clientLineUUID, err := uuid.Parse(lineReq.ClientID) + if err != nil { + tx.Rollback() + return dtodomain.InventoryIssueResponse{}, err + } + lines = append(lines, entities.TInventoryIssueLineEntity{ + CurrentStock: lineReq.CurrentStock, + MinStock: lineReq.MinStock, + RequestQuantity: lineReq.RequestQuantity, + IssuedQuantity: lineReq.IssuedQuantity, + Remarks: lineReq.Remarks, + InvIssueID: created.ID, + ProductID: productUUID, + WarehouseID: warehouseUUID, + ClientID: clientLineUUID, + }) + } + if len(lines) > 0 { + err = s.issueLineRepo.BulkCreate(ctx, tx, lines) + if err != nil { + tx.Rollback() + return dtodomain.InventoryIssueResponse{}, err + } + } + tx.Commit() + result, err := s.issueRepo.GetById(ctx, nil, created.ID.String()) + if err != nil { + return dtodomain.InventoryIssueResponse{}, err + } + return toInventoryIssueResponse(result), nil +} + +func (s *inventoryIssueService) GetById(ctx context.Context, id string) (dtodomain.InventoryIssueResponse, error) { + issue, err := s.issueRepo.GetById(ctx, nil, id) + if err != nil { + return dtodomain.InventoryIssueResponse{}, err + } + return toInventoryIssueResponse(issue), nil +} + +func (s *inventoryIssueService) GetAll(ctx context.Context, filter query.InventoryIssueFilter) ([]dtodomain.InventoryIssueResponse, int64, error) { + issues, total, err := s.issueRepo.GetAll(ctx, filter) + if err != nil { + return nil, 0, err + } + var responses []dtodomain.InventoryIssueResponse + for _, e := range issues { + responses = append(responses, toInventoryIssueResponse(e)) + } + if responses == nil { + responses = make([]dtodomain.InventoryIssueResponse, 0) + } + return responses, total, nil +} + +func (s *inventoryIssueService) Update(ctx context.Context, req dtodomain.InventoryIssueUpdateRequest, id string) (dtodomain.InventoryIssueResponse, error) { + tx := s.db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + issue, err := s.issueRepo.GetById(ctx, tx, id) + if err != nil { + tx.Rollback() + return dtodomain.InventoryIssueResponse{}, err + } + issue.DocumentDate = utils.StringToDateTime(req.DocumentDate) + issue.DueDate = utils.StringToDateTime(req.DueDate) + issue.Status = req.Status + updated, err := s.issueRepo.Update(ctx, tx, issue) + if err != nil { + tx.Rollback() + return dtodomain.InventoryIssueResponse{}, err + } + tx.Commit() + result, err := s.issueRepo.GetById(ctx, nil, updated.ID.String()) + if err != nil { + return dtodomain.InventoryIssueResponse{}, err + } + return toInventoryIssueResponse(result), nil +} + +func (s *inventoryIssueService) Delete(ctx context.Context, id string) error { + tx := s.db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + if err := s.issueRepo.Delete(ctx, tx, id); err != nil { + tx.Rollback() + return err + } + tx.Commit() + return nil +} + +func (s *inventoryIssueService) CreateLine(ctx context.Context, issueId string, req dtodomain.InventoryIssueLineCreateRequest) (dtodomain.InventoryIssueLineResponse, error) { + issueUUID, err := uuid.Parse(issueId) + if err != nil { + return dtodomain.InventoryIssueLineResponse{}, err + } + productUUID, err := uuid.Parse(req.ProductID) + if err != nil && req.ProductID != "" { + return dtodomain.InventoryIssueLineResponse{}, err + } + warehouseUUID, err := uuid.Parse(req.WarehouseID) + if err != nil && req.WarehouseID != "" { + return dtodomain.InventoryIssueLineResponse{}, err + } + clientLineUUID, err := uuid.Parse(req.ClientID) + if err != nil { + return dtodomain.InventoryIssueLineResponse{}, err + } + line := entities.TInventoryIssueLineEntity{ + CurrentStock: req.CurrentStock, + MinStock: req.MinStock, + RequestQuantity: req.RequestQuantity, + IssuedQuantity: req.IssuedQuantity, + Remarks: req.Remarks, + InvIssueID: issueUUID, + ProductID: productUUID, + WarehouseID: warehouseUUID, + ClientID: clientLineUUID, + } + created, err := s.issueLineRepo.Create(ctx, nil, line) + if err != nil { + return dtodomain.InventoryIssueLineResponse{}, err + } + product := dtodomain.InventoryIssueProductResponse{} + if created.Product.ID != uuid.Nil { + product = dtodomain.InventoryIssueProductResponse{ + ID: created.Product.ID.String(), + RefNumber: created.Product.RefNumber, + Name: created.Product.Name, + } + } + warehouse := pkgdto.IdNameResponse{} + if created.Warehouse.ID != uuid.Nil { + warehouse = pkgdto.IdNameResponse{ + ID: created.Warehouse.ID.String(), + Name: created.Warehouse.Name, + } + } + clientLine := pkgdto.IdNameResponse{} + if created.Client.ID != uuid.Nil { + clientLine = pkgdto.IdNameResponse{ + ID: created.Client.ID.String(), + Name: created.Client.Name, + } + } + return dtodomain.InventoryIssueLineResponse{ + ID: created.ID.String(), + CurrentStock: created.CurrentStock, + MinStock: created.MinStock, + RequestQuantity: created.RequestQuantity, + IssuedQuantity: created.IssuedQuantity, + Remarks: created.Remarks, + Product: product, + Warehouse: warehouse, + Client: clientLine, + }, nil +} + +func (s *inventoryIssueService) UpdateLine(ctx context.Context, lineId string, req dtodomain.InventoryIssueLineUpdateRequest) (dtodomain.InventoryIssueLineResponse, error) { + line, err := s.issueLineRepo.GetById(ctx, nil, lineId) + if err != nil { + return dtodomain.InventoryIssueLineResponse{}, err + } + if req.CurrentStock != nil { + line.CurrentStock = *req.CurrentStock + } + if req.MinStock != nil { + line.MinStock = *req.MinStock + } + if req.RequestQuantity != nil { + line.RequestQuantity = *req.RequestQuantity + } + if req.IssuedQuantity != nil { + line.IssuedQuantity = *req.IssuedQuantity + } + if req.Remarks != nil { + line.Remarks = *req.Remarks + } + if req.ProductID != nil { + if *req.ProductID != "" { + tmp, err := uuid.Parse(*req.ProductID) + if err != nil { + return dtodomain.InventoryIssueLineResponse{}, err + } + line.ProductID = tmp + } else { + line.ProductID = uuid.Nil + } + } + if req.WarehouseID != nil { + if *req.WarehouseID != "" { + tmp, err := uuid.Parse(*req.WarehouseID) + if err != nil { + return dtodomain.InventoryIssueLineResponse{}, err + } + line.WarehouseID = tmp + } else { + line.WarehouseID = uuid.Nil + } + } + if req.ClientID != nil { + if *req.ClientID != "" { + tmp, err := uuid.Parse(*req.ClientID) + if err != nil { + return dtodomain.InventoryIssueLineResponse{}, err + } + line.ClientID = tmp + } else { + line.ClientID = uuid.Nil + } + } + updated, err := s.issueLineRepo.Update(ctx, nil, line) + if err != nil { + return dtodomain.InventoryIssueLineResponse{}, err + } + product := dtodomain.InventoryIssueProductResponse{} + if updated.Product.ID != uuid.Nil { + product = dtodomain.InventoryIssueProductResponse{ + ID: updated.Product.ID.String(), + RefNumber: updated.Product.RefNumber, + Name: updated.Product.Name, + } + } + warehouse := pkgdto.IdNameResponse{} + if updated.Warehouse.ID != uuid.Nil { + warehouse = pkgdto.IdNameResponse{ + ID: updated.Warehouse.ID.String(), + Name: updated.Warehouse.Name, + } + } + clientLine := pkgdto.IdNameResponse{} + if updated.Client.ID != uuid.Nil { + clientLine = pkgdto.IdNameResponse{ + ID: updated.Client.ID.String(), + Name: updated.Client.Name, + } + } + return dtodomain.InventoryIssueLineResponse{ + ID: updated.ID.String(), + CurrentStock: updated.CurrentStock, + MinStock: updated.MinStock, + RequestQuantity: updated.RequestQuantity, + IssuedQuantity: updated.IssuedQuantity, + Remarks: updated.Remarks, + Product: product, + Warehouse: warehouse, + Client: clientLine, + }, nil +} + +func (s *inventoryIssueService) DeleteLine(ctx context.Context, lineId string) error { + return s.issueLineRepo.Delete(ctx, nil, lineId) +} diff --git a/modules/inventory_receipt/repository/inventory_receipt_repository.go b/modules/inventory_receipt/repository/inventory_receipt_repository.go index 00e4955..5abb9ce 100644 --- a/modules/inventory_receipt/repository/inventory_receipt_repository.go +++ b/modules/inventory_receipt/repository/inventory_receipt_repository.go @@ -45,14 +45,20 @@ func (r *inventoryReceiptRepository) GetById(ctx context.Context, tx *gorm.DB, i Preload("ReceiptLines.Product"). Preload("ReceiptLines.Product.Uom"). Preload("ReceiptLines.Product.DimUom"). - Preload("Assignment"). - Preload("Assignment.AssignmentUsers"). - Preload("Assignment.AssignmentUsers.User"). - Preload("Assignment.AssignmentUsers.Role"). First(&receipt, "id = ?", id).Error; err != nil { return receipt, err } + // Ambil assignment manual + var assignment entities.TAssignmentEntity + if err := tx.WithContext(ctx). + Preload("AssignmentUsers"). + Preload("AssignmentUsers.User"). + Preload("AssignmentUsers.Role"). + First(&assignment, "document_id = ? AND document_type = ?", receipt.ID, "InventoryReceipt").Error; err == nil { + receipt.Assignment = assignment + } + return receipt, nil } diff --git a/modules/inventory_request/repository/inventory_request_repository.go b/modules/inventory_request/repository/inventory_request_repository.go index c68712c..6a721f8 100644 --- a/modules/inventory_request/repository/inventory_request_repository.go +++ b/modules/inventory_request/repository/inventory_request_repository.go @@ -43,13 +43,19 @@ func (r *inventoryRequestRepository) GetById(ctx context.Context, tx *gorm.DB, i Preload("Client"). Preload("RequestLines"). Preload("RequestLines.Product"). - Preload("Assignment"). - Preload("Assignment.AssignmentUsers"). - Preload("Assignment.AssignmentUsers.User"). - Preload("Assignment.AssignmentUsers.Role"). First(&request, "id = ?", id).Error; err != nil { return request, err } + + // Ambil assignment manual + var assignment entities.TAssignmentEntity + if err := tx.WithContext(ctx). + Preload("AssignmentUsers"). + Preload("AssignmentUsers.User"). + Preload("AssignmentUsers.Role"). + First(&assignment, "document_id = ? AND document_type = ?", request.ID, "InventoryRequest").Error; err == nil { + request.Assignment = assignment + } return request, nil } diff --git a/providers/core.go b/providers/core.go index 60c92ee..b60b04d 100644 --- a/providers/core.go +++ b/providers/core.go @@ -73,6 +73,11 @@ import ( inventoryRequestRepo "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_request/repository" inventoryRequestService "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_request/service" + inventoryIssueController "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue/controller" + inventoryIssueLineRepo "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue/repository" + inventoryIssueRepo "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue/repository" + inventoryIssueService "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue/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" @@ -128,6 +133,8 @@ func RegisterDependencies(injector *do.Injector) { assignmentUserRepository := assignmentUserRepo.NewAssignmentUserRepository(db) inventoryRequestRepository := inventoryRequestRepo.NewInventoryRequestRepository(db) inventoryRequestLineRepository := inventoryRequestLineRepo.NewInventoryRequestLineRepository(db) + inventoryIssueRepository := inventoryIssueRepo.NewInventoryIssueRepository(db) + inventoryIssueLineRepository := inventoryIssueLineRepo.NewInventoryIssueLineRepository(db) // Service userServ := userService.NewUserService(userRepository, refreshTokenRepository, jwtService, db) @@ -146,6 +153,7 @@ func RegisterDependencies(injector *do.Injector) { inventoryReceiptServ := inventoryReceiptService.NewInventoryReceiptService(db, inventoryReceiptRepository, inventoryReceiptLineRepository) assignmentServ := assignmentService.NewAssignmentService(db, assignmentRepository, assignmentUserRepository) inventoryRequestServ := inventoryRequestService.NewInventoryRequestService(db, inventoryRequestRepository, inventoryRequestLineRepository) + inventoryIssueServ := inventoryIssueService.NewInventoryIssueService(db, inventoryIssueRepository, inventoryIssueLineRepository) // Controller do.Provide( @@ -238,4 +246,9 @@ func RegisterDependencies(injector *do.Injector) { return inventoryRequestController.NewInventoryRequestController(i, inventoryRequestServ), nil }, ) + do.Provide( + injector, func(i *do.Injector) (inventoryIssueController.InventoryIssueController, error) { + return inventoryIssueController.NewInventoryIssueController(i, inventoryIssueServ), nil + }, + ) }