mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-06-09 15:07:49 +00:00
create api get depresiasi v2
This commit is contained in:
@@ -43,6 +43,7 @@ import (
|
||||
type RepportService interface {
|
||||
GetExpense(ctx *fiber.Ctx, params *validation.ExpenseQuery) ([]dto.RepportExpenseListDTO, int64, error)
|
||||
GetExpenseDepreciation(ctx *fiber.Ctx) ([]dto.ExpenseDepreciationRowDTO, *dto.ExpenseDepreciationMetaDTO, error)
|
||||
GetExpenseDepreciationV2(ctx *fiber.Ctx) ([]dto.ExpenseDepreciationV2RowDTO, *dto.ExpenseDepreciationV2MetaDTO, error)
|
||||
GetExpenseDepreciationManualInputs(ctx *fiber.Ctx) ([]dto.ExpenseDepreciationManualInputRowDTO, *dto.ExpenseDepreciationMetaDTO, error)
|
||||
UpsertExpenseDepreciationManualInput(ctx *fiber.Ctx, req *validation.ExpenseDepreciationManualInputUpsert) (*dto.ExpenseDepreciationManualInputRowDTO, error)
|
||||
GetMarketing(ctx *fiber.Ctx, params *validation.MarketingQuery) ([]dto.RepportMarketingItemDTO, int64, error)
|
||||
@@ -355,6 +356,182 @@ func (s *repportService) GetExpenseDepreciation(ctx *fiber.Ctx) ([]dto.ExpenseDe
|
||||
return rows[offset:end], meta, nil
|
||||
}
|
||||
|
||||
func (s *repportService) GetExpenseDepreciationV2(ctx *fiber.Ctx) ([]dto.ExpenseDepreciationV2RowDTO, *dto.ExpenseDepreciationV2MetaDTO, error) {
|
||||
params, err := s.parseExpenseDepreciationV2Query(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := s.Validate.Struct(params); err != nil {
|
||||
return nil, nil, fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
if s.ExpenseDepreciationRepo == nil {
|
||||
return nil, nil, fiber.NewError(fiber.StatusInternalServerError, "expense depreciation repository is not configured")
|
||||
}
|
||||
if s.HppCostRepo == nil {
|
||||
return nil, nil, fiber.NewError(fiber.StatusInternalServerError, "hpp cost repository is not configured")
|
||||
}
|
||||
if s.HppV2Svc == nil {
|
||||
return nil, nil, fiber.NewError(fiber.StatusInternalServerError, "hpp v2 service is not configured")
|
||||
}
|
||||
|
||||
location, err := time.LoadLocation("Asia/Jakarta")
|
||||
if err != nil {
|
||||
return nil, nil, fiber.NewError(fiber.StatusInternalServerError, "failed to load timezone configuration")
|
||||
}
|
||||
periodDate, err := time.ParseInLocation("2006-01-02", params.Period, location)
|
||||
if err != nil {
|
||||
return nil, nil, fiber.NewError(fiber.StatusBadRequest, "period must follow format YYYY-MM-DD")
|
||||
}
|
||||
|
||||
limit := params.Limit
|
||||
if limit <= 0 {
|
||||
limit = 10
|
||||
}
|
||||
|
||||
farmID := uint(params.ProjectFlockID)
|
||||
kandangIDs, err := s.HppCostRepo.GetProjectFlockKandangIDs(ctx.Context(), farmID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(kandangIDs) == 0 {
|
||||
return nil, nil, fiber.NewError(fiber.StatusNotFound, "project flock has no kandangs")
|
||||
}
|
||||
|
||||
var farmName string
|
||||
if err := s.db.WithContext(ctx.Context()).
|
||||
Table("project_flocks").
|
||||
Select("flock_name").
|
||||
Where("id = ? AND deleted_at IS NULL", farmID).
|
||||
Scan(&farmName).Error; err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if farmName == "" {
|
||||
return nil, nil, fiber.NewError(fiber.StatusNotFound, "project flock not found")
|
||||
}
|
||||
|
||||
rows := make([]dto.ExpenseDepreciationV2RowDTO, 0, limit)
|
||||
actualDays := 0
|
||||
|
||||
for i := 0; i < limit; i++ {
|
||||
dayDate := periodDate.AddDate(0, 0, i)
|
||||
dayStr := dayDate.Format("2006-01-02")
|
||||
|
||||
var totalDepreciationValue float64
|
||||
var totalPulletCostDayN float64
|
||||
var totalPopulation float64
|
||||
var allKandangComponents []depreciationKandangComponent
|
||||
|
||||
for _, kandangID := range kandangIDs {
|
||||
breakdown, err := s.HppV2Svc.CalculateHppBreakdown(kandangID, &dayDate)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if breakdown == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
depreciationComponent := hppV2FindDepreciationComponent(breakdown)
|
||||
if depreciationComponent == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, part := range depreciationComponent.Parts {
|
||||
if part.Total <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
houseType := approvalService.NormalizeDepreciationHouseType(breakdown.HouseType)
|
||||
component := depreciationKandangComponent{
|
||||
ProjectFlockKandangID: breakdown.ProjectFlockKandangID,
|
||||
KandangID: breakdown.KandangID,
|
||||
KandangName: breakdown.KandangName,
|
||||
SourceProjectFlockID: hppV2DetailUint(part.Details, "source_project_flock_id"),
|
||||
HouseType: houseType,
|
||||
DayN: hppV2DetailInt(part.Details, "schedule_day"),
|
||||
DepreciationPercent: hppV2DetailFloat(part.Details, "depreciation_percent"),
|
||||
MultiplicationPercentage: hppV2DetailFloat(part.Details, "multiplication_percentage"),
|
||||
PulletCostDayN: hppV2DetailFloat(part.Details, "pullet_cost_day_n"),
|
||||
DepreciationValue: part.Total,
|
||||
TotalValuePulletAfterDepreciation: hppV2DetailFloat(part.Details, "total_value_pullet_after_depreciation"),
|
||||
DepreciationSource: part.Code,
|
||||
OriginDate: hppV2DetailString(part.Details, "origin_date"),
|
||||
ChickinDate: hppV2DetailString(part.Details, "origin_date"),
|
||||
StandardEffectiveDate: hppV2DetailString(part.Details, "standard_effective_date"),
|
||||
Population: hppV2DetailFloat(part.Details, "kandang_population"),
|
||||
}
|
||||
|
||||
if component.HouseType == "" {
|
||||
component.HouseType = approvalService.NormalizeDepreciationHouseType(hppV2DetailString(part.Details, "house_type"))
|
||||
}
|
||||
|
||||
if ref := hppV2FindReference(part.References, "laying_transfer"); ref != nil {
|
||||
component.TransferID = ref.ID
|
||||
component.TransferDate = ref.Date
|
||||
component.TransferQty = ref.Qty
|
||||
}
|
||||
|
||||
if part.Code == "manual_cutover" {
|
||||
if startDay := hppV2DetailInt(part.Details, "start_schedule_day"); startDay > 0 {
|
||||
component.StartScheduleDay = &startDay
|
||||
}
|
||||
component.CutoverDate = hppV2DetailString(part.Details, "cutover_date")
|
||||
if manualID := hppV2DetailUint(part.Details, "manual_input_id"); manualID > 0 {
|
||||
component.ManualInputID = &manualID
|
||||
}
|
||||
if component.ManualInputID == nil {
|
||||
if ref := hppV2FindReference(part.References, "farm_depreciation_manual_input"); ref != nil && ref.ID > 0 {
|
||||
manualID := ref.ID
|
||||
component.ManualInputID = &manualID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
totalPulletCostDayN += component.PulletCostDayN
|
||||
totalDepreciationValue += component.DepreciationValue
|
||||
totalPopulation += component.Population
|
||||
allKandangComponents = append(allKandangComponents, component)
|
||||
}
|
||||
}
|
||||
|
||||
effectivePercent := approvalService.CalculateEffectiveDepreciationPercent(totalDepreciationValue, totalPulletCostDayN)
|
||||
|
||||
components := depreciationFarmComponents{
|
||||
KandangCount: len(allKandangComponents),
|
||||
TotalPopulation: totalPopulation,
|
||||
Kandang: allKandangComponents,
|
||||
}
|
||||
componentsJSON, _ := json.Marshal(components)
|
||||
|
||||
multiplicationPercentage, dayN, chickinDate, standardEffectiveDate := depreciationSnapshotInfo(parseSnapshotComponents(componentsJSON))
|
||||
|
||||
rows = append(rows, dto.ExpenseDepreciationV2RowDTO{
|
||||
Date: dayStr,
|
||||
DepreciationPercentEffective: effectivePercent,
|
||||
DepreciationValue: totalDepreciationValue,
|
||||
PulletCostDayNTotal: totalPulletCostDayN,
|
||||
MultiplicationPercentage: multiplicationPercentage,
|
||||
DayN: dayN,
|
||||
ChickinDate: chickinDate,
|
||||
TotalValuePulletAfterDepreciation: totalPulletCostDayN - totalDepreciationValue,
|
||||
StandardEffectiveDate: standardEffectiveDate,
|
||||
TotalPopulation: totalPopulation,
|
||||
Components: parseSnapshotComponents(componentsJSON),
|
||||
})
|
||||
actualDays++
|
||||
}
|
||||
|
||||
meta := &dto.ExpenseDepreciationV2MetaDTO{
|
||||
ProjectFlockID: params.ProjectFlockID,
|
||||
FarmName: farmName,
|
||||
LocationID: params.LocationID,
|
||||
Period: params.Period,
|
||||
Limit: limit,
|
||||
TotalDays: actualDays,
|
||||
}
|
||||
|
||||
return rows, meta, nil
|
||||
}
|
||||
|
||||
func (s *repportService) GetExpenseDepreciationManualInputs(ctx *fiber.Ctx) ([]dto.ExpenseDepreciationManualInputRowDTO, *dto.ExpenseDepreciationMetaDTO, error) {
|
||||
params, filters, err := s.parseExpenseDepreciationQuery(ctx)
|
||||
if err != nil {
|
||||
@@ -3025,6 +3202,45 @@ func (s *repportService) parseExpenseDepreciationQuery(ctx *fiber.Ctx) (*validat
|
||||
return params, filters, nil
|
||||
}
|
||||
|
||||
func (s *repportService) parseExpenseDepreciationV2Query(ctx *fiber.Ctx) (*validation.ExpenseDepreciationV2Query, error) {
|
||||
limit := ctx.QueryInt("limit", 10)
|
||||
if limit < 1 {
|
||||
limit = 10
|
||||
}
|
||||
period := strings.TrimSpace(ctx.Query("period", ""))
|
||||
locationID := ctx.QueryInt("location_id", 0)
|
||||
projectFlockID := ctx.QueryInt("project_flock_id", 0)
|
||||
|
||||
locationScope, err := m.ResolveLocationScope(ctx, s.ExpenseRealizationRepo.DB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if locationScope.Restrict {
|
||||
allowed := toInt64Slice(locationScope.IDs)
|
||||
if len(allowed) == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusForbidden, "no location access")
|
||||
}
|
||||
found := false
|
||||
for _, id := range allowed {
|
||||
if id == int64(locationID) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, fiber.NewError(fiber.StatusForbidden, "location not in scope")
|
||||
}
|
||||
}
|
||||
|
||||
return &validation.ExpenseDepreciationV2Query{
|
||||
Limit: limit,
|
||||
Period: period,
|
||||
LocationID: int64(locationID),
|
||||
ProjectFlockID: int64(projectFlockID),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseCommaSeparatedInt64s(raw string) ([]int64, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
|
||||
Reference in New Issue
Block a user