Files
onboard/app/Services/MetricCalculators/PlanCalculator.php
2026-07-06 14:56:46 +00:00

115 lines
5.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Services\MetricCalculators;
use App\Contracts\MetricCalculatorInterface;
use App\Models\Department;
use App\Services\Base\BaseMetricService;
use App\Services\DateRange;
use App\Services\DateRangeService;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
class PlanCalculator extends BaseMetricService implements MetricCalculatorInterface
{
public function __construct(
) {
}
public function getMetricId(): int
{
return 7;
}
public function calculate(array $departmentIds, string $startDate, string $endDate): array
{
$startDate = Carbon::parse($startDate);
$endDate = Carbon::parse($endDate);
$startYear = $startDate->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);
// Процент выполнения плана с начала года на дату конца периода
$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,
];
}
return $results;
}
}