209 lines
7.2 KiB
PHP
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) : ''
|
|
);
|
|
}
|
|
}
|