up
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use App\Models\Producto;
|
||||
use App\Models\Bodega;
|
||||
use Filament\Pages\Page;
|
||||
use App\Services\TransferenciaBodegaService;
|
||||
use Filament\Notifications\Notification;
|
||||
|
||||
class GestionStockPage extends Page
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-chart-bar-square';
|
||||
|
||||
protected static ?string $navigationLabel = 'Gestión de Stock';
|
||||
|
||||
protected static ?string $navigationGroup = 'Inventario';
|
||||
|
||||
protected static string $view = 'filament.pages.gestion-stock';
|
||||
|
||||
public $productosStockBajo;
|
||||
public $bodegasEstado;
|
||||
public $sugerenciasTransferencia;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
// Datos para la vista
|
||||
$this->productosStockBajo = $this->getProductosStockBajo();
|
||||
$this->bodegasEstado = $this->getBodegasEstado();
|
||||
$this->sugerenciasTransferencia = $this->getSugerenciasTransferencia();
|
||||
}
|
||||
|
||||
protected function getProductosStockBajo()
|
||||
{
|
||||
return Producto::with(['bodegas', 'categoria'])
|
||||
->where('estado', true)
|
||||
->get()
|
||||
->filter(function ($producto) {
|
||||
return $producto->getStockEfectivo() <= $producto->stock_minimo;
|
||||
})
|
||||
->map(function ($producto) {
|
||||
return [
|
||||
'id' => $producto->id,
|
||||
'nombre' => $producto->nombre,
|
||||
'categoria' => $producto->categoria->nombre ?? 'Sin categoría',
|
||||
'stock_actual' => $producto->getStockEfectivo(),
|
||||
'stock_minimo' => $producto->stock_minimo,
|
||||
'distribucion' => $producto->bodegas->map(function ($bodega) {
|
||||
return [
|
||||
'bodega' => $bodega->nombre,
|
||||
'stock' => $bodega->pivot->stock,
|
||||
];
|
||||
})->toArray(),
|
||||
];
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
protected function getBodegasEstado()
|
||||
{
|
||||
return Bodega::with('productos')
|
||||
->get()
|
||||
->map(function ($bodega) {
|
||||
$totalProductos = $bodega->productos()->count();
|
||||
$productosConStock = $bodega->productos()->wherePivot('stock', '>', 0)->count();
|
||||
$stockTotal = $bodega->productos()->sum('producto_bodega.stock');
|
||||
|
||||
return [
|
||||
'id' => $bodega->id,
|
||||
'nombre' => $bodega->nombre,
|
||||
'total_productos' => $totalProductos,
|
||||
'productos_con_stock' => $productosConStock,
|
||||
'stock_total' => $stockTotal,
|
||||
'utilizacion' => $totalProductos > 0 ? round(($productosConStock / $totalProductos) * 100, 1) : 0,
|
||||
];
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
protected function getSugerenciasTransferencia()
|
||||
{
|
||||
$sugerencias = [];
|
||||
|
||||
// Buscar productos con stock desbalanceado entre bodegas
|
||||
$productos = Producto::with('bodegas')
|
||||
->where('estado', true)
|
||||
->get();
|
||||
|
||||
foreach ($productos as $producto) {
|
||||
if ($producto->bodegas->count() > 1) {
|
||||
$bodegas = $producto->bodegas->sortByDesc('pivot.stock');
|
||||
$mayor = $bodegas->first();
|
||||
$menor = $bodegas->last();
|
||||
|
||||
// Si hay gran diferencia de stock entre bodegas
|
||||
if ($mayor->pivot->stock > 0 && $menor->pivot->stock <= $producto->stock_minimo) {
|
||||
$cantidadSugerida = min(
|
||||
floor($mayor->pivot->stock * 0.3), // Máximo 30% del stock de la bodega con más stock
|
||||
$producto->stock_minimo - $menor->pivot->stock + 5 // Lo necesario para estar sobre el mínimo
|
||||
);
|
||||
|
||||
if ($cantidadSugerida > 0) {
|
||||
$sugerencias[] = [
|
||||
'producto_id' => $producto->id,
|
||||
'producto_nombre' => $producto->nombre,
|
||||
'bodega_origen' => $mayor->nombre,
|
||||
'bodega_origen_id' => $mayor->id,
|
||||
'bodega_destino' => $menor->nombre,
|
||||
'bodega_destino_id' => $menor->id,
|
||||
'cantidad_sugerida' => $cantidadSugerida,
|
||||
'razon' => "Rebalanceo: {$menor->nombre} bajo mínimo",
|
||||
'stock_origen' => $mayor->pivot->stock,
|
||||
'stock_destino' => $menor->pivot->stock,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return collect($sugerencias)->take(10)->toArray();
|
||||
}
|
||||
|
||||
public function ejecutarTransferenciaSugerida($data)
|
||||
{
|
||||
try {
|
||||
$service = new TransferenciaBodegaService();
|
||||
|
||||
$transferencia = $service->transferir(
|
||||
$data['producto_id'],
|
||||
$data['bodega_origen_id'],
|
||||
$data['bodega_destino_id'],
|
||||
$data['cantidad_sugerida'],
|
||||
$data['razon'] . ' (Transferencia automática sugerida)'
|
||||
);
|
||||
|
||||
Notification::make()
|
||||
->title('Transferencia ejecutada')
|
||||
->body("Se transfirieron {$data['cantidad_sugerida']} unidades de {$data['producto_nombre']}")
|
||||
->success()
|
||||
->send();
|
||||
|
||||
// Recargar datos
|
||||
$this->mount();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Notification::make()
|
||||
->title('Error en transferencia')
|
||||
->body($e->getMessage())
|
||||
->danger()
|
||||
->send();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use App\Models\Caja;
|
||||
use App\Models\Cliente;
|
||||
use App\Models\MovimientoCaja;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Forms;
|
||||
use Filament\Tables;
|
||||
use App\Models\Producto;
|
||||
use App\Models\ProductVariant;
|
||||
use App\Models\Venta;
|
||||
use Livewire\Component;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class PuntoVenta extends Page
|
||||
{
|
||||
protected static ?string $navigationGroup = 'Operación';
|
||||
protected static ?string $navigationIcon = 'heroicon-o-shopping-cart';
|
||||
protected static string $view = 'filament.pages.punto-venta';
|
||||
|
||||
public $barcodeBusqueda = '';
|
||||
|
||||
public $productos = [];
|
||||
public $carrito = [];
|
||||
public $total = 0;
|
||||
public $tipo_pago = 'Efectivo';
|
||||
|
||||
public $numeroDocumentoCliente = '';
|
||||
public $datosCliente = [
|
||||
'nombre' => null,
|
||||
'correo' => null,
|
||||
'telefono' => null,
|
||||
];
|
||||
public $cliente_id = null;
|
||||
|
||||
public $correoCotizacion = '';
|
||||
public $bodega_id = null; // Bodega seleccionada para la venta
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->productos = Producto::where('estado', true)
|
||||
->where(function ($query) {
|
||||
$query->where('stock', '>', 0)
|
||||
->orWhereHas('bodegas', function ($q) {
|
||||
$q->where('producto_bodega.stock', '>', 0);
|
||||
})
|
||||
->orWhereHas('variants', function ($q) {
|
||||
$q->where('stock', '>', 0)
|
||||
->orWhereHas('bodegas', function ($bq) {
|
||||
$bq->where('variante_bodega.stock', '>', 0);
|
||||
});
|
||||
});
|
||||
})
|
||||
->with('variants.color', 'variants.size', 'bodegas')
|
||||
->limit(20)
|
||||
->get();
|
||||
|
||||
// Seleccionar bodega "Principal" por defecto
|
||||
$bodegaPrincipal = \App\Models\Bodega::where('nombre', 'Principal')->first();
|
||||
$this->bodega_id = $bodegaPrincipal ? $bodegaPrincipal->id : null;
|
||||
}
|
||||
|
||||
public function agregarAlCarrito($productoId, $tipo)
|
||||
{
|
||||
if ($tipo == 'p') {
|
||||
$producto = Producto::find($productoId);
|
||||
if (!$producto)
|
||||
return;
|
||||
|
||||
$key = 'p-' . $producto->id;
|
||||
|
||||
if (!isset($this->carrito[$key])) {
|
||||
$this->carrito[$key] = [
|
||||
'id' => $producto->id,
|
||||
'nombre' => $producto->nombre,
|
||||
'precio_venta' => $producto->precio_venta,
|
||||
'precio_original' => $producto->precio_venta,
|
||||
'precio_modificado' => $producto->precio_venta,
|
||||
'cantidad' => 1,
|
||||
'tipo' => 'p',
|
||||
];
|
||||
} else {
|
||||
$this->carrito[$key]['cantidad']++;
|
||||
}
|
||||
} else {
|
||||
$producto = ProductVariant::find($productoId);
|
||||
if (!$producto)
|
||||
return;
|
||||
|
||||
$key = 'v-' . $producto->id;
|
||||
|
||||
if (!isset($this->carrito[$key])) {
|
||||
$this->carrito[$key] = [
|
||||
'id' => $producto->producto->id,
|
||||
'nombre' => $producto->producto->nombre,
|
||||
'precio_venta' => $producto->producto->precio_venta,
|
||||
'precio_original' => $producto->producto->precio_venta,
|
||||
'precio_modificado' => $producto->producto->precio_venta,
|
||||
'variante' => 'Color: ' . $producto->color->name . ', Talla: ' . $producto->size->name,
|
||||
'variante_id' => $producto->id,
|
||||
'cantidad' => 1,
|
||||
'tipo' => 'v',
|
||||
];
|
||||
} else {
|
||||
$this->carrito[$key]['cantidad']++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->calcularTotal();
|
||||
}
|
||||
|
||||
|
||||
public function calcularTotal()
|
||||
{
|
||||
$this->total = collect($this->carrito)->sum(fn($item) => $item['precio_modificado'] * $item['cantidad']);
|
||||
}
|
||||
|
||||
public function actualizarPrecio($key, $nuevoPrecio)
|
||||
{
|
||||
if (isset($this->carrito[$key])) {
|
||||
// Validar que el precio sea válido (mayor o igual a 0)
|
||||
$precio = floatval($nuevoPrecio);
|
||||
if ($precio >= 0) {
|
||||
$this->carrito[$key]['precio_modificado'] = $precio;
|
||||
$this->calcularTotal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function procesarVenta()
|
||||
{
|
||||
if (empty($this->carrito)) {
|
||||
session()->flash('error', 'No se puede procesar la venta: el carrito está vacío.');
|
||||
return;
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$clienteId = null;
|
||||
|
||||
if ($this->numeroDocumentoCliente) {
|
||||
$cliente = Cliente::where('numero_documento', $this->numeroDocumentoCliente)->first();
|
||||
|
||||
if (!$cliente) {
|
||||
$datosCliente = [
|
||||
'nombre' => $this->datosCliente['nombre'] ?? "Cliente General",
|
||||
'correo' => $this->datosCliente['correo'] ?? null,
|
||||
'telefono' => $this->datosCliente['telefono'] ?? null,
|
||||
'numero_documento' => $this->numeroDocumentoCliente,
|
||||
'tipo_documento' => $this->datosCliente['tipo_documento'] ?? 'CC',
|
||||
];
|
||||
|
||||
$cliente = Cliente::create($datosCliente);
|
||||
}
|
||||
|
||||
$clienteId = $cliente->id;
|
||||
} elseif ($this->cliente_id) {
|
||||
$cliente = Cliente::find($this->cliente_id);
|
||||
if ($cliente) {
|
||||
$clienteId = $cliente->id;
|
||||
} else {
|
||||
$this->cliente_id = null;
|
||||
$clienteId = null;
|
||||
}
|
||||
}
|
||||
|
||||
$venta = Venta::create([
|
||||
'cliente_id' => $clienteId,
|
||||
'total' => $this->total,
|
||||
'tipo_pago' => $this->tipo_pago,
|
||||
'estado' => 'Pagado',
|
||||
]);
|
||||
|
||||
foreach ($this->carrito as $item) {
|
||||
$precioOriginal = $item['precio_original'];
|
||||
$precioModificado = $item['precio_modificado'];
|
||||
$descuentoAplicado = $precioOriginal - $precioModificado;
|
||||
$cantidadPendiente = $item['cantidad'];
|
||||
|
||||
// Identificar el producto o variante real
|
||||
$producto = null;
|
||||
$variante = null;
|
||||
|
||||
if (isset($item['variante_id'])) {
|
||||
$variante = ProductVariant::find($item['variante_id']);
|
||||
$producto = $variante->producto;
|
||||
} else {
|
||||
$producto = Producto::find($item['id']);
|
||||
}
|
||||
|
||||
// Estrategia de deducción de stock:
|
||||
// 1. Buscar en Bodega Principal
|
||||
// 2. Buscar en otras bodegas con stock
|
||||
// 3. Si falta, descontar de stock directo (o dejar negativo en Principal si se prefiere, aquí usaremos Principal como fallback)
|
||||
|
||||
$bodegas = \App\Models\Bodega::orderByRaw("CASE WHEN nombre = 'Principal' THEN 0 ELSE 1 END")->get();
|
||||
|
||||
foreach ($bodegas as $bodega) {
|
||||
if ($cantidadPendiente <= 0) break;
|
||||
|
||||
$stockDisponible = 0;
|
||||
|
||||
if ($variante) {
|
||||
$bodegaVariante = $variante->bodegas()->where('bodega_id', $bodega->id)->first();
|
||||
$stockDisponible = $bodegaVariante ? $bodegaVariante->pivot->stock : 0;
|
||||
} else {
|
||||
$bodegaProducto = $producto->bodegas()->where('bodega_id', $bodega->id)->first();
|
||||
$stockDisponible = $bodegaProducto ? $bodegaProducto->pivot->stock : 0;
|
||||
}
|
||||
|
||||
if ($stockDisponible > 0) {
|
||||
$cantidadADescontar = min($cantidadPendiente, $stockDisponible);
|
||||
|
||||
// Registrar detalle para esta bodega
|
||||
$venta->detalles()->create([
|
||||
'producto_id' => $item['id'],
|
||||
'variante_id' => $item['variante_id'] ?? null,
|
||||
'bodega_id' => $bodega->id,
|
||||
'cantidad' => $cantidadADescontar,
|
||||
'precio_unitario' => $precioModificado,
|
||||
'precio_original' => $precioOriginal,
|
||||
'descuento_aplicado' => $descuentoAplicado,
|
||||
'usuario_modifico_precio_id' => ($descuentoAplicado != 0) ? auth()->id() : null,
|
||||
'subtotal' => $cantidadADescontar * $precioModificado,
|
||||
]);
|
||||
|
||||
// Actualizar stock
|
||||
if ($variante) {
|
||||
$variante->bodegas()->updateExistingPivot($bodega->id, [
|
||||
'stock' => $stockDisponible - $cantidadADescontar
|
||||
]);
|
||||
} else {
|
||||
$producto->bodegas()->updateExistingPivot($bodega->id, [
|
||||
'stock' => $stockDisponible - $cantidadADescontar
|
||||
]);
|
||||
}
|
||||
|
||||
$cantidadPendiente -= $cantidadADescontar;
|
||||
}
|
||||
}
|
||||
|
||||
// Si aún queda cantidad pendiente (no había stock suficiente en ninguna bodega)
|
||||
// Lo asignamos a la Bodega Principal (o NULL si no hay bodegas) y dejamos que el stock se vaya a negativo o se descuente del directo
|
||||
if ($cantidadPendiente > 0) {
|
||||
$bodegaFallback = $bodegas->first(); // Principal por el ordenamiento
|
||||
$bodegaIdFallback = $bodegaFallback ? $bodegaFallback->id : null;
|
||||
|
||||
$venta->detalles()->create([
|
||||
'producto_id' => $item['id'],
|
||||
'variante_id' => $item['variante_id'] ?? null,
|
||||
'bodega_id' => $bodegaIdFallback,
|
||||
'cantidad' => $cantidadPendiente,
|
||||
'precio_unitario' => $precioModificado,
|
||||
'precio_original' => $precioOriginal,
|
||||
'descuento_aplicado' => $descuentoAplicado,
|
||||
'usuario_modifico_precio_id' => ($descuentoAplicado != 0) ? auth()->id() : null,
|
||||
'subtotal' => $cantidadPendiente * $precioModificado,
|
||||
]);
|
||||
|
||||
// Intentar descontar del fallback
|
||||
if ($bodegaFallback) {
|
||||
if ($variante) {
|
||||
$bodegaVariante = $variante->bodegas()->where('bodega_id', $bodegaFallback->id)->first();
|
||||
if ($bodegaVariante) {
|
||||
$variante->bodegas()->updateExistingPivot($bodegaFallback->id, [
|
||||
'stock' => $bodegaVariante->pivot->stock - $cantidadPendiente
|
||||
]);
|
||||
} else {
|
||||
// Si no existe la relación, la creamos con stock negativo
|
||||
$variante->bodegas()->attach($bodegaFallback->id, ['stock' => -$cantidadPendiente]);
|
||||
}
|
||||
} else {
|
||||
$bodegaProducto = $producto->bodegas()->where('bodega_id', $bodegaFallback->id)->first();
|
||||
if ($bodegaProducto) {
|
||||
$producto->bodegas()->updateExistingPivot($bodegaFallback->id, [
|
||||
'stock' => $bodegaProducto->pivot->stock - $cantidadPendiente
|
||||
]);
|
||||
} else {
|
||||
$producto->bodegas()->attach($bodegaFallback->id, ['stock' => -$cantidadPendiente]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Si no hay bodegas en absoluto, descontar del stock directo
|
||||
if ($variante) {
|
||||
$variante->decrement('stock', $cantidadPendiente);
|
||||
} else {
|
||||
$producto->decrement('stock', $cantidadPendiente);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cajaAbierta = Caja::where('estado', 'Abierta')
|
||||
->orderBy('fecha_apertura', 'desc')
|
||||
->first();
|
||||
|
||||
if ($cajaAbierta) {
|
||||
MovimientoCaja::create([
|
||||
'caja_id' => $cajaAbierta->id,
|
||||
'tipo' => 'Ingreso',
|
||||
'monto' => $this->total,
|
||||
'descripcion' => 'Venta realizada. Venta ID: ' . $venta->id,
|
||||
]);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
$this->resetVentaState();
|
||||
|
||||
session()->flash('message', 'Venta realizada con éxito.');
|
||||
$this->js("window.dispatchEvent(new CustomEvent('imprimir-recibo', { detail: { venta_id: {$venta->id} } }));");
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
$this->resetVentaState();
|
||||
session()->flash('error', 'Error al procesar la venta. ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resetea el estado de la venta para evitar inconsistencias
|
||||
*/
|
||||
private function resetVentaState()
|
||||
{
|
||||
$this->carrito = [];
|
||||
$this->total = 0;
|
||||
$this->numeroDocumentoCliente = '';
|
||||
$this->datosCliente = [
|
||||
'nombre' => null,
|
||||
'correo' => null,
|
||||
'telefono' => null,
|
||||
];
|
||||
$this->cliente_id = null;
|
||||
$this->tipo_pago = 'Efectivo';
|
||||
|
||||
// Mantener la bodega seleccionada para la próxima venta
|
||||
// $this->bodega_id se mantiene
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function buscarProductoPorBarcode()
|
||||
{
|
||||
$barcode = trim($this->barcodeBusqueda);
|
||||
|
||||
$producto = Producto::where('codigo_barras', $barcode)->first();
|
||||
$tipo = 'p';
|
||||
|
||||
|
||||
if (!$producto) {
|
||||
$producto = ProductVariant::where('barcode', $barcode)->first();
|
||||
$tipo = 'v';
|
||||
}
|
||||
|
||||
if ($producto) {
|
||||
$this->agregarAlCarrito($producto->id, $tipo);
|
||||
$this->barcodeBusqueda = '';
|
||||
} else {
|
||||
session()->flash('error', 'Producto no encontrado.');
|
||||
}
|
||||
}
|
||||
|
||||
public function incrementarCantidad($key)
|
||||
{
|
||||
if (isset($this->carrito[$key])) {
|
||||
$this->carrito[$key]['cantidad']++;
|
||||
$this->calcularTotal();
|
||||
}
|
||||
}
|
||||
|
||||
public function decrementarCantidad($key)
|
||||
{
|
||||
if (isset($this->carrito[$key])) {
|
||||
$this->carrito[$key]['cantidad']--;
|
||||
|
||||
if ($this->carrito[$key]['cantidad'] <= 0) {
|
||||
unset($this->carrito[$key]);
|
||||
}
|
||||
|
||||
$this->calcularTotal();
|
||||
}
|
||||
}
|
||||
|
||||
public function eliminarDelCarrito($key)
|
||||
{
|
||||
if (isset($this->carrito[$key])) {
|
||||
unset($this->carrito[$key]);
|
||||
$this->calcularTotal();
|
||||
}
|
||||
}
|
||||
public function enviarCotizacion()
|
||||
{
|
||||
$correo = $this->correoCotizacion;
|
||||
|
||||
if (!filter_var($correo, FILTER_VALIDATE_EMAIL)) {
|
||||
session()->flash('error', 'Correo inválido.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$carrito = $this->carrito;
|
||||
$total = $this->total;
|
||||
|
||||
Mail::send('emails.cotizacion', compact('carrito', 'total'), function ($message) use ($correo) {
|
||||
$message->to($correo)
|
||||
->subject('Cotización de productos');
|
||||
});
|
||||
|
||||
session()->flash('message', 'Cotización enviada exitosamente.');
|
||||
$this->correoCotizacion = '';
|
||||
} catch (\Exception $e) {
|
||||
session()->flash('error', 'Error al enviar la cotización: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function buscarClientePorDocumento()
|
||||
{
|
||||
$documento = trim($this->numeroDocumentoCliente);
|
||||
|
||||
if (!$documento) {
|
||||
// No se requiere documento
|
||||
$this->cliente_id = null;
|
||||
$this->datosCliente = [
|
||||
'nombre' => null,
|
||||
'correo' => null,
|
||||
'telefono' => null,
|
||||
];
|
||||
return;
|
||||
}
|
||||
|
||||
$cliente = Cliente::where('numero_documento', $documento)->first();
|
||||
|
||||
if ($cliente) {
|
||||
// Verificar que el cliente encontrado existe realmente
|
||||
$clienteValidado = Cliente::find($cliente->id);
|
||||
if ($clienteValidado) {
|
||||
$this->cliente_id = $clienteValidado->id;
|
||||
$this->datosCliente = [
|
||||
'nombre' => $clienteValidado->nombre,
|
||||
'correo' => $clienteValidado->correo,
|
||||
'telefono' => $clienteValidado->telefono,
|
||||
];
|
||||
} else {
|
||||
// Cliente no válido, resetear
|
||||
$this->cliente_id = null;
|
||||
$this->datosCliente = [
|
||||
'nombre' => null,
|
||||
'correo' => null,
|
||||
'telefono' => null,
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$this->cliente_id = null;
|
||||
$this->datosCliente = [
|
||||
'nombre' => null,
|
||||
'correo' => null,
|
||||
'telefono' => null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function cambiarBodega($bodegaId)
|
||||
{
|
||||
$this->bodega_id = $bodegaId;
|
||||
|
||||
// Opcional: mostrar notificación de cambio
|
||||
session()->flash('message', 'Bodega cambiada. Las ventas se realizarán desde la nueva bodega seleccionada.');
|
||||
}
|
||||
|
||||
public function getBodegasProperty()
|
||||
{
|
||||
return \App\Models\Bodega::all();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use Filament\Actions\MountableAction;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Components\ColorPicker;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Contracts\HasForms;
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Filament\Forms\Form;
|
||||
use App\Models\Setting;
|
||||
use Filament\Actions;
|
||||
use Filament\Notifications\Notification;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
|
||||
|
||||
class SettingPage extends Page implements HasForms
|
||||
{
|
||||
use InteractsWithForms;
|
||||
public $logo;
|
||||
public $description;
|
||||
public $primary_color;
|
||||
public $secondary_color;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-document-text';
|
||||
protected static string $view = 'filament.pages.settings';
|
||||
protected static ?string $title = 'Configuraciones del Sistema';
|
||||
protected static ?string $navigationGroup = 'Administración'; //
|
||||
|
||||
protected static ?string $navigationLabel = 'Ajustes';
|
||||
|
||||
|
||||
public ?array $data = [];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
// Obtener la configuración existente o valores predeterminados
|
||||
$setting = Setting::first();
|
||||
|
||||
$this->form->fill($setting?->toArray() ?? [
|
||||
'logo' => null,
|
||||
'description' => null,
|
||||
'primary_color' => '#3498db',
|
||||
'secondary_color' => '#2ecc71',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getFormSchema(): array
|
||||
{
|
||||
return [
|
||||
FileUpload::make('logo')
|
||||
->image()
|
||||
->label('Logo')
|
||||
->directory('settings/logos')
|
||||
->required(),
|
||||
|
||||
Textarea::make('description')
|
||||
->label('Descripción')
|
||||
->maxLength(500)
|
||||
->required(),
|
||||
|
||||
ColorPicker::make('primary_color')
|
||||
->label('Color Primario')
|
||||
->required(),
|
||||
|
||||
ColorPicker::make('secondary_color')
|
||||
->label('Color Secundario')
|
||||
->required(),
|
||||
];
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$data = $this->form->getState();
|
||||
|
||||
Setting::updateOrCreate(['id' => 1], $data);
|
||||
|
||||
// Enviar notificación de éxito
|
||||
Notification::make()
|
||||
->title('Éxito')
|
||||
->body('Configuraciones guardadas exitosamente.')
|
||||
->success()
|
||||
->send();
|
||||
|
||||
//$this->notify('success', 'Configuraciones guardadas exitosamente.');
|
||||
}
|
||||
|
||||
protected function makeForm(): Form
|
||||
{
|
||||
return Form::make($this)
|
||||
->schema($this->getFormSchema())
|
||||
->statePath('data');
|
||||
}
|
||||
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\Action::make('Cerrar todas las sesiones')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->action(function () {
|
||||
if (config('session.driver') === 'file') {
|
||||
// Eliminar todos los archivos de sesión
|
||||
$files = File::files(storage_path('framework/sessions'));
|
||||
foreach ($files as $file) {
|
||||
File::delete($file);
|
||||
}
|
||||
} elseif (config('session.driver') === 'database') {
|
||||
DB::table('sessions')->truncate();
|
||||
}
|
||||
// Limpia tu propia sesión para desconectarte también
|
||||
Session::flush();
|
||||
|
||||
|
||||
|
||||
Notification::make()
|
||||
->title('Sesiones cerradas')
|
||||
->body('Todas las sesiones han sido cerradas exitosamente.')
|
||||
->success()
|
||||
->send();
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user