mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
2008 lines
70 KiB
Go
2008 lines
70 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
|
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations"
|
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
|
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type ClosingRepository interface {
|
|
repository.BaseRepository[entity.ProjectFlock]
|
|
GetSapronak(ctx context.Context, params SapronakQueryParams) ([]SapronakRow, int64, error)
|
|
GetSapronakSummary(ctx context.Context, params SapronakQueryParams) ([]SapronakSummaryRow, error)
|
|
SumFeedPurchaseAndUsedByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, error)
|
|
SumProjectChickinUsageByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, error)
|
|
SumClaimCullingByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, error)
|
|
SumMarketingWeightAndQtyByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, float64, error)
|
|
SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, float64, float64, error)
|
|
SumRecordingEggQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, error)
|
|
GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error)
|
|
FetchSapronakIncoming(ctx context.Context, projectFlockKandangID uint, kandangID uint, start, end *time.Time) ([]SapronakIncomingRow, error)
|
|
FetchSapronakIncomingDetails(ctx context.Context, projectFlockKandangID uint, kandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error)
|
|
FetchSapronakUsage(ctx context.Context, pfkID uint, start, end *time.Time) ([]SapronakUsageRow, error)
|
|
FetchSapronakUsageDetails(ctx context.Context, pfkID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error)
|
|
FetchSapronakChickinUsage(ctx context.Context, pfkID uint, start, end *time.Time) ([]SapronakUsageRow, error)
|
|
FetchSapronakChickinUsageDetails(ctx context.Context, pfkID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error)
|
|
FetchSapronakUsageAllocatedDetails(ctx context.Context, projectFlockKandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error)
|
|
FetchSapronakAdjustments(ctx context.Context, kandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error)
|
|
FetchSapronakTransfers(ctx context.Context, kandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error)
|
|
FetchSapronakSales(ctx context.Context, projectFlockKandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error)
|
|
FetchSapronakSalesAllocatedDetails(ctx context.Context, projectFlockKandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error)
|
|
GetProductsWithFlagsByIDs(ctx context.Context, productIDs []uint) ([]entity.Product, error)
|
|
}
|
|
|
|
type ClosingRepositoryImpl struct {
|
|
*repository.BaseRepositoryImpl[entity.ProjectFlock]
|
|
}
|
|
|
|
func NewClosingRepository(db *gorm.DB) ClosingRepository {
|
|
return &ClosingRepositoryImpl{
|
|
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"`
|
|
UnitID uint `gorm:"column:unit_id"`
|
|
Unit string `gorm:"column:unit"`
|
|
Notes string `gorm:"column:notes"`
|
|
}
|
|
|
|
type SapronakSummaryRow struct {
|
|
Category string `gorm:"column:category"`
|
|
TotalQty int64 `gorm:"column:total_qty"`
|
|
UomID uint `gorm:"column:uom_id"`
|
|
UomName string `gorm:"column:uom_name"`
|
|
}
|
|
|
|
type ExpeditionHPPRow struct {
|
|
SupplierName string `gorm:"column:supplier_name"`
|
|
TotalAmount float64 `gorm:"column:total_amount"`
|
|
}
|
|
|
|
type SapronakQueryParams struct {
|
|
Type string
|
|
WarehouseIDs []uint
|
|
ProjectFlockKandangIDs []uint
|
|
Limit int
|
|
Offset int
|
|
Search string
|
|
StartDate *time.Time
|
|
EndDate *time.Time
|
|
}
|
|
|
|
func sapronakIncomingPurchaseQueryParts(params SapronakQueryParams) (string, []any) {
|
|
if len(params.ProjectFlockKandangIDs) > 0 {
|
|
return sapronakIncomingPurchasesScopedSQL(), []any{
|
|
fifo.UsableKeyRecordingStock.String(),
|
|
fifo.UsableKeyProjectChickin.String(),
|
|
fifo.StockableKeyPurchaseItems.String(),
|
|
entity.StockAllocationStatusActive,
|
|
entity.StockAllocationPurposeConsume,
|
|
params.ProjectFlockKandangIDs,
|
|
params.ProjectFlockKandangIDs,
|
|
params.WarehouseIDs,
|
|
}
|
|
}
|
|
|
|
return sapronakIncomingPurchasesSQL, []any{params.WarehouseIDs}
|
|
}
|
|
|
|
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
|
|
}
|
|
purchasesSQL, purchaseArgs := sapronakIncomingPurchaseQueryParts(params)
|
|
unionParts = append(unionParts, purchasesSQL, sapronakIncomingTransfersSQL, sapronakIncomingAdjustmentsSQL)
|
|
args = append(args, purchaseArgs...)
|
|
args = append(args, params.WarehouseIDs, params.WarehouseIDs)
|
|
case validation.SapronakTypeOutgoing:
|
|
if len(params.WarehouseIDs) > 0 {
|
|
unionParts = append(unionParts, sapronakOutgoingTransfersSQL, sapronakOutgoingAdjustmentsSQL)
|
|
args = append(args, params.WarehouseIDs, 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 ")
|
|
|
|
search := strings.TrimSpace(params.Search)
|
|
searchClause := ""
|
|
var searchArgs []any
|
|
if search != "" {
|
|
searchClause = `
|
|
WHERE (
|
|
reference_number ILIKE ?
|
|
OR product_name ILIKE ?
|
|
OR product_category ILIKE ?
|
|
OR source_warehouse ILIKE ?
|
|
OR destination_warehouse ILIKE ?
|
|
OR CAST(quantity AS TEXT) ILIKE ?
|
|
OR unit ILIKE ?
|
|
OR notes ILIKE ?
|
|
OR transaction_type ILIKE ?
|
|
)`
|
|
like := "%" + search + "%"
|
|
searchArgs = append(searchArgs, like, like, like, like, like, like, like, like, like)
|
|
}
|
|
|
|
var totalResults int64
|
|
dateClause := ""
|
|
var dateArgs []any
|
|
if params.StartDate != nil {
|
|
dateClause += " AND sort_date::date >= ?"
|
|
dateArgs = append(dateArgs, params.StartDate)
|
|
}
|
|
if params.EndDate != nil {
|
|
dateClause += " AND sort_date::date <= ?"
|
|
dateArgs = append(dateArgs, params.EndDate)
|
|
}
|
|
whereClause := searchClause
|
|
if dateClause != "" {
|
|
if whereClause == "" {
|
|
whereClause = " WHERE " + strings.TrimPrefix(dateClause, " AND ")
|
|
} else {
|
|
whereClause += dateClause
|
|
}
|
|
}
|
|
countSQL := fmt.Sprintf("SELECT COUNT(*) FROM (%s) AS combined%s", unionSQL, whereClause)
|
|
countArgs := append(append(append([]any{}, args...), searchArgs...), dateArgs...)
|
|
if err := db.Raw(countSQL, countArgs...).Scan(&totalResults).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
dataArgs := append(append(append([]any{}, args...), searchArgs...), dateArgs...)
|
|
dataArgs = append(dataArgs, params.Limit, params.Offset)
|
|
dataSQL := fmt.Sprintf("SELECT * FROM (%s) AS combined%s ORDER BY sort_date ASC, id ASC LIMIT ? OFFSET ?", unionSQL, whereClause)
|
|
|
|
var rows []SapronakRow
|
|
if err := db.Raw(dataSQL, dataArgs...).Scan(&rows).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
return rows, totalResults, nil
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) GetSapronakSummary(ctx context.Context, params SapronakQueryParams) ([]SapronakSummaryRow, error) {
|
|
db := r.DB().WithContext(ctx)
|
|
|
|
var (
|
|
unionParts []string
|
|
args []any
|
|
)
|
|
|
|
switch params.Type {
|
|
case validation.SapronakTypeIncoming:
|
|
if len(params.WarehouseIDs) == 0 {
|
|
return []SapronakSummaryRow{}, nil
|
|
}
|
|
purchasesSQL, purchaseArgs := sapronakIncomingPurchaseQueryParts(params)
|
|
unionParts = append(unionParts, purchasesSQL, sapronakIncomingTransfersSQL, sapronakIncomingAdjustmentsSQL)
|
|
args = append(args, purchaseArgs...)
|
|
args = append(args, params.WarehouseIDs, params.WarehouseIDs)
|
|
case validation.SapronakTypeOutgoing:
|
|
if len(params.WarehouseIDs) > 0 {
|
|
unionParts = append(unionParts, sapronakOutgoingTransfersSQL, sapronakOutgoingAdjustmentsSQL)
|
|
args = append(args, params.WarehouseIDs, params.WarehouseIDs)
|
|
}
|
|
if len(params.ProjectFlockKandangIDs) > 0 {
|
|
unionParts = append(unionParts, sapronakOutgoingMarketingsSQL)
|
|
args = append(args, params.ProjectFlockKandangIDs)
|
|
}
|
|
if len(unionParts) == 0 {
|
|
return []SapronakSummaryRow{}, nil
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("invalid sapronak type: %s", params.Type)
|
|
}
|
|
|
|
unionSQL := strings.Join(unionParts, " UNION ALL ")
|
|
|
|
search := strings.TrimSpace(params.Search)
|
|
searchClause := ""
|
|
var searchArgs []any
|
|
if search != "" {
|
|
searchClause = `
|
|
WHERE (
|
|
reference_number ILIKE ?
|
|
OR product_name ILIKE ?
|
|
OR product_category ILIKE ?
|
|
OR source_warehouse ILIKE ?
|
|
OR destination_warehouse ILIKE ?
|
|
OR CAST(quantity AS TEXT) ILIKE ?
|
|
OR unit ILIKE ?
|
|
OR notes ILIKE ?
|
|
OR transaction_type ILIKE ?
|
|
)`
|
|
like := "%" + search + "%"
|
|
searchArgs = append(searchArgs, like, like, like, like, like, like, like, like, like)
|
|
}
|
|
|
|
dateClause := ""
|
|
var dateArgs []any
|
|
if params.StartDate != nil {
|
|
dateClause += " AND sort_date::date >= ?"
|
|
dateArgs = append(dateArgs, params.StartDate)
|
|
}
|
|
if params.EndDate != nil {
|
|
dateClause += " AND sort_date::date <= ?"
|
|
dateArgs = append(dateArgs, params.EndDate)
|
|
}
|
|
whereClause := searchClause
|
|
if dateClause != "" {
|
|
if whereClause == "" {
|
|
whereClause = " WHERE " + strings.TrimPrefix(dateClause, " AND ")
|
|
} else {
|
|
whereClause += dateClause
|
|
}
|
|
}
|
|
|
|
querySQL := fmt.Sprintf(`
|
|
SELECT
|
|
product_category AS category,
|
|
CAST(COALESCE(SUM(quantity), 0) AS BIGINT) AS total_qty,
|
|
unit_id AS uom_id,
|
|
unit AS uom_name
|
|
FROM (%s) AS combined%s
|
|
GROUP BY product_category, unit_id, unit
|
|
ORDER BY product_category ASC, unit ASC
|
|
`, unionSQL, whereClause)
|
|
queryArgs := append(append(append([]any{}, args...), searchArgs...), dateArgs...)
|
|
|
|
var rows []SapronakSummaryRow
|
|
if err := db.Raw(querySQL, queryArgs...).Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) SumFeedPurchaseAndUsedByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, error) {
|
|
if len(projectFlockKandangIDs) == 0 {
|
|
return 0, 0, nil
|
|
}
|
|
|
|
var purchaseAgg struct {
|
|
TotalIn float64 `gorm:"column:total_in"`
|
|
}
|
|
|
|
err := r.DB().WithContext(ctx).
|
|
Table("purchase_items pi").
|
|
Joins("JOIN flags f ON f.flagable_id = pi.product_id AND f.flagable_type = 'products'").
|
|
Where("f.name = ?", "PAKAN").
|
|
Where("pi.project_flock_kandang_id IN ?", projectFlockKandangIDs).
|
|
Select("COALESCE(SUM(pi.total_qty), 0) AS total_in").
|
|
Scan(&purchaseAgg).Error
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
var usageAgg struct {
|
|
TotalUsed float64 `gorm:"column:total_used"`
|
|
}
|
|
|
|
err = r.DB().WithContext(ctx).
|
|
Table("recording_stocks rs").
|
|
Joins("JOIN recordings rec ON rec.id = rs.recording_id").
|
|
Joins("JOIN product_warehouses pw ON pw.id = rs.product_warehouse_id").
|
|
Joins("JOIN products prod ON prod.id = pw.product_id").
|
|
Joins("JOIN flags f ON f.flagable_id = prod.id AND f.flagable_type = ?", "products").
|
|
Where("rec.project_flock_kandangs_id IN ?", projectFlockKandangIDs).
|
|
Where("f.name = ?", "PAKAN").
|
|
Select("COALESCE(SUM(COALESCE(rs.usage_qty, 0) + COALESCE(rs.pending_qty, 0)), 0) AS total_used").
|
|
Scan(&usageAgg).Error
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
return purchaseAgg.TotalIn, usageAgg.TotalUsed, nil
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) SumProjectChickinUsageByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) {
|
|
if len(projectFlockKandangIDs) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
var total float64
|
|
if err := r.DB().WithContext(ctx).
|
|
Model(&entity.ProjectChickin{}).
|
|
Where("project_flock_kandang_id IN ?", projectFlockKandangIDs).
|
|
Select("COALESCE(SUM(usage_qty), 0)").
|
|
Scan(&total).Error; err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return total, nil
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) SumClaimCullingByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) {
|
|
if len(projectFlockKandangIDs) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
var agg struct {
|
|
Total float64 `gorm:"column:total_culling"`
|
|
}
|
|
|
|
err := r.DB().WithContext(ctx).
|
|
Table("recording_depletions rd").
|
|
Joins("JOIN recordings rec ON rec.id = rd.recording_id").
|
|
Joins("JOIN product_warehouses pw ON pw.id = rd.product_warehouse_id").
|
|
Joins("JOIN products prod ON prod.id = pw.product_id").
|
|
Joins("JOIN flags f ON f.flagable_id = prod.id AND f.flagable_type = ?", "products").
|
|
Where("COALESCE(rd.source_project_flock_kandang_id, rec.project_flock_kandangs_id) IN ?", projectFlockKandangIDs).
|
|
Where("f.name = ?", utils.FlagAyamCulling).
|
|
Select("COALESCE(SUM(rd.qty), 0) AS total_culling").
|
|
Scan(&agg).Error
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return agg.Total, nil
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) SumMarketingWeightAndQtyByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, float64, error) {
|
|
if len(projectFlockKandangIDs) == 0 {
|
|
return 0, 0, 0, nil
|
|
}
|
|
return r.sumMarketingAttributedByProjectFlockKandangIDs(ctx, projectFlockKandangIDs, nil)
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, float64, float64, error) {
|
|
if len(projectFlockKandangIDs) == 0 || len(flagNames) == 0 {
|
|
return 0, 0, 0, nil
|
|
}
|
|
return r.sumMarketingAttributedByProjectFlockKandangIDs(ctx, projectFlockKandangIDs, flagNames)
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) SumRecordingEggQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, error) {
|
|
if len(projectFlockKandangIDs) == 0 || len(flagNames) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
var agg struct {
|
|
TotalQty float64 `gorm:"column:total_qty"`
|
|
}
|
|
|
|
err := r.DB().WithContext(ctx).
|
|
Table("recording_eggs re").
|
|
Joins("JOIN recordings rec ON rec.id = re.recording_id").
|
|
Joins("JOIN product_warehouses pw ON pw.id = re.product_warehouse_id").
|
|
Joins("JOIN products prod ON prod.id = pw.product_id").
|
|
Joins("JOIN flags f ON f.flagable_id = prod.id AND f.flagable_type = ?", "products").
|
|
Where("COALESCE(re.project_flock_kandang_id, rec.project_flock_kandangs_id) IN ?", projectFlockKandangIDs).
|
|
Where("f.name IN ?", flagNames).
|
|
Select("COALESCE(SUM(re.qty), 0) AS total_qty").
|
|
Scan(&agg).Error
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return agg.TotalQty, nil
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error) {
|
|
db := r.DB().WithContext(ctx)
|
|
|
|
if projectFlockID == 0 {
|
|
return nil, fmt.Errorf("invalid project flock id")
|
|
}
|
|
|
|
query := db.
|
|
Table("expense_realizations AS er").
|
|
Joins("JOIN expense_nonstocks ens ON ens.id = er.expense_nonstock_id").
|
|
Joins("JOIN expenses e ON e.id = ens.expense_id").
|
|
Joins("JOIN project_flock_kandangs pfk ON pfk.id = ens.project_flock_kandang_id").
|
|
Joins("JOIN nonstocks n ON n.id = ens.nonstock_id").
|
|
Joins("JOIN flags f ON f.flagable_id = n.id AND f.flagable_type = ?", entity.FlagableTypeNonstock).
|
|
Joins("JOIN suppliers s ON s.id = e.supplier_id").
|
|
Where("pfk.project_flock_id = ?", projectFlockID).
|
|
Where("e.category = ?", "BOP").
|
|
Where("e.realization_date IS NOT NULL").
|
|
Where("UPPER(f.name) = ?", strings.ToUpper(string(utils.FlagEkspedisi)))
|
|
|
|
if projectFlockKandangID != nil && *projectFlockKandangID != 0 {
|
|
query = query.Where("pfk.id = ?", *projectFlockKandangID)
|
|
}
|
|
|
|
var rows []ExpeditionHPPRow
|
|
err := query.
|
|
Select(
|
|
"e.supplier_id AS supplier_id, " +
|
|
"s.name AS supplier_name, " +
|
|
"SUM(er.qty * er.price) AS total_amount",
|
|
).
|
|
Group("e.supplier_id, s.name").
|
|
Scan(&rows).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, 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,
|
|
'Pembelian' AS transaction_type,
|
|
prod.name AS product_name,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
f.name
|
|
)
|
|
FROM flags f
|
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
|
), '') AS product_category,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
f.name
|
|
)
|
|
FROM flags f
|
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
|
), '') AS product_sub_category,
|
|
'-' AS source_warehouse,
|
|
w.name AS destination_warehouse,
|
|
'' AS destination,
|
|
pi.total_qty AS quantity,
|
|
u.id AS unit_id,
|
|
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 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,
|
|
'Mutasi' AS transaction_type,
|
|
prod.name AS product_name,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
f.name
|
|
)
|
|
FROM flags f
|
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
|
), '') AS product_category,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
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.usage_qty AS quantity,
|
|
u.id AS unit_id,
|
|
u.name AS unit,
|
|
st.reason 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 uoms u ON u.id = prod.uom_id
|
|
WHERE st.to_warehouse_id IN ?
|
|
`
|
|
|
|
sapronakIncomingAdjustmentsSQL = `
|
|
SELECT
|
|
CAST(ast.id AS BIGINT) AS id,
|
|
ast.created_at AS sort_date,
|
|
COALESCE(TO_CHAR(ast.created_at, 'DD-Mon-YYYY'), '') AS date_text,
|
|
COALESCE(ast.adj_number, '') AS reference_number,
|
|
'Adjustment stock' AS transaction_type,
|
|
prod.name AS product_name,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
f.name
|
|
)
|
|
FROM flags f
|
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
|
), '') AS product_category,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
f.name
|
|
)
|
|
FROM flags f
|
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
|
), '') AS product_sub_category,
|
|
'-' AS source_warehouse,
|
|
COALESCE(w.name, '') AS destination_warehouse,
|
|
'' AS destination,
|
|
COALESCE(ast.total_qty, 0) AS quantity,
|
|
u.id AS unit_id,
|
|
u.name AS unit,
|
|
'-' AS notes
|
|
FROM adjustment_stocks ast
|
|
JOIN product_warehouses pw ON pw.id = ast.product_warehouse_id
|
|
JOIN warehouses w ON w.id = pw.warehouse_id
|
|
JOIN products prod ON prod.id = pw.product_id
|
|
JOIN uoms u ON u.id = prod.uom_id
|
|
WHERE pw.warehouse_id IN ?
|
|
AND COALESCE(ast.total_qty, 0) <> 0
|
|
`
|
|
|
|
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,
|
|
'Mutasi' AS transaction_type,
|
|
prod.name AS product_name,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
f.name
|
|
)
|
|
FROM flags f
|
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
|
), '') AS product_category,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
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.usage_qty AS quantity,
|
|
u.id AS unit_id,
|
|
u.name AS unit,
|
|
st.reason 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 uoms u ON u.id = prod.uom_id
|
|
WHERE st.from_warehouse_id IN ?
|
|
`
|
|
|
|
sapronakOutgoingAdjustmentsSQL = `
|
|
SELECT
|
|
CAST(ast.id AS BIGINT) AS id,
|
|
ast.created_at AS sort_date,
|
|
COALESCE(TO_CHAR(ast.created_at, 'DD-Mon-YYYY'), '') AS date_text,
|
|
COALESCE(ast.adj_number, '') AS reference_number,
|
|
'Adjustment stock' AS transaction_type,
|
|
prod.name AS product_name,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
f.name
|
|
)
|
|
FROM flags f
|
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
|
), '') AS product_category,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
f.name
|
|
)
|
|
FROM flags f
|
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
|
), '') AS product_sub_category,
|
|
COALESCE(w.name, '') AS source_warehouse,
|
|
'-' AS destination_warehouse,
|
|
'' AS destination,
|
|
COALESCE(ast.usage_qty, 0) AS quantity,
|
|
u.id AS unit_id,
|
|
u.name AS unit,
|
|
'-' AS notes
|
|
FROM adjustment_stocks ast
|
|
JOIN product_warehouses pw ON pw.id = ast.product_warehouse_id
|
|
JOIN warehouses w ON w.id = pw.warehouse_id
|
|
JOIN products prod ON prod.id = pw.product_id
|
|
JOIN uoms u ON u.id = prod.uom_id
|
|
WHERE pw.warehouse_id IN ?
|
|
AND COALESCE(ast.usage_qty, 0) <> 0
|
|
AND EXISTS (
|
|
SELECT 1
|
|
FROM flags f
|
|
WHERE f.flagable_id = pw.product_id
|
|
AND f.flagable_type = 'products'
|
|
AND UPPER(f.name) NOT IN ('DOC', 'LAYER', 'PULLET', 'AYAM-AFKIR', 'AYAM-MATI', 'AYAM-CULLING', 'TELUR-UTUH', 'TELUR-PECAH', 'TELUR-PUTIH', 'TELUR-RETAK')
|
|
)
|
|
`
|
|
|
|
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,
|
|
'Penjualan' AS transaction_type,
|
|
prod.name AS product_name,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
f.name
|
|
)
|
|
FROM flags f
|
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
|
), '') AS product_category,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
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,
|
|
COALESCE(c.name, '') AS destination_warehouse,
|
|
'' AS destination,
|
|
mp.qty AS quantity,
|
|
u.id AS unit_id,
|
|
u.name AS unit,
|
|
m.notes AS notes
|
|
FROM marketing_products mp
|
|
JOIN marketings m ON m.id = mp.marketing_id
|
|
LEFT JOIN customers c ON c.id = m.customer_id
|
|
JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id
|
|
JOIN products prod ON prod.id = pw.product_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 ?
|
|
AND EXISTS (
|
|
SELECT 1
|
|
FROM flags f
|
|
WHERE f.flagable_id = pw.product_id
|
|
AND f.flagable_type = 'products'
|
|
AND UPPER(f.name) NOT IN ('DOC', 'LAYER', 'PULLET', 'AYAM-AFKIR', 'AYAM-MATI', 'AYAM-CULLING', 'TELUR-UTUH', 'TELUR-PECAH', 'TELUR-PUTIH', 'TELUR-RETAK')
|
|
)
|
|
`
|
|
)
|
|
|
|
type SapronakIncomingRow struct {
|
|
ProductID uint
|
|
ProductName string
|
|
Flag string
|
|
Qty float64
|
|
Value float64
|
|
DefaultPrice float64
|
|
}
|
|
|
|
type SapronakUsageRow struct {
|
|
ProductID uint
|
|
ProductName string
|
|
Flag string
|
|
Qty float64
|
|
DefaultPrice float64
|
|
}
|
|
|
|
type SapronakDetailRow struct {
|
|
ProductID uint
|
|
ProductName string
|
|
Flag string
|
|
Date *time.Time
|
|
Reference string
|
|
QtyIn float64
|
|
QtyOut float64
|
|
Price float64
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) withCtx(ctx context.Context) *gorm.DB { return r.DB().WithContext(ctx) }
|
|
|
|
func (r *ClosingRepositoryImpl) sumMarketingAttributedByProjectFlockKandangIDs(
|
|
ctx context.Context,
|
|
projectFlockKandangIDs []uint,
|
|
flagNames []string,
|
|
) (float64, float64, float64, error) {
|
|
var agg struct {
|
|
TotalWeight float64 `gorm:"column:total_weight"`
|
|
TotalQty float64 `gorm:"column:total_qty"`
|
|
TotalPrice float64 `gorm:"column:total_price"`
|
|
}
|
|
|
|
query := r.withCtx(ctx).
|
|
Table("(?) AS mda", repository.MarketingDeliveryAttributionRowsQuery(r.withCtx(ctx))).
|
|
Joins("JOIN marketing_delivery_products mdp ON mdp.id = mda.marketing_delivery_product_id").
|
|
Joins("JOIN marketing_products mp ON mp.id = mdp.marketing_product_id").
|
|
Joins("JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id").
|
|
Joins("JOIN products prod ON prod.id = pw.product_id").
|
|
Where("mda.project_flock_kandang_id IN ?", projectFlockKandangIDs).
|
|
Where("mdp.delivery_date IS NOT NULL")
|
|
|
|
if len(flagNames) > 0 {
|
|
query = query.
|
|
Joins("JOIN flags f ON f.flagable_id = prod.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
|
Where("f.name IN ?", flagNames)
|
|
}
|
|
|
|
err := query.
|
|
Select(`
|
|
COALESCE(SUM(CASE
|
|
WHEN COALESCE(mdp.usage_qty, 0) > 0 THEN mdp.total_weight * (mda.allocated_qty / mdp.usage_qty)
|
|
ELSE 0
|
|
END), 0) AS total_weight,
|
|
COALESCE(SUM(mda.allocated_qty), 0) AS total_qty,
|
|
COALESCE(SUM(CASE
|
|
WHEN COALESCE(mdp.usage_qty, 0) > 0 THEN mdp.total_price * (mda.allocated_qty / mdp.usage_qty)
|
|
ELSE 0
|
|
END), 0) AS total_price
|
|
`).
|
|
Scan(&agg).Error
|
|
if err != nil {
|
|
return 0, 0, 0, err
|
|
}
|
|
|
|
return agg.TotalWeight, agg.TotalQty, agg.TotalPrice, nil
|
|
}
|
|
|
|
func applyDateRange(db *gorm.DB, column string, start, end *time.Time) *gorm.DB {
|
|
if start != nil {
|
|
db = db.Where(column+"::date >= ?", start)
|
|
}
|
|
if end != nil {
|
|
db = db.Where(column+"::date <= ?", end)
|
|
}
|
|
return db
|
|
}
|
|
|
|
func applyJoins(db *gorm.DB, joins ...string) *gorm.DB {
|
|
for _, j := range joins {
|
|
if strings.TrimSpace(j) != "" {
|
|
db = db.Joins(j)
|
|
}
|
|
}
|
|
return db
|
|
}
|
|
|
|
func sapronakFlags(flags ...utils.FlagType) []string {
|
|
out := make([]string, len(flags))
|
|
for i, f := range flags {
|
|
out[i] = string(f)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func sapronakLegacyFlagByProductCategoryCase(categoryCodeExpr string) string {
|
|
return fmt.Sprintf(
|
|
`CASE
|
|
WHEN UPPER(%s) = 'DOC' THEN '%s'
|
|
WHEN UPPER(%s) = 'PLT' THEN '%s'
|
|
WHEN UPPER(%s) IN ('RAW', 'PST', 'STR', 'FSR') THEN '%s'
|
|
WHEN UPPER(%s) IN ('OBT', 'VTM', 'KMA') THEN '%s'
|
|
ELSE NULL
|
|
END`,
|
|
categoryCodeExpr, utils.FlagDOC,
|
|
categoryCodeExpr, utils.FlagPullet,
|
|
categoryCodeExpr, utils.FlagPakan,
|
|
categoryCodeExpr, utils.FlagOVK,
|
|
)
|
|
}
|
|
|
|
func sapronakIncomingPurchasesScopedSQL() string {
|
|
return `
|
|
WITH scoped_farm_allocations AS (
|
|
SELECT
|
|
sa.stockable_id AS purchase_item_id,
|
|
COALESCE(SUM(sa.qty), 0) AS allocated_qty
|
|
FROM stock_allocations sa
|
|
LEFT JOIN recording_stocks rs ON rs.id = sa.usable_id AND sa.usable_type = ?
|
|
LEFT JOIN recordings rec ON rec.id = rs.recording_id AND rec.deleted_at IS NULL
|
|
LEFT JOIN project_chickins pc ON pc.id = sa.usable_id AND sa.usable_type = ?
|
|
WHERE sa.stockable_type = ?
|
|
AND sa.status = ?
|
|
AND sa.allocation_purpose = ?
|
|
AND COALESCE(rec.project_flock_kandangs_id, pc.project_flock_kandang_id) IN ?
|
|
GROUP BY sa.stockable_id
|
|
)
|
|
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,
|
|
'Pembelian' AS transaction_type,
|
|
prod.name AS product_name,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
f.name
|
|
)
|
|
FROM flags f
|
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
|
), '') AS product_category,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
f.name
|
|
)
|
|
FROM flags f
|
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
|
), '') AS product_sub_category,
|
|
'-' AS source_warehouse,
|
|
w.name AS destination_warehouse,
|
|
'' AS destination,
|
|
pi.total_qty AS quantity,
|
|
u.id AS unit_id,
|
|
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 uoms u ON u.id = prod.uom_id
|
|
JOIN warehouses w ON w.id = pi.warehouse_id
|
|
WHERE w.kandang_id IS NOT NULL
|
|
AND (
|
|
pi.project_flock_kandang_id IN ?
|
|
OR (pi.project_flock_kandang_id IS NULL AND pi.warehouse_id IN ?)
|
|
)
|
|
UNION ALL
|
|
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,
|
|
'Pembelian' AS transaction_type,
|
|
prod.name AS product_name,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
f.name
|
|
)
|
|
FROM flags f
|
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
|
), '') AS product_category,
|
|
COALESCE((
|
|
SELECT string_agg(
|
|
f.name,
|
|
' ' ORDER BY
|
|
CASE
|
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
|
ELSE 1
|
|
END,
|
|
f.name
|
|
)
|
|
FROM flags f
|
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
|
), '') AS product_sub_category,
|
|
'-' AS source_warehouse,
|
|
w.name AS destination_warehouse,
|
|
'' AS destination,
|
|
sfa.allocated_qty AS quantity,
|
|
u.id AS unit_id,
|
|
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 uoms u ON u.id = prod.uom_id
|
|
JOIN warehouses w ON w.id = pi.warehouse_id
|
|
JOIN scoped_farm_allocations sfa ON sfa.purchase_item_id = pi.id
|
|
WHERE w.kandang_id IS NULL
|
|
AND COALESCE(sfa.allocated_qty, 0) > 0
|
|
`
|
|
}
|
|
|
|
var (
|
|
sapronakFlagsAll = sapronakFlags(utils.FlagDOC, utils.FlagPakan, utils.FlagOVK, utils.FlagPullet)
|
|
sapronakFlagsUsage = sapronakFlags(utils.FlagPakan, utils.FlagOVK)
|
|
sapronakFlagsChickin = sapronakFlags(utils.FlagDOC, utils.FlagPullet)
|
|
)
|
|
|
|
func (r *ClosingRepositoryImpl) joinSapronakProductFlag(db *gorm.DB, productAlias string) *gorm.DB {
|
|
actualFlags := r.DB().
|
|
Table("flags").
|
|
Select(`
|
|
flagable_id,
|
|
MIN(CASE
|
|
WHEN UPPER(name) = 'DOC' THEN 1
|
|
WHEN UPPER(name) = 'PULLET' THEN 2
|
|
WHEN UPPER(name) = 'PAKAN' THEN 3
|
|
WHEN UPPER(name) = 'OVK' THEN 4
|
|
ELSE 5
|
|
END) AS priority
|
|
`).
|
|
Where("flagable_type = ?", entity.FlagableTypeProduct).
|
|
Where("UPPER(name) IN ?", sapronakFlagsAll).
|
|
Group("flagable_id")
|
|
|
|
legacyFlagExpr := sapronakLegacyFlagByProductCategoryCase("pc.code")
|
|
subquery := r.DB().
|
|
Table("products AS sapronak_products").
|
|
Select(fmt.Sprintf(`
|
|
sapronak_products.id AS flagable_id,
|
|
CASE
|
|
WHEN actual_flags.priority = 1 THEN '%s'
|
|
WHEN actual_flags.priority = 2 THEN '%s'
|
|
WHEN actual_flags.priority = 3 THEN '%s'
|
|
WHEN actual_flags.priority = 4 THEN '%s'
|
|
ELSE %s
|
|
END AS name
|
|
`,
|
|
utils.FlagDOC,
|
|
utils.FlagPullet,
|
|
utils.FlagPakan,
|
|
utils.FlagOVK,
|
|
legacyFlagExpr,
|
|
)).
|
|
Joins("LEFT JOIN (?) AS actual_flags ON actual_flags.flagable_id = sapronak_products.id", actualFlags).
|
|
Joins("LEFT JOIN product_categories pc ON pc.id = sapronak_products.product_category_id").
|
|
Where("actual_flags.priority IS NOT NULL OR " + legacyFlagExpr + " IS NOT NULL")
|
|
|
|
return db.Joins("JOIN (?) f ON f.flagable_id = "+productAlias+".id", subquery)
|
|
}
|
|
|
|
func groupSapronakDetails(rows []SapronakDetailRow) map[uint][]SapronakDetailRow {
|
|
m := make(map[uint][]SapronakDetailRow)
|
|
for _, row := range rows {
|
|
m[row.ProductID] = append(m[row.ProductID], row)
|
|
}
|
|
return m
|
|
}
|
|
|
|
func scanAndGroupDetails(db *gorm.DB) (map[uint][]SapronakDetailRow, error) {
|
|
rows := make([]SapronakDetailRow, 0)
|
|
if err := db.Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return groupSapronakDetails(rows), nil
|
|
}
|
|
|
|
// =========================
|
|
// Usage (summary + details)
|
|
// =========================
|
|
|
|
func (r *ClosingRepositoryImpl) usageQuery(
|
|
ctx context.Context,
|
|
table string,
|
|
pwJoinCond string,
|
|
joins []string,
|
|
where string,
|
|
args ...any,
|
|
) *gorm.DB {
|
|
db := r.withCtx(ctx).Table(table).Select(`
|
|
pw.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
COALESCE(SUM(usage_qty), 0) AS qty,
|
|
COALESCE(p.product_price, 0) AS default_price
|
|
`)
|
|
db = applyJoins(db, joins...)
|
|
db = db.
|
|
Joins("JOIN product_warehouses pw ON "+pwJoinCond).
|
|
Joins("JOIN products p ON p.id = pw.product_id").
|
|
Where(where, args...)
|
|
db = r.joinSapronakProductFlag(db, "p")
|
|
return db
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) fetchSapronakUsage(
|
|
ctx context.Context,
|
|
table string,
|
|
pwJoinCond string,
|
|
joins []string,
|
|
where string,
|
|
args ...any,
|
|
) ([]SapronakUsageRow, error) {
|
|
rows := make([]SapronakUsageRow, 0)
|
|
db := r.usageQuery(ctx, table, pwJoinCond, joins, where, args...)
|
|
if err := db.Group("pw.product_id, p.name, f.name, p.product_price").Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return rows, nil
|
|
}
|
|
|
|
func scanUsage(db *gorm.DB) ([]SapronakUsageRow, error) {
|
|
rows := make([]SapronakUsageRow, 0)
|
|
if err := db.Group("pw.product_id, p.name, f.name, p.product_price").Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) detailQuery(
|
|
ctx context.Context,
|
|
table string,
|
|
pwJoinCond string,
|
|
joins []string,
|
|
selectSQL string,
|
|
where string,
|
|
args ...any,
|
|
) *gorm.DB {
|
|
db := r.withCtx(ctx).
|
|
Table(table).
|
|
Joins("JOIN product_warehouses pw ON " + pwJoinCond).
|
|
Joins("JOIN products p ON p.id = pw.product_id")
|
|
|
|
db = applyJoins(db, joins...)
|
|
db = r.joinSapronakProductFlag(db, "p")
|
|
return db.Select(selectSQL).Where(where, args...)
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) fetchSapronakDetails(
|
|
ctx context.Context,
|
|
table string,
|
|
pwJoinCond string,
|
|
joins []string,
|
|
selectSQL string,
|
|
where string,
|
|
args ...any,
|
|
) (map[uint][]SapronakDetailRow, error) {
|
|
return scanAndGroupDetails(r.detailQuery(ctx, table, pwJoinCond, joins, selectSQL, where, args...))
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) FetchSapronakUsage(ctx context.Context, pfkID uint, start, end *time.Time) ([]SapronakUsageRow, error) {
|
|
if pfkID == 0 {
|
|
return nil, nil
|
|
}
|
|
db := r.usageQuery(
|
|
ctx,
|
|
"recording_stocks rs",
|
|
"pw.id = rs.product_warehouse_id",
|
|
[]string{"JOIN recordings r ON r.id = rs.recording_id AND r.deleted_at IS NULL"},
|
|
"r.project_flock_kandangs_id = ? AND f.name IN ?",
|
|
pfkID,
|
|
sapronakFlagsUsage,
|
|
)
|
|
db = applyDateRange(db, "r.record_datetime", start, end)
|
|
return scanUsage(db)
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) FetchSapronakChickinUsage(ctx context.Context, pfkID uint, start, end *time.Time) ([]SapronakUsageRow, error) {
|
|
if pfkID == 0 {
|
|
return []SapronakUsageRow{}, nil
|
|
}
|
|
db := r.usageQuery(
|
|
ctx,
|
|
"project_chickins pc",
|
|
"pw.id = pc.product_warehouse_id",
|
|
nil,
|
|
"pc.project_flock_kandang_id = ? AND pc.usage_qty > 0 AND f.name IN ?",
|
|
pfkID,
|
|
sapronakFlagsChickin,
|
|
)
|
|
db = applyDateRange(db, "pc.chick_in_date", start, end)
|
|
return scanUsage(db)
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) FetchSapronakUsageDetails(ctx context.Context, pfkID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error) {
|
|
db := r.detailQuery(
|
|
ctx,
|
|
"recording_stocks rs",
|
|
"pw.id = rs.product_warehouse_id",
|
|
[]string{"JOIN recordings r ON r.id = rs.recording_id AND r.deleted_at IS NULL"}, // penting: supaya alias r valid
|
|
`
|
|
pw.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
r.record_datetime AS date,
|
|
CAST(r.id AS TEXT) AS reference,
|
|
0 AS qty_in,
|
|
COALESCE(rs.usage_qty,0) AS qty_out,
|
|
COALESCE(p.product_price,0) AS price
|
|
`,
|
|
"r.project_flock_kandangs_id = ? AND f.name IN ?",
|
|
pfkID,
|
|
sapronakFlagsUsage,
|
|
)
|
|
db = applyDateRange(db, "r.record_datetime", start, end)
|
|
return scanAndGroupDetails(db)
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) FetchSapronakChickinUsageDetails(ctx context.Context, pfkID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error) {
|
|
db := r.detailQuery(
|
|
ctx,
|
|
"project_chickins pc",
|
|
"pw.id = pc.product_warehouse_id",
|
|
nil,
|
|
`
|
|
pw.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
pc.chick_in_date AS date,
|
|
CAST(pc.id AS TEXT) AS reference,
|
|
0 AS qty_in,
|
|
COALESCE(pc.usage_qty,0) AS qty_out,
|
|
COALESCE(p.product_price,0) AS price
|
|
`,
|
|
"pc.project_flock_kandang_id = ? AND pc.usage_qty > 0 AND f.name IN ?",
|
|
pfkID,
|
|
sapronakFlagsChickin,
|
|
)
|
|
db = applyDateRange(db, "pc.chick_in_date", start, end)
|
|
return scanAndGroupDetails(db)
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) FetchSapronakUsageAllocatedDetails(ctx context.Context, projectFlockKandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error) {
|
|
if projectFlockKandangID == 0 {
|
|
return map[uint][]SapronakDetailRow{}, nil
|
|
}
|
|
|
|
dateExpr := "COALESCE(pi.received_date, st.transfer_date, lt.transfer_date, ast.created_at, pc.chick_in_date, r.record_datetime)"
|
|
query := r.withCtx(ctx).
|
|
Table("stock_allocations AS sa").
|
|
Select(`
|
|
p_resolve.id AS product_id,
|
|
p_resolve.name AS product_name,
|
|
f.name AS flag,
|
|
COALESCE(
|
|
pi.received_date,
|
|
st.transfer_date,
|
|
lt.transfer_date,
|
|
ast.created_at,
|
|
pc.chick_in_date,
|
|
r.record_datetime
|
|
) AS date,
|
|
COALESCE(
|
|
po.po_number,
|
|
st.movement_number,
|
|
lt.transfer_number,
|
|
CONCAT('ADJ-', ast.id),
|
|
CONCAT('CHICKIN-', pc.id),
|
|
CAST(r.id AS TEXT),
|
|
''
|
|
) AS reference,
|
|
0 AS qty_in,
|
|
COALESCE(SUM(sa.qty), 0) AS qty_out,
|
|
COALESCE(pi.price, p_resolve.product_price, 0) AS price
|
|
`).
|
|
Joins("JOIN product_warehouses pw ON pw.id = sa.product_warehouse_id").
|
|
Joins("LEFT JOIN recording_stocks rs ON rs.id = sa.usable_id AND sa.usable_type = ?", fifo.UsableKeyRecordingStock.String()).
|
|
Joins("LEFT JOIN recordings r ON r.id = rs.recording_id").
|
|
Joins("LEFT JOIN project_chickins pc_used ON pc_used.id = sa.usable_id AND sa.usable_type = ?", fifo.UsableKeyProjectChickin.String()).
|
|
Joins("LEFT JOIN purchase_items pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyPurchaseItems.String()).
|
|
Joins("LEFT JOIN purchases po ON po.id = pi.purchase_id").
|
|
Joins("LEFT JOIN stock_transfer_details std ON std.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyStockTransferIn.String()).
|
|
Joins("LEFT JOIN stock_transfers st ON st.id = std.stock_transfer_id").
|
|
Joins("LEFT JOIN laying_transfer_targets ltt ON ltt.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyTransferToLayingIn.String()).
|
|
Joins("LEFT JOIN laying_transfers lt ON lt.id = ltt.laying_transfer_id").
|
|
Joins("LEFT JOIN product_warehouses pw_ltt ON pw_ltt.id = ltt.product_warehouse_id").
|
|
Joins("LEFT JOIN adjustment_stocks ast ON ast.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyAdjustmentIn.String()).
|
|
Joins("LEFT JOIN project_flock_populations pfp ON pfp.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyProjectFlockPopulation.String()).
|
|
Joins("LEFT JOIN project_chickins pc ON pc.id = pfp.project_chickin_id").
|
|
Joins("LEFT JOIN product_warehouses pw_pc ON pw_pc.id = pc.product_warehouse_id").
|
|
Joins("LEFT JOIN products p_resolve ON p_resolve.id = COALESCE(pi.product_id, pw_ltt.product_id, pw_pc.product_id, pw.product_id)").
|
|
Where("sa.status = ?", entity.StockAllocationStatusActive).
|
|
Where("sa.allocation_purpose = ?", entity.StockAllocationPurposeConsume).
|
|
Where("sa.stockable_type <> ?", fifo.StockableKeyProjectFlockPopulation.String()).
|
|
Where("f.name IN ?", sapronakFlagsAll).
|
|
Where(`
|
|
(sa.usable_type = ? AND r.project_flock_kandangs_id = ? AND f.name IN ?)
|
|
OR
|
|
(sa.usable_type = ? AND pc_used.project_flock_kandang_id = ? AND f.name IN ?)
|
|
`,
|
|
fifo.UsableKeyRecordingStock.String(), projectFlockKandangID, sapronakFlagsUsage,
|
|
fifo.UsableKeyProjectChickin.String(), projectFlockKandangID, sapronakFlagsChickin,
|
|
)
|
|
query = r.joinSapronakProductFlag(query, "p_resolve").
|
|
Group(`
|
|
p_resolve.id, p_resolve.name, f.name,
|
|
pi.received_date, st.transfer_date, lt.transfer_date, ast.created_at, pc.chick_in_date, r.record_datetime,
|
|
po.po_number, st.movement_number, lt.transfer_number, ast.id, pc.id, r.id,
|
|
pi.price, p_resolve.product_price
|
|
`)
|
|
query = applyDateRange(query, dateExpr, start, end)
|
|
|
|
return scanAndGroupDetails(query)
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) incomingPurchaseBase(ctx context.Context, projectFlockKandangID uint, kandangID uint, start, end *time.Time) *gorm.DB {
|
|
db := r.withCtx(ctx).
|
|
Table("purchase_items AS pi").
|
|
Joins("JOIN purchases po ON po.id = pi.purchase_id AND po.deleted_at IS NULL").
|
|
Joins("JOIN products p ON p.id = pi.product_id").
|
|
Joins("JOIN warehouses w ON w.id = pi.warehouse_id").
|
|
Where("f.name IN ?", sapronakFlagsAll).
|
|
Where("pi.received_date IS NOT NULL")
|
|
if projectFlockKandangID > 0 {
|
|
db = db.Where(
|
|
"w.kandang_id = ? AND (pi.project_flock_kandang_id = ? OR pi.project_flock_kandang_id IS NULL)",
|
|
kandangID,
|
|
projectFlockKandangID,
|
|
)
|
|
} else {
|
|
db = db.Where("w.kandang_id = ?", kandangID)
|
|
}
|
|
db = applyDateRange(db, "pi.received_date", start, end)
|
|
return r.joinSapronakProductFlag(db, "p")
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) incomingFarmPurchaseAllocationBase(ctx context.Context, projectFlockKandangID uint, start, end *time.Time) *gorm.DB {
|
|
db := r.withCtx(ctx).
|
|
Table("stock_allocations AS sa").
|
|
Joins("JOIN purchase_items pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyPurchaseItems.String()).
|
|
Joins("JOIN purchases po ON po.id = pi.purchase_id AND po.deleted_at IS NULL").
|
|
Joins("JOIN products p ON p.id = pi.product_id").
|
|
Joins("JOIN warehouses w ON w.id = pi.warehouse_id").
|
|
Joins("LEFT JOIN recording_stocks rs ON rs.id = sa.usable_id AND sa.usable_type = ?", fifo.UsableKeyRecordingStock.String()).
|
|
Joins("LEFT JOIN recordings rec ON rec.id = rs.recording_id AND rec.deleted_at IS NULL").
|
|
Joins("LEFT JOIN project_chickins pc ON pc.id = sa.usable_id AND sa.usable_type = ?", fifo.UsableKeyProjectChickin.String()).
|
|
Where("sa.status = ?", entity.StockAllocationStatusActive).
|
|
Where("sa.allocation_purpose = ?", entity.StockAllocationPurposeConsume).
|
|
Where("w.kandang_id IS NULL").
|
|
Where("COALESCE(rec.project_flock_kandangs_id, pc.project_flock_kandang_id) = ?", projectFlockKandangID).
|
|
Where("f.name IN ?", sapronakFlagsAll).
|
|
Where("pi.received_date IS NOT NULL")
|
|
db = applyDateRange(db, "pi.received_date", start, end)
|
|
return r.joinSapronakProductFlag(db, "p")
|
|
}
|
|
|
|
func mergeSapronakIncomingRows(primary []SapronakIncomingRow, extra []SapronakIncomingRow) []SapronakIncomingRow {
|
|
if len(extra) == 0 {
|
|
return primary
|
|
}
|
|
|
|
type key struct {
|
|
productID uint
|
|
flag string
|
|
}
|
|
|
|
merged := make(map[key]*SapronakIncomingRow, len(primary)+len(extra))
|
|
order := make([]key, 0, len(primary)+len(extra))
|
|
|
|
add := func(rows []SapronakIncomingRow) {
|
|
for _, row := range rows {
|
|
k := key{productID: row.ProductID, flag: row.Flag}
|
|
if existing, ok := merged[k]; ok {
|
|
existing.Qty += row.Qty
|
|
existing.Value += row.Value
|
|
if existing.ProductName == "" {
|
|
existing.ProductName = row.ProductName
|
|
}
|
|
if existing.DefaultPrice == 0 {
|
|
existing.DefaultPrice = row.DefaultPrice
|
|
}
|
|
continue
|
|
}
|
|
|
|
copyRow := row
|
|
merged[k] = ©Row
|
|
order = append(order, k)
|
|
}
|
|
}
|
|
|
|
add(primary)
|
|
add(extra)
|
|
|
|
result := make([]SapronakIncomingRow, 0, len(order))
|
|
for _, k := range order {
|
|
result = append(result, *merged[k])
|
|
}
|
|
return result
|
|
}
|
|
|
|
func mergeSapronakDetailMaps(primary map[uint][]SapronakDetailRow, extra map[uint][]SapronakDetailRow) map[uint][]SapronakDetailRow {
|
|
if len(primary) == 0 && len(extra) == 0 {
|
|
return map[uint][]SapronakDetailRow{}
|
|
}
|
|
if len(extra) == 0 {
|
|
return primary
|
|
}
|
|
if len(primary) == 0 {
|
|
return extra
|
|
}
|
|
|
|
for productID, rows := range extra {
|
|
primary[productID] = append(primary[productID], rows...)
|
|
}
|
|
return primary
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) FetchSapronakIncoming(ctx context.Context, projectFlockKandangID uint, kandangID uint, start, end *time.Time) ([]SapronakIncomingRow, error) {
|
|
rows := make([]SapronakIncomingRow, 0)
|
|
db := r.incomingPurchaseBase(ctx, projectFlockKandangID, kandangID, start, end).Select(`
|
|
pi.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
COALESCE(SUM(pi.total_qty), 0) AS qty,
|
|
COALESCE(SUM(pi.total_qty * pi.price), 0) AS value,
|
|
COALESCE(p.product_price, 0) AS default_price
|
|
`)
|
|
if err := db.Group("pi.product_id, p.name, f.name, p.product_price").Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if projectFlockKandangID == 0 {
|
|
return rows, nil
|
|
}
|
|
|
|
farmRows := make([]SapronakIncomingRow, 0)
|
|
farmDB := r.incomingFarmPurchaseAllocationBase(ctx, projectFlockKandangID, start, end).Select(`
|
|
pi.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
COALESCE(SUM(sa.qty), 0) AS qty,
|
|
COALESCE(SUM(sa.qty * pi.price), 0) AS value,
|
|
COALESCE(p.product_price, 0) AS default_price
|
|
`)
|
|
if err := farmDB.Group("pi.product_id, p.name, f.name, p.product_price").Scan(&farmRows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return mergeSapronakIncomingRows(rows, farmRows), nil
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) FetchSapronakIncomingDetails(ctx context.Context, projectFlockKandangID uint, kandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error) {
|
|
rows, err := scanAndGroupDetails(
|
|
r.incomingPurchaseBase(ctx, projectFlockKandangID, kandangID, start, end).Select(`
|
|
pi.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
pi.received_date AS date,
|
|
COALESCE(po.po_number, '') AS reference,
|
|
COALESCE(pi.total_qty,0) AS qty_in,
|
|
0 AS qty_out,
|
|
COALESCE(pi.price,0) AS price
|
|
`),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if projectFlockKandangID == 0 {
|
|
return rows, nil
|
|
}
|
|
|
|
farmRows, err := scanAndGroupDetails(
|
|
r.incomingFarmPurchaseAllocationBase(ctx, projectFlockKandangID, start, end).Select(`
|
|
pi.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
pi.received_date AS date,
|
|
COALESCE(po.po_number, '') AS reference,
|
|
COALESCE(SUM(sa.qty),0) AS qty_in,
|
|
0 AS qty_out,
|
|
COALESCE(pi.price,0) AS price
|
|
`).Group(`
|
|
pi.id, pi.product_id, p.name, f.name,
|
|
pi.received_date, po.po_number, pi.price
|
|
`),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return mergeSapronakDetailMaps(rows, farmRows), nil
|
|
}
|
|
|
|
type stockLogSapronakRow struct {
|
|
ID uint `gorm:"column:id"`
|
|
ProductID uint `gorm:"column:product_id"`
|
|
ProductName string `gorm:"column:product_name"`
|
|
Flag string `gorm:"column:flag"`
|
|
CreatedAt *time.Time `gorm:"column:created_at"`
|
|
Increase float64 `gorm:"column:increase"`
|
|
Decrease float64 `gorm:"column:decrease"`
|
|
Price float64 `gorm:"column:price"`
|
|
MovementNumber string `gorm:"column:movement_number"`
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) fetchStockLogs(ctx context.Context, kandangID uint, logType any, withMovement bool) ([]stockLogSapronakRow, error) {
|
|
rows := make([]stockLogSapronakRow, 0)
|
|
|
|
movementSelect := "'' AS movement_number"
|
|
joins := []string{}
|
|
if withMovement {
|
|
movementSelect = "COALESCE(st.movement_number,'') AS movement_number"
|
|
joins = append(joins, "JOIN stock_transfers st ON st.id = sl.loggable_id")
|
|
}
|
|
|
|
db := r.withCtx(ctx).
|
|
Table("stock_logs sl").
|
|
Select(`
|
|
sl.id AS id,
|
|
pw.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
sl.created_at AS created_at,
|
|
COALESCE(sl.increase,0) AS increase,
|
|
COALESCE(sl.decrease,0) AS decrease,
|
|
COALESCE(p.product_price,0) AS price,
|
|
` + movementSelect + `
|
|
`).
|
|
Joins("JOIN product_warehouses pw ON pw.id = sl.product_warehouse_id").
|
|
Joins("JOIN products p ON p.id = pw.product_id").
|
|
Joins("JOIN warehouses w ON w.id = pw.warehouse_id")
|
|
|
|
db = applyJoins(db, joins...)
|
|
db = r.joinSapronakProductFlag(db, "p")
|
|
|
|
if err := db.
|
|
Where("sl.loggable_type = ?", logType).
|
|
Where("w.kandang_id = ?", kandangID).
|
|
Where("f.name IN ?", sapronakFlagsAll).
|
|
Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func splitStockLogs(rows []stockLogSapronakRow, refFn func(stockLogSapronakRow) string) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow) {
|
|
in := make(map[uint][]SapronakDetailRow)
|
|
out := make(map[uint][]SapronakDetailRow)
|
|
|
|
for _, row := range rows {
|
|
base := SapronakDetailRow{
|
|
ProductID: row.ProductID,
|
|
ProductName: row.ProductName,
|
|
Flag: row.Flag,
|
|
Date: row.CreatedAt,
|
|
Reference: refFn(row),
|
|
Price: row.Price,
|
|
}
|
|
|
|
if row.Increase > 0 {
|
|
d := base
|
|
d.QtyIn = row.Increase
|
|
in[row.ProductID] = append(in[row.ProductID], d)
|
|
}
|
|
if row.Decrease > 0 {
|
|
d := base
|
|
d.QtyOut = row.Decrease
|
|
out[row.ProductID] = append(out[row.ProductID], d)
|
|
}
|
|
}
|
|
|
|
return in, out
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) FetchSapronakAdjustments(ctx context.Context, kandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error) {
|
|
poByWarehouse := r.DB().
|
|
Table("purchase_items pi").
|
|
Select("DISTINCT ON (pi.product_warehouse_id) pi.product_warehouse_id, po.po_number, pi.received_date").
|
|
Joins("JOIN purchases po ON po.id = pi.purchase_id").
|
|
Where("pi.received_date IS NOT NULL").
|
|
Order("pi.product_warehouse_id, pi.received_date ASC")
|
|
|
|
incomingQuery := r.withCtx(ctx).
|
|
Table("adjustment_stocks AS ast").
|
|
Select(`
|
|
pw.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
ast.created_at AS date,
|
|
CONCAT('ADJ-', ast.id) AS reference,
|
|
COALESCE(ast.total_qty, 0) AS qty_in,
|
|
0 AS qty_out,
|
|
COALESCE(p.product_price, 0) AS price
|
|
`).
|
|
Joins("JOIN product_warehouses pw ON pw.id = ast.product_warehouse_id").
|
|
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
|
|
Joins("JOIN products p ON p.id = pw.product_id").
|
|
Where("w.kandang_id = ?", kandangID).
|
|
Where("f.name IN ?", sapronakFlagsAll).
|
|
Where("COALESCE(ast.total_qty, 0) > 0")
|
|
incomingQuery = r.joinSapronakProductFlag(incomingQuery, "p")
|
|
incomingQuery = applyDateRange(incomingQuery, "ast.created_at", start, end)
|
|
incoming, err := scanAndGroupDetails(incomingQuery)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
dateExpr := "COALESCE(pi.received_date, st.transfer_date, lt.transfer_date, pfp_po.received_date, pc.chick_in_date, ast_in.created_at, ast.created_at)"
|
|
outgoingQuery := r.withCtx(ctx).
|
|
Table("stock_allocations AS sa").
|
|
Select(`
|
|
pw.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
COALESCE(pi.received_date, st.transfer_date, lt.transfer_date, pfp_po.received_date, pc.chick_in_date, ast_in.created_at, ast.created_at) AS date,
|
|
COALESCE(po.po_number, st.movement_number, lt.transfer_number, pfp_po.po_number, CONCAT('CHICKIN-', pc.id), CONCAT('ADJ-', ast_in.id), CONCAT('ADJ-', ast.id)) AS reference,
|
|
0 AS qty_in,
|
|
COALESCE(SUM(sa.qty), 0) AS qty_out,
|
|
COALESCE(p.product_price, 0) AS price
|
|
`).
|
|
Joins("JOIN adjustment_stocks ast ON ast.id = sa.usable_id AND sa.usable_type = ?", fifo.UsableKeyAdjustmentOut.String()).
|
|
Joins("LEFT JOIN purchase_items pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyPurchaseItems.String()).
|
|
Joins("LEFT JOIN purchases po ON po.id = pi.purchase_id").
|
|
Joins("LEFT JOIN stock_transfer_details std ON std.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyStockTransferIn.String()).
|
|
Joins("LEFT JOIN stock_transfers st ON st.id = std.stock_transfer_id").
|
|
Joins("LEFT JOIN laying_transfer_targets ltt ON ltt.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyTransferToLayingIn.String()).
|
|
Joins("LEFT JOIN laying_transfers lt ON lt.id = ltt.laying_transfer_id").
|
|
Joins("LEFT JOIN adjustment_stocks ast_in ON ast_in.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyAdjustmentIn.String()).
|
|
Joins("LEFT JOIN project_flock_populations pfp ON pfp.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyProjectFlockPopulation.String()).
|
|
Joins("LEFT JOIN project_chickins pc ON pc.id = pfp.project_chickin_id").
|
|
Joins("LEFT JOIN (?) pfp_po ON pfp_po.product_warehouse_id = pfp.product_warehouse_id", poByWarehouse).
|
|
Joins("JOIN product_warehouses pw ON pw.id = sa.product_warehouse_id").
|
|
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
|
|
Joins("JOIN products p ON p.id = pw.product_id").
|
|
Where("sa.status = ?", entity.StockAllocationStatusActive).
|
|
Where("sa.allocation_purpose = ?", entity.StockAllocationPurposeConsume).
|
|
Where("w.kandang_id = ?", kandangID).
|
|
Where("f.name IN ?", sapronakFlagsAll).
|
|
Where("f.name NOT IN ?", sapronakFlags(utils.FlagDOC, utils.FlagPullet)).
|
|
Group("pw.product_id, p.name, f.name, pi.received_date, st.transfer_date, lt.transfer_date, pfp_po.received_date, pc.chick_in_date, ast_in.created_at, ast.created_at, po.po_number, st.movement_number, lt.transfer_number, pfp_po.po_number, pc.id, ast_in.id, ast.id, p.product_price")
|
|
outgoingQuery = r.joinSapronakProductFlag(outgoingQuery, "p")
|
|
outgoingQuery = applyDateRange(outgoingQuery, dateExpr, start, end)
|
|
outgoing, err := scanAndGroupDetails(outgoingQuery)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return incoming, outgoing, nil
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) FetchSapronakTransfers(ctx context.Context, kandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error) {
|
|
incomingQuery := r.withCtx(ctx).
|
|
Table("stock_transfer_details AS std").
|
|
Select(`
|
|
std.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
st.transfer_date::timestamp AS date,
|
|
COALESCE(st.movement_number, '') AS reference,
|
|
COALESCE(std.total_qty, 0) AS qty_in,
|
|
0 AS qty_out,
|
|
COALESCE(p.product_price, 0) AS price
|
|
`).
|
|
Joins("JOIN stock_transfers st ON st.id = std.stock_transfer_id").
|
|
Joins("LEFT JOIN warehouses fw ON fw.id = st.from_warehouse_id").
|
|
Joins("JOIN product_warehouses pw ON pw.id = std.dest_product_warehouse_id").
|
|
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
|
|
Joins("JOIN products p ON p.id = std.product_id").
|
|
Where("w.kandang_id = ?", kandangID).
|
|
Where("(fw.kandang_id IS NULL OR fw.kandang_id <> w.kandang_id)").
|
|
Where("f.name IN ?", sapronakFlagsAll)
|
|
incomingQuery = r.joinSapronakProductFlag(incomingQuery, "p")
|
|
incomingQuery = applyDateRange(incomingQuery, "st.transfer_date", start, end)
|
|
incoming, err := scanAndGroupDetails(incomingQuery)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
incomingLayingQuery := r.withCtx(ctx).
|
|
Table("laying_transfer_targets AS ltt").
|
|
Select(`
|
|
pw.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
lt.transfer_date::timestamp AS date,
|
|
COALESCE(lt.transfer_number, '') AS reference,
|
|
COALESCE(ltt.total_qty, 0) AS qty_in,
|
|
0 AS qty_out,
|
|
COALESCE(p.product_price, 0) AS price
|
|
`).
|
|
Joins("JOIN laying_transfers lt ON lt.id = ltt.laying_transfer_id").
|
|
Joins("LEFT JOIN product_warehouses pw_source ON pw_source.id = lt.source_product_warehouse_id").
|
|
Joins("LEFT JOIN warehouses w_source ON w_source.id = pw_source.warehouse_id").
|
|
Joins("JOIN product_warehouses pw ON pw.id = ltt.product_warehouse_id").
|
|
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
|
|
Joins("JOIN products p ON p.id = pw.product_id").
|
|
Where("w.kandang_id = ?", kandangID).
|
|
Where("(w_source.kandang_id IS NULL OR w_source.kandang_id <> w.kandang_id)").
|
|
Where("f.name IN ?", sapronakFlagsAll)
|
|
incomingLayingQuery = r.joinSapronakProductFlag(incomingLayingQuery, "p")
|
|
incomingLayingQuery = applyDateRange(incomingLayingQuery, "lt.transfer_date", start, end)
|
|
incomingLaying, err := scanAndGroupDetails(incomingLayingQuery)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
for pid, rows := range incomingLaying {
|
|
incoming[pid] = append(incoming[pid], rows...)
|
|
}
|
|
|
|
outgoingQuery := r.withCtx(ctx).
|
|
Table("stock_allocations AS sa").
|
|
Select(`
|
|
std.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
st.transfer_date::timestamp AS date,
|
|
COALESCE(st.movement_number, '') AS reference,
|
|
0 AS qty_in,
|
|
COALESCE(SUM(sa.qty), 0) AS qty_out,
|
|
COALESCE(p.product_price, 0) AS price
|
|
`).
|
|
Joins("JOIN stock_transfer_details std ON std.id = sa.usable_id AND sa.usable_type = ?", fifo.UsableKeyStockTransferOut.String()).
|
|
Joins("JOIN stock_transfers st ON st.id = std.stock_transfer_id").
|
|
Joins("JOIN product_warehouses pw ON pw.id = sa.product_warehouse_id").
|
|
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
|
|
Joins("LEFT JOIN product_warehouses pw_dest ON pw_dest.id = std.dest_product_warehouse_id").
|
|
Joins("LEFT JOIN warehouses w_dest ON w_dest.id = pw_dest.warehouse_id").
|
|
Joins("JOIN products p ON p.id = std.product_id").
|
|
Where("sa.status = ?", entity.StockAllocationStatusActive).
|
|
Where("sa.allocation_purpose = ?", entity.StockAllocationPurposeConsume).
|
|
Where("w.kandang_id = ?", kandangID).
|
|
Where("(w_dest.kandang_id IS NULL OR w_dest.kandang_id <> w.kandang_id)").
|
|
Where("f.name IN ?", sapronakFlagsAll).
|
|
Group("std.id, std.product_id, p.name, f.name, st.transfer_date, st.movement_number, p.product_price")
|
|
outgoingQuery = r.joinSapronakProductFlag(outgoingQuery, "p")
|
|
outgoingQuery = applyDateRange(outgoingQuery, "st.transfer_date", start, end)
|
|
outgoing, err := scanAndGroupDetails(outgoingQuery)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
outgoingLayingQuery := r.withCtx(ctx).
|
|
Table("stock_allocations AS sa").
|
|
Select(`
|
|
pw.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
lt.transfer_date::timestamp AS date,
|
|
COALESCE(lt.transfer_number, '') AS reference,
|
|
0 AS qty_in,
|
|
COALESCE(SUM(sa.qty), 0) AS qty_out,
|
|
COALESCE(p.product_price, 0) AS price
|
|
`).
|
|
Joins("JOIN laying_transfers lt ON lt.id = sa.usable_id AND sa.usable_type = ?", fifo.UsableKeyTransferToLayingOut.String()).
|
|
Joins("LEFT JOIN laying_transfer_targets ltt ON ltt.laying_transfer_id = lt.id").
|
|
Joins("LEFT JOIN product_warehouses pw_dest ON pw_dest.id = ltt.product_warehouse_id").
|
|
Joins("LEFT JOIN warehouses w_dest ON w_dest.id = pw_dest.warehouse_id").
|
|
Joins("JOIN product_warehouses pw ON pw.id = sa.product_warehouse_id").
|
|
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
|
|
Joins("JOIN products p ON p.id = pw.product_id").
|
|
Where("sa.status = ?", entity.StockAllocationStatusActive).
|
|
Where("sa.allocation_purpose = ?", entity.StockAllocationPurposeConsume).
|
|
Where("w.kandang_id = ?", kandangID).
|
|
Where("(w_dest.kandang_id IS NULL OR w_dest.kandang_id <> w.kandang_id)").
|
|
Where("f.name IN ?", sapronakFlagsAll).
|
|
Group("lt.id, pw.product_id, p.name, f.name, lt.transfer_date, lt.transfer_number, p.product_price")
|
|
outgoingLayingQuery = r.joinSapronakProductFlag(outgoingLayingQuery, "p")
|
|
outgoingLayingQuery = applyDateRange(outgoingLayingQuery, "lt.transfer_date", start, end)
|
|
outgoingLaying, err := scanAndGroupDetails(outgoingLayingQuery)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
for pid, rows := range outgoingLaying {
|
|
outgoing[pid] = append(outgoing[pid], rows...)
|
|
}
|
|
|
|
return incoming, outgoing, nil
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) FetchSapronakSales(ctx context.Context, projectFlockKandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error) {
|
|
attributedProjectFlockKandangExpr := `
|
|
COALESCE(
|
|
pc.project_flock_kandang_id,
|
|
pi.project_flock_kandang_id,
|
|
source_pw.project_flock_kandang_id,
|
|
ltt.target_project_flock_kandang_id,
|
|
pw.project_flock_kandang_id
|
|
)
|
|
`
|
|
|
|
query := r.withCtx(ctx).
|
|
Table("stock_allocations AS sa").
|
|
Select(`
|
|
pw.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
COALESCE(mdp.delivery_date, mdp.created_at) AS date,
|
|
COALESCE(m.so_number, '') AS reference,
|
|
0 AS qty_in,
|
|
COALESCE(SUM(sa.qty), 0) AS qty_out,
|
|
COALESCE(mdp.unit_price, mp.unit_price, 0) AS price
|
|
`).
|
|
Joins("JOIN marketing_delivery_products mdp ON mdp.id = sa.usable_id AND sa.usable_type = ?", fifo.UsableKeyMarketingDelivery.String()).
|
|
Joins("JOIN marketing_products mp ON mp.id = mdp.marketing_product_id").
|
|
Joins("JOIN marketings m ON m.id = mp.marketing_id").
|
|
Joins("JOIN product_warehouses pw ON pw.id = sa.product_warehouse_id").
|
|
Joins("JOIN products p ON p.id = pw.product_id").
|
|
Joins("LEFT JOIN purchase_items pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyPurchaseItems.String()).
|
|
Joins("LEFT JOIN stock_transfer_details std ON std.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyStockTransferIn.String()).
|
|
Joins("LEFT JOIN product_warehouses source_pw ON source_pw.id = std.source_product_warehouse_id").
|
|
Joins("LEFT JOIN laying_transfer_targets ltt ON ltt.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyTransferToLayingIn.String()).
|
|
Joins("LEFT JOIN project_flock_populations pfp ON pfp.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyProjectFlockPopulation.String()).
|
|
Joins("LEFT JOIN project_chickins pc ON pc.id = pfp.project_chickin_id").
|
|
Where("sa.status = ?", entity.StockAllocationStatusActive).
|
|
Where("sa.allocation_purpose = ?", entity.StockAllocationPurposeConsume).
|
|
Where(attributedProjectFlockKandangExpr+" = ?", projectFlockKandangID).
|
|
Where("f.name IN ?", sapronakFlagsAll).
|
|
Group("mdp.id, pw.product_id, p.name, f.name, mdp.delivery_date, mdp.created_at, m.so_number, mdp.unit_price, mp.unit_price")
|
|
|
|
query = r.joinSapronakProductFlag(query, "p")
|
|
query = applyDateRange(query, "COALESCE(mdp.delivery_date, mdp.created_at)", start, end)
|
|
sales, err := scanAndGroupDetails(query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nonFifoQuery := r.withCtx(ctx).
|
|
Table("marketing_delivery_products AS mdp").
|
|
Select(`
|
|
pw.product_id AS product_id,
|
|
p.name AS product_name,
|
|
f.name AS flag,
|
|
COALESCE(mdp.delivery_date, mdp.created_at) AS date,
|
|
COALESCE(m.so_number, '') AS reference,
|
|
0 AS qty_in,
|
|
COALESCE(mdp.usage_qty, 0) AS qty_out,
|
|
COALESCE(mdp.unit_price, mp.unit_price, 0) AS price
|
|
`).
|
|
Joins("JOIN marketing_products mp ON mp.id = mdp.marketing_product_id").
|
|
Joins("JOIN marketings m ON m.id = mp.marketing_id").
|
|
Joins("JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id").
|
|
Joins("JOIN products p ON p.id = pw.product_id").
|
|
Joins("LEFT JOIN stock_allocations sa ON sa.usable_id = mdp.id AND sa.usable_type = ? AND sa.status = ? AND sa.allocation_purpose = ?",
|
|
fifo.UsableKeyMarketingDelivery.String(),
|
|
entity.StockAllocationStatusActive,
|
|
entity.StockAllocationPurposeConsume,
|
|
).
|
|
Where("mdp.usage_qty > 0").
|
|
Where("sa.id IS NULL").
|
|
Where("pw.project_flock_kandang_id = ?", projectFlockKandangID).
|
|
Where("f.name IN ?", sapronakFlagsAll).
|
|
Group("mdp.id, pw.product_id, p.name, f.name, mdp.delivery_date, mdp.created_at, m.so_number, mdp.unit_price, mp.unit_price")
|
|
|
|
nonFifoQuery = r.joinSapronakProductFlag(nonFifoQuery, "p")
|
|
nonFifoQuery = applyDateRange(nonFifoQuery, "COALESCE(mdp.delivery_date, mdp.created_at)", start, end)
|
|
nonFifoSales, err := scanAndGroupDetails(nonFifoQuery)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for pid, rows := range nonFifoSales {
|
|
sales[pid] = append(sales[pid], rows...)
|
|
}
|
|
|
|
return sales, nil
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) FetchSapronakSalesAllocatedDetails(ctx context.Context, projectFlockKandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error) {
|
|
if projectFlockKandangID == 0 {
|
|
return map[uint][]SapronakDetailRow{}, nil
|
|
}
|
|
|
|
pfpType := fifo.StockableKeyProjectFlockPopulation.String()
|
|
dateExpr := fmt.Sprintf(`
|
|
CASE
|
|
WHEN sa.stockable_type = '%s' THEN COALESCE(
|
|
pi_pc.received_date,
|
|
st_pc.transfer_date,
|
|
lt_pc.transfer_date,
|
|
ast_pc.created_at,
|
|
pc.chick_in_date
|
|
)
|
|
ELSE COALESCE(
|
|
pi.received_date,
|
|
st.transfer_date,
|
|
lt.transfer_date,
|
|
ast.created_at
|
|
)
|
|
END
|
|
`, pfpType)
|
|
|
|
attributedProjectFlockKandangExpr := `
|
|
COALESCE(
|
|
pc.project_flock_kandang_id,
|
|
pi.project_flock_kandang_id,
|
|
source_pw.project_flock_kandang_id,
|
|
ltt.target_project_flock_kandang_id,
|
|
pw_sales.project_flock_kandang_id
|
|
)
|
|
`
|
|
|
|
query := r.withCtx(ctx).
|
|
Table("stock_allocations AS sa").
|
|
Select(fmt.Sprintf(`
|
|
p_resolve.id AS product_id,
|
|
p_resolve.name AS product_name,
|
|
f.name AS flag,
|
|
CASE
|
|
WHEN sa.stockable_type = '%s' THEN COALESCE(
|
|
pi_pc.received_date,
|
|
st_pc.transfer_date,
|
|
lt_pc.transfer_date,
|
|
ast_pc.created_at,
|
|
pc.chick_in_date
|
|
)
|
|
ELSE COALESCE(
|
|
pi.received_date,
|
|
st.transfer_date,
|
|
lt.transfer_date,
|
|
ast.created_at
|
|
)
|
|
END AS date,
|
|
CASE
|
|
WHEN sa.stockable_type = '%s' THEN COALESCE(
|
|
po_pc.po_number,
|
|
st_pc.movement_number,
|
|
lt_pc.transfer_number,
|
|
CASE WHEN ast_pc.id IS NOT NULL THEN CONCAT('ADJ-', ast_pc.id) END,
|
|
CONCAT('CHICKIN-', pc.id),
|
|
''
|
|
)
|
|
ELSE COALESCE(
|
|
po.po_number,
|
|
st.movement_number,
|
|
lt.transfer_number,
|
|
CASE WHEN ast.id IS NOT NULL THEN CONCAT('ADJ-', ast.id) END,
|
|
''
|
|
)
|
|
END AS reference,
|
|
0 AS qty_in,
|
|
COALESCE(SUM(sa.qty), 0) AS qty_out,
|
|
CASE
|
|
WHEN sa.stockable_type = '%s' THEN COALESCE(pi_pc.price, p_resolve.product_price, 0)
|
|
ELSE COALESCE(pi.price, p_resolve.product_price, 0)
|
|
END AS price
|
|
`, pfpType, pfpType, pfpType)).
|
|
Joins("JOIN marketing_delivery_products mdp ON mdp.id = sa.usable_id AND sa.usable_type = ?", fifo.UsableKeyMarketingDelivery.String()).
|
|
Joins("JOIN product_warehouses pw_sales ON pw_sales.id = mdp.product_warehouse_id").
|
|
Joins("JOIN product_warehouses pw ON pw.id = sa.product_warehouse_id").
|
|
Joins("LEFT JOIN purchase_items pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyPurchaseItems.String()).
|
|
Joins("LEFT JOIN purchases po ON po.id = pi.purchase_id").
|
|
Joins("LEFT JOIN stock_transfer_details std ON std.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyStockTransferIn.String()).
|
|
Joins("LEFT JOIN stock_transfers st ON st.id = std.stock_transfer_id").
|
|
Joins("LEFT JOIN product_warehouses source_pw ON source_pw.id = std.source_product_warehouse_id").
|
|
Joins("LEFT JOIN laying_transfer_targets ltt ON ltt.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyTransferToLayingIn.String()).
|
|
Joins("LEFT JOIN laying_transfers lt ON lt.id = ltt.laying_transfer_id").
|
|
Joins("LEFT JOIN product_warehouses pw_ltt ON pw_ltt.id = ltt.product_warehouse_id").
|
|
Joins("LEFT JOIN adjustment_stocks ast ON ast.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyAdjustmentIn.String()).
|
|
Joins("LEFT JOIN project_flock_populations pfp ON pfp.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyProjectFlockPopulation.String()).
|
|
Joins("LEFT JOIN project_chickins pc ON pc.id = pfp.project_chickin_id").
|
|
Joins("LEFT JOIN stock_allocations sa_pc ON sa_pc.usable_type = ? AND sa_pc.usable_id = pc.id", fifo.UsableKeyProjectChickin.String()).
|
|
Joins("LEFT JOIN purchase_items pi_pc ON pi_pc.id = sa_pc.stockable_id AND sa_pc.stockable_type = ?", fifo.StockableKeyPurchaseItems.String()).
|
|
Joins("LEFT JOIN purchases po_pc ON po_pc.id = pi_pc.purchase_id").
|
|
Joins("LEFT JOIN stock_transfer_details std_pc ON std_pc.id = sa_pc.stockable_id AND sa_pc.stockable_type = ?", fifo.StockableKeyStockTransferIn.String()).
|
|
Joins("LEFT JOIN stock_transfers st_pc ON st_pc.id = std_pc.stock_transfer_id").
|
|
Joins("LEFT JOIN laying_transfer_targets ltt_pc ON ltt_pc.id = sa_pc.stockable_id AND sa_pc.stockable_type = ?", fifo.StockableKeyTransferToLayingIn.String()).
|
|
Joins("LEFT JOIN laying_transfers lt_pc ON lt_pc.id = ltt_pc.laying_transfer_id").
|
|
Joins("LEFT JOIN adjustment_stocks ast_pc ON ast_pc.id = sa_pc.stockable_id AND sa_pc.stockable_type = ?", fifo.StockableKeyAdjustmentIn.String()).
|
|
Joins("LEFT JOIN product_warehouses pw_pc ON pw_pc.id = pc.product_warehouse_id").
|
|
Joins(fmt.Sprintf("LEFT JOIN products p_resolve ON p_resolve.id = CASE WHEN sa.stockable_type = '%s' THEN pw_pc.product_id ELSE COALESCE(pi.product_id, pw_ltt.product_id, pw.product_id) END", pfpType)).
|
|
Where("sa.status = ?", entity.StockAllocationStatusActive).
|
|
Where("sa.allocation_purpose = ?", entity.StockAllocationPurposeConsume).
|
|
Where("sa.stockable_type <> ?", fifo.StockableKeyProjectFlockPopulation.String()).
|
|
Where(attributedProjectFlockKandangExpr+" = ?", projectFlockKandangID).
|
|
Where("f.name IN ?", sapronakFlagsAll).
|
|
Group(`
|
|
p_resolve.id, p_resolve.name, f.name,
|
|
pi_pc.received_date, st_pc.transfer_date, lt_pc.transfer_date, ast_pc.created_at, pc.chick_in_date,
|
|
pi.received_date, st.transfer_date, lt.transfer_date, ast.created_at,
|
|
po_pc.po_number, st_pc.movement_number, lt_pc.transfer_number, ast_pc.id, pc.id,
|
|
po.po_number, st.movement_number, lt.transfer_number, ast.id,
|
|
pi_pc.price, pi.price, p_resolve.product_price, sa.stockable_type
|
|
`)
|
|
|
|
query = r.joinSapronakProductFlag(query, "p_resolve")
|
|
query = applyDateRange(query, dateExpr, start, end)
|
|
return scanAndGroupDetails(query)
|
|
}
|
|
|
|
func (r *ClosingRepositoryImpl) GetProductsWithFlagsByIDs(ctx context.Context, productIDs []uint) ([]entity.Product, error) {
|
|
if len(productIDs) == 0 {
|
|
return []entity.Product{}, nil
|
|
}
|
|
|
|
var products []entity.Product
|
|
err := r.DB().WithContext(ctx).
|
|
Preload("Flags").
|
|
Where("id IN ?", productIDs).
|
|
Find(&products).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return products, nil
|
|
}
|