Projects/3BIT/winter-semester/IIS/xnecasr00/app/Services/BulkPlannedTaskService.php
2026-04-14 19:28:46 +02:00

209 lines
7.2 KiB
PHP

<?php
namespace App\Services;
use App\Models\Fertilization;
use App\Models\Harvest;
use App\Models\PlannedTask;
use App\Models\Pruning;
use App\Models\Spraying;
use App\Models\Treatment;
use App\Models\VineyardRow;
use App\Models\Watering;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use RuntimeException;
class BulkPlannedTaskService
{
/**
* Create planned tasks for multiple rows at once.
*/
public function handle(array $payload): array
{
$rows = VineyardRow::with('varietyVariation')
->whereIn('id', $payload['rows'])
->get();
if ($rows->isEmpty()) {
return [
'success' => false,
'message' => 'No vineyard rows matched the provided selection.',
'created' => 0,
'task_ids' => [],
'errors' => [],
];
}
$plannedDate = Carbon::parse($payload['planned_date'])->startOfDay();
$executionDate = isset($payload['execution_date']) ? Carbon::parse($payload['execution_date'])->startOfDay() : null;
$note = $payload['note'] ?? null;
$details = $payload['details'] ?? [];
$action = $payload['action'];
$createdTasks = collect();
$errors = [];
DB::transaction(function () use ($rows, $action, $details, $plannedDate, $executionDate, $note, &$createdTasks, &$errors) {
foreach ($rows as $row) {
try {
$taskable = $this->createTaskable($action, $row, $details, $note, $plannedDate);
$plannedTask = $taskable->plannedTask()->create([
'planned_date' => $plannedDate->toDateString(),
'execution_date' => $executionDate?->toDateString(),
'type' => ucfirst($action),
'note' => $note,
]);
$createdTasks->push($plannedTask);
} catch (RuntimeException $exception) {
$errors[] = [
'row_id' => $row->getKey(),
'message' => $exception->getMessage(),
];
}
}
});
$success = $createdTasks->isNotEmpty() && empty($errors);
return [
'success' => $success,
'message' => $this->buildMessage($createdTasks, $errors, $rows->count()),
'created_tasks_count' => $createdTasks->count(),
'task_ids' => $createdTasks->map(fn (PlannedTask $task) => $task->getKey()),
'errors' => $errors,
];
}
/**
* Create the appropriate taskable model for the action.
*/
protected function createTaskable(string $action, VineyardRow $row, array $details, ?string $note, Carbon $plannedDate)
{
return match ($action) {
'watering' => $this->createWatering($row, $details, $note),
'fertilization' => $this->createFertilization($row, $details, $note),
'spraying' => $this->createSpraying($row, $details, $note),
'pruning' => $this->createPruning($row, $details, $note),
'harvest' => $this->createHarvest($row, $details, $note, $plannedDate),
default => throw new RuntimeException("Unsupported planned task action: {$action}"),
};
}
protected function createWatering(VineyardRow $row, array $details, ?string $note): Watering
{
$treatment = Treatment::create([
'row_id' => $row->getKey(),
'note' => $note,
]);
return Watering::create([
'treatment_id' => $treatment->getKey(),
'time_interval' => $details['time_interval'] ?? null,
'amount' => $details['amount'] ?? null,
'note' => $note,
]);
}
protected function createFertilization(VineyardRow $row, array $details, ?string $note): Fertilization
{
if (empty($details['substance'])) {
throw new RuntimeException('Fertilization requires a substance name.');
}
$treatment = Treatment::create([
'row_id' => $row->getKey(),
'note' => $note,
]);
return Fertilization::create([
'treatment_id' => $treatment->getKey(),
'substance' => $details['substance'],
'concentration' => $details['concentration'] ?? null,
'note' => $note,
]);
}
protected function createSpraying(VineyardRow $row, array $details, ?string $note): Spraying
{
if (empty($details['pesticide'])) {
throw new RuntimeException('Spraying requires a pesticide value.');
}
$treatment = Treatment::create([
'row_id' => $row->getKey(),
'note' => $note,
]);
return Spraying::create([
'treatment_id' => $treatment->getKey(),
'pesticide' => $details['pesticide'],
'concentration' => $details['concentration'] ?? null,
'note' => $note,
]);
}
protected function createPruning(VineyardRow $row, array $details, ?string $note): Pruning
{
if (empty($details['method'])) {
throw new RuntimeException('Pruning requires a pruning method.');
}
$treatment = Treatment::create([
'row_id' => $row->getKey(),
'note' => $note,
]);
return Pruning::create([
'treatment_id' => $treatment->getKey(),
'method' => $details['method'],
'percentage_removed' => $details['percentage_removed'] ?? null,
'note' => $note,
]);
}
protected function createHarvest(VineyardRow $row, array $details, ?string $note, Carbon $plannedDate): Harvest
{
$variationId = $details['variety_variation_id'] ?? $row->variety_variation_id;
if (! $variationId) {
throw new RuntimeException('A variety variation must be defined for harvest planning.');
}
return Harvest::create([
'vineyard_row_id' => $row->getKey(),
'variety_variation_id' => $variationId,
'user_id' => $details['user_id'] ?? null,
'weight' => $details['weight'] ?? 0,
'sugar_content' => $details['sugar_content'] ?? null,
'date' => $details['date'] ?? $plannedDate->toDateString(),
'quality' => $details['quality'] ?? null,
'grape_condition' => $details['condition'] ?? null,
'notes' => $note,
'weather_conditions' => $details['weather'] ?? null,
'harvest_time' => $details['time'] ?? null,
]);
}
protected function buildMessage(Collection $createdTasks, array $errors, int $requestedCount): string
{
if ($createdTasks->isEmpty()) {
return $errors ? 'No tasks were created.' : 'Unable to create planned tasks.';
}
if (empty($errors) && $createdTasks->count() === $requestedCount) {
return 'All planned tasks were created successfully.';
}
$failed = count($errors);
return sprintf(
'Created %d planned tasks%s.',
$createdTasks->count(),
$failed > 0 ? sprintf(' (%d rows could not be processed)', $failed) : ''
);
}
}