422 lines
17 KiB
PHP
422 lines
17 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\PlannedTask;
|
|
use App\Models\Treatment;
|
|
use App\Models\Wine;
|
|
use App\Models\WineProduction;
|
|
use App\Models\Harvest;
|
|
use App\Models\VineyardRow;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class WinemakerController extends Controller
|
|
{
|
|
/**
|
|
* Display the winemaker dashboard.
|
|
*/
|
|
public function dashboard()
|
|
{
|
|
// ========================================
|
|
// PLANNED ACTIONS
|
|
// ========================================
|
|
|
|
// Get planned tasks (not yet executed) - including overdue tasks from past 7 days and upcoming tasks for next 7 days
|
|
$plannedTasksRaw = PlannedTask::with(['taskable'])
|
|
->whereNull('execution_date')
|
|
->where('planned_date', '>=', now()->subDays(7))
|
|
->where('planned_date', '<=', now()->addDays(7))
|
|
->orderBy('planned_date')
|
|
->get();
|
|
|
|
// Load parent Treatment records for treatment types (to access vineyard row data)
|
|
foreach ($plannedTasksRaw as $task) {
|
|
if (in_array($task->taskable_type, [
|
|
'App\Models\Spraying',
|
|
'App\Models\Watering',
|
|
'App\Models\Fertilization',
|
|
'App\Models\Pruning'
|
|
]) && $task->taskable) {
|
|
$task->treatment = Treatment::with('vineyardRow')->find($task->taskable->treatment_id);
|
|
}
|
|
}
|
|
|
|
// Group planned tasks by type, date, and note
|
|
$plannedTasks = $plannedTasksRaw->groupBy(function($task) {
|
|
return $task->type . '|' . $task->planned_date->format('Y-m-d') . '|' . ($task->note ?? '');
|
|
})->map(function($group) {
|
|
$first = $group->first();
|
|
$first->vineyard_rows = $group->map(function($task) {
|
|
if ($task->taskable_type === VineyardRow::class) {
|
|
return $task->taskable;
|
|
}
|
|
// For Treatment types, use the parent Treatment record
|
|
if (isset($task->treatment) && $task->treatment) {
|
|
return $task->treatment->vineyardRow;
|
|
}
|
|
// For Harvest, load the vineyardRow if it exists
|
|
if ($task->taskable && method_exists($task->taskable, 'vineyardRow')) {
|
|
return $task->taskable->vineyardRow;
|
|
}
|
|
return null;
|
|
})->filter()->values();
|
|
// Map task IDs to their vineyard rows
|
|
$first->task_rows = $group->map(function($task) {
|
|
$row = null;
|
|
if ($task->taskable_type === VineyardRow::class) {
|
|
$row = $task->taskable;
|
|
} elseif (isset($task->treatment) && $task->treatment) {
|
|
$row = $task->treatment->vineyardRow;
|
|
} elseif ($task->taskable && method_exists($task->taskable, 'vineyardRow')) {
|
|
$row = $task->taskable->vineyardRow;
|
|
}
|
|
return $row ? [
|
|
'task_id' => $task->planned_task_id,
|
|
'row_id' => $row->id,
|
|
'row' => $row,
|
|
] : null;
|
|
})->filter()->values();
|
|
$first->task_count = $group->count();
|
|
return $first;
|
|
})->values();
|
|
|
|
// Sort all planned tasks chronologically
|
|
$allPlannedTasks = $plannedTasks->sortBy('planned_date')->values();
|
|
|
|
// ========================================
|
|
// COMPLETED ACTIONS
|
|
// ========================================
|
|
|
|
$completedActivities = collect();
|
|
|
|
// 1. Completed treatments and tasks (including completed Harvests)
|
|
$completedTasksRaw = PlannedTask::with(['taskable'])
|
|
->whereNotNull('execution_date')
|
|
->where('execution_date', '>=', now()->subDays(30))
|
|
->orderBy('execution_date', 'desc')
|
|
->get();
|
|
|
|
// Load parent Treatment records for treatment types
|
|
foreach ($completedTasksRaw as $task) {
|
|
if (in_array($task->taskable_type, [
|
|
'App\Models\Spraying',
|
|
'App\Models\Watering',
|
|
'App\Models\Fertilization',
|
|
'App\Models\Pruning'
|
|
]) && $task->taskable) {
|
|
$task->treatment = Treatment::with('vineyardRow')->find($task->taskable->treatment_id);
|
|
}
|
|
}
|
|
|
|
// Group completed tasks by type, execution date, and note
|
|
$completedTasks = $completedTasksRaw->groupBy(function($task) {
|
|
return $task->type . '|' . $task->execution_date->format('Y-m-d') . '|' . ($task->note ?? '');
|
|
})->map(function($group) {
|
|
$first = $group->first();
|
|
$first->vineyard_rows = $group->map(function($task) {
|
|
if ($task->taskable_type === VineyardRow::class) {
|
|
return $task->taskable;
|
|
}
|
|
// For Treatment types, use the parent Treatment record
|
|
if (isset($task->treatment) && $task->treatment) {
|
|
return $task->treatment->vineyardRow;
|
|
}
|
|
// For Harvest, load the vineyardRow if it exists
|
|
if ($task->taskable && method_exists($task->taskable, 'vineyardRow')) {
|
|
return $task->taskable->vineyardRow;
|
|
}
|
|
return null;
|
|
})->filter()->values();
|
|
$first->task_count = $group->count();
|
|
$first->activity_type = 'task';
|
|
$first->activity_date = $first->execution_date;
|
|
return $first;
|
|
})->values();
|
|
|
|
$completedActivities = $completedActivities->concat($completedTasks);
|
|
|
|
// 2. Recent wine productions (bottling/blending) - DISABLED FOR RECENT ACTIVITY
|
|
// Note: Bottling and blending activities are only shown in "All Completed Activities"
|
|
// They are excluded from the recent activity timeline on the dashboard
|
|
/*
|
|
$recentProductions = WineProduction::with(['wine.grapeVariety', 'harvest.vineyardRow.varietyVariation'])
|
|
->where('created_at', '>=', now()->subDays(30))
|
|
->orderBy('created_at', 'desc')
|
|
->get();
|
|
|
|
// Group by wine to detect blends
|
|
$productionsByWine = $recentProductions->groupBy('wine_id');
|
|
|
|
foreach ($productionsByWine as $wineId => $productions) {
|
|
$wine = $productions->first()->wine;
|
|
if (!$wine) continue;
|
|
|
|
$activity = (object)[
|
|
'activity_type' => 'production',
|
|
'activity_date' => $productions->first()->created_at,
|
|
'wine' => $wine,
|
|
'productions' => $productions,
|
|
'is_blend' => $productions->count() > 1,
|
|
'total_weight' => $productions->sum('consumed_weight'),
|
|
];
|
|
|
|
$completedActivities->push($activity);
|
|
}
|
|
*/
|
|
|
|
// 3. Recent harvests (not from planned tasks) - DISABLED
|
|
// Note: Only showing "Harvest completed" from PlannedTask, not "Harvest Recorded"
|
|
// Harvest recorded activities are excluded from recent activity timeline
|
|
/*
|
|
$recentHarvests = Harvest::with(['vineyardRow.varietyVariation.grapeVariety'])
|
|
->where('date', '>=', now()->subDays(30))
|
|
->orderBy('date', 'desc')
|
|
->get()
|
|
->map(function($harvest) {
|
|
// Check if this harvest is already represented in completed tasks
|
|
$existingTask = PlannedTask::where('taskable_type', Harvest::class)
|
|
->where('taskable_id', $harvest->id)
|
|
->whereNotNull('execution_date')
|
|
->exists();
|
|
|
|
if ($existingTask) {
|
|
return null; // Skip to avoid duplicates
|
|
}
|
|
|
|
return (object)[
|
|
'activity_type' => 'harvest',
|
|
'activity_date' => $harvest->date,
|
|
'harvest' => $harvest,
|
|
];
|
|
})
|
|
->filter();
|
|
|
|
$completedActivities = $completedActivities->concat($recentHarvests);
|
|
*/
|
|
|
|
// Sort all completed activities by date (most recent first)
|
|
$completedActivities = $completedActivities
|
|
->sortByDesc('activity_date')
|
|
->take(20)
|
|
->values();
|
|
|
|
// ========================================
|
|
// STATISTICS
|
|
// ========================================
|
|
|
|
// Wine statistics
|
|
$wineStats = [
|
|
'total_bottles' => Wine::sum('bottles_in_stock') ?? 0,
|
|
'by_status' => Wine::select('status', DB::raw('count(*) as count'))
|
|
->groupBy('status')
|
|
->pluck('count', 'status')
|
|
->toArray(),
|
|
];
|
|
|
|
// Harvest statistics
|
|
$totalHarvestWeight = Harvest::sum('weight') ?? 0;
|
|
$totalConsumedWeight = WineProduction::sum('consumed_weight') ?? 0;
|
|
$harvestStats = [
|
|
'total_harvest' => $totalHarvestWeight,
|
|
'consumed' => $totalConsumedWeight,
|
|
'available_weight' => max(0, $totalHarvestWeight - $totalConsumedWeight),
|
|
];
|
|
|
|
// Vineyard statistics
|
|
$vineyardStats = [
|
|
'total_rows' => VineyardRow::count(),
|
|
'active_rows' => VineyardRow::where('status', 'active')->count(),
|
|
'total_area' => VineyardRow::sum('area') ?? 0,
|
|
];
|
|
|
|
return view('winemaker.dashboard.index', compact(
|
|
'allPlannedTasks',
|
|
'completedActivities',
|
|
'wineStats',
|
|
'harvestStats',
|
|
'vineyardStats'
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Display all planned tasks.
|
|
*/
|
|
public function allPlannedTasks()
|
|
{
|
|
// Get all planned tasks (not yet executed) - including overdue tasks
|
|
$plannedTasksRaw = PlannedTask::with(['taskable'])
|
|
->whereNull('execution_date')
|
|
->orderBy('planned_date')
|
|
->get();
|
|
|
|
// Load parent Treatment records for treatment types
|
|
foreach ($plannedTasksRaw as $task) {
|
|
if (in_array($task->taskable_type, [
|
|
'App\Models\Spraying',
|
|
'App\Models\Watering',
|
|
'App\Models\Fertilization',
|
|
'App\Models\Pruning'
|
|
]) && $task->taskable) {
|
|
$task->treatment = Treatment::with('vineyardRow')->find($task->taskable->treatment_id);
|
|
}
|
|
}
|
|
|
|
// Group planned tasks by type, date, and note
|
|
$plannedTasks = $plannedTasksRaw->groupBy(function($task) {
|
|
return $task->type . '|' . $task->planned_date->format('Y-m-d') . '|' . ($task->note ?? '');
|
|
})->map(function($group) {
|
|
$first = $group->first();
|
|
$first->vineyard_rows = $group->map(function($task) {
|
|
if ($task->taskable_type === VineyardRow::class) {
|
|
return $task->taskable;
|
|
}
|
|
// For Treatment types, use the parent Treatment record
|
|
if (isset($task->treatment) && $task->treatment) {
|
|
return $task->treatment->vineyardRow;
|
|
}
|
|
// For Harvest, load the vineyardRow if it exists
|
|
if ($task->taskable && method_exists($task->taskable, 'vineyardRow')) {
|
|
return $task->taskable->vineyardRow;
|
|
}
|
|
return null;
|
|
})->filter()->values();
|
|
// Map task IDs to their vineyard rows
|
|
$first->task_rows = $group->map(function($task) {
|
|
$row = null;
|
|
if ($task->taskable_type === VineyardRow::class) {
|
|
$row = $task->taskable;
|
|
} elseif (isset($task->treatment) && $task->treatment) {
|
|
$row = $task->treatment->vineyardRow;
|
|
} elseif ($task->taskable && method_exists($task->taskable, 'vineyardRow')) {
|
|
$row = $task->taskable->vineyardRow;
|
|
}
|
|
return $row ? [
|
|
'task_id' => $task->planned_task_id,
|
|
'row_id' => $row->id,
|
|
'row' => $row,
|
|
] : null;
|
|
})->filter()->values();
|
|
$first->task_count = $group->count();
|
|
return $first;
|
|
})->values();
|
|
|
|
// Sort all planned tasks chronologically
|
|
$allPlannedTasks = $plannedTasks->sortBy('planned_date')->values();
|
|
|
|
return view('winemaker.dashboard.planned-tasks', compact('allPlannedTasks'));
|
|
}
|
|
|
|
/**
|
|
* Display all completed activities.
|
|
*/
|
|
public function allCompletedActivities()
|
|
{
|
|
$completedActivities = collect();
|
|
|
|
// 1. Completed treatments and tasks
|
|
$completedTasksRaw = PlannedTask::with(['taskable'])
|
|
->whereNotNull('execution_date')
|
|
->orderBy('execution_date', 'desc')
|
|
->get();
|
|
|
|
// Load parent Treatment records for treatment types
|
|
foreach ($completedTasksRaw as $task) {
|
|
if (in_array($task->taskable_type, [
|
|
'App\Models\Spraying',
|
|
'App\Models\Watering',
|
|
'App\Models\Fertilization',
|
|
'App\Models\Pruning'
|
|
]) && $task->taskable) {
|
|
$task->treatment = Treatment::with('vineyardRow')->find($task->taskable->treatment_id);
|
|
}
|
|
}
|
|
|
|
// Group completed tasks by type, execution date, and note
|
|
$completedTasks = $completedTasksRaw->groupBy(function($task) {
|
|
return $task->type . '|' . $task->execution_date->format('Y-m-d') . '|' . ($task->note ?? '');
|
|
})->map(function($group) {
|
|
$first = $group->first();
|
|
$first->vineyard_rows = $group->map(function($task) {
|
|
if ($task->taskable_type === VineyardRow::class) {
|
|
return $task->taskable;
|
|
}
|
|
// For Treatment types, use the parent Treatment record
|
|
if (isset($task->treatment) && $task->treatment) {
|
|
return $task->treatment->vineyardRow;
|
|
}
|
|
// For Harvest, load the vineyardRow if it exists
|
|
if ($task->taskable && method_exists($task->taskable, 'vineyardRow')) {
|
|
return $task->taskable->vineyardRow;
|
|
}
|
|
return null;
|
|
})->filter()->values();
|
|
$first->task_count = $group->count();
|
|
$first->activity_type = 'task';
|
|
$first->activity_date = $first->execution_date;
|
|
return $first;
|
|
})->values();
|
|
|
|
$completedActivities = $completedActivities->concat($completedTasks);
|
|
|
|
// 2. All wine productions (bottling/blending)
|
|
$allProductions = WineProduction::with(['wine.grapeVariety', 'harvest.vineyardRow.varietyVariation'])
|
|
->orderBy('created_at', 'desc')
|
|
->get();
|
|
|
|
// Group by wine to detect blends
|
|
$productionsByWine = $allProductions->groupBy('wine_id');
|
|
|
|
foreach ($productionsByWine as $wineId => $productions) {
|
|
$wine = $productions->first()->wine;
|
|
if (!$wine) continue;
|
|
|
|
$activity = (object)[
|
|
'activity_type' => 'production',
|
|
'activity_date' => $productions->first()->created_at,
|
|
'wine' => $wine,
|
|
'productions' => $productions,
|
|
'is_blend' => $productions->count() > 1,
|
|
'total_weight' => $productions->sum('consumed_weight'),
|
|
];
|
|
|
|
$completedActivities->push($activity);
|
|
}
|
|
|
|
// 3. All harvests (not from planned tasks)
|
|
$allHarvests = Harvest::with(['vineyardRow.varietyVariation.grapeVariety', 'plannedTask'])
|
|
->orderBy('date', 'desc')
|
|
->get()
|
|
->map(function($harvest) {
|
|
$existingTask = PlannedTask::where('taskable_type', Harvest::class)
|
|
->where('taskable_id', $harvest->id)
|
|
->whereNotNull('execution_date')
|
|
->exists();
|
|
|
|
if ($existingTask) {
|
|
return null;
|
|
}
|
|
|
|
// Use execution_date from plannedTask if available, otherwise use harvest date
|
|
$activityDate = ($harvest->plannedTask && $harvest->plannedTask->execution_date)
|
|
? $harvest->plannedTask->execution_date
|
|
: $harvest->date;
|
|
|
|
return (object)[
|
|
'activity_type' => 'harvest',
|
|
'activity_date' => $activityDate,
|
|
'harvest' => $harvest,
|
|
];
|
|
})
|
|
->filter();
|
|
|
|
$completedActivities = $completedActivities->concat($allHarvests);
|
|
|
|
// Sort all completed activities by date (most recent first)
|
|
$completedActivities = $completedActivities
|
|
->sortByDesc('activity_date')
|
|
->values();
|
|
|
|
return view('winemaker.dashboard.completed-activities', compact('completedActivities'));
|
|
}
|
|
}
|