From 832f9228af71c109e70ced30e713a80062a44cfb Mon Sep 17 00:00:00 2001 From: GianellaDelMestre <146126967+GianellaDelMestre@users.noreply.github.com> Date: Sun, 7 Dec 2025 00:08:18 -0300 Subject: [PATCH 1/8] tablas y modelos --- app/Models/Appointment.php | 18 ++++++++++ app/Models/Client.php | 16 +++++++++ app/Models/Product.php | 36 +++++++++++++++---- app/Models/Sale.php | 28 +++++++++++++++ app/Models/SaleDetail.php | 23 ++++++++++++ app/Models/Supplier.php | 18 ++++++++++ ...25_12_06_182356_create_suppliers_table.php | 30 ++++++++++++++++ ...25_12_06_182358_create_products_table.php} | 15 ++++---- ...2025_12_06_184833_create_clients_table.php | 30 ++++++++++++++++ .../2025_12_06_193913_create_sales_table.php | 34 ++++++++++++++++++ ...12_06_201853_create_sale_details_table.php | 31 ++++++++++++++++ ...12_07_023045_create_appointments_table.php | 33 +++++++++++++++++ 12 files changed, 298 insertions(+), 14 deletions(-) create mode 100644 app/Models/Appointment.php create mode 100644 app/Models/Client.php create mode 100644 app/Models/Sale.php create mode 100644 app/Models/SaleDetail.php create mode 100644 app/Models/Supplier.php create mode 100644 database/migrations/2025_12_06_182356_create_suppliers_table.php rename database/migrations/{2025_08_05_005156_create_products_table.php => 2025_12_06_182358_create_products_table.php} (64%) create mode 100644 database/migrations/2025_12_06_184833_create_clients_table.php create mode 100644 database/migrations/2025_12_06_193913_create_sales_table.php create mode 100644 database/migrations/2025_12_06_201853_create_sale_details_table.php create mode 100644 database/migrations/2025_12_07_023045_create_appointments_table.php diff --git a/app/Models/Appointment.php b/app/Models/Appointment.php new file mode 100644 index 0000000..95ee0df --- /dev/null +++ b/app/Models/Appointment.php @@ -0,0 +1,18 @@ +fecha_programada->format('d/m/Y') + protected $casts = [ + 'fecha_programada' => 'datetime', + ]; + public function clients() + { + return $this->belongsTo(Client::class); + } +} diff --git a/app/Models/Client.php b/app/Models/Client.php new file mode 100644 index 0000000..1ac14d2 --- /dev/null +++ b/app/Models/Client.php @@ -0,0 +1,16 @@ +hasMany(Sale::class); + } +} diff --git a/app/Models/Product.php b/app/Models/Product.php index 143a9b8..a4e3f49 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -9,13 +9,35 @@ class Product extends Model { use HasFactory; protected $fillable = [ + 'codigo', 'nombre', - 'marca', - 'modelo', - 'rodado', - 'color', - 'tipo', - 'descripcion', - 'precio' + 'costo', + 'precio', + 'stock', + 'min_stock', + 'categoria', + 'descripcion' ]; + public function supplier() + { + return $this->belongsTo(Suppliers::class); + } + + // Opción 1: Relación directa con los detalles (Renglones de ticket) + // Útil para saber cantidad total vendida: $product->saleDetails->sum('quantity') + public function saleDetails() + { + return $this->hasMany(SaleDetail::class); + } + + // Opción 2 (Pro Tip): Relación directa con las Ventas a través de los detalles + // Útil para saber EN QUÉ fechas se vendió: $product->sales + public function sales() + { + return $this->belongsToMany(Sale::class, 'sale_details'); + } + } +/**$user->sales: Te da la lista de todas las compras de ese usuario. + +$product->saleDetails->count(): Te dice cuántas veces aparece ese producto en tickets.**/ \ No newline at end of file diff --git a/app/Models/Sale.php b/app/Models/Sale.php new file mode 100644 index 0000000..c9c88ad --- /dev/null +++ b/app/Models/Sale.php @@ -0,0 +1,28 @@ +belongsTo(User::class); + } + + // Relación 2: Una venta tiene muchos items o detalles + public function details() + { + return $this->hasMany(SaleDetail::class); + } +} diff --git a/app/Models/SaleDetail.php b/app/Models/SaleDetail.php new file mode 100644 index 0000000..bbe36e6 --- /dev/null +++ b/app/Models/SaleDetail.php @@ -0,0 +1,23 @@ +belongsTo(Sale::class); + } + + // este detalle corresponde a un Producto + public function product() + { + return $this->belongsTo(Product::class); + } +} \ No newline at end of file diff --git a/app/Models/Supplier.php b/app/Models/Supplier.php new file mode 100644 index 0000000..8acf047 --- /dev/null +++ b/app/Models/Supplier.php @@ -0,0 +1,18 @@ +hasMany(Product::class); + } +} diff --git a/database/migrations/2025_12_06_182356_create_suppliers_table.php b/database/migrations/2025_12_06_182356_create_suppliers_table.php new file mode 100644 index 0000000..c5e0c12 --- /dev/null +++ b/database/migrations/2025_12_06_182356_create_suppliers_table.php @@ -0,0 +1,30 @@ +id(); + $table->string('nombre'); + $table->string('telefono'); + $table->string('email'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('suppliers'); + } +}; diff --git a/database/migrations/2025_08_05_005156_create_products_table.php b/database/migrations/2025_12_06_182358_create_products_table.php similarity index 64% rename from database/migrations/2025_08_05_005156_create_products_table.php rename to database/migrations/2025_12_06_182358_create_products_table.php index ecbf13a..c785e32 100644 --- a/database/migrations/2025_08_05_005156_create_products_table.php +++ b/database/migrations/2025_12_06_182358_create_products_table.php @@ -13,14 +13,15 @@ return new class extends Migration { Schema::create('products', function (Blueprint $table) { $table->id(); + $table->string('codigo')->unique(); $table->string('nombre'); - $table->string('marca'); - $table->string('modelo'); - $table->string('rodado'); - $table->string('color'); - $table->string('tipo'); + $table->decimal('costo', 12, 2); + $table->decimal('precio', 12, 2); + $table->integer('stock'); + $table->integer('min_stock')->default(5); + $table->string('categoria'); $table->text('descripcion'); - $table->float('precio'); + $table->foreignId('suppliers_id')->constrained(); $table->timestamps(); }); } @@ -32,4 +33,4 @@ return new class extends Migration { Schema::dropIfExists('products'); } -}; +}; \ No newline at end of file diff --git a/database/migrations/2025_12_06_184833_create_clients_table.php b/database/migrations/2025_12_06_184833_create_clients_table.php new file mode 100644 index 0000000..398e55b --- /dev/null +++ b/database/migrations/2025_12_06_184833_create_clients_table.php @@ -0,0 +1,30 @@ +id(); + $table->string('nombre'); + $table->string('telefono'); + $table->string('email'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('clients'); + } +}; diff --git a/database/migrations/2025_12_06_193913_create_sales_table.php b/database/migrations/2025_12_06_193913_create_sales_table.php new file mode 100644 index 0000000..b0bfc22 --- /dev/null +++ b/database/migrations/2025_12_06_193913_create_sales_table.php @@ -0,0 +1,34 @@ +id(); + // 1. Cliente que compra + $table->foreignId('user_id')->constrained()->onDelete('cascade'); + // 2. Datos generales de la venta + $table->decimal('total', 10, 2); // Total final del ticket + $table->string('metodo_pago'); // Ej: "Efectivo", "Tarjeta", "MercadoPago" + //capaz van mas datos ni idea + // 3. Fecha y Hora (created_at servirá como fecha de venta) + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('sales'); + } +}; diff --git a/database/migrations/2025_12_06_201853_create_sale_details_table.php b/database/migrations/2025_12_06_201853_create_sale_details_table.php new file mode 100644 index 0000000..e357fa9 --- /dev/null +++ b/database/migrations/2025_12_06_201853_create_sale_details_table.php @@ -0,0 +1,31 @@ +id(); + $table->foreignId('sale_id')->constrained()->onDelete('cascade'); + $table->foreignId('product_id')->constrained(); + $table->integer('cantidad'); + $table->decimal('precio', 10, 2); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('sale_details'); + } +}; diff --git a/database/migrations/2025_12_07_023045_create_appointments_table.php b/database/migrations/2025_12_07_023045_create_appointments_table.php new file mode 100644 index 0000000..3bac73b --- /dev/null +++ b/database/migrations/2025_12_07_023045_create_appointments_table.php @@ -0,0 +1,33 @@ +id(); + $table->foreignId('client_id')->constrained(); + $table->datetime('fecha_programada'); + $table->string('modelo_bici')->nullable(); + $table->string('descripcion')->nullable(); + $table->string('estado')->default('pendiente'); + $table->string('notas')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('appointments'); + } +}; From 9a5e84e73b16a5b737cfb222760b05f1409c47b2 Mon Sep 17 00:00:00 2001 From: Coassolo-Lucas <131891029+Coassolo-Lucas@users.noreply.github.com> Date: Mon, 8 Dec 2025 01:36:36 -0300 Subject: [PATCH 2/8] l --- app/Http/Controllers/SaleController.php | 56 +++++++++++++++++++++ resources/views/components/navbar.blade.php | 6 ++- resources/views/sales/index.blade.php | 12 +++++ routes/web.php | 4 ++ 4 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 app/Http/Controllers/SaleController.php create mode 100644 resources/views/sales/index.blade.php diff --git a/app/Http/Controllers/SaleController.php b/app/Http/Controllers/SaleController.php new file mode 100644 index 0000000..6d99fb3 --- /dev/null +++ b/app/Http/Controllers/SaleController.php @@ -0,0 +1,56 @@ +Taller
  • - Ventas -
  • + + Ventas + diff --git a/resources/views/sales/index.blade.php b/resources/views/sales/index.blade.php new file mode 100644 index 0000000..8174db7 --- /dev/null +++ b/resources/views/sales/index.blade.php @@ -0,0 +1,12 @@ + + + + + + + Document + + +

    Gwssssssssssssssss

    + + \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index f819ba4..79aa241 100644 --- a/routes/web.php +++ b/routes/web.php @@ -7,6 +7,7 @@ use App\Http\Controllers\HomeController; use App\Http\Controllers\LoginController; use App\Http\Controllers\ProductosController; use App\Http\Controllers\RegisterController; +use App\Http\Controllers\SaleController; use App\Models\Product; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Session; @@ -45,6 +46,9 @@ Route::middleware(['auth'])->group(function () { Route::post('/productos',[ProductosController::class,'store'])->name('productos.store'); Route::put('/productos/{id}',[ProductosController::class,'update'])->name('productos.update'); Route::delete('/productos/{id}', [ProductosController::class,'destroy'])->name('productos.destroy'); + + Route::get('/sales', [SaleController::class, 'index'])->name('sales.index'); + Route::get('/sales/create', [SaleController::class, 'create'])->name('sales.create'); }); Route::get('/catalogo', [CatalogoController::class,'catalogo'])->name('catalogo'); From 04a8ecbd9a7e8d67eafa3726e17383b6762ab5a9 Mon Sep 17 00:00:00 2001 From: BryamE Date: Mon, 8 Dec 2025 21:41:27 -0300 Subject: [PATCH 3/8] Empezando prototipo de "Registro de Ventas", usa la tabla productos para ayuda visual. Agregado componente select. --- app/Http/Controllers/CatalogoController.php | 24 +++++ .../views/components/forms/select.blade.php | 48 +++++++--- resources/views/components/navbar.blade.php | 10 +- .../views/components/section-header.blade.php | 2 +- resources/views/layouts/app.blade.php | 17 ---- resources/views/sales.blade.php | 91 +++++++++++++++++++ routes/web.php | 1 + 7 files changed, 162 insertions(+), 31 deletions(-) delete mode 100644 resources/views/layouts/app.blade.php create mode 100644 resources/views/sales.blade.php diff --git a/app/Http/Controllers/CatalogoController.php b/app/Http/Controllers/CatalogoController.php index b7e5632..e74a30a 100644 --- a/app/Http/Controllers/CatalogoController.php +++ b/app/Http/Controllers/CatalogoController.php @@ -13,4 +13,28 @@ class CatalogoController extends Controller $productos = Product::all(); return view('catalogo', compact('productos')); } + + /** + * Muestra los registros de ventas con buscador y paginación. + */ + public function index(Request $request) + { + // Recuperamos lo que el usuario escribió en el buscador (si escribió algo) + $query = $request->input('search'); + // Recuperamos el filtro aplicado (si aplica) + $filter = $request->input('filter'); + + // Construimos la consulta + $sales = Product::query() + ->when($query, function ($q) use ($query) { + // Si hay búsqueda, filtra por nombre o SKU + return $q->where('name', 'like', "%{$query}%") + ->orWhere('sku', 'like', "%{$query}%"); + }) + ->orderBy('stock_quantity', 'asc') // Ordenamos primero los que tienen poco stock (Alerta visual) + ->paginate(10) // Paginamos de a 10 + ->withQueryString(); // Mantiene el filtro de búsqueda al cambiar de página + + return view('sales', compact('sales')); + } } diff --git a/resources/views/components/forms/select.blade.php b/resources/views/components/forms/select.blade.php index 1346f60..697461c 100644 --- a/resources/views/components/forms/select.blade.php +++ b/resources/views/components/forms/select.blade.php @@ -1,13 +1,39 @@ -@props(['disabled' => false, 'options', 'error' => null]) +@props(['disabled' => false, 'error' => null, 'options' => [], 'placeholder' => 'Seleccionar...']) -merge(['class' => ' + appearance-none bg-transparent border-0 border-b-2 border-neutral-700 text-white text-sm + py-2.5 px-0 w-full focus:outline-none focus:ring-0 focus:border-neon-lime peer cursor-pointer + transition-colors' . ($error ? 'border-red-500 focus:border-red-500' : '') + ]) !!}> + + @if($placeholder) + + @endif + + {{ $slot }} + + + + +
    + + + +
    + + + @if($error) +

    {{ $error }}

    @endif - -@if($error) -

    {{ $error }}

    -@endif \ No newline at end of file + + +{{-- + NOTA DE ESTILO: + - `appearance-none`: Quita el estilo feo por defecto del navegador. + - `bg-transparent`: Fondo transparente para que se vea el color de fondo de tu web. + - `border-b-2`: Borde solo abajo (estilo línea). + - `focus:ring-0`: Quita el anillo azul de Chrome al hacer click. + - `peer`: Permite que el icono de la flecha cambie de color cuando el select tiene foco. +--}} \ No newline at end of file diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php index b08ef68..7bf6f93 100644 --- a/resources/views/components/navbar.blade.php +++ b/resources/views/components/navbar.blade.php @@ -36,10 +36,16 @@
  • - Taller + + Taller +
  • - Ventas + + Ventas +
  • diff --git a/resources/views/components/section-header.blade.php b/resources/views/components/section-header.blade.php index 4864bbf..560d266 100644 --- a/resources/views/components/section-header.blade.php +++ b/resources/views/components/section-header.blade.php @@ -5,7 +5,7 @@

    {{ $title }} @if($highlight) - {{ $highlight }} + {{ $highlight }} @endif

    \ No newline at end of file diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php deleted file mode 100644 index 67c46ad..0000000 --- a/resources/views/layouts/app.blade.php +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - @yield('title','Lauck - Home') - - - -
    Cabeza
    - - @yield('main') - - - - \ No newline at end of file diff --git a/resources/views/sales.blade.php b/resources/views/sales.blade.php new file mode 100644 index 0000000..40a18ab --- /dev/null +++ b/resources/views/sales.blade.php @@ -0,0 +1,91 @@ + + + + + + + +
    + + +
    +
    +
    + +
    + +
    +
    + + + + + +
    +
    + + + + + Registrar Venta + +
    + + +
    + + + + + + + + + + + + @forelse($sales as $product) + + + + + + + + @empty + + + + @endforelse + +
    ID VentaDescripcionCant. ProductosTotal VentaAcciones
    +
    {{ $product->sku }}
    +
    +
    {{ $product->name }}
    +
    + {{ $product->stock_quantity }} + + ${{ number_format(($product->price * 2), 2) }} + {{-- @if($product->type === 'service') + - + @elseif($product->stock_quantity <= $product->min_stock_alert) + {{ $product->stock_quantity }} + @else + {{ $product->stock_quantity }} + @endif --}} + + Editar +
    + No se encontraron productos. +
    +
    + + +
    + {{ $sales->links() }} +
    + +
    \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index f9b9f3d..11f21bc 100644 --- a/routes/web.php +++ b/routes/web.php @@ -43,6 +43,7 @@ Route::middleware(['auth'])->group(function () { ]); Route::get('/productos',[ProductosController::class,'index'])->name('productos.index'); Route::get('/checker', [ProductosController::class, 'checker'])->name('productos.checker'); + Route::get('/sales', [CatalogoController::class,'index'])->name('sales.index'); }); Route::get('/catalogo', [CatalogoController::class,'catalogo'])->name('catalogo'); From 68534a760345fdac8fa81a51504f9b0c2aee6a85 Mon Sep 17 00:00:00 2001 From: GianellaDelMestre <146126967+GianellaDelMestre@users.noreply.github.com> Date: Mon, 8 Dec 2025 23:57:02 -0300 Subject: [PATCH 4/8] salecontroller en proceso creativo --- app/Http/Controllers/SaleController.php | 72 +++++++++++++++++++++++++ resources/views/sales/create.blade.php | 72 +++++++++++++++++++++++++ routes/web.php | 9 ++++ 3 files changed, 153 insertions(+) create mode 100644 app/Http/Controllers/SaleController.php create mode 100644 resources/views/sales/create.blade.php diff --git a/app/Http/Controllers/SaleController.php b/app/Http/Controllers/SaleController.php new file mode 100644 index 0000000..b96847f --- /dev/null +++ b/app/Http/Controllers/SaleController.php @@ -0,0 +1,72 @@ +', 0)->get(); + + // Retornamos la vista (que crearemos en el paso 4) pasando los productos + return view('sales.create', compact('products')); + } + + public function store(Request $request) + { + $request->validate([ + 'payment_method' => 'required|string', + 'items' => 'required|array', + 'items.*.product_id' => 'required|exists:products,id', + 'items.*.quantity' => 'required|integer|min:1', + ]); + + try { + DB::transaction(function () use ($request) { + $totalVenta = 0; + foreach ($request->items as $item) { + $producto = Product::find($item['product_id']); + $totalVenta += $producto->price * $item['quantity']; + + if ($producto->stock < $item['quantity']) { + throw new \Exception("No hay suficiente stock de " . $producto->name); + } + } + + // raro arreglar + $sale = Sale::create([ + 'user_id' => auth()->id(), // El empleado logueado + 'total' => $totalVenta, + 'payment_method' => $request->payment_method, + ]); + + foreach ($request->items as $item) { + $producto = Product::find($item['product_id']); + + SaleDetail::create([ + 'sale_id' => $sale->id, + 'product_id' => $producto->id, + 'quantity' => $item['quantity'], + 'price' => $producto->price, + ]); + $producto->decrement('stock', $item['quantity']); + } + }); + + // 5. REDIRECCIÓN DE ÉXITO + // Redirigimos al usuario con un mensaje flash [cite: 668] + return redirect()->route('sales.create')->with('success', '¡Venta registrada correctamente!'); + + } catch (\Exception $e) { + // Si algo falló (ej: falta stock), volvemos atrás con el error + return back()->with('error', 'Error en la venta: ' . $e->getMessage()); + } + } +} diff --git a/resources/views/sales/create.blade.php b/resources/views/sales/create.blade.php new file mode 100644 index 0000000..8af8f72 --- /dev/null +++ b/resources/views/sales/create.blade.php @@ -0,0 +1,72 @@ +@extends('layouts.app') + +@section('title', 'Nueva Venta') + +@section('main') +
    +
    + +
    +

    Nueva Venta en Mostrador

    + + @if (session('success')) + + @endif + + @if (session('error')) + + @endif + +
    + @csrf + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + +
    + + + + Cancelar + +
    +
    +
    +
    +
    +@endsection \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index f819ba4..ab1325e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -2,6 +2,7 @@ use App\Http\Controllers\AuthController; use App\Http\Controllers\CatalogoController; +use App\Http\Controllers\SaleController; use Illuminate\Support\Facades\Route; use App\Http\Controllers\HomeController; use App\Http\Controllers\LoginController; @@ -47,5 +48,13 @@ Route::middleware(['auth'])->group(function () { Route::delete('/productos/{id}', [ProductosController::class,'destroy'])->name('productos.destroy'); }); +Route::middleware(['auth'])->group(function () { + // mostrar + Route::get('/sales/create', [SaleController::class, 'create'])->name('sales.create'); + + // procesar + Route::post('/sales', [SaleController::class, 'store'])->name('sales.store'); +}); + Route::get('/catalogo', [CatalogoController::class,'catalogo'])->name('catalogo'); Route::get('/productos/{id}/vistaUsuario', [ProductosController::class,'vistaUsuario'])->name('productos.vistaUsuario'); From a40d43b631ece04c9df5db3752423f22f0461e4f Mon Sep 17 00:00:00 2001 From: BryamE Date: Tue, 9 Dec 2025 01:52:43 -0300 Subject: [PATCH 5/8] cambios para que ande el navbar --- app/Http/Controllers/SaleController.php | 2 +- resources/views/components/navbar.blade.php | 2 +- routes/web.php | 9 ++------- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/app/Http/Controllers/SaleController.php b/app/Http/Controllers/SaleController.php index 82cf09d..d0d63b2 100644 --- a/app/Http/Controllers/SaleController.php +++ b/app/Http/Controllers/SaleController.php @@ -13,7 +13,7 @@ class SaleController extends Controller public function create() { // Buscamos productos que tengan stock mayor a 0 para mostrar en el selector - $products = Product::where('stock', '>', 0)->get(); + $products = Product::where('stock_quantity', '>', 0)->get(); return view('sales.create', compact('products')); } diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php index 6719c83..c8adb37 100644 --- a/resources/views/components/navbar.blade.php +++ b/resources/views/components/navbar.blade.php @@ -39,7 +39,7 @@ Taller
  • - Ventas diff --git a/routes/web.php b/routes/web.php index b10cec5..6740b18 100644 --- a/routes/web.php +++ b/routes/web.php @@ -8,7 +8,6 @@ use App\Http\Controllers\HomeController; use App\Http\Controllers\LoginController; use App\Http\Controllers\ProductosController; use App\Http\Controllers\RegisterController; -use App\Http\Controllers\SaleController; use App\Models\Product; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Session; @@ -48,16 +47,12 @@ Route::middleware(['auth'])->group(function () { Route::put('/productos/{id}',[ProductosController::class,'update'])->name('productos.update'); Route::delete('/productos/{id}', [ProductosController::class,'destroy'])->name('productos.destroy'); - Route::get('/sales', [SaleController::class, 'index'])->name('sales.index'); - Route::get('/sales/create', [SaleController::class, 'create'])->name('sales.create'); -}); - -Route::middleware(['auth'])->group(function () { // mostrar Route::get('/sales/create', [SaleController::class, 'create'])->name('sales.create'); - // procesar Route::post('/sales', [SaleController::class, 'store'])->name('sales.store'); + // Route::get('/sales', [SaleController::class, 'index'])->name('sales.index'); + // Route::get('/sales/create', [SaleController::class, 'create'])->name('sales.create'); }); Route::get('/catalogo', [CatalogoController::class,'catalogo'])->name('catalogo'); From 75ce7142b03881efd0bf71874f7e22a1465323b6 Mon Sep 17 00:00:00 2001 From: Bryam105 Date: Tue, 9 Dec 2025 11:13:02 -0330 Subject: [PATCH 6/8] cambio para compatibilidad --- app/Http/Controllers/SaleController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/SaleController.php b/app/Http/Controllers/SaleController.php index d0d63b2..27fa950 100644 --- a/app/Http/Controllers/SaleController.php +++ b/app/Http/Controllers/SaleController.php @@ -34,7 +34,7 @@ class SaleController extends Controller $producto = Product::find($item['product_id']); $totalVenta += $producto->price * $item['quantity']; - if ($producto->stock < $item['quantity']) { + if ($producto->stock_quantity < $item['quantity']) { throw new \Exception("No hay suficiente stock de " . $producto->name); } } From 03cc1fe7ad398894a4293a230b543296f05ee352 Mon Sep 17 00:00:00 2001 From: BryamE Date: Tue, 9 Dec 2025 13:38:14 -0300 Subject: [PATCH 7/8] del --- resources/a.md | 165 ------------------------------------------------- 1 file changed, 165 deletions(-) delete mode 100644 resources/a.md diff --git a/resources/a.md b/resources/a.md deleted file mode 100644 index ad025d6..0000000 --- a/resources/a.md +++ /dev/null @@ -1,165 +0,0 @@ -¡Perfecto\! Tus respuestas aclaran muchísimo el panorama. Al ser un sistema **Intranet (Local)** con un **Admin** y **Empleados** (visores/operadores), y con procesos manuales en el taller, podemos simplificar la arquitectura para lanzar un MVP (Producto Mínimo Viable) rápido y robusto, pero preparado para crecer. - -Aquí tienes la **Guía Maestra de Desarrollo para Lauck Systems**. - ------ - -### 1\. Arquitectura de Base de Datos (El Cimiento) - -Antes de escribir código, definamos las tablas. Laravel hace esto fácil con migraciones. - -#### Tablas Principales: - -1. **`users`** (Ya viene en Laravel) - - * Agregamos campo: `role` (enum: 'admin', 'employee'). - * *Lógica:* El Admin crea, edita y borra. El Empleado solo ve y registra ventas/turnos (según definas). - -2. **`clients`** (Clientes) - - * `name`, `phone` (clave para WhatsApp), `email`, `address`. - * *Nota:* Fundamental para agendar turnos y registrar garantías. - -3. **`products`** (Inventario Mixto) - - * `name`, `sku` (código único), `description`. - * `price` (precio venta), `cost` (costo, solo visible para admin). - * `stock_quantity` (entero). - * `min_stock_alert` (entero, para la alerta que pediste). - * `type` (enum: 'bike', 'accessory', 'service'). - * `serial_number` (nullable, solo para bicicletas). - -4. **`appointments`** (Turnos / Taller) - - * `client_id` (relación). - * `scheduled_at` (datetime - fecha y hora del turno). - * `bike_model` (texto libre, ej: "Venzo Loki 29"). - * `problem_description` (motivo de la consulta). - * `status` (enum: 'pending', 'confirmed', 'in\_progress', 'ready', 'delivered'). - * `notes` (uso interno del mecánico). - -5. **`sales`** (Ventas Internas / Historial) - - * `user_id` (quién vendió). - * `client_id` (opcional, si es consumidor final anónimo). - * `total_amount`. - * `payment_method` (efectivo, tarjeta, transferencia). - * `created_at` (fecha de venta). - -6. **`sale_items`** (Detalle de venta) - - * `sale_id`, `product_id`, `quantity`, `unit_price`. - ------ - -### 2\. Estructura de Rutas y Controladores (Backend) - -En Laravel, organizaremos esto por "Dominios". - - * **Autenticación:** (Ya lo tienes con Breeze/Jetstream). - * **DashboardController:** - * `index()`: Muestra las tarjetas, alertas de stock bajo (query simple `Product::whereColumn('stock', '<=', 'min_stock')->get()`) y turnos de hoy. - * **ProductController:** - * CRUD completo (Crear, Leer, Actualizar, Borrar). - * Función extra `search()`: Para el buscador de precios de los empleados. - * **ClientController:** - * CRUD simple. - * **AppointmentController (Gestión de Taller):** - * `calendar()`: Vista de calendario o lista cronológica. - * `statusUpdate()`: Para mover el turno de "Pendiente" a "Listo". - * **SaleController:** - * `create()`: Formulario para registrar una salida de mercadería. - * `store()`: Resta el stock y guarda la venta. - ------ - -### 3\. Componentes de UI (Frontend - Blade + Tailwind) - -Para mantener el estilo "Lauck" que ya definimos, necesitaremos crear estos componentes reutilizables (además de los que ya tienes): - -1. **`x-ui.status-badge`**: - * Una etiqueta pequeña redondeada que cambia de color según el estado (Verde para 'Stock Alto', Rojo para 'Sin Stock' o 'Turno Atrasado', Amarillo para 'En Reparación'). -2. **`x-ui.table`**: - * Una tabla estilizada con el modo oscuro, filas alternadas y cabeceras fijas. Vital para listas de precios y clientes. -3. **`x-forms.input` / `x-forms.select`**: - * Inputs con el estilo oscuro y borde neón al hacer foco, para no repetir las clases de Tailwind en cada formulario. -4. **`x-ui.alert`**: - * Para mostrar mensajes de éxito ("Producto guardado") o alertas ("¡Quedan solo 2 cámaras rodado 29\!"). - ------ - -### 4\. Funcionalidades Específicas a Implementar - -Aquí está la lógica para los requerimientos que mencionaste: - -#### A. Alertas de Stock ⚠️ - - * **Lógica:** No necesitas un sistema complejo de notificaciones en tiempo real todavía. - * **Implementación:** En el `DashboardController`, pasas una variable `$lowStockProducts` a la vista. - * **Vista:** En el Dashboard, si esa lista no está vacía, muestras una tarjeta roja o amarilla avisando "X productos con stock crítico". - -#### B. Consulta de Precios (Modo Solo Lectura) 🔍 - - * **Requerimiento:** Empleados consultan, no editan. - * **Implementación:** - * Crear una vista `products.checker`. - * Un input de búsqueda grande en el centro. - * Al escribir (AJAX o Livewire sería ideal aquí, pero un form simple con botón "Buscar" funciona), muestra una tarjeta gigante con el Nombre y el Precio. - * *Seguridad:* Usar **Laravel Gates** o **Policies**. - ```php - // En AuthServiceProvider - Gate::define('edit-products', function ($user) { - return $user->role === 'admin'; - }); - ``` - En Blade: `@can('edit-products') @endcan`. Así el empleado ve el producto pero no el botón de editar. - -#### C. Agenda de Turnos 📅 - - * **Lógica:** "Consulta -\> Cita". - * **Implementación:** - * No te compliques con un calendario visual complejo (tipo Google Calendar) al principio. - * Usa una **Lista Agrupada por Días**. - * *Ejemplo visual:* - * **HOY:** - * 09:00 - Jose (Pincharura) [Ver] - * 10:30 - Maria (Service General) [Ver] - * **MAÑANA:** - * ... - ------ - -### 5\. Roadmap Sugerido (Paso a Paso) - -Este es el orden lógico para programar sin perderse: - -1. **Semana 1: Cimientos y Stock (Lo más urgente)** - - * Configurar Migraciones (`products`, `clients`). - * Crear Modelos y Seeders (datos falsos para probar). - * Hacer el CRUD de Productos (Alta, Baja y Modificación). - * *Hito:* Poder cargar una bicicleta y verla en la lista. - -2. **Semana 2: Seguridad y Consultas** - - * Agregar campo `role` a Users. - * Crear la vista "Consulta de Precios" (solo lectura). - * Proteger las rutas de edición para que solo el Admin entre. - * *Hito:* El empleado puede loguearse y buscar un precio, pero no borrar nada. - -3. **Semana 3: El Taller (Turnos)** - - * Crear migración `appointments`. - * Crear formulario para "Nuevo Turno" (Seleccionar Cliente + Fecha + Motivo). - * Crear vista de "Lista de Turnos" en el Dashboard. - * *Hito:* Dejar de usar el cuaderno de papel para los turnos. - -4. **Semana 4: Refinamiento** - - * Alertas de stock visuales. - * Mejoras estéticas (Dark Mode en tablas). - * Pruebas finales en el servidor local. - -### ¿Cómo seguimos? - -¿Te gustaría que generemos el código para la **Migración de Productos** y el **Modelo**, o prefieres que diseñemos primero el componente visual de la **Tabla de Stock**? \ No newline at end of file From d71f33b0ed216b3ec93c54ef26ce0914623d6bc9 Mon Sep 17 00:00:00 2001 From: gianella Date: Tue, 9 Dec 2025 17:28:51 -0300 Subject: [PATCH 8/8] intento --- app/Http/Controllers/ClientController.php | 64 ++++++ app/Http/Controllers/SaleController.php | 35 +-- app/Models/Product.php | 11 +- app/Models/Sale.php | 7 +- app/Models/SaleDetail.php | 2 +- .../2025_12_06_193913_create_sales_table.php | 12 +- resources/views/clients/create.blade.php | 5 + resources/views/layouts/app.blade.php | 23 +- resources/views/sales/create.blade.php | 215 +++++++++++++++--- routes/web.php | 3 +- 10 files changed, 305 insertions(+), 72 deletions(-) create mode 100644 app/Http/Controllers/ClientController.php create mode 100644 resources/views/clients/create.blade.php diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php new file mode 100644 index 0000000..fcf5db8 --- /dev/null +++ b/app/Http/Controllers/ClientController.php @@ -0,0 +1,64 @@ +', 0)->get(); - - return view('sales.create', compact('products')); + + $clients = \App\Models\Client::orderBy('nombre')->get(); + + return view('sales.create', compact('products', 'clients')); } public function store(Request $request) { $request->validate([ 'payment_method' => 'required|string', + 'client_id' => 'nullable|exists:clients,id', 'items' => 'required|array', 'items.*.product_id' => 'required|exists:products,id', 'items.*.quantity' => 'required|integer|min:1', @@ -30,32 +35,36 @@ class SaleController extends Controller try { DB::transaction(function () use ($request) { $totalVenta = 0; + foreach ($request->items as $item) { - $producto = Product::find($item['product_id']); - $totalVenta += $producto->price * $item['quantity']; + $product = Product::find($item['product_id']); + $totalVenta += $product->precio * $item['quantity']; - if ($producto->stock < $item['quantity']) { - throw new \Exception("No hay suficiente stock de " . $producto->name); + if ($product->stock < $item['quantity']) { + throw new \Exception("No hay suficiente stock de " . $product->nombre); } } - // raro arreglar + //cabecera $sale = Sale::create([ - 'user_id' => auth()->id(), // El empleado logueado + //'user_id' => auth()->id(), //empleado logueado + 'client_id' => $request->client_id, 'total' => $totalVenta, 'payment_method' => $request->payment_method, ]); foreach ($request->items as $item) { - $producto = Product::find($item['product_id']); + $product = Product::find($item['product_id']); SaleDetail::create([ 'sale_id' => $sale->id, - 'product_id' => $producto->id, - 'quantity' => $item['quantity'], - 'price' => $producto->price, + 'product_id' => $product->id, + 'cantidad' => $item['quantity'], + 'precio' => $product->precio, ]); - $producto->decrement('stock', $item['quantity']); + $nuevoStock = $product->stock - $item['quantity']; + $product->stock = $nuevoStock; + $product->save(); } }); diff --git a/app/Models/Product.php b/app/Models/Product.php index a4e3f49..9165684 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -8,16 +8,7 @@ use Illuminate\Database\Eloquent\Model; class Product extends Model { use HasFactory; - protected $fillable = [ - 'codigo', - 'nombre', - 'costo', - 'precio', - 'stock', - 'min_stock', - 'categoria', - 'descripcion' - ]; + protected $guarded = []; public function supplier() { return $this->belongsTo(Suppliers::class); diff --git a/app/Models/Sale.php b/app/Models/Sale.php index c9c88ad..aeeca90 100644 --- a/app/Models/Sale.php +++ b/app/Models/Sale.php @@ -3,13 +3,10 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; - +use Illuminate\Database\Eloquent\Factories\HasFactory; class Sale extends Model { - use HasFactory; - protected $fillable = [ - 'metodo_pago' - ]; + //use HasFactory; // Permitimos asignación masiva para poder guardar rápido protected $guarded = []; diff --git a/app/Models/SaleDetail.php b/app/Models/SaleDetail.php index bbe36e6..597eff4 100644 --- a/app/Models/SaleDetail.php +++ b/app/Models/SaleDetail.php @@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model; class SaleDetail extends Model { - use HasFactory; + //use HasFactory; protected $guarded = []; // este detalle pertenece a una Venta específica diff --git a/database/migrations/2025_12_06_193913_create_sales_table.php b/database/migrations/2025_12_06_193913_create_sales_table.php index b0bfc22..2125264 100644 --- a/database/migrations/2025_12_06_193913_create_sales_table.php +++ b/database/migrations/2025_12_06_193913_create_sales_table.php @@ -13,13 +13,11 @@ return new class extends Migration { Schema::create('sales', function (Blueprint $table) { $table->id(); - // 1. Cliente que compra - $table->foreignId('user_id')->constrained()->onDelete('cascade'); - // 2. Datos generales de la venta - $table->decimal('total', 10, 2); // Total final del ticket - $table->string('metodo_pago'); // Ej: "Efectivo", "Tarjeta", "MercadoPago" - //capaz van mas datos ni idea - // 3. Fecha y Hora (created_at servirá como fecha de venta) + //$table->foreignId('user_id')->constrained(); + // nullable para que no sea obligatrio + $table->foreignId('client_id')->nullable()->constrained(); + $table->decimal('total', 10, 2); + $table->string('payment_method'); $table->timestamps(); }); } diff --git a/resources/views/clients/create.blade.php b/resources/views/clients/create.blade.php new file mode 100644 index 0000000..6265ad0 --- /dev/null +++ b/resources/views/clients/create.blade.php @@ -0,0 +1,5 @@ +@extends('layouts.app') +@section('main') +

    formulario de crear cliente

    + +@endsection \ No newline at end of file diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 67c46ad..4b2d15f 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -3,15 +3,28 @@ - - @yield('title','Lauck - Home') + + + + @yield('title','Lauck - Home') -
    Cabeza
    +
    +

    Lauck Bicicletería

    +
    - @yield('main') +
    + @yield('main') +
    -
    Pies
    +
    + © {{ date('Y') }} Sistema Lauck +
    + + + + + @stack('scripts') \ No newline at end of file diff --git a/resources/views/sales/create.blade.php b/resources/views/sales/create.blade.php index 8af8f72..fd20768 100644 --- a/resources/views/sales/create.blade.php +++ b/resources/views/sales/create.blade.php @@ -7,44 +7,102 @@
    -

    Nueva Venta en Mostrador

    +
    +

    Nueva Venta en Mostrador

    +
    @if (session('success')) - + +@push('scripts') + +@endpush @endsection \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index b10cec5..4308583 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,12 +3,12 @@ use App\Http\Controllers\AuthController; use App\Http\Controllers\CatalogoController; use App\Http\Controllers\SaleController; +use App\Http\Controllers\ClientController; use Illuminate\Support\Facades\Route; use App\Http\Controllers\HomeController; use App\Http\Controllers\LoginController; use App\Http\Controllers\ProductosController; use App\Http\Controllers\RegisterController; -use App\Http\Controllers\SaleController; use App\Models\Product; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Session; @@ -50,6 +50,7 @@ Route::middleware(['auth'])->group(function () { Route::get('/sales', [SaleController::class, 'index'])->name('sales.index'); Route::get('/sales/create', [SaleController::class, 'create'])->name('sales.create'); + Route::resource('clients', ClientController::class); }); Route::middleware(['auth'])->group(function () {