mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
init depresiasi
This commit is contained in:
@@ -0,0 +1,326 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user