mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 05:21:57 +00:00
327 lines
9.2 KiB
Go
327 lines
9.2 KiB
Go
package repositories
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
type FarmDepreciationCandidateRow struct {
|
|
ProjectFlockID uint
|
|
FarmName string
|
|
}
|
|
|
|
type FarmDepreciationLatestTransferRow struct {
|
|
ProjectFlockID uint
|
|
FarmName string
|
|
ProjectFlockKandangID uint
|
|
KandangID uint
|
|
KandangName string
|
|
HouseType *string
|
|
SourceProjectFlockID uint
|
|
TransferDate time.Time
|
|
TransferQty float64
|
|
TransferID uint
|
|
}
|
|
|
|
type FarmDepreciationManualInputRow struct {
|
|
Id uint
|
|
ProjectFlockID uint
|
|
FarmName string
|
|
TotalCost float64
|
|
Note *string
|
|
}
|
|
|
|
type houseDepreciationPercentRow struct {
|
|
HouseType string
|
|
Day int
|
|
DepreciationPercent float64
|
|
}
|
|
|
|
type ExpenseDepreciationRepository interface {
|
|
GetCandidateFarms(ctx context.Context, period time.Time, areaIDs, locationIDs, projectFlockIDs []int64) ([]FarmDepreciationCandidateRow, error)
|
|
GetSnapshotsByPeriodAndFarmIDs(ctx context.Context, period time.Time, farmIDs []uint) ([]entity.FarmDepreciationSnapshot, error)
|
|
UpsertSnapshots(ctx context.Context, rows []entity.FarmDepreciationSnapshot) error
|
|
DeleteSnapshotsFromDate(ctx context.Context, fromDate time.Time, farmIDs []uint) error
|
|
GetLatestTransferInputsByFarms(ctx context.Context, period time.Time, farmIDs []uint) ([]FarmDepreciationLatestTransferRow, error)
|
|
GetDepreciationPercents(ctx context.Context, houseTypes []string, maxDay int) (map[string]map[int]float64, error)
|
|
GetLatestManualInputsByFarms(ctx context.Context, areaIDs, locationIDs, projectFlockIDs []int64) ([]FarmDepreciationManualInputRow, error)
|
|
UpsertManualInput(ctx context.Context, row *entity.FarmDepreciationManualInput) error
|
|
DB() *gorm.DB
|
|
}
|
|
|
|
type expenseDepreciationRepository struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewExpenseDepreciationRepository(db *gorm.DB) ExpenseDepreciationRepository {
|
|
return &expenseDepreciationRepository{db: db}
|
|
}
|
|
|
|
func (r *expenseDepreciationRepository) DB() *gorm.DB {
|
|
return r.db
|
|
}
|
|
|
|
func (r *expenseDepreciationRepository) GetCandidateFarms(
|
|
ctx context.Context,
|
|
period time.Time,
|
|
areaIDs, locationIDs, projectFlockIDs []int64,
|
|
) ([]FarmDepreciationCandidateRow, error) {
|
|
rows := make([]FarmDepreciationCandidateRow, 0)
|
|
|
|
query := r.db.WithContext(ctx).
|
|
Table("project_flocks AS pf").
|
|
Select("DISTINCT pf.id AS project_flock_id, pf.flock_name AS farm_name").
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.project_flock_id = pf.id").
|
|
Where("pf.deleted_at IS NULL").
|
|
Where("pf.category = ?", utils.ProjectFlockCategoryLaying).
|
|
Where("(pfk.closed_at IS NULL OR DATE(pfk.closed_at) >= DATE(?))", period)
|
|
|
|
if len(areaIDs) > 0 {
|
|
query = query.Where("pf.area_id IN ?", areaIDs)
|
|
}
|
|
if len(locationIDs) > 0 {
|
|
query = query.Where("pf.location_id IN ?", locationIDs)
|
|
}
|
|
if len(projectFlockIDs) > 0 {
|
|
query = query.Where("pf.id IN ?", projectFlockIDs)
|
|
}
|
|
|
|
if err := query.Order("pf.id ASC").Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *expenseDepreciationRepository) GetSnapshotsByPeriodAndFarmIDs(
|
|
ctx context.Context,
|
|
period time.Time,
|
|
farmIDs []uint,
|
|
) ([]entity.FarmDepreciationSnapshot, error) {
|
|
if len(farmIDs) == 0 {
|
|
return []entity.FarmDepreciationSnapshot{}, nil
|
|
}
|
|
|
|
rows := make([]entity.FarmDepreciationSnapshot, 0)
|
|
if err := r.db.WithContext(ctx).
|
|
Where("project_flock_id IN ?", farmIDs).
|
|
Where("period_date = DATE(?)", period).
|
|
Find(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *expenseDepreciationRepository) UpsertSnapshots(ctx context.Context, rows []entity.FarmDepreciationSnapshot) error {
|
|
if len(rows) == 0 {
|
|
return nil
|
|
}
|
|
|
|
return r.db.WithContext(ctx).
|
|
Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{
|
|
{Name: "project_flock_id"},
|
|
{Name: "period_date"},
|
|
},
|
|
DoUpdates: clause.AssignmentColumns([]string{
|
|
"depreciation_percent_effective",
|
|
"depreciation_value",
|
|
"pullet_cost_day_n_total",
|
|
"components",
|
|
"updated_at",
|
|
}),
|
|
}).
|
|
Create(&rows).Error
|
|
}
|
|
|
|
func (r *expenseDepreciationRepository) DeleteSnapshotsFromDate(
|
|
ctx context.Context,
|
|
fromDate time.Time,
|
|
farmIDs []uint,
|
|
) error {
|
|
if fromDate.IsZero() {
|
|
return nil
|
|
}
|
|
|
|
query := r.db.WithContext(ctx).
|
|
Table("farm_depreciation_snapshots").
|
|
Where("period_date >= DATE(?)", fromDate)
|
|
if len(farmIDs) > 0 {
|
|
query = query.Where("project_flock_id IN ?", farmIDs)
|
|
}
|
|
return query.Delete(nil).Error
|
|
}
|
|
|
|
func (r *expenseDepreciationRepository) GetLatestTransferInputsByFarms(
|
|
ctx context.Context,
|
|
period time.Time,
|
|
farmIDs []uint,
|
|
) ([]FarmDepreciationLatestTransferRow, error) {
|
|
if len(farmIDs) == 0 {
|
|
return []FarmDepreciationLatestTransferRow{}, nil
|
|
}
|
|
|
|
rows := make([]FarmDepreciationLatestTransferRow, 0)
|
|
query := `
|
|
WITH latest_transfer_approval AS (
|
|
SELECT a.approvable_id, a.action
|
|
FROM approvals a
|
|
JOIN (
|
|
SELECT approvable_id, MAX(action_at) AS latest_action_at
|
|
FROM approvals
|
|
WHERE approvable_type = @approval_type
|
|
GROUP BY approvable_id
|
|
) la
|
|
ON la.approvable_id = a.approvable_id
|
|
AND la.latest_action_at = a.action_at
|
|
WHERE a.approvable_type = @approval_type
|
|
),
|
|
approved_transfers AS (
|
|
SELECT
|
|
lt.id,
|
|
lt.from_project_flock_id,
|
|
lt.to_project_flock_id,
|
|
COALESCE(DATE(lt.effective_move_date), DATE(lt.economic_cutoff_date), DATE(lt.transfer_date)) AS effective_date
|
|
FROM laying_transfers lt
|
|
JOIN latest_transfer_approval lta ON lta.approvable_id = lt.id
|
|
WHERE lt.deleted_at IS NULL
|
|
AND lt.executed_at IS NOT NULL
|
|
AND lta.action = 'APPROVED'
|
|
)
|
|
SELECT DISTINCT ON (ltt.target_project_flock_kandang_id)
|
|
pf.id AS project_flock_id,
|
|
pf.flock_name AS farm_name,
|
|
pfk.id AS project_flock_kandang_id,
|
|
k.id AS kandang_id,
|
|
k.name AS kandang_name,
|
|
k.house_type::text AS house_type,
|
|
at.from_project_flock_id AS source_project_flock_id,
|
|
at.effective_date AS transfer_date,
|
|
ltt.total_qty AS transfer_qty,
|
|
at.id AS transfer_id
|
|
FROM laying_transfer_targets ltt
|
|
JOIN approved_transfers at ON at.id = ltt.laying_transfer_id
|
|
JOIN project_flock_kandangs pfk ON pfk.id = ltt.target_project_flock_kandang_id
|
|
JOIN project_flocks pf ON pf.id = pfk.project_flock_id
|
|
JOIN kandangs k ON k.id = pfk.kandang_id
|
|
WHERE ltt.deleted_at IS NULL
|
|
AND pf.id IN @farm_ids
|
|
AND at.effective_date <= DATE(@period_date)
|
|
ORDER BY ltt.target_project_flock_kandang_id, at.effective_date DESC, at.id DESC
|
|
`
|
|
|
|
if err := r.db.WithContext(ctx).Raw(query, map[string]any{
|
|
"approval_type": utils.ApprovalWorkflowTransferToLaying.String(),
|
|
"farm_ids": farmIDs,
|
|
"period_date": period,
|
|
}).Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *expenseDepreciationRepository) GetDepreciationPercents(
|
|
ctx context.Context,
|
|
houseTypes []string,
|
|
maxDay int,
|
|
) (map[string]map[int]float64, error) {
|
|
result := make(map[string]map[int]float64)
|
|
if len(houseTypes) == 0 || maxDay <= 0 {
|
|
return result, nil
|
|
}
|
|
|
|
rows := make([]houseDepreciationPercentRow, 0)
|
|
if err := r.db.WithContext(ctx).
|
|
Table("house_depreciation_standards").
|
|
Select("house_type::text AS house_type, day, depreciation_percent").
|
|
Where("house_type::text IN ?", houseTypes).
|
|
Where("day <= ?", maxDay).
|
|
Order("house_type ASC, day ASC").
|
|
Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, row := range rows {
|
|
if _, exists := result[row.HouseType]; !exists {
|
|
result[row.HouseType] = make(map[int]float64)
|
|
}
|
|
result[row.HouseType][row.Day] = row.DepreciationPercent
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (r *expenseDepreciationRepository) GetLatestManualInputsByFarms(
|
|
ctx context.Context,
|
|
areaIDs, locationIDs, projectFlockIDs []int64,
|
|
) ([]FarmDepreciationManualInputRow, error) {
|
|
rows := make([]FarmDepreciationManualInputRow, 0)
|
|
|
|
query := r.db.WithContext(ctx).
|
|
Table("farm_depreciation_manual_inputs AS fdmi").
|
|
Select(`
|
|
fdmi.id AS id,
|
|
fdmi.project_flock_id AS project_flock_id,
|
|
pf.flock_name AS farm_name,
|
|
fdmi.total_cost AS total_cost,
|
|
fdmi.note AS note
|
|
`).
|
|
Joins("JOIN project_flocks AS pf ON pf.id = fdmi.project_flock_id").
|
|
Where("pf.deleted_at IS NULL").
|
|
Where("pf.category = ?", utils.ProjectFlockCategoryLaying)
|
|
|
|
if len(areaIDs) > 0 {
|
|
query = query.Where("pf.area_id IN ?", areaIDs)
|
|
}
|
|
if len(locationIDs) > 0 {
|
|
query = query.Where("pf.location_id IN ?", locationIDs)
|
|
}
|
|
if len(projectFlockIDs) > 0 {
|
|
query = query.Where("pf.id IN ?", projectFlockIDs)
|
|
}
|
|
|
|
if err := query.
|
|
Order("pf.id ASC").
|
|
Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *expenseDepreciationRepository) UpsertManualInput(ctx context.Context, row *entity.FarmDepreciationManualInput) error {
|
|
if row == nil {
|
|
return nil
|
|
}
|
|
|
|
now := time.Now().UTC()
|
|
err := r.db.WithContext(ctx).
|
|
Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{
|
|
{Name: "project_flock_id"},
|
|
},
|
|
DoUpdates: clause.Assignments(map[string]any{
|
|
"total_cost": row.TotalCost,
|
|
"note": row.Note,
|
|
"updated_at": now,
|
|
}),
|
|
}).
|
|
Create(row).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return r.db.WithContext(ctx).
|
|
Table("farm_depreciation_manual_inputs").
|
|
Select("id, project_flock_id, total_cost, note").
|
|
Where("project_flock_id = ?", row.ProjectFlockId).
|
|
Take(row).Error
|
|
}
|