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) : '' ); } }