143 lines
4.7 KiB
PHP
143 lines
4.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Reports;
|
|
|
|
use App\Enums\ExpenseCategory;
|
|
use App\Enums\FundingSource;
|
|
use App\Models\Department;
|
|
use App\Models\MedicationExpenseRow;
|
|
use App\Models\ReportPeriod;
|
|
use Illuminate\Support\Collection;
|
|
|
|
class MedicationExpenseService
|
|
{
|
|
/**
|
|
* Get all funding and category values for the given row.
|
|
*
|
|
* @return array<string, array<string, float>>
|
|
*/
|
|
public function valueMatrixForRow(?MedicationExpenseRow $row): array
|
|
{
|
|
$matrix = [];
|
|
|
|
foreach (FundingSource::cases() as $fundingSource) {
|
|
foreach (ExpenseCategory::cases() as $expenseCategory) {
|
|
$matrix[$fundingSource->value][$expenseCategory->value] = 0.0;
|
|
}
|
|
}
|
|
|
|
if ($row === null) {
|
|
return $matrix;
|
|
}
|
|
|
|
$row->loadMissing('values');
|
|
|
|
foreach ($row->values as $value) {
|
|
$matrix[$value->funding_source->value][$value->expense_category->value] = (float) $value->amount;
|
|
}
|
|
|
|
return $matrix;
|
|
}
|
|
|
|
/**
|
|
* Calculate medication expense totals for the given matrix.
|
|
*
|
|
* @param array<string, array<string, float|int|string|null>> $matrix
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function calculateFromMatrix(array $matrix): array
|
|
{
|
|
$byFundingSource = [];
|
|
$totalWithoutDressing = 0.0;
|
|
$totalDressing = 0.0;
|
|
|
|
foreach (FundingSource::cases() as $fundingSource) {
|
|
$withoutDressing = 0.0;
|
|
$dressing = 0.0;
|
|
|
|
foreach (ExpenseCategory::cases() as $expenseCategory) {
|
|
$amount = (float) ($matrix[$fundingSource->value][$expenseCategory->value] ?? 0);
|
|
|
|
if ($expenseCategory->countsTowardsWithoutDressing()) {
|
|
$withoutDressing += $amount;
|
|
} else {
|
|
$dressing += $amount;
|
|
}
|
|
}
|
|
|
|
$byFundingSource[$fundingSource->value] = [
|
|
'label' => $fundingSource->label(),
|
|
'without_dressing' => round($withoutDressing, 2),
|
|
'dressing' => round($dressing, 2),
|
|
'total' => round($withoutDressing + $dressing, 2),
|
|
];
|
|
|
|
$totalWithoutDressing += $withoutDressing;
|
|
$totalDressing += $dressing;
|
|
}
|
|
|
|
$budgetWithoutDressing = $byFundingSource[FundingSource::Budget->value]['without_dressing'];
|
|
$budgetDressing = $byFundingSource[FundingSource::Budget->value]['dressing'];
|
|
|
|
return [
|
|
'byFundingSource' => $byFundingSource,
|
|
'total_without_dressing' => round($totalWithoutDressing, 2),
|
|
'total_dressing' => round($totalDressing, 2),
|
|
'total_expense' => round($totalWithoutDressing + $totalDressing, 2),
|
|
'without_budget_no_dressing' => round($totalWithoutDressing - $budgetWithoutDressing, 2),
|
|
'without_budget_dressing' => round($totalDressing - $budgetDressing, 2),
|
|
'without_budget_total' => round(($totalWithoutDressing - $budgetWithoutDressing) + ($totalDressing - $budgetDressing), 2),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Calculate medication expense totals for a department in the given period.
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function calculateForDepartmentPeriod(ReportPeriod $period, Department $department): array
|
|
{
|
|
$row = MedicationExpenseRow::query()
|
|
->with('values')
|
|
->whereBelongsTo($period)
|
|
->whereBelongsTo($department)
|
|
->first();
|
|
|
|
$matrix = $this->valueMatrixForRow($row);
|
|
|
|
return [
|
|
'rowId' => $row?->id,
|
|
'matrix' => $matrix,
|
|
'totals' => $this->calculateFromMatrix($matrix),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Build totals for many departments in the period.
|
|
*
|
|
* @param Collection<int, Department> $departments
|
|
* @return array<int, array<string, mixed>>
|
|
*/
|
|
public function calculateForDepartments(ReportPeriod $period, Collection $departments): array
|
|
{
|
|
$rows = MedicationExpenseRow::query()
|
|
->with('values')
|
|
->whereBelongsTo($period)
|
|
->whereIn('department_id', $departments->pluck('id'))
|
|
->get()
|
|
->keyBy('department_id');
|
|
|
|
return $departments->mapWithKeys(function (Department $department) use ($rows): array {
|
|
/** @var MedicationExpenseRow|null $row */
|
|
$row = $rows->get($department->id);
|
|
$matrix = $this->valueMatrixForRow($row);
|
|
|
|
return [$department->id => [
|
|
'rowId' => $row?->id,
|
|
'matrix' => $matrix,
|
|
'totals' => $this->calculateFromMatrix($matrix),
|
|
]];
|
|
})->all();
|
|
}
|
|
}
|