119 lines
5.9 KiB
PHP
119 lines
5.9 KiB
PHP
<?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);
|
||
|
||
// Индикатор вывода плана с учетом долга (только если дата начала = началу года)
|
||
$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;
|
||
}
|
||
}
|