59 lines
1.9 KiB
PHP
59 lines
1.9 KiB
PHP
<?php
|
|
|
|
namespace App\Actions;
|
|
|
|
use App\Models\Bucket;
|
|
use App\Models\Scenario;
|
|
use App\Services\Projection\PipelineAllocationService;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class ApplyDistributionAction
|
|
{
|
|
public function __construct(
|
|
private readonly PipelineAllocationService $pipelineAllocationService,
|
|
) {}
|
|
|
|
/**
|
|
* Apply an income distribution to bucket balances.
|
|
*
|
|
* Re-runs the allocation algorithm server-side and updates each bucket's
|
|
* starting_amount by adding the allocated amount.
|
|
*
|
|
* @return array{allocations: Collection<int, array{bucket_id: string, bucket_name: string, bucket_type: string, allocated_amount: int}>, total_allocated: int, unallocated: int}
|
|
*/
|
|
public function execute(Scenario $scenario, int $amount): array
|
|
{
|
|
$draws = $this->pipelineAllocationService->allocateInflow($scenario, $amount);
|
|
|
|
/** @var array<int, Bucket> $bucketLookup */
|
|
$bucketLookup = $scenario->buckets->keyBy('id')->all();
|
|
|
|
DB::transaction(function () use ($draws, $bucketLookup) {
|
|
foreach ($draws as $draw) {
|
|
$bucket = $bucketLookup[$draw->bucket_id];
|
|
$bucket->increment('starting_amount', (int) $draw->amount);
|
|
}
|
|
});
|
|
|
|
$allocations = $draws->map(function ($draw) use ($bucketLookup) {
|
|
$bucket = $bucketLookup[$draw->bucket_id];
|
|
|
|
return [
|
|
'bucket_id' => $bucket->uuid,
|
|
'bucket_name' => $bucket->name,
|
|
'bucket_type' => $bucket->type->value,
|
|
'allocated_amount' => (int) $draw->amount,
|
|
];
|
|
})->values();
|
|
|
|
/** @var int $totalAllocated */
|
|
$totalAllocated = $draws->sum('amount');
|
|
|
|
return [
|
|
'allocations' => $allocations,
|
|
'total_allocated' => $totalAllocated,
|
|
'unallocated' => $amount - $totalAllocated,
|
|
];
|
|
}
|
|
}
|