Merge branch 'bryam' of https://github.com/BryamE/ProyectoLauck into giane
This commit is contained in:
@@ -10,7 +10,7 @@ class CatalogoController extends Controller
|
||||
/**
|
||||
* Muestra los registros de ventas con buscador y paginación.
|
||||
*/
|
||||
public function catalogo(Request $request)
|
||||
public function index(Request $request)
|
||||
{
|
||||
// Consulta base
|
||||
$query = Product::query();
|
||||
@@ -20,17 +20,22 @@ class CatalogoController extends Controller
|
||||
$searchTerm = $request->input('search');
|
||||
|
||||
$query->where(function($q) use ($searchTerm) {
|
||||
$q->where('name', 'like', "%{$searchTerm}%") // Buscar por nombre
|
||||
->orWhere('sku', 'like', "%{$searchTerm}%"); // O por código SKU
|
||||
$q->where('name', 'like', "%{$searchTerm}%")
|
||||
->orWhere('sku', 'like', "%{$searchTerm}%");
|
||||
});
|
||||
}
|
||||
$query->whereIn('type', ['bike', 'accessory']);// Filtros para el público
|
||||
$query->whereIn('type', ['bike', 'accessory', 'clothing', 'spare']);
|
||||
$query->where('stock_quantity', '>', 0); // Solo mostrar si tiene stock
|
||||
|
||||
// Resultados paginados
|
||||
$products = $query->paginate(12)->withQueryString(); // withQueryString mantiene la búsqueda al cambiar de página
|
||||
|
||||
// Devuelve la vista
|
||||
return view('catalogo', compact('products'));
|
||||
return view('catalogo.index', compact('products'));
|
||||
}
|
||||
|
||||
public function show(Product $product)
|
||||
{
|
||||
return view('catalogo.show', compact('product'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ class ClientController extends Controller
|
||||
return $q->where('name', 'like', "%{$query}%");
|
||||
})
|
||||
->orderBy('name', 'asc')
|
||||
//->orderBy('create_at', 'asc')
|
||||
->paginate(10) // Paginamos de a 10
|
||||
->withQueryString(); // Mantiene el filtro de búsqueda al cambiar de página
|
||||
|
||||
@@ -53,13 +52,18 @@ class ClientController extends Controller
|
||||
// 2. Verificamos el origen
|
||||
if ($request->input('origin') === 'sales') {
|
||||
|
||||
// Si vino de ventas, volvemos a ventas
|
||||
// Y pasamos el ID del nuevo cliente para auto-seleccionarlo
|
||||
// Si vino de ventas, volvemos a ventas con el cliente nuevo
|
||||
return redirect()->route('sales.create', ['new_client_id' => $client->id])
|
||||
->with('success', 'Cliente creado. Ya puedes seleccionarlo.');
|
||||
}
|
||||
else if ($request->input('origin') === 'taller') {
|
||||
|
||||
// Si vino de taller, volvemos a taller con el cliente nuevo
|
||||
return redirect()->route('taller.create', ['new_client_id' => $client->id])
|
||||
->with('success', 'Cliente creado. Ya puedes seleccionarlo.');
|
||||
}
|
||||
|
||||
// 3. Si no, comportamiento normal (volver al index de clientes)
|
||||
// 3. Si no, vuelvo al index de clientes
|
||||
return redirect()->route('clients.index')
|
||||
->with('success', 'Cliente creado correctamente.');
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ class HomeController extends Controller
|
||||
{
|
||||
// Si tuvieras un campo 'sales_count', podrías usar ->orderByDesc('sales_count')
|
||||
$destacados = Product::where('type', 'bike')
|
||||
->latest() // Las más nuevas
|
||||
->take(5)
|
||||
->get();
|
||||
->latest() // Las más nuevas
|
||||
->take(5)
|
||||
->get();
|
||||
|
||||
// Si no hay bicis, traemos cualquier cosa para que no se rompa
|
||||
if ($destacados->isEmpty()) {
|
||||
|
||||
@@ -14,13 +14,13 @@ class LoginController extends Controller
|
||||
'email' => ['required', 'email'],
|
||||
'password' => ['required'],
|
||||
]);
|
||||
|
||||
|
||||
if (Auth::attempt($credentials)) {
|
||||
$request->session()->regenerate();
|
||||
|
||||
|
||||
return redirect()->intended('dashboard');
|
||||
}
|
||||
|
||||
|
||||
return back()->withErrors([
|
||||
'email' => 'Los datos no coinciden.',
|
||||
])->onlyInput('email');
|
||||
|
||||
@@ -4,8 +4,8 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Product;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule; // Necesario para validar unicidad al editar
|
||||
use Illuminate\Support\Facades\Storage; // <--- IMPORTANTE: Agregar esto arriba
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ProductosController extends Controller
|
||||
{
|
||||
@@ -14,33 +14,30 @@ class ProductosController extends Controller
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
// Recuperamos lo que el usuario escribió en el buscador (si escribió algo)
|
||||
// Recuperamos lo escrito en el buscador (si aplica)
|
||||
$query = $request->input('search');
|
||||
$status = $request->input('stock_status');
|
||||
|
||||
// Construimos la consulta
|
||||
$products = Product::query()
|
||||
->when($query, function ($q) use ($query) {
|
||||
// Si hay búsqueda, filtra por nombre o SKU
|
||||
// Filtra por nombre o SKU
|
||||
return $q->where('name', 'like', "%{$query}%")
|
||||
->orWhere('sku', 'like', "%{$query}%");
|
||||
->orWhere('sku', 'like', "%{$query}%");
|
||||
})
|
||||
->when($status, function ($q) use ($status) {
|
||||
if ($status === 'low') {
|
||||
// Rojo: Menor o igual a la alerta
|
||||
return $q->whereColumn('stock_quantity', '<', 'min_stock_alert')
|
||||
->where('type', '!=', 'service'); // Ignoramos servicios
|
||||
return $q->whereColumn('stock_quantity', '<', 'min_stock_alert');
|
||||
}
|
||||
elseif ($status === 'medium') {
|
||||
// Amarillo: Mayor a alerta PERO menor o igual a alerta + 2 (margen pequeño)
|
||||
// Amarillo: Mayor al min y menor o igual al min + 5
|
||||
return $q->whereColumn('stock_quantity', '>=', 'min_stock_alert')
|
||||
->whereRaw('stock_quantity <= (min_stock_alert + 1)') // Ajusta este "+ 5" según tu criterio de "amarillo"
|
||||
->where('type', '!=', 'service');
|
||||
->whereRaw('stock_quantity <= (min_stock_alert + 5)');
|
||||
}
|
||||
elseif ($status === 'ok') {
|
||||
// Verde: Stock saludable
|
||||
return $q->whereRaw('stock_quantity > (min_stock_alert + 1)')
|
||||
->where('type', '!=', 'service');
|
||||
return $q->whereRaw('stock_quantity > (min_stock_alert + 5)');
|
||||
}
|
||||
})
|
||||
->orderBy('stock_quantity', 'asc') // Ordenamos primero los que tienen poco stock (Alerta visual)
|
||||
@@ -72,7 +69,7 @@ class ProductosController extends Controller
|
||||
'cost' => 'nullable|numeric|min:0', // Costo opcional
|
||||
'stock_quantity' => 'required|integer|min:0',
|
||||
'min_stock_alert' => 'required|integer|min:0',
|
||||
'type' => 'required|in:bike,accessory,service', // Solo permite estos 3 valores
|
||||
'type' => 'required|in:bike,accessory,clothing,spare', // Solo permite estos valores
|
||||
'serial_number' => 'nullable|string|max:100',
|
||||
'image' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:2048',
|
||||
]);
|
||||
@@ -93,6 +90,7 @@ class ProductosController extends Controller
|
||||
|
||||
// Para no romper la logica del supplier
|
||||
$validated['suppliers_id'] = 1;
|
||||
|
||||
// 3. Creamos el producto
|
||||
Product::create($validated);
|
||||
|
||||
@@ -103,7 +101,6 @@ class ProductosController extends Controller
|
||||
|
||||
/**
|
||||
* Muestra el detalle de un producto.
|
||||
* Usamos Route Model Binding: Laravel busca el ID solo.
|
||||
*/
|
||||
public function show(Product $product)
|
||||
{
|
||||
@@ -132,7 +129,7 @@ class ProductosController extends Controller
|
||||
'cost' => 'nullable|numeric|min:0',
|
||||
'stock_quantity' => 'required|integer|min:0',
|
||||
'min_stock_alert' => 'required|integer|min:0',
|
||||
'type' => 'required|in:bike,accessory,service',
|
||||
'type' => 'required|in:bike,accessory,clothing,spare',
|
||||
'serial_number' => 'nullable|string|max:100',
|
||||
|
||||
'image' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:2048',
|
||||
|
||||
@@ -18,7 +18,6 @@ class RegisterController extends Controller
|
||||
]);
|
||||
|
||||
$userData['password'] = bcrypt($userData['password']);
|
||||
|
||||
$user = User::create($userData);
|
||||
|
||||
Auth::login($user);
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Appointment;
|
||||
use App\Models\Client;
|
||||
use App\Models\Product;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TallerController extends Controller
|
||||
{
|
||||
/**
|
||||
* Vista principal
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Trabajos activos ordenados por fecha
|
||||
$jobs = Appointment::with('client')
|
||||
->whereIn('status', ['pending', 'in_progress', 'ready']) // Entregados/historial no se muestra, implementar vista aparte
|
||||
->orderBy('scheduled_at', 'asc')
|
||||
->get();
|
||||
|
||||
// Agrupado por estado para las columnas
|
||||
$pending = $jobs->where('status', 'pending');
|
||||
$inProgress = $jobs->where('status', 'in_progress');
|
||||
$ready = $jobs->where('status', 'ready');
|
||||
|
||||
return view('taller.index', compact('pending', 'inProgress', 'ready'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formulario para nuevo servicio
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$clients = Client::orderBy('name')->get();
|
||||
|
||||
$products = Product::whereIn('type', ['accessory','spare'])
|
||||
->where('stock_quantity', '>', 0)
|
||||
->get(['id', 'name', 'price', 'sku', 'stock_quantity']); // Solo campos necesarios
|
||||
|
||||
return view('taller.create', compact('clients', 'products'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Guardar el servicio
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'client_id' => 'required|exists:clients,id',
|
||||
'bike_model' => 'required|string',
|
||||
'problem_description' => 'required|string',
|
||||
'scheduled_at' => 'required|date',
|
||||
'estimated_cost' => 'nullable|numeric',
|
||||
]);
|
||||
|
||||
// Buscamos el cliente para copiar su teléfono
|
||||
$client = Client::find($request->client_id);
|
||||
|
||||
Appointment::create([
|
||||
'client_id' => $request->client_id,
|
||||
'contact_phone' => $request->contact_phone ?? $client->phone,
|
||||
'bike_model' => $request->bike_model,
|
||||
'problem_description' => $request->problem_description,
|
||||
'parts_needed' => $request->parts_needed,
|
||||
'estimated_cost' => $request->estimated_cost,
|
||||
'scheduled_at' => $request->scheduled_at,
|
||||
'status' => 'pending', // Estado inicial: Pendiente
|
||||
]);
|
||||
|
||||
return redirect()->route('taller.index')->with('success', 'Bicicleta ingresada al taller correctamente.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Mover de estado
|
||||
*/
|
||||
public function updateStatus(Request $request, Appointment $appointment)
|
||||
{
|
||||
$appointment->update(['status' => $request->status]);
|
||||
return back()->with('success', 'Estado actualizado.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user