mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
Merge branch 'feat/BE/Sprint-6' of https://gitlab.com/mbugroup/lti-api into dev/teguh
This commit is contained in:
@@ -20,5 +20,6 @@ type Kandang struct {
|
|||||||
CreatedUser User `gorm:"foreignKey:CreatedBy;references:Id"`
|
CreatedUser User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||||
Location Location `gorm:"foreignKey:LocationId;references:Id"`
|
Location Location `gorm:"foreignKey:LocationId;references:Id"`
|
||||||
Pic User `gorm:"foreignKey:PicId;references:Id"`
|
Pic User `gorm:"foreignKey:PicId;references:Id"`
|
||||||
|
Warehouses []Warehouse `gorm:"foreignKey:KandangId;references:Id"`
|
||||||
ProjectFlockKandangs []ProjectFlockKandang `gorm:"foreignKey:KandangId;references:Id" json:"-"`
|
ProjectFlockKandangs []ProjectFlockKandang `gorm:"foreignKey:KandangId;references:Id" json:"-"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/closings/dto"
|
"gitlab.com/mbugroup/lti-api.git/internal/modules/closings/dto"
|
||||||
service "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/services"
|
service "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/services"
|
||||||
@@ -39,17 +40,17 @@ func (u *ClosingController) GetAll(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return c.Status(fiber.StatusOK).
|
return c.Status(fiber.StatusOK).
|
||||||
JSON(response.SuccessWithPaginate[dto.ClosingListDTO]{
|
JSON(response.SuccessWithPaginate[dto.ClosingListItemDTO]{
|
||||||
Code: fiber.StatusOK,
|
Code: fiber.StatusOK,
|
||||||
Status: "success",
|
Status: "success",
|
||||||
Message: "Get all closings successfully",
|
Message: "Retrieved closing projects list successfully",
|
||||||
Meta: response.Meta{
|
Meta: response.Meta{
|
||||||
Page: query.Page,
|
Page: query.Page,
|
||||||
Limit: query.Limit,
|
Limit: query.Limit,
|
||||||
TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
|
TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
|
||||||
TotalResults: totalResults,
|
TotalResults: totalResults,
|
||||||
},
|
},
|
||||||
Data: dto.ToClosingListDTOs(result),
|
Data: result,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,3 +146,45 @@ func (u *ClosingController) GetOverhead(c *fiber.Ctx) error {
|
|||||||
Data: result,
|
Data: result,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *ClosingController) GetClosingSapronak(c *fiber.Ctx) error {
|
||||||
|
param := c.Params("projectFlockId")
|
||||||
|
|
||||||
|
id, err := strconv.Atoi(param)
|
||||||
|
if err != nil || id <= 0 {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid projectFlockId")
|
||||||
|
}
|
||||||
|
|
||||||
|
query := &validation.SapronakQuery{
|
||||||
|
Type: strings.ToLower(c.Query("type")),
|
||||||
|
Page: c.QueryInt("page", 1),
|
||||||
|
Limit: c.QueryInt("limit", 10),
|
||||||
|
}
|
||||||
|
|
||||||
|
if query.Page < 1 || query.Limit < 1 {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if query.Type != validation.SapronakTypeIncoming && query.Type != validation.SapronakTypeOutgoing {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "type must be either incoming or outgoing")
|
||||||
|
}
|
||||||
|
|
||||||
|
result, totalResults, err := u.ClosingService.GetClosingSapronak(c, uint(id), query)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).
|
||||||
|
JSON(response.SuccessWithPaginate[dto.ClosingSapronakItemDTO]{
|
||||||
|
Code: fiber.StatusOK,
|
||||||
|
Status: "success",
|
||||||
|
Message: "Retrieved closing report (sapronak) successfully",
|
||||||
|
Meta: response.Meta{
|
||||||
|
Page: query.Page,
|
||||||
|
Limit: query.Limit,
|
||||||
|
TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
|
||||||
|
TotalResults: totalResults,
|
||||||
|
},
|
||||||
|
Data: result,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,20 +27,35 @@ type ClosingDetailDTO struct {
|
|||||||
ClosingListDTO
|
ClosingListDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ClosingListItemDTO struct {
|
||||||
|
Id uint `json:"id"`
|
||||||
|
LocationID uint `json:"location_id"`
|
||||||
|
LocationName string `json:"location_name"`
|
||||||
|
ProjectCategory string `json:"project_category"`
|
||||||
|
Period int `json:"period"`
|
||||||
|
ClosingDate string `json:"closing_date"`
|
||||||
|
ShedLabel string `json:"shed_label"`
|
||||||
|
ShedCount int `json:"shed_count"`
|
||||||
|
SalesPaidAmount int64 `json:"sales_paid_amount"`
|
||||||
|
SalesRemainingAmount int64 `json:"sales_remaining_amount"`
|
||||||
|
SalesPaymentStatus string `json:"sales_payment_status"`
|
||||||
|
ProjectStatus string `json:"project_status"`
|
||||||
|
}
|
||||||
|
|
||||||
type ClosingSummaryDTO struct {
|
type ClosingSummaryDTO struct {
|
||||||
LocationID uint `json:"location_id"`
|
FlockID uint `json:"flock_id"`
|
||||||
Periode int `json:"periode"`
|
Period int `json:"period"`
|
||||||
JenisProduk string `json:"jenis_produk"`
|
// JenisProduk string `json:"jenis_produk"`
|
||||||
LabelPopulasi string `json:"label_populasi"`
|
// LabelPopulasi string `json:"label_populasi"`
|
||||||
JumlahPopulasi int `json:"jumlah_populasi"`
|
Population int `json:"population"`
|
||||||
JumlahPopulasiFormatted string `json:"jumlah_populasi_formatted"`
|
PopulationFormatted string `json:"population_formatted"`
|
||||||
JenisProject string `json:"jenis_project"`
|
ProjectType string `json:"project_type"`
|
||||||
KandangAktif int `json:"kandang_aktif"`
|
ActiveHouseCount int `json:"active_house_count"`
|
||||||
KandangAktifFormatted string `json:"kandang_aktif_formatted"`
|
ActiveHouseLabel string `json:"active_house_label"`
|
||||||
StatusPembayaranPenjualan string `json:"status_pembayaran_penjualan"`
|
SalesPaymentStatus string `json:"sales_payment_status"`
|
||||||
StatusPembayaranMitra string `json:"status_pembayaran_mitra"`
|
// StatusPembayaranMitra string `json:"status_pembayaran_mitra"`
|
||||||
StatusProject string `json:"status_project"`
|
StatusProject string `json:"project_status"`
|
||||||
StatusClosing string `json:"status_closing"`
|
StatusClosing string `json:"closing_status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToClosingSummaryDTO(project entity.ProjectFlock, statusProject, statusClosing string) ClosingSummaryDTO {
|
func ToClosingSummaryDTO(project entity.ProjectFlock, statusProject, statusClosing string) ClosingSummaryDTO {
|
||||||
@@ -52,19 +67,38 @@ func ToClosingSummaryDTO(project entity.ProjectFlock, statusProject, statusClosi
|
|||||||
populationInt := int(population)
|
populationInt := int(population)
|
||||||
|
|
||||||
return ClosingSummaryDTO{
|
return ClosingSummaryDTO{
|
||||||
LocationID: project.LocationId,
|
FlockID: project.Id,
|
||||||
Periode: period,
|
Period: period,
|
||||||
JenisProduk: project.Category,
|
// JenisProduk: project.Category,
|
||||||
LabelPopulasi: "",
|
// LabelPopulasi: "",
|
||||||
JumlahPopulasi: populationInt,
|
Population: populationInt,
|
||||||
JumlahPopulasiFormatted: fmt.Sprintf("%d Ekor", populationInt),
|
PopulationFormatted: fmt.Sprintf("%d Ekor", populationInt),
|
||||||
JenisProject: "",
|
ProjectType: project.Category,
|
||||||
KandangAktif: kandangCount,
|
ActiveHouseCount: kandangCount,
|
||||||
KandangAktifFormatted: fmt.Sprintf("%d Kandang", kandangCount),
|
ActiveHouseLabel: fmt.Sprintf("%d Kandang", kandangCount),
|
||||||
StatusPembayaranPenjualan: "Tempo",
|
SalesPaymentStatus: "Tempo",
|
||||||
StatusPembayaranMitra: "",
|
// StatusPembayaranMitra: "",
|
||||||
StatusProject: statusProject,
|
StatusProject: statusProject,
|
||||||
StatusClosing: statusClosing,
|
StatusClosing: statusClosing,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToClosingListItemDTO(project entity.ProjectFlock, projectStatus string) ClosingListItemDTO {
|
||||||
|
shedCount := len(project.KandangHistory)
|
||||||
|
|
||||||
|
return ClosingListItemDTO{
|
||||||
|
Id: project.Id,
|
||||||
|
LocationID: project.LocationId,
|
||||||
|
LocationName: project.Location.Name,
|
||||||
|
ProjectCategory: project.Category,
|
||||||
|
Period: maxPeriod(project.KandangHistory),
|
||||||
|
ClosingDate: "17-Nov-2025",
|
||||||
|
ShedLabel: fmt.Sprintf("%d Kandang", shedCount),
|
||||||
|
ShedCount: shedCount,
|
||||||
|
SalesPaidAmount: 21993726,
|
||||||
|
SalesRemainingAmount: 11075919,
|
||||||
|
SalesPaymentStatus: "Lunas",
|
||||||
|
ProjectStatus: projectStatus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type ClosingSapronakItemDTO struct {
|
||||||
|
Id uint64 `json:"id"`
|
||||||
|
Date string `json:"date"`
|
||||||
|
ReferenceNumber string `json:"reference_number"`
|
||||||
|
TransactionType string `json:"transaction_type"`
|
||||||
|
ProductName string `json:"product_name"`
|
||||||
|
ProductCategory string `json:"product_category"`
|
||||||
|
ProductSubCategory string `json:"product_sub_category"`
|
||||||
|
SourceWarehouse string `json:"source_warehouse"`
|
||||||
|
DestinationWarehouse string `json:"destination_warehouse,omitempty"`
|
||||||
|
// Destination string `json:"destination,omitempty"`
|
||||||
|
Quantity float64 `json:"quantity"`
|
||||||
|
Unit string `json:"unit"`
|
||||||
|
FormattedQuantity string `json:"formatted_quantity"`
|
||||||
|
Notes string `json:"notes"`
|
||||||
|
SortDate time.Time `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClosingSapronakDTO struct {
|
||||||
|
IncomingSapronak []ClosingSapronakItemDTO `json:"incoming_sapronak"`
|
||||||
|
OutgoingSapronak []ClosingSapronakItemDTO `json:"outgoing_sapronak"`
|
||||||
|
}
|
||||||
@@ -1,13 +1,20 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
|
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClosingRepository interface {
|
type ClosingRepository interface {
|
||||||
repository.BaseRepository[entity.ProjectFlock]
|
repository.BaseRepository[entity.ProjectFlock]
|
||||||
|
GetSapronak(ctx context.Context, params SapronakQueryParams) ([]SapronakRow, int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClosingRepositoryImpl struct {
|
type ClosingRepositoryImpl struct {
|
||||||
@@ -19,3 +26,199 @@ func NewClosingRepository(db *gorm.DB) ClosingRepository {
|
|||||||
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectFlock](db),
|
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectFlock](db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SapronakRow struct {
|
||||||
|
Id uint64 `gorm:"column:id"`
|
||||||
|
SortDate time.Time `gorm:"column:sort_date"`
|
||||||
|
DateText string `gorm:"column:date_text"`
|
||||||
|
ReferenceNumber string `gorm:"column:reference_number"`
|
||||||
|
TransactionType string `gorm:"column:transaction_type"`
|
||||||
|
ProductName string `gorm:"column:product_name"`
|
||||||
|
ProductCategory string `gorm:"column:product_category"`
|
||||||
|
ProductSubCategory string `gorm:"column:product_sub_category"`
|
||||||
|
SourceWarehouse string `gorm:"column:source_warehouse"`
|
||||||
|
DestinationWarehouse string `gorm:"column:destination_warehouse"`
|
||||||
|
Destination string `gorm:"column:destination"`
|
||||||
|
Quantity float64 `gorm:"column:quantity"`
|
||||||
|
Unit string `gorm:"column:unit"`
|
||||||
|
Notes string `gorm:"column:notes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SapronakQueryParams struct {
|
||||||
|
Type string
|
||||||
|
WarehouseIDs []uint
|
||||||
|
ProjectFlockKandangIDs []uint
|
||||||
|
Limit int
|
||||||
|
Offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ClosingRepositoryImpl) GetSapronak(ctx context.Context, params SapronakQueryParams) ([]SapronakRow, int64, error) {
|
||||||
|
db := r.DB().WithContext(ctx)
|
||||||
|
|
||||||
|
var (
|
||||||
|
unionParts []string
|
||||||
|
args []any
|
||||||
|
)
|
||||||
|
|
||||||
|
switch params.Type {
|
||||||
|
case validation.SapronakTypeIncoming:
|
||||||
|
if len(params.WarehouseIDs) == 0 {
|
||||||
|
return []SapronakRow{}, 0, nil
|
||||||
|
}
|
||||||
|
unionParts = append(unionParts, sapronakIncomingPurchasesSQL, sapronakIncomingTransfersSQL)
|
||||||
|
args = append(args, params.WarehouseIDs, params.WarehouseIDs)
|
||||||
|
case validation.SapronakTypeOutgoing:
|
||||||
|
if len(params.WarehouseIDs) > 0 {
|
||||||
|
unionParts = append(unionParts, sapronakOutgoingTransfersSQL)
|
||||||
|
args = append(args, params.WarehouseIDs)
|
||||||
|
}
|
||||||
|
if len(params.ProjectFlockKandangIDs) > 0 {
|
||||||
|
unionParts = append(unionParts, sapronakOutgoingMarketingsSQL)
|
||||||
|
args = append(args, params.ProjectFlockKandangIDs)
|
||||||
|
}
|
||||||
|
if len(unionParts) == 0 {
|
||||||
|
return []SapronakRow{}, 0, nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, 0, fmt.Errorf("invalid sapronak type: %s", params.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
unionSQL := strings.Join(unionParts, " UNION ALL ")
|
||||||
|
|
||||||
|
var totalResults int64
|
||||||
|
countSQL := fmt.Sprintf("SELECT COUNT(*) FROM (%s) AS combined", unionSQL)
|
||||||
|
if err := db.Raw(countSQL, args...).Scan(&totalResults).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dataArgs := append(append([]any{}, args...), params.Limit, params.Offset)
|
||||||
|
dataSQL := fmt.Sprintf("SELECT * FROM (%s) AS combined ORDER BY sort_date ASC, id ASC LIMIT ? OFFSET ?", unionSQL)
|
||||||
|
|
||||||
|
var rows []SapronakRow
|
||||||
|
if err := db.Raw(dataSQL, dataArgs...).Scan(&rows).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows, totalResults, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
sapronakIncomingPurchasesSQL = `
|
||||||
|
SELECT
|
||||||
|
CAST(pi.id AS BIGINT) AS id,
|
||||||
|
COALESCE(pi.received_date, '1970-01-01') AS sort_date,
|
||||||
|
COALESCE(TO_CHAR(pi.received_date, 'DD-Mon-YYYY'), '') AS date_text,
|
||||||
|
COALESCE(p.po_number, '') AS reference_number,
|
||||||
|
'Purchase' AS transaction_type,
|
||||||
|
prod.name AS product_name,
|
||||||
|
pc.name AS product_category,
|
||||||
|
COALESCE((
|
||||||
|
SELECT string_agg(f.name, ' ')
|
||||||
|
FROM flags f
|
||||||
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
||||||
|
), '') AS product_sub_category,
|
||||||
|
'External Supplier' AS source_warehouse,
|
||||||
|
w.name AS destination_warehouse,
|
||||||
|
'' AS destination,
|
||||||
|
pi.total_qty AS quantity,
|
||||||
|
u.name AS unit,
|
||||||
|
COALESCE(p.notes, '') AS notes
|
||||||
|
FROM purchase_items pi
|
||||||
|
JOIN purchases p ON p.id = pi.purchase_id
|
||||||
|
JOIN products prod ON prod.id = pi.product_id
|
||||||
|
JOIN product_categories pc ON pc.id = prod.product_category_id
|
||||||
|
JOIN uoms u ON u.id = prod.uom_id
|
||||||
|
JOIN warehouses w ON w.id = pi.warehouse_id
|
||||||
|
WHERE pi.warehouse_id IN ?
|
||||||
|
`
|
||||||
|
|
||||||
|
sapronakIncomingTransfersSQL = `
|
||||||
|
SELECT
|
||||||
|
CAST(st.id AS BIGINT) AS id,
|
||||||
|
st.transfer_date AS sort_date,
|
||||||
|
TO_CHAR(st.transfer_date, 'DD-Mon-YYYY') AS date_text,
|
||||||
|
st.movement_number AS reference_number,
|
||||||
|
'Internal Transfer In' AS transaction_type,
|
||||||
|
prod.name AS product_name,
|
||||||
|
pc.name AS product_category,
|
||||||
|
COALESCE((
|
||||||
|
SELECT string_agg(f.name, ' ')
|
||||||
|
FROM flags f
|
||||||
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
||||||
|
), '') AS product_sub_category,
|
||||||
|
COALESCE(fw.name, '') AS source_warehouse,
|
||||||
|
COALESCE(tw.name, '') AS destination_warehouse,
|
||||||
|
'' AS destination,
|
||||||
|
std.quantity AS quantity,
|
||||||
|
u.name AS unit,
|
||||||
|
'Stock Refill' AS notes
|
||||||
|
FROM stock_transfer_details std
|
||||||
|
JOIN stock_transfers st ON st.id = std.stock_transfer_id
|
||||||
|
LEFT JOIN warehouses fw ON fw.id = st.from_warehouse_id
|
||||||
|
LEFT JOIN warehouses tw ON tw.id = st.to_warehouse_id
|
||||||
|
JOIN products prod ON prod.id = std.product_id
|
||||||
|
JOIN product_categories pc ON pc.id = prod.product_category_id
|
||||||
|
JOIN uoms u ON u.id = prod.uom_id
|
||||||
|
WHERE st.to_warehouse_id IN ?
|
||||||
|
`
|
||||||
|
|
||||||
|
sapronakOutgoingTransfersSQL = `
|
||||||
|
SELECT
|
||||||
|
CAST(st.id AS BIGINT) AS id,
|
||||||
|
st.transfer_date AS sort_date,
|
||||||
|
TO_CHAR(st.transfer_date, 'DD-Mon-YYYY') AS date_text,
|
||||||
|
st.movement_number AS reference_number,
|
||||||
|
'Internal Transfer Out' AS transaction_type,
|
||||||
|
prod.name AS product_name,
|
||||||
|
pc.name AS product_category,
|
||||||
|
COALESCE((
|
||||||
|
SELECT string_agg(f.name, ' ')
|
||||||
|
FROM flags f
|
||||||
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
||||||
|
), '') AS product_sub_category,
|
||||||
|
COALESCE(fw.name, '') AS source_warehouse,
|
||||||
|
'' AS destination_warehouse,
|
||||||
|
COALESCE(tw.name, '') AS destination,
|
||||||
|
std.quantity AS quantity,
|
||||||
|
u.name AS unit,
|
||||||
|
'Transfer to other unit' AS notes
|
||||||
|
FROM stock_transfer_details std
|
||||||
|
JOIN stock_transfers st ON st.id = std.stock_transfer_id
|
||||||
|
LEFT JOIN warehouses fw ON fw.id = st.from_warehouse_id
|
||||||
|
LEFT JOIN warehouses tw ON tw.id = st.to_warehouse_id
|
||||||
|
JOIN products prod ON prod.id = std.product_id
|
||||||
|
JOIN product_categories pc ON pc.id = prod.product_category_id
|
||||||
|
JOIN uoms u ON u.id = prod.uom_id
|
||||||
|
WHERE st.from_warehouse_id IN ?
|
||||||
|
`
|
||||||
|
|
||||||
|
sapronakOutgoingMarketingsSQL = `
|
||||||
|
SELECT
|
||||||
|
CAST(mp.id AS BIGINT) AS id,
|
||||||
|
m.so_date AS sort_date,
|
||||||
|
TO_CHAR(m.so_date, 'DD-Mon-YYYY') AS date_text,
|
||||||
|
m.so_number AS reference_number,
|
||||||
|
'Trading Sales' AS transaction_type,
|
||||||
|
prod.name AS product_name,
|
||||||
|
pc.name AS product_category,
|
||||||
|
COALESCE((
|
||||||
|
SELECT string_agg(f.name, ' ')
|
||||||
|
FROM flags f
|
||||||
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
||||||
|
), '') AS product_sub_category,
|
||||||
|
w.name AS source_warehouse,
|
||||||
|
'' AS destination_warehouse,
|
||||||
|
'RETAIL CUSTOMER' AS destination,
|
||||||
|
mp.qty AS quantity,
|
||||||
|
u.name AS unit,
|
||||||
|
m.notes AS notes
|
||||||
|
FROM marketing_products mp
|
||||||
|
JOIN marketings m ON m.id = mp.marketing_id
|
||||||
|
JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id
|
||||||
|
JOIN products prod ON prod.id = pw.product_id
|
||||||
|
JOIN product_categories pc ON pc.id = prod.product_category_id
|
||||||
|
JOIN uoms u ON u.id = prod.uom_id
|
||||||
|
JOIN warehouses w ON w.id = pw.warehouse_id
|
||||||
|
WHERE pw.project_flock_kandang_id IN ?
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService) {
|
func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService) {
|
||||||
ctrl := controller.NewClosingController(s)
|
ctrl := controller.NewClosingController(s)
|
||||||
|
|
||||||
route := v1.Group("/closing")
|
route := v1.Group("/closings")
|
||||||
|
|
||||||
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
@@ -24,5 +24,5 @@ func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService
|
|||||||
route.Get("/:project_flock_id/penjualan", ctrl.GetPenjualan)
|
route.Get("/:project_flock_id/penjualan", ctrl.GetPenjualan)
|
||||||
route.Get("/:project_flock_id/overhead", ctrl.GetOverhead)
|
route.Get("/:project_flock_id/overhead", ctrl.GetOverhead)
|
||||||
route.Get("/:projectFlockId", ctrl.GetClosingSummary)
|
route.Get("/:projectFlockId", ctrl.GetClosingSummary)
|
||||||
|
route.Get("/:projectFlockId/sapronak", ctrl.GetClosingSapronak)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
@@ -24,11 +25,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ClosingService interface {
|
type ClosingService interface {
|
||||||
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error)
|
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]dto.ClosingListItemDTO, int64, error)
|
||||||
GetProjectFlockByID(ctx *fiber.Ctx, id uint) (*entity.ProjectFlock, error)
|
GetProjectFlockByID(ctx *fiber.Ctx, id uint) (*entity.ProjectFlock, error)
|
||||||
GetPenjualan(ctx *fiber.Ctx, projectFlockID uint) ([]entity.MarketingDeliveryProduct, error)
|
GetPenjualan(ctx *fiber.Ctx, projectFlockID uint) ([]entity.MarketingDeliveryProduct, error)
|
||||||
GetClosingSummary(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingSummaryDTO, error)
|
GetClosingSummary(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingSummaryDTO, error)
|
||||||
GetOverhead(ctx *fiber.Ctx, projectFlockID uint) (*dto.OverheadListDTO, error)
|
GetOverhead(ctx *fiber.Ctx, projectFlockID uint) (*dto.OverheadListDTO, error)
|
||||||
|
GetClosingSapronak(ctx *fiber.Ctx, projectFlockID uint, params *validation.SapronakQuery) ([]dto.ClosingSapronakItemDTO, int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type closingService struct {
|
type closingService struct {
|
||||||
@@ -65,11 +67,12 @@ func (s closingService) withRelations(db *gorm.DB) *gorm.DB {
|
|||||||
|
|
||||||
func (s closingService) withClosingRelations(db *gorm.DB) *gorm.DB {
|
func (s closingService) withClosingRelations(db *gorm.DB) *gorm.DB {
|
||||||
return s.withRelations(db).
|
return s.withRelations(db).
|
||||||
|
Preload("Location").
|
||||||
Preload("KandangHistory").
|
Preload("KandangHistory").
|
||||||
Preload("KandangHistory.Chickins")
|
Preload("KandangHistory.Chickins")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s closingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error) {
|
func (s closingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]dto.ClosingListItemDTO, int64, error) {
|
||||||
if err := s.Validate.Struct(params); err != nil {
|
if err := s.Validate.Struct(params); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
@@ -77,9 +80,9 @@ func (s closingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity
|
|||||||
offset := (params.Page - 1) * params.Limit
|
offset := (params.Page - 1) * params.Limit
|
||||||
|
|
||||||
closings, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
closings, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||||
db = s.withRelations(db)
|
db = s.withClosingRelations(db)
|
||||||
if params.Search != "" {
|
if params.Search != "" {
|
||||||
return db.Where("name LIKE ?", "%"+params.Search+"%")
|
return db.Where("flock_name LIKE ?", "%"+params.Search+"%")
|
||||||
}
|
}
|
||||||
return db.Order("created_at DESC").Order("updated_at DESC")
|
return db.Order("created_at DESC").Order("updated_at DESC")
|
||||||
})
|
})
|
||||||
@@ -88,7 +91,19 @@ func (s closingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity
|
|||||||
s.Log.Errorf("Failed to get closings: %+v", err)
|
s.Log.Errorf("Failed to get closings: %+v", err)
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
return closings, total, nil
|
|
||||||
|
result := make([]dto.ClosingListItemDTO, 0, len(closings))
|
||||||
|
for _, closing := range closings {
|
||||||
|
statusProject, _, err := s.getApprovalStatuses(c.Context(), closing.Id)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Failed to retrieve approval statuses for project flock %d: %+v", closing.Id, err)
|
||||||
|
return nil, 0, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch approval status")
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, dto.ToClosingListItemDTO(closing, statusProject))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s closingService) GetProjectFlockByID(c *fiber.Ctx, id uint) (*entity.ProjectFlock, error) {
|
func (s closingService) GetProjectFlockByID(c *fiber.Ctx, id uint) (*entity.ProjectFlock, error) {
|
||||||
@@ -153,6 +168,147 @@ func (s closingService) GetClosingSummary(c *fiber.Ctx, projectFlockID uint) (*d
|
|||||||
return &summary, nil
|
return &summary, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s closingService) GetClosingSapronak(c *fiber.Ctx, projectFlockID uint, params *validation.SapronakQuery) ([]dto.ClosingSapronakItemDTO, int64, error) {
|
||||||
|
if projectFlockID == 0 {
|
||||||
|
return nil, 0, fiber.NewError(fiber.StatusBadRequest, "Invalid project flock id")
|
||||||
|
}
|
||||||
|
|
||||||
|
if params == nil {
|
||||||
|
params = &validation.SapronakQuery{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Page == 0 {
|
||||||
|
params.Page = 1
|
||||||
|
}
|
||||||
|
if params.Limit == 0 {
|
||||||
|
params.Limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Validate.Struct(params); err != nil {
|
||||||
|
return nil, 0, fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Type != validation.SapronakTypeIncoming && params.Type != validation.SapronakTypeOutgoing {
|
||||||
|
return nil, 0, fiber.NewError(fiber.StatusBadRequest, "type must be either incoming or outgoing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := s.Repository.GetByID(c.Context(), projectFlockID, nil); err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, 0, fiber.NewError(fiber.StatusNotFound, "Project flock tidak ditemukan")
|
||||||
|
}
|
||||||
|
s.Log.Errorf("Failed get project flock %d for sapronak closing: %+v", projectFlockID, err)
|
||||||
|
return nil, 0, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock")
|
||||||
|
}
|
||||||
|
|
||||||
|
warehouseIDs, err := s.getWarehouseIDsByProjectFlock(c.Context(), projectFlockID)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Failed to fetch warehouses for project flock %d: %+v", projectFlockID, err)
|
||||||
|
return nil, 0, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch warehouses for project flock")
|
||||||
|
}
|
||||||
|
|
||||||
|
var projectFlockKandangIDs []uint
|
||||||
|
if params.Type == validation.SapronakTypeOutgoing {
|
||||||
|
projectFlockKandangIDs, err = s.getProjectFlockKandangIDs(c.Context(), projectFlockID)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Failed to fetch project flock kandang IDs for project flock %d: %+v", projectFlockID, err)
|
||||||
|
return nil, 0, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock kandang")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := (params.Page - 1) * params.Limit
|
||||||
|
rows, totalResults, err := s.Repository.GetSapronak(c.Context(), repository.SapronakQueryParams{
|
||||||
|
Type: params.Type,
|
||||||
|
WarehouseIDs: warehouseIDs,
|
||||||
|
ProjectFlockKandangIDs: projectFlockKandangIDs,
|
||||||
|
Limit: params.Limit,
|
||||||
|
Offset: offset,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Failed to fetch sapronak %s for project flock %d: %+v", params.Type, projectFlockID, err)
|
||||||
|
return nil, 0, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch sapronak data")
|
||||||
|
}
|
||||||
|
|
||||||
|
items := make([]dto.ClosingSapronakItemDTO, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
dateStr := row.DateText
|
||||||
|
if dateStr == "" && !row.SortDate.IsZero() {
|
||||||
|
dateStr = row.SortDate.Format("02-Jan-2006")
|
||||||
|
}
|
||||||
|
items = append(items, dto.ClosingSapronakItemDTO{
|
||||||
|
Id: row.Id,
|
||||||
|
Date: dateStr,
|
||||||
|
ReferenceNumber: row.ReferenceNumber,
|
||||||
|
TransactionType: row.TransactionType,
|
||||||
|
ProductName: row.ProductName,
|
||||||
|
ProductCategory: row.ProductCategory,
|
||||||
|
ProductSubCategory: row.ProductSubCategory,
|
||||||
|
SourceWarehouse: row.SourceWarehouse,
|
||||||
|
DestinationWarehouse: row.DestinationWarehouse,
|
||||||
|
// Destination: row.Destination,
|
||||||
|
Quantity: row.Quantity,
|
||||||
|
Unit: row.Unit,
|
||||||
|
FormattedQuantity: formatQuantity(row.Quantity, row.Unit),
|
||||||
|
Notes: row.Notes,
|
||||||
|
SortDate: row.SortDate,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return items, totalResults, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s closingService) getWarehouseIDsByProjectFlock(ctx context.Context, projectFlockID uint) ([]uint, error) {
|
||||||
|
var kandangIDs []uint
|
||||||
|
db := s.Repository.DB().WithContext(ctx)
|
||||||
|
|
||||||
|
if err := db.Model(&entity.ProjectFlockKandang{}).
|
||||||
|
Where("project_flock_id = ?", projectFlockID).
|
||||||
|
Pluck("kandang_id", &kandangIDs).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(kandangIDs) == 0 {
|
||||||
|
return []uint{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var warehouses []entity.Warehouse
|
||||||
|
if err := db.Where("kandang_id IN ?", kandangIDs).Find(&warehouses).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
unique := make(map[uint]struct{})
|
||||||
|
for _, warehouse := range warehouses {
|
||||||
|
unique[warehouse.Id] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
ids := make([]uint, 0, len(unique))
|
||||||
|
for id := range unique {
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s closingService) getProjectFlockKandangIDs(ctx context.Context, projectFlockID uint) ([]uint, error) {
|
||||||
|
var ids []uint
|
||||||
|
err := s.Repository.DB().WithContext(ctx).
|
||||||
|
Model(&entity.ProjectFlockKandang{}).
|
||||||
|
Where("project_flock_id = ?", projectFlockID).
|
||||||
|
Pluck("id", &ids).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatQuantity(qty float64, uom string) string {
|
||||||
|
qtyStr := strconv.FormatFloat(qty, 'f', -1, 64)
|
||||||
|
if uom == "" {
|
||||||
|
return qtyStr
|
||||||
|
}
|
||||||
|
return qtyStr + " " + uom
|
||||||
|
}
|
||||||
|
|
||||||
func (s closingService) getApprovalStatuses(ctx context.Context, projectFlockID uint) (string, string, error) {
|
func (s closingService) getApprovalStatuses(ctx context.Context, projectFlockID uint) (string, string, error) {
|
||||||
if s.ApprovalSvc == nil {
|
if s.ApprovalSvc == nil {
|
||||||
return "", "Belum Selesai", nil
|
return "", "Belum Selesai", nil
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package validation
|
package validation
|
||||||
|
|
||||||
type Create struct {
|
type Create struct {
|
||||||
Name string `json:"name" validate:"required_strict,min=3"`
|
Name string `json:"name" validate:"required_strict,min=3"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Update struct {
|
type Update struct {
|
||||||
Name *string `json:"name,omitempty" validate:"omitempty"`
|
Name *string `json:"name,omitempty" validate:"omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Query struct {
|
type Query struct {
|
||||||
@@ -13,3 +13,14 @@ type Query struct {
|
|||||||
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"`
|
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"`
|
||||||
Search string `query:"search" validate:"omitempty,max=50"`
|
Search string `query:"search" validate:"omitempty,max=50"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
SapronakTypeIncoming = "incoming"
|
||||||
|
SapronakTypeOutgoing = "outgoing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SapronakQuery struct {
|
||||||
|
Type string `query:"type" validate:"required,oneof=incoming outgoing"`
|
||||||
|
Page int `query:"page" validate:"omitempty,number,min=1,gt=0"`
|
||||||
|
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||||
rproduct "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/repositories"
|
rproduct "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/repositories"
|
||||||
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||||
|
rProjectFlockKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||||
rStockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
rStockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
||||||
|
|
||||||
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
||||||
@@ -21,10 +22,11 @@ func (AdjustmentModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validat
|
|||||||
stockLogsRepo := rStockLogs.NewStockLogRepository(db)
|
stockLogsRepo := rStockLogs.NewStockLogRepository(db)
|
||||||
warehouseRepo := rWarehouse.NewWarehouseRepository(db)
|
warehouseRepo := rWarehouse.NewWarehouseRepository(db)
|
||||||
productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
|
productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
|
||||||
|
projectFlockKandangRepo := rProjectFlockKandang.NewProjectFlockKandangRepository(db)
|
||||||
userRepo := rUser.NewUserRepository(db)
|
userRepo := rUser.NewUserRepository(db)
|
||||||
productRepo := rproduct.NewProductRepository(db)
|
productRepo := rproduct.NewProductRepository(db)
|
||||||
|
|
||||||
adjustmentService := sAdjustment.NewAdjustmentService(productRepo, stockLogsRepo, warehouseRepo, productWarehouseRepo, validate)
|
adjustmentService := sAdjustment.NewAdjustmentService(productRepo, stockLogsRepo, warehouseRepo, productWarehouseRepo, validate, projectFlockKandangRepo)
|
||||||
userService := sUser.NewUserService(userRepo, validate)
|
userService := sUser.NewUserService(userRepo, validate)
|
||||||
|
|
||||||
AdjustmentRoutes(router, userService, adjustmentService)
|
AdjustmentRoutes(router, userService, adjustmentService)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
common "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
common "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||||
@@ -11,6 +13,7 @@ import (
|
|||||||
ProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
ProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||||
productRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/repositories"
|
productRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/repositories"
|
||||||
warehouseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
warehouseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||||
|
projectFlockKandangRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||||
stockLogsRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
stockLogsRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@@ -27,22 +30,24 @@ type AdjustmentService interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type adjustmentService struct {
|
type adjustmentService struct {
|
||||||
Log *logrus.Logger
|
Log *logrus.Logger
|
||||||
Validate *validator.Validate
|
Validate *validator.Validate
|
||||||
StockLogsRepository stockLogsRepo.StockLogRepository
|
StockLogsRepository stockLogsRepo.StockLogRepository
|
||||||
WarehouseRepo warehouseRepo.WarehouseRepository
|
WarehouseRepo warehouseRepo.WarehouseRepository
|
||||||
ProductWarehouseRepo ProductWarehouse.ProductWarehouseRepository
|
ProductWarehouseRepo ProductWarehouse.ProductWarehouseRepository
|
||||||
ProductRepo productRepo.ProductRepository
|
ProductRepo productRepo.ProductRepository
|
||||||
|
ProjectFlockKandangRepo projectFlockKandangRepo.ProjectFlockKandangRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAdjustmentService(productRepo productRepo.ProductRepository, stockLogsRepo stockLogsRepo.StockLogRepository, warehouseRepo warehouseRepo.WarehouseRepository, productWarehouseRepo ProductWarehouse.ProductWarehouseRepository, validate *validator.Validate) AdjustmentService {
|
func NewAdjustmentService(productRepo productRepo.ProductRepository, stockLogsRepo stockLogsRepo.StockLogRepository, warehouseRepo warehouseRepo.WarehouseRepository, productWarehouseRepo ProductWarehouse.ProductWarehouseRepository, validate *validator.Validate, projectFlockKandangRepo projectFlockKandangRepo.ProjectFlockKandangRepository) AdjustmentService {
|
||||||
return &adjustmentService{
|
return &adjustmentService{
|
||||||
Log: utils.Log,
|
Log: utils.Log,
|
||||||
Validate: validate,
|
Validate: validate,
|
||||||
StockLogsRepository: stockLogsRepo,
|
StockLogsRepository: stockLogsRepo,
|
||||||
WarehouseRepo: warehouseRepo,
|
WarehouseRepo: warehouseRepo,
|
||||||
ProductWarehouseRepo: productWarehouseRepo,
|
ProductWarehouseRepo: productWarehouseRepo,
|
||||||
ProductRepo: productRepo,
|
ProductRepo: productRepo,
|
||||||
|
ProjectFlockKandangRepo: projectFlockKandangRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,11 +110,15 @@ func (s *adjustmentService) Adjustment(c *fiber.Ctx, req *validation.Create) (*e
|
|||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to validate product warehouse")
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to validate product warehouse")
|
||||||
}
|
}
|
||||||
if !isProductWarehouseExist {
|
if !isProductWarehouseExist {
|
||||||
|
projectFlockKandangID, err := s.getActiveProjectFlockKandangID(ctx, uint(req.WarehouseID))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
newPW := &entity.ProductWarehouse{
|
newPW := &entity.ProductWarehouse{
|
||||||
ProductId: uint(req.ProductID),
|
ProductId: uint(req.ProductID),
|
||||||
WarehouseId: uint(req.WarehouseID),
|
WarehouseId: uint(req.WarehouseID),
|
||||||
Quantity: 0,
|
Quantity: 0,
|
||||||
|
ProjectFlockKandangId: &projectFlockKandangID,
|
||||||
// CreatedBy: 1, // TODO: should Get from auth middleware
|
// CreatedBy: 1, // TODO: should Get from auth middleware
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +179,32 @@ func (s *adjustmentService) Adjustment(c *fiber.Ctx, req *validation.Create) (*e
|
|||||||
return s.GetOne(c, createdLogId)
|
return s.GetOne(c, createdLogId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *adjustmentService) getActiveProjectFlockKandangID(ctx context.Context, warehouseID uint) (uint, error) {
|
||||||
|
warehouse, err := s.WarehouseRepo.GetByID(ctx, warehouseID, nil)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return 0, fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Gudang dengan ID %d tidak ditemukan", warehouseID))
|
||||||
|
}
|
||||||
|
s.Log.Errorf("Failed to get warehouse %d: %+v", warehouseID, err)
|
||||||
|
return 0, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data gudang")
|
||||||
|
}
|
||||||
|
|
||||||
|
if warehouse.KandangId == nil || *warehouse.KandangId == 0 {
|
||||||
|
return 0, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Gudang %d belum terhubung ke kandang", warehouseID))
|
||||||
|
}
|
||||||
|
|
||||||
|
projectFlockKandang, err := s.ProjectFlockKandangRepo.GetActiveByKandangID(ctx, uint(*warehouse.KandangId))
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return 0, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Kandang %d belum memiliki project flock aktif", *warehouse.KandangId))
|
||||||
|
}
|
||||||
|
s.Log.Errorf("Failed to get active project flock for kandang %d: %+v", *warehouse.KandangId, err)
|
||||||
|
return 0, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil project flock kandang")
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint(projectFlockKandang.Id), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *adjustmentService) AdjustmentHistory(c *fiber.Ctx, query *validation.Query) ([]*entity.StockLog, int64, error) {
|
func (s *adjustmentService) AdjustmentHistory(c *fiber.Ctx, query *validation.Query) ([]*entity.StockLog, int64, error) {
|
||||||
if err := s.Validate.Struct(query); err != nil {
|
if err := s.Validate.Struct(query); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import (
|
|||||||
rStockTransfer "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers/repositories"
|
rStockTransfer "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers/repositories"
|
||||||
sTransfer "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers/services"
|
sTransfer "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers/services"
|
||||||
rSupplier "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/repositories"
|
rSupplier "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/repositories"
|
||||||
|
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||||
|
rProjectFlockKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||||
rStockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
rStockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
||||||
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
||||||
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -25,8 +27,10 @@ func (TransferModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
|||||||
supplierRepo := rSupplier.NewSupplierRepository(db)
|
supplierRepo := rSupplier.NewSupplierRepository(db)
|
||||||
productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
|
productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
|
||||||
userRepo := rUser.NewUserRepository(db)
|
userRepo := rUser.NewUserRepository(db)
|
||||||
|
warehouseRepo := rWarehouse.NewWarehouseRepository(db)
|
||||||
|
projectFlockKandangRepo := rProjectFlockKandang.NewProjectFlockKandangRepository(db)
|
||||||
|
|
||||||
transferService := sTransfer.NewTransferService(validate, stockTransferRepo, stockTransferDetailRepo, stockTransferDeliveryRepo, StockTransferDeliveryItemRepo, stockLogsRepo, productWarehouseRepo, supplierRepo)
|
transferService := sTransfer.NewTransferService(validate, stockTransferRepo, stockTransferDetailRepo, stockTransferDeliveryRepo, StockTransferDeliveryItemRepo, stockLogsRepo, productWarehouseRepo, supplierRepo, warehouseRepo, projectFlockKandangRepo)
|
||||||
userService := sUser.NewUserService(userRepo, validate)
|
userService := sUser.NewUserService(userRepo, validate)
|
||||||
|
|
||||||
TransferRoutes(router, userService, transferService)
|
TransferRoutes(router, userService, transferService)
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||||
rStockTransfer "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers/repositories"
|
rStockTransfer "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers/repositories"
|
||||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers/validations"
|
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers/validations"
|
||||||
rSupplier "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/repositories"
|
rSupplier "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/repositories"
|
||||||
|
warehouseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||||
|
projectFlockKandangRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||||
rStockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
rStockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
@@ -35,9 +39,11 @@ type transferService struct {
|
|||||||
StockLogsRepository rStockLogs.StockLogRepository
|
StockLogsRepository rStockLogs.StockLogRepository
|
||||||
ProductWarehouseRepo rProductWarehouse.ProductWarehouseRepository
|
ProductWarehouseRepo rProductWarehouse.ProductWarehouseRepository
|
||||||
SupplierRepo rSupplier.SupplierRepository
|
SupplierRepo rSupplier.SupplierRepository
|
||||||
|
WarehouseRepo warehouseRepo.WarehouseRepository
|
||||||
|
ProjectFlockKandangRepo projectFlockKandangRepo.ProjectFlockKandangRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTransferService(validate *validator.Validate, stockTransferRepo rStockTransfer.StockTransferRepository, stockTransferDetailRepo rStockTransfer.StockTransferDetailRepository, stockTransferDeliveryRepo rStockTransfer.StockTransferDeliveryRepository, stockTransferDeliveryItemRepo rStockTransfer.StockTransferDeliveryItemRepository, stockLogsRepo rStockLogs.StockLogRepository, productWarehouseRepo rProductWarehouse.ProductWarehouseRepository, supplierRepo rSupplier.SupplierRepository) TransferService {
|
func NewTransferService(validate *validator.Validate, stockTransferRepo rStockTransfer.StockTransferRepository, stockTransferDetailRepo rStockTransfer.StockTransferDetailRepository, stockTransferDeliveryRepo rStockTransfer.StockTransferDeliveryRepository, stockTransferDeliveryItemRepo rStockTransfer.StockTransferDeliveryItemRepository, stockLogsRepo rStockLogs.StockLogRepository, productWarehouseRepo rProductWarehouse.ProductWarehouseRepository, supplierRepo rSupplier.SupplierRepository, warehouseRepo warehouseRepo.WarehouseRepository, projectFlockKandangRepo projectFlockKandangRepo.ProjectFlockKandangRepository) TransferService {
|
||||||
return &transferService{
|
return &transferService{
|
||||||
Log: utils.Log,
|
Log: utils.Log,
|
||||||
Validate: validate,
|
Validate: validate,
|
||||||
@@ -48,6 +54,8 @@ func NewTransferService(validate *validator.Validate, stockTransferRepo rStockTr
|
|||||||
StockLogsRepository: stockLogsRepo,
|
StockLogsRepository: stockLogsRepo,
|
||||||
ProductWarehouseRepo: productWarehouseRepo,
|
ProductWarehouseRepo: productWarehouseRepo,
|
||||||
SupplierRepo: supplierRepo,
|
SupplierRepo: supplierRepo,
|
||||||
|
WarehouseRepo: warehouseRepo,
|
||||||
|
ProjectFlockKandangRepo: projectFlockKandangRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (s transferService) withRelations(db *gorm.DB) *gorm.DB {
|
func (s transferService) withRelations(db *gorm.DB) *gorm.DB {
|
||||||
@@ -301,10 +309,16 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
}
|
}
|
||||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
// Jika belum ada record untuk produk di gudang tujuan, buat baru
|
// Jika belum ada record untuk produk di gudang tujuan, buat baru
|
||||||
|
ctx := c.Context()
|
||||||
|
projectFlockKandangID, err := s.getActiveProjectFlockKandangID(ctx, uint(req.DestinationWarehouseID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
destPW = &entity.ProductWarehouse{
|
destPW = &entity.ProductWarehouse{
|
||||||
ProductId: uint(product.ProductID),
|
ProductId: uint(product.ProductID),
|
||||||
WarehouseId: uint(req.DestinationWarehouseID),
|
WarehouseId: uint(req.DestinationWarehouseID),
|
||||||
Quantity: 0,
|
Quantity: 0,
|
||||||
|
ProjectFlockKandangId: &projectFlockKandangID,
|
||||||
// CreatedBy: 1, // TODO: should Get from auth middleware
|
// CreatedBy: 1, // TODO: should Get from auth middleware
|
||||||
}
|
}
|
||||||
if err := s.ProductWarehouseRepo.WithTx(tx).CreateOne(c.Context(), destPW, nil); err != nil {
|
if err := s.ProductWarehouseRepo.WithTx(tx).CreateOne(c.Context(), destPW, nil); err != nil {
|
||||||
@@ -357,3 +371,29 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *transferService) getActiveProjectFlockKandangID(ctx context.Context, warehouseID uint) (uint, error) {
|
||||||
|
warehouse, err := s.WarehouseRepo.GetByID(ctx, warehouseID, nil)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return 0, fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Gudang dengan ID %d tidak ditemukan", warehouseID))
|
||||||
|
}
|
||||||
|
s.Log.Errorf("Failed to get warehouse %d: %+v", warehouseID, err)
|
||||||
|
return 0, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data gudang")
|
||||||
|
}
|
||||||
|
|
||||||
|
if warehouse.KandangId == nil || *warehouse.KandangId == 0 {
|
||||||
|
return 0, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Gudang %d belum terhubung ke kandang", warehouseID))
|
||||||
|
}
|
||||||
|
|
||||||
|
projectFlockKandang, err := s.ProjectFlockKandangRepo.GetActiveByKandangID(ctx, uint(*warehouse.KandangId))
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return 0, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Kandang %d belum memiliki project flock aktif", *warehouse.KandangId))
|
||||||
|
}
|
||||||
|
s.Log.Errorf("Failed to get active project flock for kandang %d: %+v", *warehouse.KandangId, err)
|
||||||
|
return 0, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil project flock kandang")
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint(projectFlockKandang.Id), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ func createProductWarehouseRow(t *testing.T, db *gorm.DB, qty float64) entity.Pr
|
|||||||
ProductId: 1,
|
ProductId: 1,
|
||||||
WarehouseId: 1,
|
WarehouseId: 1,
|
||||||
Quantity: qty,
|
Quantity: qty,
|
||||||
|
// CreatedBy: 1,
|
||||||
}
|
}
|
||||||
if err := db.Create(&pw).Error; err != nil {
|
if err := db.Create(&pw).Error; err != nil {
|
||||||
t.Fatalf("create product warehouse: %v", err)
|
t.Fatalf("create product warehouse: %v", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user