copy()->startOfYear()->setHours(9); // Кол-во дней в выбранном диапазоне (для нормы за период) и с начала года (для долга) $daysInRange = max(1, $startDate->diffInDays($endDate)); $daysFromYearStart = max(1, $startYear->diffInDays($endDate)); $daysFromYearStartToRangeStart = max(0, $startYear->diffInDays($startDate)); // Факт с начала года по конец выбранного периода, день в день $actualYearToDateByDept = DB::table('report_duties as r') ->join('duty_report_metric_results as mr', 'r.id', '=', 'mr.rf_report_id') ->whereIn('r.rf_department_id', $departmentIds) ->where('mr.rf_metrika_item_id', self::getMetricId()) ->where('r.period_end', '>=', $startYear) ->where('r.period_end', '<=', $endDate) ->select('r.rf_department_id', DB::raw('SUM(CAST(mr.value AS DECIMAL)) as total')) ->groupBy('r.rf_department_id') ->get() ->keyBy('rf_department_id'); // Факт с начала года по начало выбранного периода (то, что уже "закрыто" до этого периода) $actualToRangeStartByDept = DB::table('report_duties as r') ->join('duty_report_metric_results as mr', 'r.id', '=', 'mr.rf_report_id') ->whereIn('r.rf_department_id', $departmentIds) ->where('mr.rf_metrika_item_id', self::getMetricId()) ->where('r.period_end', '>=', $startYear) ->where('r.period_end', '<=', $startDate) ->select('r.rf_department_id', DB::raw('SUM(CAST(mr.value AS DECIMAL)) as total')) ->groupBy('r.rf_department_id') ->get() ->keyBy('rf_department_id'); $results = []; foreach ($departmentIds as $departmentId) { $department = Department::find($departmentId); // Получаем годовой план $annualPlanModel = $department->recipientPlanOfYear($endDate); $annualPlan = $annualPlanModel ? (int) $annualPlanModel->value : 0; // План на 1 месяц (равномерно) $oneMonthPlan = $annualPlan > 0 ? ceil($annualPlan / 12) : 0; // План на 1 день (равномерно, от годового плана) $dailyPlan = $annualPlan > 0 ? $annualPlan / 365 : 0; // Факт с начала года по конец выбранного периода $actualYearToDate = (int) ($actualYearToDateByDept[$departmentId]->total ?? 0); // Факт с начала года по начало выбранного периода $actualToRangeStart = (int) ($actualToRangeStartByDept[$departmentId]->total ?? 0); // План с начала года на дату конца периода — день в день, а не помесячно $cumulativePlan = (int) round($dailyPlan * $daysFromYearStart); // План с начала года на дату начала периода (то, что должно было быть выполнено ДО этого периода) $cumulativePlanAtStart = (int) round($dailyPlan * $daysFromYearStartToRangeStart); // Долг с начала года на дату конца периода (для справки, в тултипе) $debtFromYearStart = max(0, $cumulativePlan - $actualYearToDate); // Долг, накопленный ДО начала выбранного периода — им "утяжеляем" план за период, // чтобы % выполнения учитывал непогашенный долг, а не только текущий период $debtAtPeriodStart = max(0, $cumulativePlanAtStart - $actualToRangeStart); // Индикатор вывода плана с учетом долга (только если дата начала = началу года) $isCalculateDebt = $startDate->equalTo($startYear); // Процент выполнения плана с начала года на дату конца периода $cumulativePercent = $cumulativePlan > 0 ? round($actualYearToDate * 100 / $cumulativePlan) : 0; $results[$departmentId] = [ 'year_plan' => $annualPlan, 'month_plan' => $oneMonthPlan, 'period_plan' => (int) round($dailyPlan * $daysInRange), // норма за выбранный период (дневной план × кол-во дней) 'debt_at_period_start' => $debtAtPeriodStart, // непогашенный долг на начало периода 'cumulative_plan' => $cumulativePlan, 'debt_from_year' => $debtFromYearStart, 'actual_year_to_date' => $actualYearToDate, 'cumulative_percent' => $cumulativePercent, 'is_calculate_debt' => $isCalculateDebt, ]; } return $results; } }