feat: implement inventory issue management

- Added migration for inventory issue and issue line entities.
- Created InventoryIssueController with CRUD operations for inventory issues and lines.
- Developed DTOs for inventory issue requests and responses.
- Implemented query filters for inventory issues.
- Created repositories for inventory issue and issue line data access.
- Developed service layer for inventory issue business logic.
- Added routes for inventory issue management in the API.
- Updated existing repositories to include assignment retrieval for inventory receipts and requests.
- Registered new dependencies in the core provider for inventory issue functionality.
This commit is contained in:
Habib Fatkhul Rohman 2025-11-18 10:56:03 +07:00
parent 0543f8a1ff
commit 52dec2f37a
16 changed files with 1078 additions and 25 deletions

View File

@ -14,6 +14,7 @@ import (
"github.com/Caknoooo/go-gin-clean-starter/modules/auth" "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/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"
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"
maintenancegroup "github.com/Caknoooo/go-gin-clean-starter/modules/maintenance_group" maintenancegroup "github.com/Caknoooo/go-gin-clean-starter/modules/maintenance_group"
@ -167,6 +168,7 @@ func main() {
inventoryreceipt.RegisterRoutes(server, injector) inventoryreceipt.RegisterRoutes(server, injector)
assignment.RegisterRoutes(server, injector) assignment.RegisterRoutes(server, injector)
inventoryrequest.RegisterRoutes(server, injector) inventoryrequest.RegisterRoutes(server, injector)
inventoryissue.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

@ -1,9 +1,12 @@
package entities package entities
import ( import (
"fmt"
"strings"
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
"gorm.io/gorm"
) )
type TInventoryIssueEntity struct { type TInventoryIssueEntity struct {
@ -11,15 +14,17 @@ type TInventoryIssueEntity struct {
DocumentNumber string `gorm:"type:varchar(100);" json:"document_number"` DocumentNumber string `gorm:"type:varchar(100);" json:"document_number"`
DocumentDate time.Time `gorm:"type:timestamp;" json:"document_date"` DocumentDate time.Time `gorm:"type:timestamp;" json:"document_date"`
DueDate time.Time `gorm:"type:timestamp;" json:"due_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"` IssuerBy uuid.UUID `gorm:"type:uuid;index;" json:"issuer_by"`
InvRequestID uuid.UUID `gorm:"type:uuid;index;" json:"inv_request_id"` InvRequestID uuid.UUID `gorm:"type:uuid;index;" json:"inv_request_id"`
ClientID uuid.UUID `gorm:"type:uuid;index;" json:"client_id"` ClientID uuid.UUID `gorm:"type:uuid;index;" json:"client_id"`
Issuer M_User `gorm:"foreignKey:IssuerBy;references:ID"` Issuer M_User `gorm:"foreignKey:IssuerBy;references:ID"`
InvRequest TInventoryRequestEntity `gorm:"foreignKey:InvRequestID;references:ID"` InvRequest TInventoryRequestEntity `gorm:"foreignKey:InvRequestID;references:ID"`
Client M_Client `gorm:"foreignKey:ClientID;references:ID"` IssueLines []TInventoryIssueLineEntity `gorm:"foreignKey:InvIssueID;references:ID"`
Assignment TAssignmentEntity `gorm:"-"`
Client M_Client `gorm:"foreignKey:ClientID;references:ID"`
FullAuditTrail FullAuditTrail
} }
@ -27,3 +32,44 @@ type TInventoryIssueEntity struct {
func (TInventoryIssueEntity) TableName() string { func (TInventoryIssueEntity) TableName() string {
return "t_inventory_issues" 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
}

View File

@ -12,13 +12,13 @@ type TInventoryIssueLineEntity struct {
IssuedQuantity float64 `gorm:"type:numeric;default:0" json:"issued_quantity"` IssuedQuantity float64 `gorm:"type:numeric;default:0" json:"issued_quantity"`
Remarks string `gorm:"type:text;" json:"remarks"` 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"` ProductID uuid.UUID `gorm:"type:uuid;index;" json:"product_id"`
WarehouseID uuid.UUID `gorm:"type:uuid;index;" json:"warehouse_id"` WarehouseID uuid.UUID `gorm:"type:uuid;index;" json:"warehouse_id"`
ClientID uuid.UUID `gorm:"type:uuid;index;" json:"client_id"` ClientID uuid.UUID `gorm:"type:uuid;index;" json:"client_id"`
Product MProductEntity `gorm:"foreignKey:ProductID;references: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"` Warehouse MWarehouseEntity `gorm:"foreignKey:WarehouseID;references:ID"`
Client M_Client `gorm:"foreignKey:ClientID;references:ID"` Client M_Client `gorm:"foreignKey:ClientID;references:ID"`

View File

@ -22,7 +22,7 @@ type TInventoryReceiptEntity struct {
ReceiptLines []TInventoryReceiptLineEntity `gorm:"foreignKey:InvReceiptID;references:ID"` ReceiptLines []TInventoryReceiptLineEntity `gorm:"foreignKey:InvReceiptID;references:ID"`
Client M_Client `gorm:"foreignKey:ClientID;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 FullAuditTrail
} }

View File

@ -21,7 +21,7 @@ type TInventoryRequestEntity struct {
ClientID uuid.UUID `gorm:"type:uuid;index;" json:"client_id"` ClientID uuid.UUID `gorm:"type:uuid;index;" json:"client_id"`
RequestLines []TInventoryRequestLineEntity `gorm:"foreignKey:InvRequestID;references: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"` Client M_Client `gorm:"foreignKey:ClientID;references:ID"`
FullAuditTrail FullAuditTrail

View File

@ -34,8 +34,8 @@ func Migrate(db *gorm.DB) error {
&entities.TInventoryReceiptLineEntity{}, &entities.TInventoryReceiptLineEntity{},
&entities.TInventoryRequestEntity{}, &entities.TInventoryRequestEntity{},
&entities.TInventoryRequestLineEntity{}, &entities.TInventoryRequestLineEntity{},
// &entities.TInventoryIssueEntity{}, &entities.TInventoryIssueEntity{},
// &entities.TInventoryIssueLineEntity{}, &entities.TInventoryIssueLineEntity{},
// &entities.InventoryTransactionEntity{}, // &entities.InventoryTransactionEntity{},
// &entities.InventoryStorageEntity{}, // &entities.InventoryStorageEntity{},
); err != nil { ); err != nil {
@ -68,15 +68,15 @@ func MigrateFresh(db *gorm.DB) error {
// &entities.MCrossReferenceEntity{}, // &entities.MCrossReferenceEntity{},
// &entities.MWarehouseEntity{}, // &entities.MWarehouseEntity{},
// &entities.MZonaEntity{}, // &entities.MZonaEntity{},
&entities.MAisleEntity{}, // &entities.MAisleEntity{},
// &entities.TAssignmentEntity{}, &entities.TAssignmentEntity{},
// &entities.TAssignmentUserEntity{}, &entities.TAssignmentUserEntity{},
// &entities.TInventoryReceiptEntity{}, // &entities.TInventoryReceiptEntity{},
// &entities.TInventoryReceiptLineEntity{}, // &entities.TInventoryReceiptLineEntity{},
&entities.TInventoryRequestEntity{}, // &entities.TInventoryRequestEntity{},
&entities.TInventoryRequestLineEntity{}, // &entities.TInventoryRequestLineEntity{},
// &entities.TInventoryIssueEntity{}, &entities.TInventoryIssueEntity{},
// &entities.TInventoryIssueLineEntity{}, &entities.TInventoryIssueLineEntity{},
// &entities.InventoryTransactionEntity{}, // &entities.InventoryTransactionEntity{},
// &entities.InventoryStorageEntity{}, // &entities.InventoryStorageEntity{},
); err != nil { ); err != nil {

View File

@ -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)
}

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -45,14 +45,20 @@ func (r *inventoryReceiptRepository) GetById(ctx context.Context, tx *gorm.DB, i
Preload("ReceiptLines.Product"). Preload("ReceiptLines.Product").
Preload("ReceiptLines.Product.Uom"). Preload("ReceiptLines.Product.Uom").
Preload("ReceiptLines.Product.DimUom"). Preload("ReceiptLines.Product.DimUom").
Preload("Assignment").
Preload("Assignment.AssignmentUsers").
Preload("Assignment.AssignmentUsers.User").
Preload("Assignment.AssignmentUsers.Role").
First(&receipt, "id = ?", id).Error; err != nil { First(&receipt, "id = ?", id).Error; err != nil {
return receipt, err 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 return receipt, nil
} }

View File

@ -43,13 +43,19 @@ func (r *inventoryRequestRepository) GetById(ctx context.Context, tx *gorm.DB, i
Preload("Client"). Preload("Client").
Preload("RequestLines"). Preload("RequestLines").
Preload("RequestLines.Product"). Preload("RequestLines.Product").
Preload("Assignment").
Preload("Assignment.AssignmentUsers").
Preload("Assignment.AssignmentUsers.User").
Preload("Assignment.AssignmentUsers.Role").
First(&request, "id = ?", id).Error; err != nil { First(&request, "id = ?", id).Error; err != nil {
return request, err 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 return request, nil
} }

View File

@ -73,6 +73,11 @@ import (
inventoryRequestRepo "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_request/repository" inventoryRequestRepo "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_request/repository"
inventoryRequestService "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_request/service" 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/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"
@ -128,6 +133,8 @@ func RegisterDependencies(injector *do.Injector) {
assignmentUserRepository := assignmentUserRepo.NewAssignmentUserRepository(db) assignmentUserRepository := assignmentUserRepo.NewAssignmentUserRepository(db)
inventoryRequestRepository := inventoryRequestRepo.NewInventoryRequestRepository(db) inventoryRequestRepository := inventoryRequestRepo.NewInventoryRequestRepository(db)
inventoryRequestLineRepository := inventoryRequestLineRepo.NewInventoryRequestLineRepository(db) inventoryRequestLineRepository := inventoryRequestLineRepo.NewInventoryRequestLineRepository(db)
inventoryIssueRepository := inventoryIssueRepo.NewInventoryIssueRepository(db)
inventoryIssueLineRepository := inventoryIssueLineRepo.NewInventoryIssueLineRepository(db)
// Service // Service
userServ := userService.NewUserService(userRepository, refreshTokenRepository, jwtService, db) userServ := userService.NewUserService(userRepository, refreshTokenRepository, jwtService, db)
@ -146,6 +153,7 @@ func RegisterDependencies(injector *do.Injector) {
inventoryReceiptServ := inventoryReceiptService.NewInventoryReceiptService(db, inventoryReceiptRepository, inventoryReceiptLineRepository) inventoryReceiptServ := inventoryReceiptService.NewInventoryReceiptService(db, inventoryReceiptRepository, inventoryReceiptLineRepository)
assignmentServ := assignmentService.NewAssignmentService(db, assignmentRepository, assignmentUserRepository) assignmentServ := assignmentService.NewAssignmentService(db, assignmentRepository, assignmentUserRepository)
inventoryRequestServ := inventoryRequestService.NewInventoryRequestService(db, inventoryRequestRepository, inventoryRequestLineRepository) inventoryRequestServ := inventoryRequestService.NewInventoryRequestService(db, inventoryRequestRepository, inventoryRequestLineRepository)
inventoryIssueServ := inventoryIssueService.NewInventoryIssueService(db, inventoryIssueRepository, inventoryIssueLineRepository)
// Controller // Controller
do.Provide( do.Provide(
@ -238,4 +246,9 @@ func RegisterDependencies(injector *do.Injector) {
return inventoryRequestController.NewInventoryRequestController(i, inventoryRequestServ), nil return inventoryRequestController.NewInventoryRequestController(i, inventoryRequestServ), nil
}, },
) )
do.Provide(
injector, func(i *do.Injector) (inventoryIssueController.InventoryIssueController, error) {
return inventoryIssueController.NewInventoryIssueController(i, inventoryIssueServ), nil
},
)
} }