orderBy('date', 'desc') ->paginate(15); return view('harvests.index', compact('harvests')); } /** * Show the form for creating a new harvest. */ public function create() { $vineyardRows = VineyardRow::with('varietyVariation.grapeVariety')->get(); $varietyVariations = VarietyVariation::with('grapeVariety')->get(); return view('harvests.create', compact('vineyardRows', 'varietyVariations')); } /** * Store a newly created harvest in storage. */ public function store(Request $request) { $validated = $request->validate([ 'vineyard_row_id' => 'required|exists:vineyard_rows,id', 'variety_variation_id' => 'required|exists:variety_variations,id', 'weight' => 'required|numeric|min:0', 'sugar_content' => 'nullable|numeric|min:0|max:50', 'date' => 'required|date', 'quality' => 'nullable|in:excellent,good,fair,poor', 'grape_condition' => 'nullable|string|max:255', 'notes' => 'nullable|string', 'weather_conditions' => 'nullable|string', 'harvest_time' => 'nullable|date_format:H:i', ]); $validated['user_id'] = Auth::id(); $harvest = Harvest::create($validated); return redirect()->route('harvests.show', $harvest) ->with('success', 'Harvest recorded successfully.'); } /** * Display the specified harvest. */ public function show(Harvest $harvest) { $harvest->load('vineyardRow.varietyVariation.grapeVariety', 'varietyVariation.grapeVariety', 'user', 'wines', 'plannedTask'); return view('harvests.show', compact('harvest')); } /** * Show the form for editing the specified harvest. */ public function edit(Harvest $harvest) { $vineyardRows = VineyardRow::with('varietyVariation.grapeVariety')->get(); $varietyVariations = VarietyVariation::with('grapeVariety')->get(); return view('harvests.edit', compact('harvest', 'vineyardRows', 'varietyVariations')); } /** * Update the specified harvest in storage. */ public function update(Request $request, Harvest $harvest) { $validated = $request->validate([ 'vineyard_row_id' => 'required|exists:vineyard_rows,id', 'variety_variation_id' => 'required|exists:variety_variations,id', 'weight' => 'required|numeric|min:0', 'sugar_content' => 'nullable|numeric|min:0|max:50', 'date' => 'required|date', 'quality' => 'nullable|in:excellent,good,fair,poor', 'grape_condition' => 'nullable|string|max:255', 'notes' => 'nullable|string', 'weather_conditions' => 'nullable|string', 'harvest_time' => 'nullable|date_format:H:i', ]); $harvest->update($validated); return redirect()->route('harvests.show', $harvest) ->with('success', 'Harvest updated successfully.'); } /** * Remove the specified harvest from storage. */ public function destroy(Harvest $harvest) { // Check if there are wines in the cellar made from this harvest if ($harvest->hasWinesInCellar()) { return redirect()->route('winemaker.harvests.index') ->with('error', 'Cannot delete harvest: There are still wines in the cellar made from this harvest. Wait until all wines are sold out.'); } $harvest->delete(); return redirect()->route('winemaker.harvests.index') ->with('success', 'Harvest record deleted successfully.'); } /** * Display harvests for winemaker (with wine production status). * Only shows completed harvests (with actual harvest data). */ public function winemakerIndex() { $harvests = Harvest::with(['vineyardRow', 'varietyVariation.grapeVariety', 'user', 'wines', 'plannedTask']) ->where('weight', '>', 0) // Only show harvests with actual weight (completed harvests) ->where(function ($query) { $query->whereHas('plannedTask', function ($sub) { $sub->whereNotNull('execution_date'); })->orWhereDoesntHave('plannedTask'); }) ->orderByRaw( 'COALESCE((select execution_date from planned_tasks where planned_tasks.taskable_type = ? and planned_tasks.taskable_id = harvests.id limit 1), harvests.date) desc', [Harvest::class] ) ->paginate(15); return view('winemaker.harvests.index', compact('harvests')); } /** * Show the form for bottling wine from a harvest. */ public function bottleForm($id) { $harvest = Harvest::with(['vineyardRow', 'varietyVariation.grapeVariety', 'user', 'plannedTask']) ->findOrFail($id); return view('winemaker.harvests.bottle', compact('harvest')); } /** * Store a new wine from harvest (bottling process). */ public function bottleStore(Request $request) { $validated = $request->validate([ 'harvest_id' => 'required|exists:harvests,id', 'weight_to_use' => 'required|numeric|min:0.01', 'wine_name' => 'required|string|max:255', 'vintage' => 'required|integer|min:1900|max:' . (date('Y') + 1), 'wine_type' => 'required|in:red,white,rose', 'sweetness' => 'required|in:dry,semi_dry,semi_sweet,sweet', 'alcohol_percentage' => 'required|numeric|min:0|max:20', 'bottles_produced' => 'required|integer|min:1', 'bottle_volume' => 'nullable|numeric|min:0.1|max:10', 'production_date' => 'required|date', 'bottling_date' => 'nullable|date', 'status' => 'required|in:in_production,aging,ready,sold_out', 'price_per_bottle' => 'nullable|numeric|min:0', 'description' => 'nullable|string', 'image' => 'nullable|image|mimes:jpeg,jpg,png,webp|max:2048', ]); try { DB::beginTransaction(); // Get the harvest $harvest = Harvest::with('varietyVariation.grapeVariety')->findOrFail($validated['harvest_id']); // Validate weight to use doesn't exceed available weight if ($validated['weight_to_use'] > $harvest->remaining_weight) { return back() ->withInput() ->with('error', 'Cannot use ' . $validated['weight_to_use'] . ' kg. Only ' . number_format($harvest->remaining_weight, 2) . ' kg available.'); } // Get or find the grape variety $grapeVariety = $harvest->varietyVariation->grapeVariety; // Handle image upload $imagePath = null; if ($request->hasFile('image')) { $imagePath = $request->file('image')->store('wines', 'public'); } // Create the wine $wine = Wine::create([ 'wine_name' => $validated['wine_name'], 'vintage' => $validated['vintage'], 'grape_variety_id' => $grapeVariety->id, 'wine_type' => $validated['wine_type'], 'sweetness' => $validated['sweetness'], 'alcohol_percentage' => $validated['alcohol_percentage'], 'bottles_produced' => $validated['bottles_produced'], 'bottles_in_stock' => $validated['bottles_produced'], // Initially all bottles are in stock 'bottle_volume' => $validated['bottle_volume'] ?? 0.75, 'production_date' => $validated['production_date'], 'bottling_date' => $validated['bottling_date'], 'status' => $validated['status'], 'price_per_bottle' => $validated['price_per_bottle'], 'description' => $validated['description'], 'image_url' => $imagePath, ]); // Create wine production record (link harvest to wine with specified weight) WineProduction::create([ 'wine_id' => $wine->id, 'harvest_id' => $harvest->id, 'consumed_weight' => $validated['weight_to_use'], // Use specified weight 'blend_percentage' => 100.00, // 100% from this harvest ]); DB::commit(); $message = 'Wine bottled successfully from harvest #' . $harvest->id . ' using ' . number_format($validated['weight_to_use'], 2) . ' kg'; if ($harvest->remaining_weight - $validated['weight_to_use'] > 0.01) { $message .= '. Remaining: ' . number_format($harvest->remaining_weight - $validated['weight_to_use'], 2) . ' kg'; } return redirect()->route('winemaker.cellar.index') ->with('success', $message); } catch (\Exception $e) { DB::rollBack(); return back() ->withInput() ->with('error', 'Failed to bottle wine: ' . $e->getMessage()); } } }