feat: update status field validation in inventory DTOs and add completed constant
Deploy Application / deploy (push) Successful in 37s Details

This commit is contained in:
Habib Fatkhul Rohman 2025-11-24 14:57:41 +07:00
parent 2676a20026
commit 14eccfb336
7 changed files with 163 additions and 163 deletions

View File

@ -26,7 +26,7 @@ type InventoryIssueCreateRequest struct {
IssuerBy string `json:"issuer_by"` IssuerBy string `json:"issuer_by"`
InvRequestID string `json:"inv_request_id"` InvRequestID string `json:"inv_request_id"`
ClientID string `json:"client_id" binding:"required"` ClientID string `json:"client_id" binding:"required"`
Status string `json:"status" binding:"required"` Status string `json:"status"`
IssueLines []InventoryIssueLineCreateRequest `json:"issue_lines,omitempty" binding:"dive"` IssueLines []InventoryIssueLineCreateRequest `json:"issue_lines,omitempty" binding:"dive"`
} }

View File

@ -29,7 +29,7 @@ type InventoryMovementCreateRequest struct {
MovementDate string `json:"movement_date"` MovementDate string `json:"movement_date"`
MovementType string `json:"movement_type"` MovementType string `json:"movement_type"`
ClientID string `json:"client_id" binding:"required"` ClientID string `json:"client_id" binding:"required"`
Status string `json:"status" binding:"required"` Status string `json:"status"`
SourceLocationID string `json:"source_location_id"` SourceLocationID string `json:"source_location_id"`
DestinationLocationID string `json:"destination_location_id"` DestinationLocationID string `json:"destination_location_id"`
MovementLines []InventoryMovementLineCreateRequest `json:"movement_lines,omitempty" binding:"dive"` MovementLines []InventoryMovementLineCreateRequest `json:"movement_lines,omitempty" binding:"dive"`

View File

@ -26,8 +26,8 @@ type InventoryReceiptCreateRequest struct {
Source string `json:"source"` Source string `json:"source"`
QrCodeFile string `json:"qr_code_file"` QrCodeFile string `json:"qr_code_file"`
ClientID string `json:"client_id" binding:"required"` ClientID string `json:"client_id" binding:"required"`
Status string `json:"status" binding:"required"` Status string `json:"status"`
ReceiptLines []InventoryReceiptLineCreateRequest `json:"inventory_lines,omitempty" binding:"dive"` ReceiptLines []InventoryReceiptLineCreateRequest `json:"receipt_lines,omitempty" binding:"dive"`
} }
type InventoryReceiptLineCreateRequest struct { type InventoryReceiptLineCreateRequest struct {

View File

@ -21,14 +21,14 @@ const (
) )
type InventoryRequestCreateRequest struct { type InventoryRequestCreateRequest struct {
ReferenceNumber string `json:"reference_number"` ReferenceNumber string `json:"reference_number"`
// DocumentNumber string `json:"document_number"` // DocumentNumber string `json:"document_number"`
DueDate string `json:"due_date"` DueDate string `json:"due_date"`
RequestType string `json:"request_type"` RequestType string `json:"request_type"`
Note string `json:"note"` Note string `json:"note"`
ClientID string `json:"client_id" binding:"required"` ClientID string `json:"client_id" binding:"required"`
Status string `json:"status" binding:"required"` Status string `json:"status"`
RequestLines []InventoryRequestLineCreateRequest `json:"request_lines,omitempty" binding:"dive"` RequestLines []InventoryRequestLineCreateRequest `json:"request_lines,omitempty" binding:"dive"`
} }
type InventoryRequestLineCreateRequest struct { type InventoryRequestLineCreateRequest struct {

View File

@ -3,7 +3,11 @@ package dto
import ( import (
"errors" "errors"
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
"github.com/Caknoooo/go-gin-clean-starter/pkg/constants"
pkgdto "github.com/Caknoooo/go-gin-clean-starter/pkg/dto" pkgdto "github.com/Caknoooo/go-gin-clean-starter/pkg/dto"
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
"github.com/google/uuid"
) )
const ( const (
@ -33,7 +37,7 @@ var (
type ( type (
ProductCreateRequest struct { ProductCreateRequest struct {
Name string `json:"name" binding:"required"` Name string `json:"name" binding:"required"`
// RefNumber string `json:"ref_number" binding:"required"` // RefNumber string `json:"ref_number" binding:"required"`
SKU string `json:"sku" binding:"required"` SKU string `json:"sku" binding:"required"`
Description string `json:"description"` Description string `json:"description"`
@ -68,7 +72,7 @@ type (
} }
ProductUpdateRequest struct { ProductUpdateRequest struct {
Name *string `json:"name"` Name *string `json:"name"`
// RefNumber *string `json:"ref_number"` // RefNumber *string `json:"ref_number"`
SKU *string `json:"sku"` SKU *string `json:"sku"`
Description *string `json:"description"` Description *string `json:"description"`
@ -164,3 +168,146 @@ type (
InvMoveRef string `json:"inv_move_ref,omitempty"` InvMoveRef string `json:"inv_move_ref,omitempty"`
} }
) )
func MapProductToResponse(product entities.MProductEntity) ProductResponse {
crossRefs := make([]ProductVendorResponse, 0, len(product.CrossReferences))
for _, v := range product.CrossReferences {
crossRefs = append(crossRefs, ProductVendorResponse{
ID: v.Vendor.ID.String(),
Name: v.Vendor.Name,
Address: v.Vendor.Address,
ContactPerson: v.Vendor.ContactPerson,
SearchKey: v.Vendor.SearchKey,
})
}
invTransactions := make([]ProductInventoryTransactionResponse, 0, len(product.InventoryTransactions))
for _, it := range product.InventoryTransactions {
var transactionQuantity float64
var lot, locater, invReceiptRef, invIssueRef, invMoveRef string
var transactionDate string
valid := false
// Receipt
if it.InvReceipt.ID != uuid.Nil && it.InvReceipt.Status == constants.COMPLETED {
invReceiptRef = it.InvReceipt.ReferenceNumber
transactionDate = utils.DateTimeToString(it.TransactionDate)
for _, line := range it.InvReceipt.ReceiptLines {
if line.ProductID == it.ProductID {
transactionQuantity = line.Quantity
lot = line.BatchNumber
valid = true
break
}
}
}
// Issue
if it.InvIssue.ID != uuid.Nil && it.InvIssue.Status == constants.COMPLETED {
invIssueRef = it.InvIssue.DocumentNumber
transactionDate = utils.DateTimeToString(it.TransactionDate)
for _, line := range it.InvIssue.IssueLines {
if line.ProductID == it.ProductID {
transactionQuantity = line.IssuedQuantity
valid = true
break
}
}
}
// Move
if it.InvMove.ID != uuid.Nil && it.InvMove.Status == constants.COMPLETED {
invMoveRef = it.InvMove.MovementNumber
transactionDate = utils.DateTimeToString(it.TransactionDate)
for _, line := range it.InvMove.MovementLines {
if line.ProductID == it.ProductID {
transactionQuantity = line.MovedQuantity
valid = true
break
}
}
}
if valid {
invTransactions = append(invTransactions, ProductInventoryTransactionResponse{
ID: it.ID.String(),
TransactionDate: transactionDate,
TransactionType: it.TransactionType,
TransactionQuantity: transactionQuantity,
Lot: lot,
Locater: locater,
InvReceiptRef: invReceiptRef,
InvIssueRef: invIssueRef,
InvMoveRef: invMoveRef,
})
}
}
return ProductResponse{
ID: product.ID.String(),
Name: product.Name,
RefNumber: product.RefNumber,
SKU: product.SKU,
Description: product.Description,
Status: product.Status,
IsReturnable: product.IsReturnable,
DimLength: product.DimLength,
DimWidth: product.DimWidth,
DimHeight: product.DimHeight,
Weight: product.Weight,
Volume: product.Volume,
MaxStackHeight: product.MaxStackHeight,
Temperature: product.Temperature,
IsHazardous: product.IsHazardous,
MinStock: product.MinStock,
MaxStock: product.MaxStock,
ReplenishType: product.ReplenishType,
CycleCount: product.CycleCount,
LotRules: product.LotRules,
LeadTime: product.LeadTime,
MultiplyRate: product.MultiplyRate,
DivideRate: product.DivideRate,
Client: pkgdto.IdNameResponse{
ID: product.Client.ID.String(),
Name: product.Client.Name,
},
Category: pkgdto.IdNameResponse{
ID: product.Category.ID.String(),
Name: product.Category.Name,
},
Uom: pkgdto.IdNameResponse{
ID: product.Uom.ID.String(),
Name: product.Uom.Name,
},
DimUom: pkgdto.IdNameResponse{
ID: product.DimUom.ID.String(),
Name: product.DimUom.Name,
},
WeightUom: pkgdto.IdNameResponse{
ID: product.WeightUom.ID.String(),
Name: product.WeightUom.Name,
},
VolumeUom: pkgdto.IdNameResponse{
ID: product.VolumeUom.ID.String(),
Name: product.VolumeUom.Name,
},
MinStockUom: pkgdto.IdNameResponse{
ID: product.MinStockUom.ID.String(),
Name: product.MinStockUom.Name,
},
MaxStockUom: pkgdto.IdNameResponse{
ID: product.MaxStockUom.ID.String(),
Name: product.MaxStockUom.Name,
},
LeadTimeUom: pkgdto.IdNameResponse{
ID: product.LeadTimeUom.ID.String(),
Name: product.LeadTimeUom.Name,
},
UomToUom: pkgdto.IdNameResponse{
ID: product.UomToUom.ID.String(),
Name: product.UomToUom.Name,
},
CrossReferences: crossRefs,
InvTransactions: invTransactions,
}
}

View File

@ -7,10 +7,7 @@ import (
"github.com/Caknoooo/go-gin-clean-starter/modules/product/dto" "github.com/Caknoooo/go-gin-clean-starter/modules/product/dto"
"github.com/Caknoooo/go-gin-clean-starter/modules/product/query" "github.com/Caknoooo/go-gin-clean-starter/modules/product/query"
"github.com/Caknoooo/go-gin-clean-starter/modules/product/repository" "github.com/Caknoooo/go-gin-clean-starter/modules/product/repository"
pkgdto "github.com/Caknoooo/go-gin-clean-starter/pkg/dto"
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/sirupsen/logrus"
"gorm.io/gorm" "gorm.io/gorm"
) )
@ -148,7 +145,7 @@ func (s *productService) GetById(ctx context.Context, productId string) (dto.Pro
if err != nil { if err != nil {
return dto.ProductResponse{}, err return dto.ProductResponse{}, err
} }
return mapProductToResponse(product), nil return dto.MapProductToResponse(product), nil
} }
func (s *productService) GetAll(ctx context.Context, filter query.ProductFilter) ([]dto.ProductResponse, int64, error) { func (s *productService) GetAll(ctx context.Context, filter query.ProductFilter) ([]dto.ProductResponse, int64, error) {
@ -158,7 +155,7 @@ func (s *productService) GetAll(ctx context.Context, filter query.ProductFilter)
} }
var responses []dto.ProductResponse var responses []dto.ProductResponse
for _, p := range products { for _, p := range products {
responses = append(responses, mapProductToResponse(p)) responses = append(responses, dto.MapProductToResponse(p))
} }
if len(responses) == 0 { if len(responses) == 0 {
responses = []dto.ProductResponse{} // <-- pastikan slice kosong, bukan nil responses = []dto.ProductResponse{} // <-- pastikan slice kosong, bukan nil
@ -322,148 +319,3 @@ func parseUUID(id string) uuid.UUID {
} }
return u return u
} }
func mapProductToResponse(product entities.MProductEntity) dto.ProductResponse {
crossRefs := make([]dto.ProductVendorResponse, 0, len(product.CrossReferences))
for _, v := range product.CrossReferences {
crossRefs = append(crossRefs, dto.ProductVendorResponse{
ID: v.Vendor.ID.String(),
Name: v.Vendor.Name,
Address: v.Vendor.Address,
ContactPerson: v.Vendor.ContactPerson,
SearchKey: v.Vendor.SearchKey,
})
}
logrus.Infof("Inventory Transactions Count: %d", len(product.InventoryTransactions))
invTransactions := make([]dto.ProductInventoryTransactionResponse, 0, len(product.InventoryTransactions))
for _, it := range product.InventoryTransactions {
var transactionQuantity float64
var lot, locater, invReceiptRef, invIssueRef, invMoveRef string
var transactionDate string
// Receipt
if it.InvReceipt.ID != uuid.Nil {
invReceiptRef = it.InvReceipt.ReferenceNumber
transactionDate = utils.DateTimeToString(it.TransactionDate)
// Cari line yang sesuai product
for _, line := range it.InvReceipt.ReceiptLines {
if line.ProductID == it.ProductID {
transactionQuantity = line.Quantity
lot = line.BatchNumber
// Jika ada field lokasi, isi di sini
// locater = line.Locater
break
}
}
}
// Issue
if it.InvIssue.ID != uuid.Nil {
invIssueRef = it.InvIssue.DocumentNumber
transactionDate = utils.DateTimeToString(it.TransactionDate)
for _, line := range it.InvIssue.IssueLines {
if line.ProductID == it.ProductID {
transactionQuantity = line.IssuedQuantity
// lot = line.BatchNumber
// locater = line.Locater
break
}
}
}
// Move
if it.InvMove.ID != uuid.Nil {
invMoveRef = it.InvMove.MovementNumber
transactionDate = utils.DateTimeToString(it.TransactionDate)
for _, line := range it.InvMove.MovementLines {
if line.ProductID == it.ProductID {
transactionQuantity = line.MovedQuantity
// lot = line.BatchNumber
// locater = line.Locater
break
}
}
}
invTransactions = append(invTransactions, dto.ProductInventoryTransactionResponse{
ID: it.ID.String(),
TransactionDate: transactionDate,
TransactionType: it.TransactionType,
TransactionQuantity: transactionQuantity,
Lot: lot,
Locater: locater,
InvReceiptRef: invReceiptRef,
InvIssueRef: invIssueRef,
InvMoveRef: invMoveRef,
})
}
return dto.ProductResponse{
ID: product.ID.String(),
Name: product.Name,
RefNumber: product.RefNumber,
SKU: product.SKU,
Description: product.Description,
Status: product.Status,
IsReturnable: product.IsReturnable,
DimLength: product.DimLength,
DimWidth: product.DimWidth,
DimHeight: product.DimHeight,
Weight: product.Weight,
Volume: product.Volume,
MaxStackHeight: product.MaxStackHeight,
Temperature: product.Temperature,
IsHazardous: product.IsHazardous,
MinStock: product.MinStock,
MaxStock: product.MaxStock,
ReplenishType: product.ReplenishType,
CycleCount: product.CycleCount,
LotRules: product.LotRules,
LeadTime: product.LeadTime,
MultiplyRate: product.MultiplyRate,
DivideRate: product.DivideRate,
Client: pkgdto.IdNameResponse{
ID: product.Client.ID.String(),
Name: product.Client.Name,
},
Category: pkgdto.IdNameResponse{
ID: product.Category.ID.String(),
Name: product.Category.Name,
},
Uom: pkgdto.IdNameResponse{
ID: product.Uom.ID.String(),
Name: product.Uom.Name,
},
DimUom: pkgdto.IdNameResponse{
ID: product.DimUom.ID.String(),
Name: product.DimUom.Name,
},
WeightUom: pkgdto.IdNameResponse{
ID: product.WeightUom.ID.String(),
Name: product.WeightUom.Name,
},
VolumeUom: pkgdto.IdNameResponse{
ID: product.VolumeUom.ID.String(),
Name: product.VolumeUom.Name,
},
MinStockUom: pkgdto.IdNameResponse{
ID: product.MinStockUom.ID.String(),
Name: product.MinStockUom.Name,
},
MaxStockUom: pkgdto.IdNameResponse{
ID: product.MaxStockUom.ID.String(),
Name: product.MaxStockUom.Name,
},
LeadTimeUom: pkgdto.IdNameResponse{
ID: product.LeadTimeUom.ID.String(),
Name: product.LeadTimeUom.Name,
},
UomToUom: pkgdto.IdNameResponse{
ID: product.UomToUom.ID.String(),
Name: product.UomToUom.Name,
},
CrossReferences: crossRefs,
InvTransactions: invTransactions,
}
}

View File

@ -14,4 +14,5 @@ const (
JWTService = "JWTService" JWTService = "JWTService"
LOGGER = "logger" LOGGER = "logger"
SUPERADMIN = "superadmin" SUPERADMIN = "superadmin"
COMPLETED = "completed"
) )