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