feat: implement product reference number generation and update inventory transaction response structure
Deploy Application / deploy (push) Successful in 28s
Details
Deploy Application / deploy (push) Successful in 28s
Details
This commit is contained in:
parent
8812a294d8
commit
2676a20026
|
|
@ -1,7 +1,12 @@
|
||||||
package entities
|
package entities
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MProductEntity struct {
|
type MProductEntity struct {
|
||||||
|
|
@ -52,6 +57,7 @@ type MProductEntity struct {
|
||||||
UomToUom MUomEntity `gorm:"foreignKey:UomToUomID;references:ID"`
|
UomToUom MUomEntity `gorm:"foreignKey:UomToUomID;references:ID"`
|
||||||
CrossReferences []MCrossReferenceEntity `gorm:"foreignKey:ProductID;references:ID"`
|
CrossReferences []MCrossReferenceEntity `gorm:"foreignKey:ProductID;references:ID"`
|
||||||
InventoryTransactions []InventoryTransactionEntity `gorm:"foreignKey:ProductID;references:ID"`
|
InventoryTransactions []InventoryTransactionEntity `gorm:"foreignKey:ProductID;references:ID"`
|
||||||
|
InventoryStorages []InventoryStorageEntity `gorm:"foreignKey:ProductID;references:ID"`
|
||||||
|
|
||||||
FullAuditTrail
|
FullAuditTrail
|
||||||
}
|
}
|
||||||
|
|
@ -59,3 +65,64 @@ type MProductEntity struct {
|
||||||
func (MProductEntity) TableName() string {
|
func (MProductEntity) TableName() string {
|
||||||
return "m_products"
|
return "m_products"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateRefNumberProduct generates a new reference number for a product
|
||||||
|
func GenerateRefNumberProduct(db *gorm.DB, clientId string, categoryId string) (string, error) {
|
||||||
|
prefix := "PRD"
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ambil search key kategori berdasarkan categoryId
|
||||||
|
var category struct {
|
||||||
|
SearchKey string
|
||||||
|
}
|
||||||
|
if err := db.Table("m_categories").Select("search_key").Where("id = ?", categoryId).First(&category).Error; err != nil {
|
||||||
|
return "", fmt.Errorf("category not found")
|
||||||
|
}
|
||||||
|
if category.SearchKey == "" {
|
||||||
|
return "", fmt.Errorf("category search key is empty")
|
||||||
|
}
|
||||||
|
categoryInitial := ""
|
||||||
|
lettersOnly := ""
|
||||||
|
for _, r := range category.SearchKey {
|
||||||
|
if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') {
|
||||||
|
lettersOnly += string(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(lettersOnly) > 0 {
|
||||||
|
categoryInitial = strings.ToUpper(lettersOnly)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ambil tahun dan bulan sekarang
|
||||||
|
now := time.Now()
|
||||||
|
period := now.Format("0601") // YYYYMM
|
||||||
|
|
||||||
|
// Cari sequence terakhir untuk client dan kategori di periode ini
|
||||||
|
var lastProduct MProductEntity
|
||||||
|
prefixQuery := fmt.Sprintf("%s-%s-%s", prefix, categoryInitial, period)
|
||||||
|
err := db.
|
||||||
|
Where("client_id = ? AND category_id = ? AND LEFT(ref_number, ?) = ?", clientId, categoryId, len(prefixQuery), prefixQuery).
|
||||||
|
Order("ref_number DESC").
|
||||||
|
First(&lastProduct).Error
|
||||||
|
|
||||||
|
seq := 1
|
||||||
|
if err == nil && lastProduct.RefNumber != "" {
|
||||||
|
parts := strings.Split(lastProduct.RefNumber, "-")
|
||||||
|
if len(parts) == 4 {
|
||||||
|
fmt.Sscanf(parts[3], "%d", &seq)
|
||||||
|
seq++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refNum := fmt.Sprintf("%s-%s-%s-%04d", prefix, categoryInitial, period, seq)
|
||||||
|
return refNum, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,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"`
|
||||||
Status string `json:"status" binding:"required"`
|
Status string `json:"status" binding:"required"`
|
||||||
|
|
@ -69,7 +69,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"`
|
||||||
Status *string `json:"status"`
|
Status *string `json:"status"`
|
||||||
|
|
@ -153,7 +153,14 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
ProductInventoryTransactionResponse struct {
|
ProductInventoryTransactionResponse struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
TransactionType string `json:"transaction_type"`
|
TransactionDate string `json:"transaction_date"`
|
||||||
|
TransactionType string `json:"transaction_type"`
|
||||||
|
TransactionQuantity float64 `json:"transaction_quantity"`
|
||||||
|
Lot string `json:"lot"`
|
||||||
|
Locater string `json:"locater"`
|
||||||
|
InvReceiptRef string `json:"inv_receipt_ref,omitempty"`
|
||||||
|
InvIssueRef string `json:"inv_issue_ref,omitempty"`
|
||||||
|
InvMoveRef string `json:"inv_move_ref,omitempty"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -116,13 +116,19 @@ func (r *productRepository) GetById(ctx context.Context, tx *gorm.DB, productId
|
||||||
Preload("UomToUom").
|
Preload("UomToUom").
|
||||||
Preload("CrossReferences").
|
Preload("CrossReferences").
|
||||||
Preload("CrossReferences.Vendor").
|
Preload("CrossReferences.Vendor").
|
||||||
Preload("InvTransactions").
|
Preload("InventoryStorages").
|
||||||
Preload("InvTransactions.Product").
|
Preload("InventoryTransactions").
|
||||||
Preload("InvTransactions.Client").
|
Preload("InventoryTransactions.Client").
|
||||||
Preload("InvTransactions.Aisle").
|
Preload("InventoryTransactions.Aisle").
|
||||||
Preload("InvTransactions.InvReceipt").
|
Preload("InventoryTransactions.InvReceipt").
|
||||||
Preload("InvTransactions.InvIssue").
|
Preload("InventoryTransactions.InvReceipt.ReceiptLines").
|
||||||
Preload("InvTransactions.InvMove").
|
Preload("InventoryTransactions.InvReceipt.ReceiptLines.Product").
|
||||||
|
Preload("InventoryTransactions.InvIssue").
|
||||||
|
Preload("InventoryTransactions.InvIssue.IssueLines").
|
||||||
|
Preload("InventoryTransactions.InvIssue.IssueLines.Product").
|
||||||
|
Preload("InventoryTransactions.InvMove").
|
||||||
|
Preload("InventoryTransactions.InvMove.MovementLines").
|
||||||
|
Preload("InventoryTransactions.InvMove.MovementLines.Product").
|
||||||
First(&product, "id = ?", productId).Error; err != nil {
|
First(&product, "id = ?", productId).Error; err != nil {
|
||||||
return product, err
|
return product, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ import (
|
||||||
"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"
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -81,9 +83,14 @@ func (s *productService) Create(ctx context.Context, req dto.ProductCreateReques
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
refNumber, err := entities.GenerateRefNumberProduct(tx, req.ClientID, *req.CategoryID)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return dto.ProductResponse{}, err
|
||||||
|
}
|
||||||
product := entities.MProductEntity{
|
product := entities.MProductEntity{
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
RefNumber: req.RefNumber,
|
RefNumber: refNumber,
|
||||||
SKU: req.SKU,
|
SKU: req.SKU,
|
||||||
Description: req.Description,
|
Description: req.Description,
|
||||||
Status: req.Status,
|
Status: req.Status,
|
||||||
|
|
@ -175,9 +182,6 @@ func (s *productService) Update(ctx context.Context, req dto.ProductUpdateReques
|
||||||
if req.Name != nil {
|
if req.Name != nil {
|
||||||
product.Name = *req.Name
|
product.Name = *req.Name
|
||||||
}
|
}
|
||||||
if req.RefNumber != nil {
|
|
||||||
product.RefNumber = *req.RefNumber
|
|
||||||
}
|
|
||||||
if req.SKU != nil {
|
if req.SKU != nil {
|
||||||
product.SKU = *req.SKU
|
product.SKU = *req.SKU
|
||||||
}
|
}
|
||||||
|
|
@ -331,6 +335,70 @@ func mapProductToResponse(product entities.MProductEntity) dto.ProductResponse {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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{
|
return dto.ProductResponse{
|
||||||
ID: product.ID.String(),
|
ID: product.ID.String(),
|
||||||
Name: product.Name,
|
Name: product.Name,
|
||||||
|
|
@ -396,5 +464,6 @@ func mapProductToResponse(product entities.MProductEntity) dto.ProductResponse {
|
||||||
Name: product.UomToUom.Name,
|
Name: product.UomToUom.Name,
|
||||||
},
|
},
|
||||||
CrossReferences: crossRefs,
|
CrossReferences: crossRefs,
|
||||||
|
InvTransactions: invTransactions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue