feat: implement inventory return management with entities, DTOs, services, and routes
This commit is contained in:
parent
ca2795a493
commit
410ecde2e9
|
|
@ -0,0 +1,73 @@
|
||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TInventoryReturnEntity struct {
|
||||||
|
ID uuid.UUID `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||||
|
DocumentNumber string `gorm:"type:varchar(100);" json:"document_number"`
|
||||||
|
DocumentDate time.Time `gorm:"type:timestamp;" json:"document_date"`
|
||||||
|
Notes string `gorm:"type:text;" json:"notes"`
|
||||||
|
Status string `gorm:"type:varchar(50);default:'draft'" json:"status"`
|
||||||
|
|
||||||
|
InvIssueID uuid.UUID `gorm:"type:uuid;index;" json:"inv_issue_id"`
|
||||||
|
ClientID uuid.UUID `gorm:"type:uuid;index;" json:"client_id"`
|
||||||
|
|
||||||
|
InvIssue TInventoryIssueEntity `gorm:"foreignKey:InvIssueID;references:ID"`
|
||||||
|
ReturnLines []TInventoryReturnLineEntity `gorm:"foreignKey:InvReturnID;references:ID"`
|
||||||
|
Assignment TAssignmentEntity `gorm:"-"`
|
||||||
|
Client M_Client `gorm:"foreignKey:ClientID;references:ID"`
|
||||||
|
|
||||||
|
FullAuditTrail
|
||||||
|
}
|
||||||
|
|
||||||
|
func (TInventoryReturnEntity) TableName() string {
|
||||||
|
return "t_inventory_returns"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateDocumentNumber generates a new document number for a client
|
||||||
|
func GenerateDocumentNumberInventoryReturn(db *gorm.DB, clientId string) (string, error) {
|
||||||
|
prefix := "RTRN"
|
||||||
|
|
||||||
|
// 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 lastRequest TInventoryReturnEntity
|
||||||
|
err := db.
|
||||||
|
Where("client_id = ?", clientId).
|
||||||
|
Order("document_number DESC").
|
||||||
|
First(&lastRequest).Error
|
||||||
|
|
||||||
|
seq := 1
|
||||||
|
if err == nil && lastRequest.DocumentNumber != "" {
|
||||||
|
parts := strings.Split(lastRequest.DocumentNumber, "-")
|
||||||
|
if len(parts) == 3 {
|
||||||
|
fmt.Sscanf(parts[2], "%d", &seq)
|
||||||
|
seq++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
docNum := fmt.Sprintf("%s-%s-%04d", prefix, initials, seq)
|
||||||
|
return docNum, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TInventoryReturnLineEntity struct {
|
||||||
|
ID uuid.UUID `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||||
|
Quantity float64 `gorm:"type:numeric;default:0" json:"quantity"`
|
||||||
|
Attachment string `gorm:"type:text;" json:"attachment"`
|
||||||
|
|
||||||
|
InvReturnID uuid.UUID `gorm:"type:uuid;index;" json:"inv_return_id"`
|
||||||
|
ProductID uuid.UUID `gorm:"type:uuid;index;" json:"product_id"`
|
||||||
|
ClientID uuid.UUID `gorm:"type:uuid;index;" json:"client_id"`
|
||||||
|
|
||||||
|
Product MProductEntity `gorm:"foreignKey:ProductID;references:ID"`
|
||||||
|
InvReturn TInventoryReturnEntity `gorm:"foreignKey:InvReturnID;references:ID"`
|
||||||
|
Client M_Client `gorm:"foreignKey:ClientID;references:ID"`
|
||||||
|
|
||||||
|
FullAuditTrail
|
||||||
|
}
|
||||||
|
|
||||||
|
func (TInventoryReturnLineEntity) TableName() string {
|
||||||
|
return "t_inventory_return_lines"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_return/dto"
|
||||||
|
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_return/service"
|
||||||
|
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InventoryReturnController 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 inventoryReturnController struct {
|
||||||
|
returnService service.InventoryReturnService
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteLine implements InventoryReturnController.
|
||||||
|
func (c *inventoryReturnController) DeleteLine(ctx *gin.Context) {
|
||||||
|
id := ctx.Param("id")
|
||||||
|
if err := c.returnService.DeleteLine(ctx, id); err != nil {
|
||||||
|
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_DELETE_INVENTORY_RETURN_LINE, err.Error(), nil)
|
||||||
|
ctx.JSON(http.StatusInternalServerError, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_DELETE_INVENTORY_RETURN_LINE, nil)
|
||||||
|
ctx.JSON(http.StatusOK, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateLine implements InventoryReturnController.
|
||||||
|
func (c *inventoryReturnController) UpdateLine(ctx *gin.Context) {
|
||||||
|
id := ctx.Param("id")
|
||||||
|
var req dto.InventoryReturnLineUpdateRequest
|
||||||
|
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
|
||||||
|
}
|
||||||
|
line, err := c.returnService.UpdateLine(ctx, id, req)
|
||||||
|
if err != nil {
|
||||||
|
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_UPDATE_INVENTORY_RETURN_LINE, err.Error(), nil)
|
||||||
|
ctx.JSON(http.StatusInternalServerError, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_UPDATE_INVENTORY_RETURN_LINE, line)
|
||||||
|
ctx.JSON(http.StatusOK, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...implementasi fungsi lain (Create, Update, Delete, GetById, GetAll, CreateLine) dengan pola yang sama...
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
package dto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
|
||||||
|
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MESSAGE_FAILED_CREATE_INVENTORY_RETURN = "failed create inventory return"
|
||||||
|
MESSAGE_FAILED_CREATE_INVENTORY_RETURN_LINE = "failed create inventory return line"
|
||||||
|
MESSAGE_SUCCESS_CREATE_INVENTORY_RETURN = "success create inventory return"
|
||||||
|
MESSAGE_SUCCESS_CREATE_INVENTORY_RETURN_LINE = "success create inventory return line"
|
||||||
|
MESSAGE_FAILED_GET_INVENTORY_RETURN = "failed get inventory return"
|
||||||
|
MESSAGE_SUCCESS_GET_INVENTORY_RETURN = "success get inventory return"
|
||||||
|
MESSAGE_FAILED_UPDATE_INVENTORY_RETURN = "failed update inventory return"
|
||||||
|
MESSAGE_FAILED_UPDATE_INVENTORY_RETURN_LINE = "failed update inventory return line"
|
||||||
|
MESSAGE_SUCCESS_UPDATE_INVENTORY_RETURN = "success update inventory return"
|
||||||
|
MESSAGE_SUCCESS_UPDATE_INVENTORY_RETURN_LINE = "success update inventory return line"
|
||||||
|
MESSAGE_FAILED_DELETE_INVENTORY_RETURN = "failed delete inventory return"
|
||||||
|
MESSAGE_FAILED_DELETE_INVENTORY_RETURN_LINE = "failed delete inventory return line"
|
||||||
|
MESSAGE_SUCCESS_DELETE_INVENTORY_RETURN = "success delete inventory return"
|
||||||
|
MESSAGE_SUCCESS_DELETE_INVENTORY_RETURN_LINE = "success delete inventory return line"
|
||||||
|
MESSAGE_FAILED_GET_DATA_FROM_BODY = "failed get data from body"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InventoryReturnCreateRequest struct {
|
||||||
|
DocumentDate string `json:"document_date"`
|
||||||
|
Notes string `json:"notes"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
InvIssueID string `json:"inv_issue_id"`
|
||||||
|
ClientID string `json:"client_id" binding:"required"`
|
||||||
|
ReturnLines []InventoryReturnLineCreateRequest `json:"return_lines,omitempty" binding:"dive"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InventoryReturnLineCreateRequest struct {
|
||||||
|
Quantity float64 `json:"quantity"`
|
||||||
|
Attachment string `json:"attachment"`
|
||||||
|
InvReturnID string `json:"inv_return_id"`
|
||||||
|
ProductID string `json:"product_id"`
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InventoryReturnUpdateRequest struct {
|
||||||
|
DocumentDate string `json:"document_date"`
|
||||||
|
Notes string `json:"notes"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
InvIssueID string `json:"inv_issue_id"`
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InventoryReturnLineUpdateRequest struct {
|
||||||
|
Quantity *float64 `json:"quantity"`
|
||||||
|
Attachment *string `json:"attachment"`
|
||||||
|
InvReturnID *string `json:"inv_return_id"`
|
||||||
|
ProductID *string `json:"product_id"`
|
||||||
|
ClientID *string `json:"client_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InventoryReturnResponse struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
DocumentNumber string `json:"document_number"`
|
||||||
|
DocumentDate string `json:"document_date"`
|
||||||
|
Notes string `json:"notes"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
InvIssueID string `json:"inv_issue_id"`
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
ReturnLines []InventoryReturnLineResponse `json:"return_lines,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InventoryReturnLineResponse struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Quantity float64 `json:"quantity"`
|
||||||
|
Attachment string `json:"attachment"`
|
||||||
|
InvReturnID string `json:"inv_return_id"`
|
||||||
|
ProductID string `json:"product_id"`
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper untuk mapping entity ke response
|
||||||
|
func ToInventoryReturnResponse(entity entities.TInventoryReturnEntity) InventoryReturnResponse {
|
||||||
|
lines := make([]InventoryReturnLineResponse, 0, len(entity.ReturnLines))
|
||||||
|
for _, line := range entity.ReturnLines {
|
||||||
|
lines = append(lines, ToInventoryReturnLineResponse(line))
|
||||||
|
}
|
||||||
|
return InventoryReturnResponse{
|
||||||
|
ID: entity.ID.String(),
|
||||||
|
DocumentNumber: entity.DocumentNumber,
|
||||||
|
DocumentDate: utils.DateTimeToString(entity.DocumentDate),
|
||||||
|
Notes: entity.Notes,
|
||||||
|
Status: entity.Status,
|
||||||
|
InvIssueID: entity.InvIssueID.String(),
|
||||||
|
ClientID: entity.ClientID.String(),
|
||||||
|
ReturnLines: lines,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToInventoryReturnLineResponse(line entities.TInventoryReturnLineEntity) InventoryReturnLineResponse {
|
||||||
|
return InventoryReturnLineResponse{
|
||||||
|
ID: line.ID.String(),
|
||||||
|
Quantity: line.Quantity,
|
||||||
|
Attachment: line.Attachment,
|
||||||
|
InvReturnID: line.InvReturnID.String(),
|
||||||
|
ProductID: line.ProductID.String(),
|
||||||
|
ClientID: line.ClientID.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package query
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InventoryReturnFilter struct {
|
||||||
|
ClientID string `form:"client_id"`
|
||||||
|
Source string `form:"source"`
|
||||||
|
PerPage int `form:"per_page"`
|
||||||
|
Page int `form:"page"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyInventoryReturnFilters(db *gorm.DB, filter InventoryReturnFilter) *gorm.DB {
|
||||||
|
if filter.ClientID != "" {
|
||||||
|
db = db.Where("client_id = ?", filter.ClientID)
|
||||||
|
}
|
||||||
|
if filter.Source != "" {
|
||||||
|
db = db.Where("source ILIKE ?", "%"+filter.Source+"%")
|
||||||
|
}
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InventoryReturnLineRepository interface {
|
||||||
|
Create(ctx context.Context, tx *gorm.DB, line entities.TInventoryReturnLineEntity) (entities.TInventoryReturnLineEntity, error)
|
||||||
|
GetById(ctx context.Context, tx *gorm.DB, id string) (entities.TInventoryReturnLineEntity, error)
|
||||||
|
GetAllByReturnId(ctx context.Context, returnId string) ([]entities.TInventoryReturnLineEntity, error)
|
||||||
|
Update(ctx context.Context, tx *gorm.DB, line entities.TInventoryReturnLineEntity) (entities.TInventoryReturnLineEntity, error)
|
||||||
|
Delete(ctx context.Context, tx *gorm.DB, id string) error
|
||||||
|
BulkCreate(ctx context.Context, tx *gorm.DB, lines []entities.TInventoryReturnLineEntity) error
|
||||||
|
DeleteByReturnId(ctx context.Context, tx *gorm.DB, returnId string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type inventoryReturnLineRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// BulkCreate implements InventoryReturnLineRepository.
|
||||||
|
func (r *inventoryReturnLineRepository) BulkCreate(ctx context.Context, tx *gorm.DB, lines []entities.TInventoryReturnLineEntity) error {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
if err := tx.WithContext(ctx).Create(&lines).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteByReturnId implements InventoryReturnLineRepository.
|
||||||
|
func (r *inventoryReturnLineRepository) DeleteByReturnId(ctx context.Context, tx *gorm.DB, returnId string) error {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
if err := tx.WithContext(ctx).Where("inv_return_id = ?", returnId).Delete(&entities.TInventoryReturnLineEntity{}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllByReturnId implements InventoryReturnLineRepository.
|
||||||
|
func (r *inventoryReturnLineRepository) GetAllByReturnId(ctx context.Context, returnId string) ([]entities.TInventoryReturnLineEntity, error) {
|
||||||
|
var lines []entities.TInventoryReturnLineEntity
|
||||||
|
if err := r.db.WithContext(ctx).Where("inv_return_id = ?", returnId).Find(&lines).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return lines, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInventoryReturnLineRepository(db *gorm.DB) InventoryReturnLineRepository {
|
||||||
|
return &inventoryReturnLineRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inventoryReturnLineRepository) Create(ctx context.Context, tx *gorm.DB, line entities.TInventoryReturnLineEntity) (entities.TInventoryReturnLineEntity, 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 *inventoryReturnLineRepository) GetById(ctx context.Context, tx *gorm.DB, id string) (entities.TInventoryReturnLineEntity, error) {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
var line entities.TInventoryReturnLineEntity
|
||||||
|
if err := tx.WithContext(ctx).First(&line, "id = ?", id).Error; err != nil {
|
||||||
|
return line, err
|
||||||
|
}
|
||||||
|
return line, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inventoryReturnLineRepository) Update(ctx context.Context, tx *gorm.DB, line entities.TInventoryReturnLineEntity) (entities.TInventoryReturnLineEntity, error) {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
if err := tx.WithContext(ctx).Save(&line).Error; err != nil {
|
||||||
|
return line, err
|
||||||
|
}
|
||||||
|
return line, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inventoryReturnLineRepository) Delete(ctx context.Context, tx *gorm.DB, id string) error {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
if err := tx.WithContext(ctx).Delete(&entities.TInventoryReturnLineEntity{}, "id = ?", id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
|
||||||
|
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_return/query"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InventoryReturnRepository interface {
|
||||||
|
Create(ctx context.Context, tx *gorm.DB, ret entities.TInventoryReturnEntity) (entities.TInventoryReturnEntity, error)
|
||||||
|
GetById(ctx context.Context, tx *gorm.DB, id string) (entities.TInventoryReturnEntity, error)
|
||||||
|
GetAll(ctx context.Context, filter query.InventoryReturnFilter) ([]entities.TInventoryReturnEntity, int64, error)
|
||||||
|
Update(ctx context.Context, tx *gorm.DB, ret entities.TInventoryReturnEntity) (entities.TInventoryReturnEntity, error)
|
||||||
|
Delete(ctx context.Context, tx *gorm.DB, id string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type inventoryReturnRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInventoryReturnRepository(db *gorm.DB) InventoryReturnRepository {
|
||||||
|
return &inventoryReturnRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inventoryReturnRepository) Create(ctx context.Context, tx *gorm.DB, ret entities.TInventoryReturnEntity) (entities.TInventoryReturnEntity, error) {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
if err := tx.WithContext(ctx).Create(&ret).Error; err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inventoryReturnRepository) GetById(ctx context.Context, tx *gorm.DB, id string) (entities.TInventoryReturnEntity, error) {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
var ret entities.TInventoryReturnEntity
|
||||||
|
if err := tx.WithContext(ctx).
|
||||||
|
Preload("Client").
|
||||||
|
Preload("ReturnLines").
|
||||||
|
Preload("ReturnLines.Product").
|
||||||
|
Preload("ReturnLines.Product.Uom").
|
||||||
|
Preload("ReturnLines.Product.DimUom").
|
||||||
|
First(&ret, "id = ?", id).Error; err != nil {
|
||||||
|
return ret, 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 = ?", ret.ID, "InventoryReturn").Error; err == nil {
|
||||||
|
ret.Assignment = assignment
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inventoryReturnRepository) GetAll(ctx context.Context, filter query.InventoryReturnFilter) ([]entities.TInventoryReturnEntity, int64, error) {
|
||||||
|
var returns []entities.TInventoryReturnEntity
|
||||||
|
var total int64
|
||||||
|
db := query.ApplyInventoryReturnFilters(r.db, filter)
|
||||||
|
db.Model(&entities.TInventoryReturnEntity{}).Count(&total)
|
||||||
|
if filter.PerPage > 0 && filter.Page > 0 {
|
||||||
|
db = db.Offset((filter.Page - 1) * filter.PerPage).Limit(filter.PerPage)
|
||||||
|
}
|
||||||
|
if err := db.Preload("Client").Find(&returns).Error; err != nil {
|
||||||
|
return returns, total, err
|
||||||
|
}
|
||||||
|
return returns, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inventoryReturnRepository) Update(ctx context.Context, tx *gorm.DB, ret entities.TInventoryReturnEntity) (entities.TInventoryReturnEntity, error) {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
if err := tx.WithContext(ctx).Model(&entities.TInventoryReturnEntity{}).Where("id = ?", ret.ID).Select("*").Updates(&ret).Error; err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inventoryReturnRepository) Delete(ctx context.Context, tx *gorm.DB, id string) error {
|
||||||
|
if tx == nil {
|
||||||
|
tx = r.db
|
||||||
|
}
|
||||||
|
if err := tx.WithContext(ctx).Delete(&entities.TInventoryReturnEntity{}, "id = ?", id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package inventoryreturn
|
||||||
|
|
||||||
|
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_return/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) {
|
||||||
|
returnController := do.MustInvoke[controller.InventoryReturnController](injector)
|
||||||
|
jwtService := do.MustInvokeNamed[service.JWTService](injector, constants.JWTService)
|
||||||
|
|
||||||
|
returnRoutes := server.Group("/api/v1/inventory-returns")
|
||||||
|
{
|
||||||
|
returnRoutes.POST("", middlewares.Authenticate(jwtService), returnController.Create)
|
||||||
|
returnRoutes.GET(":id", middlewares.Authenticate(jwtService), returnController.GetById)
|
||||||
|
returnRoutes.PUT(":id", middlewares.Authenticate(jwtService), returnController.Update)
|
||||||
|
returnRoutes.DELETE(":id", middlewares.Authenticate(jwtService), returnController.Delete)
|
||||||
|
returnRoutes.GET("", middlewares.Authenticate(jwtService), returnController.GetAll)
|
||||||
|
returnRoutes.POST(":id/lines", middlewares.Authenticate(jwtService), returnController.CreateLine)
|
||||||
|
returnRoutes.PUT("lines/:id", middlewares.Authenticate(jwtService), returnController.UpdateLine)
|
||||||
|
returnRoutes.DELETE("lines/:id", middlewares.Authenticate(jwtService), returnController.DeleteLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,267 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
|
||||||
|
repositoryissue "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_issue/repository"
|
||||||
|
dtodomain "github.com/Caknoooo/go-gin-clean-starter/modules/inventory_return/dto"
|
||||||
|
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_return/query"
|
||||||
|
"github.com/Caknoooo/go-gin-clean-starter/modules/inventory_return/repository"
|
||||||
|
productrepository "github.com/Caknoooo/go-gin-clean-starter/modules/product/repository"
|
||||||
|
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InventoryReturnService interface {
|
||||||
|
Create(ctx context.Context, req dtodomain.InventoryReturnCreateRequest) (dtodomain.InventoryReturnResponse, error)
|
||||||
|
GetById(ctx context.Context, id string) (dtodomain.InventoryReturnResponse, error)
|
||||||
|
GetAll(ctx context.Context, filter query.InventoryReturnFilter) ([]dtodomain.InventoryReturnResponse, int64, error)
|
||||||
|
Update(ctx context.Context, req dtodomain.InventoryReturnUpdateRequest, id string) (dtodomain.InventoryReturnResponse, error)
|
||||||
|
Delete(ctx context.Context, id string) error
|
||||||
|
CreateLine(ctx context.Context, returnId string, req dtodomain.InventoryReturnLineCreateRequest) (dtodomain.InventoryReturnLineResponse, error)
|
||||||
|
UpdateLine(ctx context.Context, lineId string, req dtodomain.InventoryReturnLineUpdateRequest) (dtodomain.InventoryReturnLineResponse, error)
|
||||||
|
DeleteLine(ctx context.Context, lineId string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type inventoryReturnService struct {
|
||||||
|
db *gorm.DB
|
||||||
|
returnRepo repository.InventoryReturnRepository
|
||||||
|
returnLineRepo repository.InventoryReturnLineRepository
|
||||||
|
issueLineRepo repositoryissue.InventoryIssueLineRepository
|
||||||
|
productRepo productrepository.ProductRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteLine implements InventoryReturnService.
|
||||||
|
func (s *inventoryReturnService) DeleteLine(ctx context.Context, lineId string) error {
|
||||||
|
return s.returnLineRepo.Delete(ctx, nil, lineId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateLine implements InventoryReturnService.
|
||||||
|
func (s *inventoryReturnService) UpdateLine(ctx context.Context, lineId string, req dtodomain.InventoryReturnLineUpdateRequest) (dtodomain.InventoryReturnLineResponse, error) {
|
||||||
|
line, err := s.returnLineRepo.GetById(ctx, nil, lineId)
|
||||||
|
if err != nil {
|
||||||
|
return dtodomain.InventoryReturnLineResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ambil product untuk cek returnable
|
||||||
|
product, err := s.productRepo.GetById(ctx, nil, line.ProductID.String())
|
||||||
|
if err != nil {
|
||||||
|
return dtodomain.InventoryReturnLineResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Quantity != nil {
|
||||||
|
if product.IsReturnable {
|
||||||
|
return dtodomain.InventoryReturnLineResponse{},
|
||||||
|
fmt.Errorf("product is not returnable, quantity cannot be updated")
|
||||||
|
} else {
|
||||||
|
line.Quantity = *req.Quantity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if req.Attachment != nil {
|
||||||
|
line.Attachment = *req.Attachment
|
||||||
|
}
|
||||||
|
if req.InvReturnID != nil {
|
||||||
|
line.InvReturnID = uuid.MustParse(*req.InvReturnID)
|
||||||
|
}
|
||||||
|
if req.ProductID != nil {
|
||||||
|
line.ProductID = uuid.MustParse(*req.ProductID)
|
||||||
|
}
|
||||||
|
if req.ClientID != nil {
|
||||||
|
line.ClientID = uuid.MustParse(*req.ClientID)
|
||||||
|
}
|
||||||
|
updatedLine, err := s.returnLineRepo.Update(ctx, nil, line)
|
||||||
|
if err != nil {
|
||||||
|
return dtodomain.InventoryReturnLineResponse{}, err
|
||||||
|
}
|
||||||
|
return dtodomain.ToInventoryReturnLineResponse(updatedLine), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *inventoryReturnService) Create(ctx context.Context, req dtodomain.InventoryReturnCreateRequest) (dtodomain.InventoryReturnResponse, 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.InventoryReturnResponse{}, err
|
||||||
|
}
|
||||||
|
docNum, err := entities.GenerateDocumentNumberInventoryReturn(s.db, req.ClientID)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return dtodomain.InventoryReturnResponse{}, err
|
||||||
|
}
|
||||||
|
invIssueId, err := uuid.Parse(req.InvIssueID)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return dtodomain.InventoryReturnResponse{}, err
|
||||||
|
}
|
||||||
|
entity := entities.TInventoryReturnEntity{
|
||||||
|
DocumentNumber: docNum,
|
||||||
|
DocumentDate: utils.StringToDateTime(req.DocumentDate),
|
||||||
|
Notes: req.Notes,
|
||||||
|
Status: req.Status,
|
||||||
|
InvIssueID: invIssueId,
|
||||||
|
ClientID: clientUUID,
|
||||||
|
}
|
||||||
|
created, err := s.returnRepo.Create(ctx, tx, entity)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return dtodomain.InventoryReturnResponse{}, err
|
||||||
|
}
|
||||||
|
// Ambil semua line dari issue dan bulk insert ke return line
|
||||||
|
issueLines, err := s.issueLineRepo.GetAllByIssueId(ctx, req.InvIssueID)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return dtodomain.InventoryReturnResponse{}, err
|
||||||
|
}
|
||||||
|
var lines []entities.TInventoryReturnLineEntity
|
||||||
|
for _, issueLine := range issueLines {
|
||||||
|
// Ambil product untuk cek returnable
|
||||||
|
product, err := s.productRepo.GetById(ctx, nil, issueLine.ProductID.String())
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return dtodomain.InventoryReturnResponse{}, err
|
||||||
|
}
|
||||||
|
qty := 0.0
|
||||||
|
if product.IsReturnable {
|
||||||
|
qty = issueLine.IssuedQuantity
|
||||||
|
}
|
||||||
|
lines = append(lines, entities.TInventoryReturnLineEntity{
|
||||||
|
Quantity: qty,
|
||||||
|
Attachment: "", // isi sesuai kebutuhan
|
||||||
|
InvReturnID: created.ID,
|
||||||
|
ProductID: issueLine.ProductID,
|
||||||
|
ClientID: issueLine.ClientID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(lines) > 0 {
|
||||||
|
if err := s.returnLineRepo.BulkCreate(ctx, tx, lines); err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return dtodomain.InventoryReturnResponse{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Commit()
|
||||||
|
result, err := s.returnRepo.GetById(ctx, nil, created.ID.String())
|
||||||
|
if err != nil {
|
||||||
|
return dtodomain.InventoryReturnResponse{}, err
|
||||||
|
}
|
||||||
|
return dtodomain.ToInventoryReturnResponse(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *inventoryReturnService) GetById(ctx context.Context, id string) (dtodomain.InventoryReturnResponse, error) {
|
||||||
|
ret, err := s.returnRepo.GetById(ctx, nil, id)
|
||||||
|
if err != nil {
|
||||||
|
return dtodomain.InventoryReturnResponse{}, err
|
||||||
|
}
|
||||||
|
return dtodomain.ToInventoryReturnResponse(ret), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *inventoryReturnService) GetAll(ctx context.Context, filter query.InventoryReturnFilter) ([]dtodomain.InventoryReturnResponse, int64, error) {
|
||||||
|
rets, total, err := s.returnRepo.GetAll(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, total, err
|
||||||
|
}
|
||||||
|
var responses []dtodomain.InventoryReturnResponse
|
||||||
|
for _, ret := range rets {
|
||||||
|
responses = append(responses, dtodomain.ToInventoryReturnResponse(ret))
|
||||||
|
}
|
||||||
|
if responses == nil {
|
||||||
|
responses = make([]dtodomain.InventoryReturnResponse, 0)
|
||||||
|
}
|
||||||
|
return responses, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *inventoryReturnService) Update(ctx context.Context, req dtodomain.InventoryReturnUpdateRequest, id string) (dtodomain.InventoryReturnResponse, error) {
|
||||||
|
tx := s.db.Begin()
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
ret, err := s.returnRepo.GetById(ctx, tx, id)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return dtodomain.InventoryReturnResponse{}, err
|
||||||
|
}
|
||||||
|
ret.DocumentDate = utils.StringToDateTime(req.DocumentDate)
|
||||||
|
ret.Notes = req.Notes
|
||||||
|
ret.Status = req.Status
|
||||||
|
ret.InvIssueID = uuid.MustParse(req.InvIssueID)
|
||||||
|
ret.ClientID = uuid.MustParse(req.ClientID)
|
||||||
|
updated, err := s.returnRepo.Update(ctx, tx, ret)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return dtodomain.InventoryReturnResponse{}, err
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
result, err := s.returnRepo.GetById(ctx, nil, updated.ID.String())
|
||||||
|
if err != nil {
|
||||||
|
return dtodomain.InventoryReturnResponse{}, err
|
||||||
|
}
|
||||||
|
return dtodomain.ToInventoryReturnResponse(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *inventoryReturnService) Delete(ctx context.Context, id string) error {
|
||||||
|
tx := s.db.Begin()
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err := s.returnRepo.Delete(ctx, tx, id); err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *inventoryReturnService) CreateLine(ctx context.Context, returnId string, req dtodomain.InventoryReturnLineCreateRequest) (dtodomain.InventoryReturnLineResponse, error) {
|
||||||
|
invReturnUUID, err := uuid.Parse(returnId)
|
||||||
|
if err != nil {
|
||||||
|
return dtodomain.InventoryReturnLineResponse{}, err
|
||||||
|
}
|
||||||
|
productUUID, err := uuid.Parse(req.ProductID)
|
||||||
|
if err != nil {
|
||||||
|
return dtodomain.InventoryReturnLineResponse{}, err
|
||||||
|
}
|
||||||
|
clientUUID, err := uuid.Parse(req.ClientID)
|
||||||
|
if err != nil {
|
||||||
|
return dtodomain.InventoryReturnLineResponse{}, err
|
||||||
|
}
|
||||||
|
line := entities.TInventoryReturnLineEntity{
|
||||||
|
Quantity: req.Quantity,
|
||||||
|
Attachment: req.Attachment,
|
||||||
|
InvReturnID: invReturnUUID,
|
||||||
|
ProductID: productUUID,
|
||||||
|
ClientID: clientUUID,
|
||||||
|
}
|
||||||
|
created, err := s.returnLineRepo.Create(ctx, nil, line)
|
||||||
|
if err != nil {
|
||||||
|
return dtodomain.InventoryReturnLineResponse{}, err
|
||||||
|
}
|
||||||
|
return dtodomain.ToInventoryReturnLineResponse(created), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInventoryReturnService(
|
||||||
|
db *gorm.DB,
|
||||||
|
returnRepo repository.InventoryReturnRepository,
|
||||||
|
returnLineRepo repository.InventoryReturnLineRepository,
|
||||||
|
issueLineRepo repositoryissue.InventoryIssueLineRepository,
|
||||||
|
productRepo productrepository.ProductRepository,
|
||||||
|
) InventoryReturnService {
|
||||||
|
return &inventoryReturnService{
|
||||||
|
db: db,
|
||||||
|
returnRepo: returnRepo,
|
||||||
|
returnLineRepo: returnLineRepo,
|
||||||
|
issueLineRepo: issueLineRepo,
|
||||||
|
productRepo: productRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue