mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 05:21:57 +00:00
add api get list stock log by product warehouse id
This commit is contained in:
@@ -66,6 +66,7 @@ const (
|
||||
P_ProductStockGetOne = "lti.inventory.product_stock.detail"
|
||||
P_ProductWarehousekGetAll = "lti.inventory.product_warehouses.list"
|
||||
P_ProductWarehouseGetOne = "lti.inventory.product_warehouses.detail"
|
||||
P_StockLogGetAll = "lti.inventory.stock_log.list"
|
||||
)
|
||||
const (
|
||||
P_ClosingGetAll = "lti.closing.list"
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
adjustments "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments"
|
||||
productStocks "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-stocks"
|
||||
productWarehouses "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses"
|
||||
stockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/stock-logs"
|
||||
transfers "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers"
|
||||
// MODULE IMPORTS
|
||||
)
|
||||
@@ -23,6 +24,7 @@ func RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Valida
|
||||
adjustments.AdjustmentModule{},
|
||||
transfers.TransferModule{},
|
||||
productStocks.ProductStockModule{},
|
||||
stockLogs.StockLogModule{},
|
||||
// MODULE REGISTRY
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/stock-logs/validations"
|
||||
stockLogDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/stock-logs/dto"
|
||||
stockLogService "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/stock-logs/services"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/response"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type StockLogController struct {
|
||||
StockLogService stockLogService.StockLogService
|
||||
}
|
||||
|
||||
func NewStockLogController(s stockLogService.StockLogService) *StockLogController {
|
||||
return &StockLogController{
|
||||
StockLogService: s,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *StockLogController) GetAll(c *fiber.Ctx) error {
|
||||
query := &validation.Query{
|
||||
Page: c.QueryInt("page", 1),
|
||||
Limit: c.QueryInt("limit", 10),
|
||||
ProductWarehouseID: uint(c.QueryInt("product_warehouse_id", 0)),
|
||||
}
|
||||
|
||||
if query.Page < 1 || query.Limit < 1 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0")
|
||||
}
|
||||
|
||||
// Export to Excel
|
||||
if strings.EqualFold(strings.TrimSpace(c.Query("export")), "excel") {
|
||||
if query.ProductWarehouseID == 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "product_warehouse_id is required for export")
|
||||
}
|
||||
results, err := u.StockLogService.GetAllForExport(c, query.ProductWarehouseID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return exportStockLogListExcel(c, results)
|
||||
}
|
||||
|
||||
result, totalResults, err := u.StockLogService.GetAll(c, query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.SuccessWithPaginate[stockLogDTO.StockLogListDTO]{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: "Get all stock logs successfully",
|
||||
Meta: response.Meta{
|
||||
Page: query.Page,
|
||||
Limit: query.Limit,
|
||||
TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
|
||||
TotalResults: totalResults,
|
||||
},
|
||||
Data: stockLogDTO.ToStockLogListDTOs(result),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
func exportStockLogListExcel(c *fiber.Ctx, stockLogs []entity.StockLog) error {
|
||||
file := excelize.NewFile()
|
||||
defer file.Close()
|
||||
|
||||
sheet := "Stock Logs"
|
||||
file.SetSheetName("Sheet1", sheet)
|
||||
|
||||
headers := []string{
|
||||
"ID",
|
||||
"Tanggal",
|
||||
"Gudang",
|
||||
"Stok Akhir",
|
||||
"Peningkatan",
|
||||
"Penurunan",
|
||||
"Jenis Transaksi",
|
||||
"Catatan",
|
||||
"Oleh",
|
||||
}
|
||||
|
||||
// Column widths
|
||||
colWidths := map[string]float64{
|
||||
"A": 8,
|
||||
"B": 20,
|
||||
"C": 25,
|
||||
"D": 14,
|
||||
"E": 14,
|
||||
"F": 14,
|
||||
"G": 20,
|
||||
"H": 30,
|
||||
"I": 20,
|
||||
}
|
||||
for col, width := range colWidths {
|
||||
file.SetColWidth(sheet, col, col, width)
|
||||
}
|
||||
|
||||
// Header style
|
||||
headerStyle, _ := file.NewStyle(&excelize.Style{
|
||||
Font: &excelize.Font{
|
||||
Bold: true,
|
||||
Size: 11,
|
||||
},
|
||||
Fill: excelize.Fill{
|
||||
Type: "pattern",
|
||||
Pattern: 1,
|
||||
Color: []string{"D9E1F2"},
|
||||
},
|
||||
Border: []excelize.Border{
|
||||
{Type: "bottom", Style: 1, Color: "000000"},
|
||||
},
|
||||
})
|
||||
|
||||
// Write header row
|
||||
for i, h := range headers {
|
||||
cell, _ := excelize.CoordinatesToCellName(i+1, 1)
|
||||
file.SetCellValue(sheet, cell, h)
|
||||
file.SetCellStyle(sheet, cell, cell, headerStyle)
|
||||
}
|
||||
|
||||
// Freeze header row
|
||||
file.SetPanes(sheet, &excelize.Panes{
|
||||
Freeze: true,
|
||||
YSplit: 1,
|
||||
TopLeftCell: "A2",
|
||||
ActivePane: "bottomLeft",
|
||||
})
|
||||
|
||||
// Write data rows
|
||||
for i, log := range stockLogs {
|
||||
row := i + 2
|
||||
|
||||
warehouseName := ""
|
||||
if log.ProductWarehouse != nil {
|
||||
warehouseName = log.ProductWarehouse.Warehouse.Name
|
||||
}
|
||||
|
||||
userName := ""
|
||||
if log.CreatedUser != nil {
|
||||
userName = log.CreatedUser.Name
|
||||
}
|
||||
|
||||
notes := ""
|
||||
if log.Notes != "" {
|
||||
notes = log.Notes
|
||||
}
|
||||
|
||||
file.SetCellInt(sheet, fmt.Sprintf("A%d", row), int(log.Id))
|
||||
file.SetCellValue(sheet, fmt.Sprintf("B%d", row), log.CreatedAt.Format("2006-01-02 15:04:05"))
|
||||
file.SetCellValue(sheet, fmt.Sprintf("C%d", row), warehouseName)
|
||||
file.SetCellFloat(sheet, fmt.Sprintf("D%d", row), log.Stock, 3, 64)
|
||||
file.SetCellFloat(sheet, fmt.Sprintf("E%d", row), log.Increase, 3, 64)
|
||||
file.SetCellFloat(sheet, fmt.Sprintf("F%d", row), log.Decrease, 3, 64)
|
||||
file.SetCellValue(sheet, fmt.Sprintf("G%d", row), log.LoggableType)
|
||||
file.SetCellValue(sheet, fmt.Sprintf("H%d", row), notes)
|
||||
file.SetCellValue(sheet, fmt.Sprintf("I%d", row), userName)
|
||||
}
|
||||
|
||||
buffer, err := file.WriteToBuffer()
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "failed to generate excel file")
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("stock_logs_%s.xlsx", time.Now().Format("20060102_150405"))
|
||||
c.Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
|
||||
return c.Status(fiber.StatusOK).Send(buffer.Bytes())
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
|
||||
)
|
||||
|
||||
type StockLogListDTO struct {
|
||||
Id uint `json:"id"`
|
||||
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||
Increase float64 `json:"increase"`
|
||||
Decrease float64 `json:"decrease"`
|
||||
Stock float64 `json:"stock"`
|
||||
LoggableType string `json:"loggable_type"`
|
||||
LoggableId uint `json:"loggable_id"`
|
||||
Notes *string `json:"notes"`
|
||||
CreatedBy uint `json:"created_by"`
|
||||
CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
func ToStockLogListDTO(e entity.StockLog) StockLogListDTO {
|
||||
var notes *string
|
||||
if e.Notes != "" {
|
||||
n := e.Notes
|
||||
notes = &n
|
||||
}
|
||||
|
||||
var createdUser *userDTO.UserRelationDTO
|
||||
if e.CreatedUser != nil && e.CreatedUser.Id != 0 {
|
||||
mapped := userDTO.ToUserRelationDTO(*e.CreatedUser)
|
||||
createdUser = &mapped
|
||||
}
|
||||
|
||||
return StockLogListDTO{
|
||||
Id: e.Id,
|
||||
ProductWarehouseId: e.ProductWarehouseId,
|
||||
Increase: e.Increase,
|
||||
Decrease: e.Decrease,
|
||||
Stock: e.Stock,
|
||||
LoggableType: e.LoggableType,
|
||||
LoggableId: e.LoggableId,
|
||||
Notes: notes,
|
||||
CreatedBy: e.CreatedBy,
|
||||
CreatedUser: createdUser,
|
||||
CreatedAt: e.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func ToStockLogListDTOs(e []entity.StockLog) []StockLogListDTO {
|
||||
if len(e) == 0 {
|
||||
return []StockLogListDTO{}
|
||||
}
|
||||
result := make([]StockLogListDTO, len(e))
|
||||
for i, log := range e {
|
||||
result[i] = ToStockLogListDTO(log)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package stockLogs
|
||||
|
||||
import (
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gorm"
|
||||
|
||||
stockLogService "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/stock-logs/services"
|
||||
stockLogRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
||||
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
||||
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||
)
|
||||
|
||||
type StockLogModule struct{}
|
||||
|
||||
func (StockLogModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) {
|
||||
userRepo := rUser.NewUserRepository(db)
|
||||
userService := sUser.NewUserService(userRepo, validate)
|
||||
|
||||
stockLogRepo := stockLogRepo.NewStockLogRepository(db)
|
||||
service := stockLogService.NewStockLogService(stockLogRepo, validate)
|
||||
|
||||
StockLogRoutes(router, userService, service)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package stockLogs
|
||||
|
||||
import (
|
||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/stock-logs/controllers"
|
||||
stockLog "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/stock-logs/services"
|
||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func StockLogRoutes(v1 fiber.Router, u user.UserService, s stockLog.StockLogService) {
|
||||
ctrl := controller.NewStockLogController(s)
|
||||
|
||||
route := v1.Group("/stock-logs")
|
||||
route.Use(m.Auth(u))
|
||||
|
||||
route.Get("/", m.RequirePermissions(m.P_StockLogGetAll), ctrl.GetAll)
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/stock-logs/validations"
|
||||
stockLogRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type StockLogService interface {
|
||||
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.StockLog, int64, error)
|
||||
GetAllForExport(ctx *fiber.Ctx, productWarehouseID uint) ([]entity.StockLog, error)
|
||||
}
|
||||
|
||||
type stockLogService struct {
|
||||
Log *logrus.Logger
|
||||
Validate *validator.Validate
|
||||
StockLogRepo stockLogRepo.StockLogRepository
|
||||
}
|
||||
|
||||
func NewStockLogService(
|
||||
stockLogRepo stockLogRepo.StockLogRepository,
|
||||
validate *validator.Validate,
|
||||
) StockLogService {
|
||||
return &stockLogService{
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
StockLogRepo: stockLogRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stockLogService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.StockLog, int64, error) {
|
||||
if err := s.Validate.Struct(params); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
locationScope, areaScope, err := m.ResolveLocationAreaScopes(c, s.StockLogRepo.DB())
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
offset := (params.Page - 1) * params.Limit
|
||||
|
||||
stockLogs, total, err := s.StockLogRepo.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||
db = db.Where("product_warehouse_id = ?", params.ProductWarehouseID)
|
||||
|
||||
if locationScope.Restrict || areaScope.Restrict {
|
||||
if (locationScope.Restrict && len(locationScope.IDs) == 0) || (areaScope.Restrict && len(areaScope.IDs) == 0) {
|
||||
return db.Where("1 = 0")
|
||||
}
|
||||
db = db.
|
||||
Joins("JOIN product_warehouses pw ON pw.id = stock_logs.product_warehouse_id").
|
||||
Joins("JOIN warehouses w ON w.id = pw.warehouse_id")
|
||||
if locationScope.Restrict {
|
||||
db = db.Where("w.location_id IN ?", locationScope.IDs)
|
||||
}
|
||||
if areaScope.Restrict {
|
||||
db = db.Where("w.area_id IN ?", areaScope.IDs)
|
||||
}
|
||||
}
|
||||
|
||||
db = db.
|
||||
Preload("CreatedUser").
|
||||
Order("stock_logs.created_at DESC")
|
||||
|
||||
return db
|
||||
})
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to get stock logs: %+v", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if total == 0 {
|
||||
return []entity.StockLog{}, 0, nil
|
||||
}
|
||||
|
||||
return stockLogs, total, nil
|
||||
}
|
||||
|
||||
func (s *stockLogService) GetAllForExport(c *fiber.Ctx, productWarehouseID uint) ([]entity.StockLog, error) {
|
||||
locationScope, areaScope, err := m.ResolveLocationAreaScopes(c, s.StockLogRepo.DB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stockLogs, _, err := s.StockLogRepo.GetAll(c.Context(), 0, -1, func(db *gorm.DB) *gorm.DB {
|
||||
db = db.Where("product_warehouse_id = ?", productWarehouseID)
|
||||
|
||||
if locationScope.Restrict || areaScope.Restrict {
|
||||
if (locationScope.Restrict && len(locationScope.IDs) == 0) || (areaScope.Restrict && len(areaScope.IDs) == 0) {
|
||||
return db.Where("1 = 0")
|
||||
}
|
||||
db = db.
|
||||
Joins("JOIN product_warehouses pw ON pw.id = stock_logs.product_warehouse_id").
|
||||
Joins("JOIN warehouses w ON w.id = pw.warehouse_id")
|
||||
if locationScope.Restrict {
|
||||
db = db.Where("w.location_id IN ?", locationScope.IDs)
|
||||
}
|
||||
if areaScope.Restrict {
|
||||
db = db.Where("w.area_id IN ?", areaScope.IDs)
|
||||
}
|
||||
}
|
||||
|
||||
db = db.
|
||||
Preload("CreatedUser").
|
||||
Preload("ProductWarehouse").
|
||||
Preload("ProductWarehouse.Warehouse").
|
||||
Order("stock_logs.created_at ASC")
|
||||
|
||||
return db
|
||||
})
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to get stock logs for export: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return stockLogs, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package validation
|
||||
|
||||
type Query struct {
|
||||
Page int `query:"page" validate:"omitempty,number,min=1,gt=0"`
|
||||
Limit int `query:"limit" validate:"omitempty,number,min=1,gt=0"`
|
||||
ProductWarehouseID uint `query:"product_warehouse_id" validate:"required,gt=0"`
|
||||
}
|
||||
Reference in New Issue
Block a user