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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type MProductEntity struct {
|
||||
|
|
@ -52,6 +57,7 @@ type MProductEntity struct {
|
|||
UomToUom MUomEntity `gorm:"foreignKey:UomToUomID;references:ID"`
|
||||
CrossReferences []MCrossReferenceEntity `gorm:"foreignKey:ProductID;references:ID"`
|
||||
InventoryTransactions []InventoryTransactionEntity `gorm:"foreignKey:ProductID;references:ID"`
|
||||
InventoryStorages []InventoryStorageEntity `gorm:"foreignKey:ProductID;references:ID"`
|
||||
|
||||
FullAuditTrail
|
||||
}
|
||||
|
|
@ -59,3 +65,64 @@ type MProductEntity struct {
|
|||
func (MProductEntity) TableName() string {
|
||||
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 (
|
||||
ProductCreateRequest struct {
|
||||
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"`
|
||||
Description string `json:"description"`
|
||||
Status string `json:"status" binding:"required"`
|
||||
|
|
@ -69,7 +69,7 @@ type (
|
|||
|
||||
ProductUpdateRequest struct {
|
||||
Name *string `json:"name"`
|
||||
RefNumber *string `json:"ref_number"`
|
||||
// RefNumber *string `json:"ref_number"`
|
||||
SKU *string `json:"sku"`
|
||||
Description *string `json:"description"`
|
||||
Status *string `json:"status"`
|
||||
|
|
@ -154,6 +154,13 @@ type (
|
|||
|
||||
ProductInventoryTransactionResponse struct {
|
||||
ID string `json:"id"`
|
||||
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("CrossReferences").
|
||||
Preload("CrossReferences.Vendor").
|
||||
Preload("InvTransactions").
|
||||
Preload("InvTransactions.Product").
|
||||
Preload("InvTransactions.Client").
|
||||
Preload("InvTransactions.Aisle").
|
||||
Preload("InvTransactions.InvReceipt").
|
||||
Preload("InvTransactions.InvIssue").
|
||||
Preload("InvTransactions.InvMove").
|
||||
Preload("InventoryStorages").
|
||||
Preload("InventoryTransactions").
|
||||
Preload("InventoryTransactions.Client").
|
||||
Preload("InventoryTransactions.Aisle").
|
||||
Preload("InventoryTransactions.InvReceipt").
|
||||
Preload("InventoryTransactions.InvReceipt.ReceiptLines").
|
||||
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 {
|
||||
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/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/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
|
|
@ -81,9 +83,14 @@ func (s *productService) Create(ctx context.Context, req dto.ProductCreateReques
|
|||
tx.Rollback()
|
||||
}
|
||||
}()
|
||||
refNumber, err := entities.GenerateRefNumberProduct(tx, req.ClientID, *req.CategoryID)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return dto.ProductResponse{}, err
|
||||
}
|
||||
product := entities.MProductEntity{
|
||||
Name: req.Name,
|
||||
RefNumber: req.RefNumber,
|
||||
RefNumber: refNumber,
|
||||
SKU: req.SKU,
|
||||
Description: req.Description,
|
||||
Status: req.Status,
|
||||
|
|
@ -175,9 +182,6 @@ func (s *productService) Update(ctx context.Context, req dto.ProductUpdateReques
|
|||
if req.Name != nil {
|
||||
product.Name = *req.Name
|
||||
}
|
||||
if req.RefNumber != nil {
|
||||
product.RefNumber = *req.RefNumber
|
||||
}
|
||||
if req.SKU != nil {
|
||||
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{
|
||||
ID: product.ID.String(),
|
||||
Name: product.Name,
|
||||
|
|
@ -396,5 +464,6 @@ func mapProductToResponse(product entities.MProductEntity) dto.ProductResponse {
|
|||
Name: product.UomToUom.Name,
|
||||
},
|
||||
CrossReferences: crossRefs,
|
||||
InvTransactions: invTransactions,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue