validate([ 'material_id' => 'required|string', 'purchase_order_id' => 'nullable|string', 'planned_qty' => 'required|numeric|min:0.01', 'actual_qty' => 'nullable|numeric|min:0', 'notes' => 'nullable|string|max:500', ]); $material = Material::where('ulid', $validated['material_id'])->firstOrFail(); $poId = null; if (!empty($validated['purchase_order_id'])) { $po = PurchaseOrder::where('ulid', $validated['purchase_order_id'])->firstOrFail(); if ($po->status !== 'approved') { return back()->with('error', 'Only materials from approved Purchase Orders can be assigned.'); } if (!$po->items()->where('material_id', $material->id)->exists()) { return back()->with('error', 'This material is not part of the selected Purchase Order.'); } $poId = $po->id; } // Prevent duplicate material per task if ($task->taskMaterials()->where('material_id', $material->id)->exists()) { return back()->with('error', 'This material is already assigned to this task.'); } $task->taskMaterials()->create([ 'material_id' => $material->id, 'purchase_order_id' => $poId, 'planned_qty' => $validated['planned_qty'], 'actual_qty' => $validated['actual_qty'] ?? 0, 'unit_cost' => $material->unit_cost, 'notes' => $validated['notes'] ?? null, ]); $project->load('tasks.taskMaterials'); $project->recalculateCapitalization(); return back()->with('success', 'Material added to task.'); } public function storeBulk(Request $request, Project $project, Task $task) { $validated = $request->validate([ 'purchase_order_id' => 'nullable|string', 'materials' => 'required|array|min:1', 'materials.*.material_id' => 'required|string', 'materials.*.planned_qty' => 'required|numeric|min:0.01', ]); $poId = null; if (!empty($validated['purchase_order_id'])) { $po = PurchaseOrder::where('ulid', $validated['purchase_order_id'])->firstOrFail(); if ($po->status !== 'approved') { return back()->with('error', 'Only materials from approved Purchase Orders can be assigned.'); } $poId = $po->id; } $added = 0; foreach ($validated['materials'] as $entry) { $material = Material::where('ulid', $entry['material_id'])->firstOrFail(); if ($task->taskMaterials()->where('material_id', $material->id)->exists()) { continue; } $task->taskMaterials()->create([ 'material_id' => $material->id, 'purchase_order_id' => $poId, 'planned_qty' => $entry['planned_qty'], 'actual_qty' => 0, 'unit_cost' => $material->unit_cost, ]); $added++; } $project->load('tasks.taskMaterials'); $project->recalculateCapitalization(); return back()->with('success', "{$added} material(s) added to task."); } public function update(Request $request, Project $project, Task $task, TaskMaterial $taskMaterial) { $validated = $request->validate([ 'planned_qty' => 'nullable|numeric|min:0', 'actual_qty' => 'nullable|numeric|min:0', 'notes' => 'nullable|string|max:500', ]); $taskMaterial->update(array_filter($validated, fn ($v) => $v !== null)); $project->load('tasks.taskMaterials'); $project->recalculateCapitalization(); return back()->with('success', 'Task material updated.'); } public function destroy(Project $project, Task $task, TaskMaterial $taskMaterial) { $taskMaterial->delete(); $project->load('tasks.taskMaterials'); $project->recalculateCapitalization(); return back()->with('success', 'Material removed from task.'); } }