Carpeta controllers
This commit is contained in:
@@ -0,0 +1,472 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Validacion de token
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])
|
||||||
|
) {
|
||||||
|
$_SESSION['flash_error'] = 'Acción inválida (CSRF)';
|
||||||
|
header('Location: index.php?opt=inicio_operador');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requiero modelos necesarios
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/cajaModel.php';
|
||||||
|
require_once __DIR__ . '/../models/productoModel.php';
|
||||||
|
|
||||||
|
$cajamodel = new cajaModel();
|
||||||
|
$productomodel = new productoModel();
|
||||||
|
|
||||||
|
// Validar sesion
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seteo el $opt
|
||||||
|
|
||||||
|
$opt = $_GET['opt'] ?? null;
|
||||||
|
|
||||||
|
// Switch de opt para manejo de caja
|
||||||
|
|
||||||
|
switch ($opt) {
|
||||||
|
|
||||||
|
// <---------------------------------- MANEJO DE CAJA PARA VENTA ---------------------------------->
|
||||||
|
|
||||||
|
// AGREGAR PRODUCTO A LA CAJA
|
||||||
|
|
||||||
|
case 'agregar_producto_caja':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: index.php?opt=crear_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = $_POST['ids'] ?? [];
|
||||||
|
$cantidades = $_POST['cantidades'] ?? [];
|
||||||
|
|
||||||
|
if (empty($ids)) {
|
||||||
|
$_SESSION['flash_error'] = 'No se seleccionó ningún producto.';
|
||||||
|
header('Location: index.php?opt=agregar_producto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$agregados = 0;
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Evitar duplicados
|
||||||
|
|
||||||
|
$ids = array_unique($ids);
|
||||||
|
|
||||||
|
foreach ($ids as $idProducto) {
|
||||||
|
|
||||||
|
$idProducto = (int) $idProducto;
|
||||||
|
$cantidad = isset($cantidades[$idProducto]) ? (int) $cantidades[$idProducto] : 1;
|
||||||
|
|
||||||
|
if ($idProducto <= 0 || $cantidad <= 0) {
|
||||||
|
$errores[] = 'Datos inválidos.';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$producto = $productomodel->obtenerPorIdParaCaja($idProducto);
|
||||||
|
|
||||||
|
if (!$producto) {
|
||||||
|
$errores[] = 'Producto inexistente.';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cajamodel->agregarProducto($producto, $cantidad)) {
|
||||||
|
$agregados++;
|
||||||
|
} else {
|
||||||
|
$errores[] = $cajamodel->getError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Condiciones para mostrar mensajes
|
||||||
|
|
||||||
|
if ($agregados === 1) {
|
||||||
|
$_SESSION['flash_success'] = "Se agregó un producto correctamente.";
|
||||||
|
} else if ($agregados > 1) {
|
||||||
|
$_SESSION['flash_success'] = "Se agregaron {$agregados} productos correctamente.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', array_unique($errores));
|
||||||
|
header('Location: index.php?opt=agregar_producto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_venta');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// ELIMINAR PRODUCTO DE CAJA
|
||||||
|
|
||||||
|
case 'eliminar_producto':
|
||||||
|
|
||||||
|
$idProducto = (int) ($_POST['id_producto'] ?? 0);
|
||||||
|
|
||||||
|
if ($idProducto <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=crear_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$cajamodel->eliminarProducto($idProducto)) {
|
||||||
|
$_SESSION['flash_error'] = $cajamodel->getError() ?? 'No se pudo eliminar el producto.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_success'] = 'Producto eliminado correctamente.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_venta');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// EDITAR CANTIDAD DE PRODUCTO
|
||||||
|
|
||||||
|
case 'editar_cantidad':
|
||||||
|
|
||||||
|
$idProducto = (int) ($_POST['id_producto'] ?? 0);
|
||||||
|
$nuevaCantidad = (int) ($_POST['cantidad'] ?? 0);
|
||||||
|
|
||||||
|
if ($idProducto <= 0 || $nuevaCantidad <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=crear_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$cajamodel->editarCantidad($idProducto, $nuevaCantidad)) {
|
||||||
|
$_SESSION['flash_error'] = $cajamodel->getError() ?? 'No se pudo actualizar la cantidad.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_success'] = 'Cantidad editada correctamente.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_venta');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// VACIAR CAJA DE PRODUCTOS
|
||||||
|
|
||||||
|
case 'vaciar_caja_venta':
|
||||||
|
|
||||||
|
if ($cajamodel->vaciarCajaVenta()) {
|
||||||
|
$_SESSION['flash_success'] = 'Carrito vaciado.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo vaciar el carrito.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_venta');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// <---------------------------------- MANEJO DE CAJA PARA PEDIDO ---------------------------------->
|
||||||
|
|
||||||
|
// AGREGAR PRODUCTO A CAJA DE PEDIDO
|
||||||
|
|
||||||
|
case 'agregar_producto_pedido_caja':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = $_POST['ids'] ?? [];
|
||||||
|
$costos = $_POST['costos'] ?? [];
|
||||||
|
$cantidades = $_POST['cantidades'] ?? [];
|
||||||
|
|
||||||
|
if (empty($ids)) {
|
||||||
|
$_SESSION['flash_error'] = 'No se seleccionó ningún producto.';
|
||||||
|
header('Location: index.php?opt=agregar_producto_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
$agregados = 0;
|
||||||
|
|
||||||
|
// Evitar duplicados
|
||||||
|
|
||||||
|
$ids = array_unique($ids);
|
||||||
|
|
||||||
|
foreach ($ids as $idProducto) {
|
||||||
|
|
||||||
|
$idProducto = (int) $idProducto;
|
||||||
|
$costo = isset($costos[$idProducto]) ? (float) $costos[$idProducto] : 0;
|
||||||
|
$cantidad = isset($cantidades[$idProducto]) ? (int) $cantidades[$idProducto] : 1;
|
||||||
|
|
||||||
|
if ($idProducto <= 0 || $cantidad <= 0 || $costo <= 0) {
|
||||||
|
$errores[] = 'Datos inválidos.';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$producto = $productomodel->obtenerPorIdParaCajaPedido($idProducto);
|
||||||
|
|
||||||
|
if (!$producto) {
|
||||||
|
$errores[] = 'Producto inexistente.';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cajamodel->agregarProductoPedido($producto, $costo, $cantidad)) {
|
||||||
|
$agregados++;
|
||||||
|
} else {
|
||||||
|
$errores[] = $cajamodel->getError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Condiciones para manejo de mensajes
|
||||||
|
|
||||||
|
if ($agregados === 1) {
|
||||||
|
$_SESSION['flash_success'] = "Se agregó un producto correctamente.";
|
||||||
|
} else if ($agregados > 1) {
|
||||||
|
$_SESSION['flash_success'] = "Se agregaron {$agregados} productos correctamente.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', array_unique($errores));
|
||||||
|
header('Location: index.php?opt=agregar_producto_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// VACIAR CAJA DE PRODUCTOS PARA PEDIDO
|
||||||
|
|
||||||
|
case 'vaciar_caja_pedido':
|
||||||
|
|
||||||
|
// Guardo datos en $_SESSION para mantener persistencia
|
||||||
|
|
||||||
|
$_SESSION['pedido']['cond_iva'] = $_POST['cond_iva'] ?? '';
|
||||||
|
$_SESSION['pedido']['cuit'] = $_POST['cuit'] ?? '';
|
||||||
|
|
||||||
|
if ($cajamodel->vaciarCajaPedido()) {
|
||||||
|
$_SESSION['flash_success'] = 'Carrito vaciado.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo vaciar el carrito.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// ELIMINAR PRODUCTO DE PEDIDO
|
||||||
|
|
||||||
|
case 'eliminar_producto_pedido':
|
||||||
|
|
||||||
|
// Guardo datos en $_SESSION para mantener persistencia
|
||||||
|
|
||||||
|
$_SESSION['pedido']['cond_iva'] = $_POST['cond_iva'] ?? '';
|
||||||
|
$_SESSION['pedido']['cuit'] = ($_POST['cuit_1'] ?? '') . ($_POST['cuit_2'] ?? '') . ($_POST['cuit_3'] ?? '');
|
||||||
|
|
||||||
|
$idProducto = (int) ($_POST['id_producto'] ?? 0);
|
||||||
|
|
||||||
|
if ($idProducto <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$cajamodel->eliminarProductoPedido($idProducto)) {
|
||||||
|
$_SESSION['flash_error'] = $cajamodel->getError() ?? 'No se pudo eliminar el producto.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// EDITAR DATOS DE PRODUCTOS PARA PEDIDO
|
||||||
|
|
||||||
|
case 'editar_datos_producto':
|
||||||
|
|
||||||
|
// Guardo datos para mantener consistencia
|
||||||
|
|
||||||
|
$_SESSION['pedido']['cond_iva'] = $_POST['cond_iva'] ?? '';
|
||||||
|
$_SESSION['pedido']['cuit'] = ($_POST['cuit_1'] ?? '') . ($_POST['cuit_2'] ?? '') . ($_POST['cuit_3'] ?? '');
|
||||||
|
|
||||||
|
// Saneamiento de datos
|
||||||
|
|
||||||
|
$idProducto = (int) ($_POST['id_producto'] ?? 0);
|
||||||
|
$nuevaCantidad = $_POST['cantidad'][$idProducto] ?? null;
|
||||||
|
$nuevoCosto = $_POST['costo'][$idProducto] ?? null;
|
||||||
|
|
||||||
|
if ($idProducto <= 0 || $nuevoCosto <= 0 || $nuevaCantidad <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$cajamodel->editarDatosProductoPedido($idProducto, $nuevoCosto, $nuevaCantidad)) {
|
||||||
|
$_SESSION['flash_error'] = $cajamodel->getError() ?? 'No se pudieron actualizar los datos.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_success'] = 'Datos actualizados.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// <---------------------------------- MANEJO DE CAJA PARA SERVICIO ---------------------------------->
|
||||||
|
|
||||||
|
// AGREGAR INSUMOS PARA SERVICIO EN LA CAJA
|
||||||
|
|
||||||
|
case 'agregar_insumo_caja':
|
||||||
|
|
||||||
|
$idServicio = (int) ($_GET['id'] ?? 0);
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: index.php?opt=actualizar_servicio');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = $_POST['ids'] ?? [];
|
||||||
|
$cantidades = $_POST['cantidades'] ?? [];
|
||||||
|
|
||||||
|
if (empty($ids)) {
|
||||||
|
$_SESSION['flash_error'] = 'No se seleccionó ningún insumo.';
|
||||||
|
header('Location: index.php?opt=agregar_insumo');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$agregados = 0;
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Evitar duplicados
|
||||||
|
|
||||||
|
$ids = array_unique($ids);
|
||||||
|
|
||||||
|
foreach ($ids as $idInsumo) {
|
||||||
|
|
||||||
|
$idInsumo = (int) $idInsumo;
|
||||||
|
$cantidad = isset($cantidades[$idInsumo]) ? (int) $cantidades[$idInsumo] : 1;
|
||||||
|
|
||||||
|
if ($idInsumo <= 0 || $cantidad <= 0) {
|
||||||
|
$errores[] = 'Datos inválidos.';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$insumo = $productomodel->obtenerPorIdParaCajaServicio($idInsumo);
|
||||||
|
|
||||||
|
if (!$insumo) {
|
||||||
|
$errores[] = 'Producto inexistente.';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cajamodel->agregarInsumo($insumo, $cantidad)) {
|
||||||
|
$agregados++;
|
||||||
|
} else {
|
||||||
|
$errores[] = $cajamodel->getError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Condiciones para mensajes
|
||||||
|
|
||||||
|
if ($agregados === 1) {
|
||||||
|
$_SESSION['flash_success'] = "Se agregó un insumo correctamente.";
|
||||||
|
} else if ($agregados > 1) {
|
||||||
|
$_SESSION['flash_success'] = "Se agregaron {$agregados} insumos correctamente.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', array_unique($errores));
|
||||||
|
header('Location: index.php?opt=agregar_insumo');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=actualizar_servicio&id=' . $idServicio);
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// VACIAR CAJA DE INSUMOS PARA SERVICIO
|
||||||
|
|
||||||
|
case 'vaciar_caja_servicio':
|
||||||
|
|
||||||
|
$idServicio = (int) ($_POST['id'] ?? 0);
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$_SESSION['servicio']['detalle_realizado'] = $_POST['detalle_realizado'] ?? '';
|
||||||
|
$_SESSION['servicio']['mano_obra'] = (float) $_POST['mano_obra'] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$manoObra = $_SESSION['servicio']['mano_obra'] ?? 0;
|
||||||
|
|
||||||
|
if ($manoObra < 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=actualizar_servicio&id=' . $idServicio);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cajamodel->vaciarCajaServicio()) {
|
||||||
|
$_SESSION['flash_success'] = 'Carrito vaciado.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo vaciar el carrito.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=actualizar_servicio&id=' . $idServicio);
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// ELIMINAR INSUMO DE SERVICIO
|
||||||
|
|
||||||
|
case 'eliminar_insumo':
|
||||||
|
|
||||||
|
$idServicio = (int) ($_GET['id'] ?? 0);
|
||||||
|
$idInsumo = (int) ($_POST['id_producto'] ?? 0);
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$_SESSION['servicio']['detalle_realizado'] = $_POST['detalle_realizado'] ?? '';
|
||||||
|
$_SESSION['servicio']['mano_obra'] = (float) $_POST['mano_obra'] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$manoObra = $_SESSION['servicio']['mano_obra'] ?? 0;
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
if ($idInsumo <= 0 || $manoObra < 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=actualizar_servicio&id=' . $idServicio);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$cajamodel->eliminarInsumoServicio($idInsumo)) {
|
||||||
|
$_SESSION['flash_error'] = $cajamodel->getError() ?? 'No se pudo eliminar el producto.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_success'] = 'Insumo eliminado correctamente.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=actualizar_servicio&id=' . $idServicio);
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// EDITAR CANTIDAD DE INSUMO DE SERVICIO
|
||||||
|
|
||||||
|
case 'editar_cantidad_insumo':
|
||||||
|
|
||||||
|
$idServicio = (int) ($_GET['id'] ?? 0);
|
||||||
|
$idInsumo = (int) ($_POST['id_producto'] ?? 0);
|
||||||
|
$nuevaCantidad = $_POST['cantidad'][$idInsumo] ?? null;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$_SESSION['servicio']['detalle_realizado'] = $_POST['detalle_realizado'] ?? '';
|
||||||
|
$_SESSION['servicio']['mano_obra'] = (float) $_POST['mano_obra'] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$manoObra = $_SESSION['servicio']['mano_obra'] ?? 0;
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
if ($idInsumo <= 0 || $nuevaCantidad <= 0 || $manoObra < 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=actualizar_servicio&id=' . $idServicio);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$cajamodel->editarCantidadInsumo($idInsumo, $nuevaCantidad)) {
|
||||||
|
$_SESSION['flash_error'] = $cajamodel->getError() ?? 'No se pudo actualizar la cantidad.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_success'] = 'Cantidad editada correctamente.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=actualizar_servicio&id=' . $idServicio);
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// DEFAULT (404)
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
header("Location: index.php?opt=404View.php");
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Helper encargado de detectar el módulo relacionado según el mensaje del usuario
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../helpers/chatbot_helper.php';
|
||||||
|
|
||||||
|
// Helper encargado de conectarse a la API de Groq
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../helpers/groq_helper.php';
|
||||||
|
|
||||||
|
class chatBotController {
|
||||||
|
|
||||||
|
public function responder() {
|
||||||
|
|
||||||
|
// Indicamos que la respuesta será JSON
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
// Validar método HTTP
|
||||||
|
// Solo se permiten peticiones POST
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false
|
||||||
|
]);
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtener mensaje enviado desde JS
|
||||||
|
// trim() elimina espacios innecesarios
|
||||||
|
|
||||||
|
$mensaje = trim($_POST['mensaje'] ?? '');
|
||||||
|
|
||||||
|
// Validar mensaje vacío
|
||||||
|
|
||||||
|
if (empty($mensaje)) {
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false
|
||||||
|
]);
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limitar longitud máxima del mensaje para evitar abuso o consumo excesivo de tokens
|
||||||
|
|
||||||
|
if (mb_strlen($mensaje) > 500) {
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'La consulta es demasiado larga.'
|
||||||
|
]);
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicializar historial del chat en sesión si aún no existe
|
||||||
|
|
||||||
|
if (!isset($_SESSION['chat_historial'])) {
|
||||||
|
$_SESSION['chat_historial'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detectar automáticamente el módulo relacionado con la consulta
|
||||||
|
|
||||||
|
$modulo = detectarModulo($mensaje);
|
||||||
|
|
||||||
|
// Cache estática para evitar leer archivos en cada consulta
|
||||||
|
|
||||||
|
static $docsCache = [];
|
||||||
|
|
||||||
|
// Cargar documentación general solamente una vez
|
||||||
|
|
||||||
|
if (!isset($docsCache['general'])) {
|
||||||
|
|
||||||
|
$docsCache['general'] = file_get_contents(
|
||||||
|
__DIR__ . '/../docs/general.txt'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cargar documentación específica del módulo detectado
|
||||||
|
|
||||||
|
if (!isset($docsCache[$modulo])) {
|
||||||
|
|
||||||
|
$rutaModulo = __DIR__ . "/../docs/{$modulo}.txt";
|
||||||
|
|
||||||
|
// Verificar si existe el archivo
|
||||||
|
|
||||||
|
if (file_exists($rutaModulo)) {
|
||||||
|
|
||||||
|
$docsCache[$modulo] = file_get_contents($rutaModulo);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Si no existe, guardar vacío
|
||||||
|
|
||||||
|
$docsCache[$modulo] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unir documentación general + documentación específica del módulo
|
||||||
|
|
||||||
|
$contexto = $docsCache['general'] . "\n\n" . $docsCache[$modulo];
|
||||||
|
|
||||||
|
// Prompt del sistema
|
||||||
|
// Define comportamiento y restricciones de la IA
|
||||||
|
|
||||||
|
$systemPrompt = "
|
||||||
|
Sos el asistente interno del sistema ByteCenter.
|
||||||
|
|
||||||
|
Tu función es ayudar a operadores y administradores
|
||||||
|
utilizando únicamente la documentación proporcionada.
|
||||||
|
|
||||||
|
REGLAS IMPORTANTES:
|
||||||
|
|
||||||
|
- Respondé únicamente usando información presente en la documentación.
|
||||||
|
|
||||||
|
- No inventes funcionalidades, botones, módulos,
|
||||||
|
pantallas, acciones, permisos ni procesos.
|
||||||
|
|
||||||
|
- No asumas comportamientos típicos de otros sistemas.
|
||||||
|
|
||||||
|
- Si el usuario consulta sobre una funcionalidad del sistema que no aparece documentada,
|
||||||
|
indicá brevemente que no encontraste información específica sobre esa funcionalidad.
|
||||||
|
|
||||||
|
- Nunca completes información faltante usando suposiciones.
|
||||||
|
|
||||||
|
- Cuando exista un procedimiento documentado,
|
||||||
|
explicalo paso a paso.
|
||||||
|
|
||||||
|
- Utilizá exactamente los nombres de botones,
|
||||||
|
módulos y acciones documentadas.
|
||||||
|
|
||||||
|
- La documentación puede contener símbolos como:
|
||||||
|
#, ##, ###, *, **
|
||||||
|
|
||||||
|
- Esos símbolos son únicamente separadores internos
|
||||||
|
de títulos y subtítulos.
|
||||||
|
|
||||||
|
- Nunca escribas esos símbolos en la respuesta.
|
||||||
|
|
||||||
|
- Respondé siempre en texto plano.
|
||||||
|
|
||||||
|
- No uses markdown.
|
||||||
|
|
||||||
|
- No uses encabezados markdown.
|
||||||
|
|
||||||
|
- No uses listas markdown.
|
||||||
|
|
||||||
|
- Para enumerar pasos, utilizá solamente:
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
- Mantené respuestas claras, naturales y breves.
|
||||||
|
|
||||||
|
EJEMPLOS DE RESPUESTAS INCORRECTAS:
|
||||||
|
- 'Exportar clientes'
|
||||||
|
- 'Venta rápida'
|
||||||
|
- 'Dashboard'
|
||||||
|
- 'Panel avanzado'
|
||||||
|
- 'Modo express'
|
||||||
|
- 'CSV'
|
||||||
|
- 'Métricas automáticas'
|
||||||
|
|
||||||
|
DOCUMENTACIÓN:
|
||||||
|
$contexto
|
||||||
|
";
|
||||||
|
|
||||||
|
// Recuperar últimos mensajes para mantener contexto conversacional
|
||||||
|
|
||||||
|
$historial = array_slice(
|
||||||
|
$_SESSION['chat_historial'],
|
||||||
|
-10
|
||||||
|
);
|
||||||
|
|
||||||
|
// Array final enviado a la IA
|
||||||
|
|
||||||
|
$messages = [];
|
||||||
|
|
||||||
|
// Primer mensaje: prompt del sistema
|
||||||
|
|
||||||
|
$messages[] = [
|
||||||
|
'role' => 'system',
|
||||||
|
'content' => $systemPrompt
|
||||||
|
];
|
||||||
|
|
||||||
|
// Agregar historial previo
|
||||||
|
|
||||||
|
foreach ($historial as $msg) {
|
||||||
|
$messages[] = $msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agregar mensaje actual del usuario
|
||||||
|
|
||||||
|
$messages[] = [
|
||||||
|
'role' => 'user',
|
||||||
|
'content' => $mensaje
|
||||||
|
];
|
||||||
|
|
||||||
|
// Consultar API
|
||||||
|
|
||||||
|
$resultado = consultarChatbot($messages);
|
||||||
|
|
||||||
|
// Validar errores
|
||||||
|
|
||||||
|
if (!$resultado['success']) {
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $resultado['error']
|
||||||
|
]);
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtener contenido de respuesta
|
||||||
|
|
||||||
|
$respuesta = $resultado['content'];
|
||||||
|
|
||||||
|
// Guardar mensaje usuario
|
||||||
|
|
||||||
|
$_SESSION['chat_historial'][] = [
|
||||||
|
'role' => 'user',
|
||||||
|
'content' => $mensaje
|
||||||
|
];
|
||||||
|
|
||||||
|
// Guardar respuesta IA
|
||||||
|
|
||||||
|
$_SESSION['chat_historial'][] = [
|
||||||
|
'role' => 'assistant',
|
||||||
|
'content' => $respuesta
|
||||||
|
];
|
||||||
|
|
||||||
|
// Limitar tamaño del historial para evitar exceso de tokens
|
||||||
|
|
||||||
|
$_SESSION['chat_historial'] = array_slice(
|
||||||
|
$_SESSION['chat_historial'],
|
||||||
|
-20
|
||||||
|
);
|
||||||
|
|
||||||
|
// Respuesta final al frontend (JSON)
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'respuesta' => $respuesta
|
||||||
|
]);
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,332 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Validacion de token
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { // Recorre todos los caracteres (siempre) y retorna true or false
|
||||||
|
$_SESSION['flash_error'] = 'Acción inválida.';
|
||||||
|
header('Location: index.php?opt=inicio_operador');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requiere modelo de cliente
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/clienteModel.php';
|
||||||
|
$clientemodel = new clienteModel;
|
||||||
|
|
||||||
|
// Validar sesión
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$opt = $_GET['opt'] ?? null;
|
||||||
|
|
||||||
|
// Manejo de opciones enviadas por URL
|
||||||
|
|
||||||
|
switch ($opt) {
|
||||||
|
|
||||||
|
// LISTAR CLIENTES
|
||||||
|
|
||||||
|
case 'clientes':
|
||||||
|
|
||||||
|
// Utilizamos el modelo para obtener la lista de clientes
|
||||||
|
|
||||||
|
$clientes = $clientemodel->listarClientes();
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/clientes/listaClientesView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// CREAR CLIENTE NUEVO
|
||||||
|
|
||||||
|
case 'crear_cliente':
|
||||||
|
|
||||||
|
// Se requiere la view del form
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
$_SESSION['return_opt'] = $_GET['from'] ?? 'clientes';
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/clientes/crearClienteView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Datos recibidos del POST del formulario
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$nombre = trim($_POST['nombre'] ?? '');
|
||||||
|
$apellido = trim($_POST['apellido'] ?? '');
|
||||||
|
$telefono = trim($_POST['telefono'] ?? '');
|
||||||
|
$email = trim($_POST['email'] ?? '');
|
||||||
|
$condIVA = trim($_POST['cond_iva'] ?? 'consumidor_final');
|
||||||
|
|
||||||
|
$cuit1 = trim($_POST['cuit_1'] ?? '');
|
||||||
|
$cuit2 = trim($_POST['cuit_2'] ?? '');
|
||||||
|
$cuit3 = trim($_POST['cuit_3'] ?? '');
|
||||||
|
|
||||||
|
$cuit = $cuit1 . $cuit2 . $cuit3;
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
if ($nombre === '')
|
||||||
|
$errores[] = "El nombre no puede estar vacío.";
|
||||||
|
else if (!preg_match("/^[A-ZÁÉÍÓÚÑ][a-záéíóúñ]+(?: [A-ZÁÉÍÓÚÑ][a-záéíóúñ]+)*$/", $nombre)) {
|
||||||
|
$errores[] = "El nombre debe comenzar con mayúscula y solo contener letras y espacios.";
|
||||||
|
} else if (strlen($nombre) < 2) {
|
||||||
|
$errores[] = "El nombre debe tener al menos 2 caracteres.";
|
||||||
|
} else if (strlen($nombre) > 50) {
|
||||||
|
$errores[] = "El nombre no puede superar los 50 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($apellido === '')
|
||||||
|
$errores[] = "El apellido no puede estar vacío.";
|
||||||
|
else if (!preg_match("/^[A-ZÁÉÍÓÚÑ][a-záéíóúñ]+(?: [A-ZÁÉÍÓÚÑ][a-záéíóúñ]+)*$/", $apellido)) {
|
||||||
|
$errores[] = "El apellido debe comenzar con mayúscula y solo contener letras y espacios.";
|
||||||
|
} else if (strlen($apellido) < 2) {
|
||||||
|
$errores[] = "El apellido debe tener al menos 2 caracteres.";
|
||||||
|
} else if (strlen($apellido) > 50) {
|
||||||
|
$errores[] = "El apellido no puede superar los 50 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($telefono === '') {
|
||||||
|
$errores[] = "El teléfono no puede estar vacío.";
|
||||||
|
} else if (!preg_match('/^[0-9]{10}$/', $telefono)) {
|
||||||
|
$errores[] = "El teléfono debe tener 10 dígitos.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($email === '') {
|
||||||
|
$errores[] = "El email no puede estar vacío.";
|
||||||
|
} else if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errores[] = "Formato de email inválido.";
|
||||||
|
} else if (strlen($email) > 100) {
|
||||||
|
$errores[] = "El email no puede superar los 100 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$condicionesPermitidas = [
|
||||||
|
'consumidor_final',
|
||||||
|
'monotributo',
|
||||||
|
'responsable_inscripto'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!in_array($condIVA, $condicionesPermitidas, true)) {
|
||||||
|
|
||||||
|
$errores[] = 'Condición de IVA inválida.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cuit !== '') {
|
||||||
|
|
||||||
|
if (!preg_match('/^\d{11}$/', $cuit)) {
|
||||||
|
|
||||||
|
$errores[] = 'El CUIT debe contener 11 dígitos.';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$cuit = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores); // Se guarda en $_SESSION el error
|
||||||
|
header("Location: index.php?opt=crear_cliente");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificación de existencia de cliente
|
||||||
|
|
||||||
|
if ($clientemodel->verificarExistencia([
|
||||||
|
'nombre' => $nombre,
|
||||||
|
'apellido' => $apellido,
|
||||||
|
'email' => $email,
|
||||||
|
'cuit' => $cuit
|
||||||
|
])) {
|
||||||
|
$_SESSION['flash_error'] = "El cliente ya existe o el email ya está registrado.";
|
||||||
|
|
||||||
|
header("Location: index.php?opt=crear_cliente");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se llama a la función para crear el cliente
|
||||||
|
|
||||||
|
if ($clientemodel->crearCliente([
|
||||||
|
'nombre' => $nombre,
|
||||||
|
'apellido' => $apellido,
|
||||||
|
'telefono' => $telefono,
|
||||||
|
'email' => $email,
|
||||||
|
'cuit' => $cuit,
|
||||||
|
'cond_iva' => $condIVA
|
||||||
|
])) {
|
||||||
|
$_SESSION['flash_success'] = 'Cliente creado correctamente.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = 'Ocurrió un error al crear el cliente.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$opt = $_SESSION['return_opt'];
|
||||||
|
|
||||||
|
header("Location: index.php?opt=$opt");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EDITAR DATOS DE CLIENTE
|
||||||
|
|
||||||
|
case 'editar_cliente':
|
||||||
|
|
||||||
|
$idEditar = (int) ($_GET['id'] ?? 0);
|
||||||
|
|
||||||
|
if ($idEditar <= 0) {
|
||||||
|
header("Location: index.php?opt=clientes");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Busca el cliente en la base de datos mediante el id
|
||||||
|
|
||||||
|
$cliente = $clientemodel->obtenerPorId($idEditar);
|
||||||
|
|
||||||
|
if (!$cliente) {
|
||||||
|
$_SESSION['flash_error'] = 'No se encontró el cliente.';
|
||||||
|
header("Location: index.php?opt=clientes");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mostrar datos de cliente
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/clientes/editarClienteView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// En caso de modificar datos del cliente
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$nombre = trim($_POST['nombre'] ?? '');
|
||||||
|
$apellido = trim($_POST['apellido'] ?? '');
|
||||||
|
$telefono = trim($_POST['telefono'] ?? '');
|
||||||
|
$email = trim($_POST['email'] ?? '');
|
||||||
|
$condIVA = trim($_POST['cond_iva'] ?? 'consumidor_final');
|
||||||
|
|
||||||
|
$cuit1 = trim($_POST['cuit_1'] ?? '');
|
||||||
|
$cuit2 = trim($_POST['cuit_2'] ?? '');
|
||||||
|
$cuit3 = trim($_POST['cuit_3'] ?? '');
|
||||||
|
|
||||||
|
$cuit = $cuit1 . $cuit2 . $cuit3;
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
if ($nombre === '') {
|
||||||
|
$errores[] = "El nombre no puede estar vacío.";
|
||||||
|
} elseif (!preg_match("/^[A-ZÁÉÍÓÚÑ][a-záéíóúñ]+(?: [A-ZÁÉÍÓÚÑ][a-záéíóúñ]+)*$/", $nombre)) {
|
||||||
|
$errores[] = "El nombre debe comenzar con mayúscula y solo contener letras y espacios.";
|
||||||
|
} elseif (strlen($nombre) < 2) {
|
||||||
|
$errores[] = "El nombre debe tener al menos 2 caracteres.";
|
||||||
|
} elseif (strlen($nombre) > 50) {
|
||||||
|
$errores[] = "El nombre no puede superar los 50 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($apellido === '') {
|
||||||
|
$errores[] = "El apellido no puede estar vacío.";
|
||||||
|
} elseif (!preg_match("/^[A-ZÁÉÍÓÚÑ][a-záéíóúñ]+(?: [A-ZÁÉÍÓÚÑ][a-záéíóúñ]+)*$/", $apellido)) {
|
||||||
|
$errores[] = "El apellido debe comenzar con mayúscula y solo contener letras y espacios.";
|
||||||
|
} elseif (strlen($apellido) < 2) {
|
||||||
|
$errores[] = "El apellido debe tener al menos 2 caracteres.";
|
||||||
|
} elseif (strlen($apellido) > 50) {
|
||||||
|
$errores[] = "El apellido no puede superar los 50 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($telefono === '') {
|
||||||
|
$errores[] = "El teléfono no puede estar vacío.";
|
||||||
|
} else if (!preg_match('/^[0-9]{10}$/', $telefono)) {
|
||||||
|
$errores[] = "El teléfono debe tener 10 dígitos.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($email === '') {
|
||||||
|
$errores[] = "El email no puede estar vacío.";
|
||||||
|
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errores[] = "Formato de email inválido.";
|
||||||
|
} elseif (strlen($email) > 100) {
|
||||||
|
$errores[] = "El email no puede superar los 100 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$condicionesPermitidas = [
|
||||||
|
'consumidor_final',
|
||||||
|
'monotributo',
|
||||||
|
'responsable_inscripto'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!in_array($condIVA, $condicionesPermitidas, true)) {
|
||||||
|
|
||||||
|
$errores[] = 'Condición de IVA inválida.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($condIVA !== 'consumidor_final') && ($cuit === '')) {
|
||||||
|
|
||||||
|
$errores[] = 'Debe ingresar el CUIT';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cuit !== '') {
|
||||||
|
|
||||||
|
if (!preg_match('/^\d{11}$/', $cuit)) {
|
||||||
|
|
||||||
|
$errores[] = 'El CUIT debe contener 11 dígitos.';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$cuit = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores); // Se guarda en $_SESSION el error
|
||||||
|
header("Location: index.php?opt=editar_cliente&id=$idEditar");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificación de existencia de cliente
|
||||||
|
|
||||||
|
if ($clientemodel->verificarExistencia([
|
||||||
|
'nombre' => $nombre,
|
||||||
|
'apellido' => $apellido,
|
||||||
|
'email' => $email,
|
||||||
|
'cuit' => $cuit
|
||||||
|
], $idEditar)) {
|
||||||
|
$_SESSION['flash_error'] = "El cliente ya existe o el email ya está registrado.";
|
||||||
|
|
||||||
|
header("Location: index.php?opt=editar_cliente&id=$idEditar");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$guardarData = [
|
||||||
|
'nombre' => $nombre,
|
||||||
|
'apellido' => $apellido,
|
||||||
|
'telefono' => $telefono,
|
||||||
|
'email' => $email,
|
||||||
|
'cuit' => $cuit,
|
||||||
|
'cond_iva' => $condIVA
|
||||||
|
];
|
||||||
|
|
||||||
|
// Actualizar los datos del cliente
|
||||||
|
|
||||||
|
if ($clientemodel->actualizarCliente($cliente['id'], $guardarData)) {
|
||||||
|
$_SESSION['flash_success'] = 'Datos actualizados correctamente'; // Se guarda en $_SESSION el succes
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo actualizar los datos.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=clientes");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEFAULT (404)
|
||||||
|
|
||||||
|
default:
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/404View.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
@@ -0,0 +1,495 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Validacion de token
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { // Recorre todos los caracteres (siempre) y retorna true or false
|
||||||
|
$_SESSION['flash_error'] = 'Acción inválida.';
|
||||||
|
header('Location: index.php?opt=inicio_operador');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requiero helper que contiene métodos para encriptar y desencriptar contraseñas
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../helpers/crypto.php';
|
||||||
|
|
||||||
|
// Requiero modelo de configuración
|
||||||
|
|
||||||
|
require __DIR__ . '/../models/configuracionModel.php';
|
||||||
|
$configuracionmodel = new configuracionModel();
|
||||||
|
|
||||||
|
// Validar sesión
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $_SESSION['user']; // Defino el array del usuario dentro de una variable
|
||||||
|
$rol = $user['rol']; // Defino el rol dentro de la variable
|
||||||
|
$esAdmin = ($rol === 'admin'); // Variable booleana, true = admin
|
||||||
|
|
||||||
|
// Verifico rol del usuarios (solo se permite admin)
|
||||||
|
|
||||||
|
if (!$esAdmin) {
|
||||||
|
header("Location: index.php?opt=inicio_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$opt = $_GET['opt'] ?? null;
|
||||||
|
|
||||||
|
// Manejo de opt
|
||||||
|
|
||||||
|
switch ($opt) {
|
||||||
|
|
||||||
|
// OBTENER CONFIGURACIÓN
|
||||||
|
|
||||||
|
case 'configuracion':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
$configuracion = $configuracionmodel->obtenerConfiguracion();
|
||||||
|
|
||||||
|
// Desencriptación de contraseña emisor
|
||||||
|
|
||||||
|
if (isset($configuracion['contrasenia_emisor'])) {
|
||||||
|
$configuracion['contrasenia_emisor_formateada'] = decryptData($configuracion['contrasenia_emisor']);
|
||||||
|
} else {
|
||||||
|
$configuracion['contrasenia_emisor_formateada'] = 'Sin Setear';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saneamiento de email emisor
|
||||||
|
|
||||||
|
if (isset($configuracion['email_emisor'])) {
|
||||||
|
$configuracion['email_emisor'] = $configuracion['email_emisor'];
|
||||||
|
} else {
|
||||||
|
$configuracion['email_emisor'] = 'Sin Setear';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
if (empty($configuracion)) {
|
||||||
|
$_SESSION['flash_error'] = 'Ocurrió un error al cargar la configuración.';
|
||||||
|
header("Location: index.php?opt=configuracion");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saneamiento de fecha
|
||||||
|
|
||||||
|
if (!empty($configuracion['inicio_act'])) {
|
||||||
|
$dt = new DateTime($configuracion['inicio_act']);
|
||||||
|
$configuracion['inicio_act_formateada'] = $dt->format('d/m/Y');
|
||||||
|
} else {
|
||||||
|
$configuracion['inicio_act_formateada'] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require_once __DIR__ . '/../views/configuracion/configuracionView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACTUALIZAR CONFIGURACIÓN
|
||||||
|
|
||||||
|
case 'actualizar_config':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$configuracion = [
|
||||||
|
|
||||||
|
'nombre' => trim($_POST['nombre'] ?? ''),
|
||||||
|
'direccion' => trim($_POST['direccion'] ?? ''),
|
||||||
|
'ciudad' => trim($_POST['ciudad'] ?? ''),
|
||||||
|
'codigo_postal' => trim($_POST['codigo_postal'] ?? ''),
|
||||||
|
'provincia' => trim($_POST['provincia'] ?? ''),
|
||||||
|
'telefono' => trim($_POST['telefono'] ?? ''),
|
||||||
|
'email' => trim($_POST['email'] ?? ''),
|
||||||
|
'cond_iva' => trim($_POST['cond_iva'] ?? 'responsable_inscripto'),
|
||||||
|
'cuit' => ($_POST['cuit_1'] ?? '') . ($_POST['cuit_2'] ?? '') . ($_POST['cuit_3'] ?? ''),
|
||||||
|
'iva' => $_POST['iva'] ?? 21,
|
||||||
|
'hora_apertura_maniana' => trim($_POST['hora_apertura_maniana'] ?? ''),
|
||||||
|
'hora_cierre_maniana' => trim($_POST['hora_cierre_maniana'] ?? ''),
|
||||||
|
'hora_apertura_tarde' => trim($_POST['hora_apertura_tarde'] ?? ''),
|
||||||
|
'hora_cierre_tarde' => trim($_POST['hora_cierre_tarde'] ?? ''),
|
||||||
|
'mail_automatico' => isset($_POST['mail_automatico']) ? 1 : 0,
|
||||||
|
'cargo_cancelacion_servicio' => $_POST['cargo_cancelacion_servicio'] ?? 0,
|
||||||
|
'email_emisor' => trim($_POST['email_emisor'] ?? ''),
|
||||||
|
'contrasenia_emisor' => trim($_POST['contrasenia_emisor'] ?? ''),
|
||||||
|
];
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
// NOMBRE
|
||||||
|
|
||||||
|
if ($configuracion['nombre'] === '') {
|
||||||
|
$errores[] = "El nombre no puede estar vacío.";
|
||||||
|
} else if (!preg_match('/^[A-Za-zÁÉÍÓÚáéíóú0-9\s\.\-]{2,100}$/', $configuracion['nombre'])) {
|
||||||
|
$errores[] = "El nombre contiene caracteres inválidos.";
|
||||||
|
} else if (strlen($configuracion['nombre']) < 2) {
|
||||||
|
$errores[] = "El nombre debe tener al menos 2 caracteres.";
|
||||||
|
} else if (strlen($configuracion['nombre']) > 50) {
|
||||||
|
$errores[] = "El nombre no puede superar los 50 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// DIRECCIÓN
|
||||||
|
|
||||||
|
if ($configuracion['direccion'] === '') {
|
||||||
|
$errores[] = "La dirección no puede estar vacía.";
|
||||||
|
} else if (strlen($configuracion['direccion']) < 3) {
|
||||||
|
$errores[] = "La dirección debe tener al menos 3 caracteres.";
|
||||||
|
} else if (strlen($configuracion['direccion']) > 100) {
|
||||||
|
$errores[] = "La dirección no puede superar los 100 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// CIUDAD
|
||||||
|
|
||||||
|
if ($configuracion['ciudad'] === '') {
|
||||||
|
$errores[] = "La ciudad no puede estar vacía.";
|
||||||
|
} else if (!preg_match('/^[A-Za-zÁÉÍÓÚáéíóúñÑ\s]{2,100}$/', $configuracion['ciudad'])) {
|
||||||
|
$errores[] = "La ciudad solo puede contener letras y espacios.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// CODIGO POSTAL
|
||||||
|
|
||||||
|
if ($configuracion['codigo_postal'] === '') {
|
||||||
|
$errores[] = "El código postal no puede estar vacío.";
|
||||||
|
} else if (!preg_match('/^[0-9]{4}$/', $configuracion['codigo_postal'])) {
|
||||||
|
$errores[] = "El código postal debe tener 4 dígitos.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROVINCIA
|
||||||
|
|
||||||
|
if ($configuracion['provincia'] === '') {
|
||||||
|
$errores[] = "La provincia no puede estar vacía.";
|
||||||
|
} else if (!preg_match('/^[A-Za-zÁÉÍÓÚáéíóúñÑ\s]{2,100}$/', $configuracion['provincia'])) {
|
||||||
|
$errores[] = "La provincia contiene caracteres inválidos.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// TELÉFONO
|
||||||
|
|
||||||
|
if ($configuracion['telefono'] === '') {
|
||||||
|
$errores[] = "El teléfono no puede estar vacío.";
|
||||||
|
} else if (!preg_match('/^[0-9]{10}$/', $configuracion['telefono'])) {
|
||||||
|
$errores[] = "El teléfono debe tener 10 dígitos.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMAIL
|
||||||
|
|
||||||
|
if ($configuracion['email'] === '') {
|
||||||
|
$errores[] = "El email no puede estar vacío.";
|
||||||
|
} else if (!filter_var($configuracion['email'], FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errores[] = "Formato de email inválido.";
|
||||||
|
} else if (strlen($configuracion['email']) > 100) {
|
||||||
|
$errores[] = "El email no puede superar los 100 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONDICIÓN_IVA
|
||||||
|
|
||||||
|
$condicionesValidas = [
|
||||||
|
'consumidor_final',
|
||||||
|
'responsable_inscripto',
|
||||||
|
'monotributo'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!in_array($configuracion['cond_iva'], $condicionesValidas)) {
|
||||||
|
$errores[] = "Condición de IVA inválida.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// CUIT
|
||||||
|
|
||||||
|
if (!preg_match('/^\d{10,11}$/', $configuracion['cuit'])) {
|
||||||
|
$errores[] = 'CUIT inválido. Debe contener solo números.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// IVA
|
||||||
|
|
||||||
|
if (!is_numeric($configuracion['iva'])) {
|
||||||
|
$errores[] = "El IVA debe ser numérico.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// HORA DE APERTURA Y CIERRE
|
||||||
|
|
||||||
|
if ($configuracion['hora_apertura_maniana'] && $configuracion['hora_cierre_maniana']) {
|
||||||
|
|
||||||
|
if ($configuracion['hora_apertura_maniana'] >= $configuracion['hora_cierre_maniana']) {
|
||||||
|
$errores[] = "El horario de apertura debe ser menor al de cierre.";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($configuracion['hora_apertura_tarde'] && $configuracion['hora_cierre_tarde']) {
|
||||||
|
|
||||||
|
if ($configuracion['hora_apertura_tarde'] >= $configuracion['hora_cierre_tarde']) {
|
||||||
|
$errores[] = "El horario de apertura debe ser menor al de cierre.";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// CARGO POR CANCELACIÓN DE SERVICIO
|
||||||
|
|
||||||
|
if (!is_numeric($configuracion['cargo_cancelacion_servicio'])) {
|
||||||
|
$errores[] = "El cargo por cancelación debe ser numérico.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMAIL EMISOR
|
||||||
|
|
||||||
|
if ($configuracion['email_emisor'] === '') {
|
||||||
|
$errores[] = "El email no puede estar vacío.";
|
||||||
|
} else if (!filter_var($configuracion['email_emisor'], FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errores[] = "Formato de email inválido.";
|
||||||
|
} else if (strlen($configuracion['email_emisor']) > 100) {
|
||||||
|
$errores[] = "El email no puede superar los 100 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONTRASEÑA EMISOR
|
||||||
|
|
||||||
|
if (strlen($configuracion['contrasenia_emisor']) > 100) {
|
||||||
|
$errores[] = "Contraseña demasiado larga.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores); // Se guarda en $_SESSION el error
|
||||||
|
header("Location: index.php?opt=configuracion");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENCRIPTACION DE CONTRASEÑA
|
||||||
|
|
||||||
|
if ($configuracion['contrasenia_emisor'] !== '') {
|
||||||
|
$passLimpia = str_replace(' ', '', $configuracion['contrasenia_emisor']);
|
||||||
|
$configuracion['contrasenia_emisor'] = encryptData($passLimpia);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualizamos datos de configuración
|
||||||
|
|
||||||
|
if ($configuracionmodel->actualizarConfiguracion($configuracion)) {
|
||||||
|
$_SESSION['flash_success'] = 'Configuración actualizada correctamente.';
|
||||||
|
header("Location: index.php?opt=configuracion");
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = 'Error al actualizar la configuración.';
|
||||||
|
header("Location: index.php?opt=configuracion");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GENERAR BACKUP
|
||||||
|
|
||||||
|
case 'generar_backup':
|
||||||
|
|
||||||
|
// Obtenemos desde donde viene la petición (puede ser backup automático)
|
||||||
|
|
||||||
|
$return = $_GET['from'] ?? 'backups';
|
||||||
|
|
||||||
|
$backupDir = $_ENV['BACKUP_PATH'] ?? (__DIR__ . '/../backups/');
|
||||||
|
|
||||||
|
$fecha = date('Y-m-d_H-i-s');
|
||||||
|
$archivo = "backup_$fecha.sql"; // Generación de nombre de archivo con extensión
|
||||||
|
$rutaBackup = $backupDir . $archivo; // Generación de ruta completa de archivo backup
|
||||||
|
|
||||||
|
// Datos de conexión
|
||||||
|
|
||||||
|
$rutaBackup = escapeshellarg($rutaBackup); // Permite omitir espacios o caracteres no deseados (puede romper ruta o comando)
|
||||||
|
$host = escapeshellarg($_ENV['DB_HOST']);
|
||||||
|
$usuario = escapeshellarg($_ENV['DB_USER']);
|
||||||
|
$database = escapeshellarg($_ENV['DB_NAME']);
|
||||||
|
$password = escapeshellarg($_ENV['DB_PASSWORD']);
|
||||||
|
|
||||||
|
// Comando pg_dump
|
||||||
|
|
||||||
|
$comando = "PGPASSWORD=$password pg_dump --clean --if-exists --no-owner -h $host -U $usuario -d $database > $rutaBackup"; // Guardamos el comando a ejecutar mediante pg_dump
|
||||||
|
|
||||||
|
exec($comando, $output, $resultado); // Lo ejecutamos
|
||||||
|
|
||||||
|
if ($resultado === 0) { // Entra al if solo si el (exec) dio como resultado 0 (éxito) en la variable $resultado
|
||||||
|
|
||||||
|
// Limitar cantidad de backups
|
||||||
|
|
||||||
|
$maxBackups = 4;
|
||||||
|
|
||||||
|
$archivos = glob($backupDir . '*.sql'); // Permite obtener todos los archivos ubicados en esa ruta
|
||||||
|
|
||||||
|
if (count($archivos) > $maxBackups) { // Contamos la cantidad de archivos (comparando con la cantidad máxima definida)
|
||||||
|
|
||||||
|
usort($archivos, function ($a, $b) {
|
||||||
|
return filemtime($a) - filemtime($b);
|
||||||
|
}); // Se ordenan en base a la fecha de modificación de mas antiguo a mas nuevo
|
||||||
|
|
||||||
|
while (count($archivos) > $maxBackups) {
|
||||||
|
$archivoEliminar = array_shift($archivos); // Se elimina el archivo del array
|
||||||
|
unlink($archivoEliminar); // Se elimina fisicamente el archivo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_GET['auto'])) {
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Copia de seguridad automática generada con éxito.";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Backup generado correctamente.";
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = "Error al generar el backup.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=$return");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// LISTAR BACKUPS
|
||||||
|
|
||||||
|
case 'backups':
|
||||||
|
|
||||||
|
$backupDir = $_ENV['BACKUP_PATH'] ?? (__DIR__ . '/../backups/');
|
||||||
|
|
||||||
|
$archivos = glob($backupDir . '*.sql');
|
||||||
|
|
||||||
|
$backups = [];
|
||||||
|
$totalSize = 0;
|
||||||
|
|
||||||
|
$ultimoBackup = null;
|
||||||
|
|
||||||
|
if ($archivos) {
|
||||||
|
|
||||||
|
usort($archivos, function ($a, $b) {
|
||||||
|
return filemtime($b) <=> filemtime($a);
|
||||||
|
});
|
||||||
|
|
||||||
|
$ultimoBackup = date('d/m/Y H:i', filemtime($archivos[0])); // Guardamos el archivo mas reciente para informar en la vista
|
||||||
|
|
||||||
|
foreach ($archivos as $archivo) {
|
||||||
|
|
||||||
|
$size = filesize($archivo); // Obtenemos el tamaño total del archivo
|
||||||
|
|
||||||
|
$backups[] = [
|
||||||
|
'nombre' => basename($archivo), // Obtenemos nombre de archivo
|
||||||
|
'fecha' => date('d/m/Y H:i', filemtime($archivo)), // Obtenemos la fecha del archivo
|
||||||
|
'size' => round($size / 1024, 2) . ' KB' // Obtenemos el tamaño del archivo en KB
|
||||||
|
];
|
||||||
|
|
||||||
|
$totalSize += $size; // Acumulamos el tamaño de los archivos para obtener el total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalSize = round($totalSize / 1024 / 1024, 2); // Total utilizado en MB
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/configuracion/buckupsView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// DESCARGAR BACKUP
|
||||||
|
|
||||||
|
case 'descargar_backup':
|
||||||
|
|
||||||
|
$file = basename($_GET['file']);
|
||||||
|
|
||||||
|
$backupDir = $_ENV['BACKUP_PATH'] ?? (__DIR__ . '/../backups/');
|
||||||
|
$ruta = $backupDir . $file;
|
||||||
|
|
||||||
|
if (!file_exists($ruta)) {
|
||||||
|
header("Location: index.php?opt=backups");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-Type: application/sql'); // Indica el tipo de contenido del archivo (SQL en este caso)
|
||||||
|
header('Content-Disposition: attachment; filename="' . $file . '"'); // Fuerza la descarga del archivo en lugar de mostrarlo en el navegador y define el nombre con el que se va a guardar
|
||||||
|
header('Content-Length: ' . filesize($ruta)); // Informa el tamaño del archivo (mejora la descarga/progreso)
|
||||||
|
|
||||||
|
readfile($ruta); // Envía el contenido del archivo al navegador
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// ELIMINAR BACKUP
|
||||||
|
|
||||||
|
case 'eliminar_backup':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$backupDir = $_ENV['BACKUP_PATH'] ?? (__DIR__ . '/../backups/');
|
||||||
|
|
||||||
|
$archivo = basename($_POST['file']); // Evita rutas maliciosas
|
||||||
|
|
||||||
|
$rutaArchivo = $backupDir . $archivo;
|
||||||
|
|
||||||
|
if (file_exists($rutaArchivo)) {
|
||||||
|
|
||||||
|
unlink($rutaArchivo);
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Backup eliminado correctamente.";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = "El archivo no existe.";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=backups");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESTAURAR BACKUP
|
||||||
|
|
||||||
|
case 'restaurar_backup':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$backupDir = $_ENV['BACKUP_PATH'] ?? (__DIR__ . '/../backups/');
|
||||||
|
|
||||||
|
$archivo = basename($_POST['file']);
|
||||||
|
$rutaBackup = $backupDir . $archivo;
|
||||||
|
|
||||||
|
if (!file_exists($rutaBackup)) {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = "El archivo de backup no existe.";
|
||||||
|
header("Location: index.php?opt=backups");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = escapeshellarg($_ENV['DB_HOST']);
|
||||||
|
$usuario = escapeshellarg($_ENV['DB_USER']);
|
||||||
|
$database = escapeshellarg($_ENV['DB_NAME']);
|
||||||
|
$password = escapeshellarg($_ENV['DB_PASSWORD']);
|
||||||
|
|
||||||
|
// 1. BACKUP AUTOMÁTICO PREVIO
|
||||||
|
|
||||||
|
$backupSeguridad = $backupDir . 'auto_pre_restore_' . date('Y-m-d_H-i-s') . '.sql';
|
||||||
|
$backupSeguridadEscaped = escapeshellarg($backupSeguridad);
|
||||||
|
|
||||||
|
$comandoBackup = "PGPASSWORD=$password pg_dump --clean --if-exists --no-owner -h $host -U $usuario -d $database > $backupSeguridadEscaped";
|
||||||
|
|
||||||
|
exec($comandoBackup, $outBackup, $resultadoBackup);
|
||||||
|
|
||||||
|
if ($resultadoBackup !== 0) {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = "No se pudo crear el backup de seguridad antes de restaurar.";
|
||||||
|
header("Location: index.php?opt=backups");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. RESTAURAR COPIA DE SEGURIDAD SELECCIONADA
|
||||||
|
|
||||||
|
$rutaBackupEscaped = escapeshellarg($rutaBackup);
|
||||||
|
|
||||||
|
$comandoRestore = "PGPASSWORD=$password psql -h $host -U $usuario -d $database < $rutaBackupEscaped";
|
||||||
|
|
||||||
|
exec($comandoRestore, $output, $resultadoRestore);
|
||||||
|
|
||||||
|
if ($resultadoRestore === 0) {
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Backup restaurado correctamente. Se creó una copia de seguridad previa.";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = "Error al restaurar el backup.";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=backups");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Validar sesión
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$opt = $_GET['opt'] ?? null;
|
||||||
|
|
||||||
|
$user = $_SESSION['user']; // Defino el array del usuario dentro de una variable
|
||||||
|
$rol = $user['rol']; // Defino el rol dentro de la variable
|
||||||
|
$esAdmin = ($rol === 'admin'); // Variable booleana, true = admin
|
||||||
|
|
||||||
|
// Verifico rol del usuarios (solo se permite admin)
|
||||||
|
|
||||||
|
if (!$esAdmin) {
|
||||||
|
header("Location: index.php?opt=home_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manejo de opt
|
||||||
|
|
||||||
|
switch ($opt) {
|
||||||
|
|
||||||
|
// CARGA DE GRÁFICO DE INFORME
|
||||||
|
|
||||||
|
case 'informe':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require_once __DIR__ . '/../views/informes/informesView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GENERAR BALANCE ANUAL
|
||||||
|
|
||||||
|
case 'generar_balance_anual':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||||
|
header("Location:index.php?opt=home_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$anio = isset($_GET['anio']) ? (int) $_GET['anio'] : date('Y');
|
||||||
|
|
||||||
|
// REQUIERO MODELO DE INFORME
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/informeModel.php';
|
||||||
|
$informemodel = new informeModel();
|
||||||
|
|
||||||
|
$datos = $informemodel->obtenerBalanceAnual($anio);
|
||||||
|
|
||||||
|
header('Content-Type: application/json'); // Indica al navegador que se mandará JSON
|
||||||
|
echo json_encode($datos); // Convierte el array de PHP en JSON
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// GENERAR CUENTAS A COBRAR (SERVICIOS)
|
||||||
|
|
||||||
|
case 'generar_cuentas_por_cobrar':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||||
|
header("Location:index.php?opt=home_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$anio = isset($_GET['anio']) ? (int) $_GET['anio'] : date('Y');
|
||||||
|
|
||||||
|
// REQUIERO INFORME DE MODELO
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/informeModel.php';
|
||||||
|
$informemodel = new informeModel();
|
||||||
|
|
||||||
|
$datos = $informemodel->obtenerACobrar($anio);
|
||||||
|
|
||||||
|
header('Content-Type: application/json'); // Indica al navegador que se mandará JSON
|
||||||
|
echo json_encode($datos); // Convierte el array de PHP en JSON
|
||||||
|
exit;
|
||||||
|
}
|
||||||
@@ -0,0 +1,646 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Requiere el modelo de logIn
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/logInModel.php';
|
||||||
|
$loginmodel = new logInModel();
|
||||||
|
|
||||||
|
$opt = $_GET['opt'] ?? 'login_operador';
|
||||||
|
|
||||||
|
// Manejo de opt enviado por URL
|
||||||
|
|
||||||
|
switch ($opt) {
|
||||||
|
|
||||||
|
// LOGIN
|
||||||
|
|
||||||
|
case 'login':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
if (isset($_SESSION['reset_autorizado'])) {
|
||||||
|
unset($_SESSION['reset_autorizado']);
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/login/logInView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
$usuario = trim($_POST['usuario'] ?? ''); // Se almacenan los datos enviados por $_POST desde el form en las variables
|
||||||
|
$password = trim($_POST['password'] ?? '');
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Validaciones para iniciar sesión
|
||||||
|
|
||||||
|
if ($usuario === '') {
|
||||||
|
$errores[] = "El usuario no puede estar vacío.";
|
||||||
|
} elseif (!preg_match('/^(?=.*[a-zA-Z])(?!.*[._]{2})[a-zA-Z0-9._]{5,30}$/', $usuario)) {
|
||||||
|
$errores[] = "El usuario debe tener 5–30 caracteres, incluir al menos una letra y solo usar letras, números, puntos o guiónes bajos no consecutivos.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($password === '') {
|
||||||
|
$errores[] = "La contraseña no puede estar vacía.";
|
||||||
|
} elseif (strpos($password, ' ') !== false) {
|
||||||
|
$errores[] = "La contraseña no puede contener espacios.";
|
||||||
|
} elseif (!preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{6,}$/', $password)) {
|
||||||
|
$errores[] = "La contraseña debe tener al menos una mayúscula, una minúscula y un número, y mínimo 6 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control de intentos de inicio de sesión
|
||||||
|
|
||||||
|
$maxIntentos = 3;
|
||||||
|
$tiempoBloqueo = 60; // segundos
|
||||||
|
|
||||||
|
if (!isset($_SESSION['login_intentos'])) {
|
||||||
|
$_SESSION['login_intentos'] = 0;
|
||||||
|
$_SESSION['login_ultimo_intento'] = time();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si pasó el tiempo, resetear
|
||||||
|
|
||||||
|
if (time() - $_SESSION['login_ultimo_intento'] > $tiempoBloqueo) {
|
||||||
|
$_SESSION['login_intentos'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar bloqueo
|
||||||
|
|
||||||
|
if ($_SESSION['login_intentos'] >= $maxIntentos) {
|
||||||
|
$_SESSION['flash_error'] = "Demasiados intentos. Espere 1 minuto antes de volver a intentar.";
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si existen errores de validación, se informan definiendo flash_error en el array asociativo $_SESSION
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
|
||||||
|
// Se suma el intento erroneo de inicio de sesión y se guarda el tiempo
|
||||||
|
|
||||||
|
$_SESSION['login_intentos']++;
|
||||||
|
$_SESSION['login_ultimo_intento'] = time();
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores); // Agrega un salto de linea entre cada error encontrado y los guarda en $_SESSION
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si no existen errores, buscamos el usuario mediante logInModel
|
||||||
|
|
||||||
|
$user = $loginmodel->buscarPorUsuario($usuario);
|
||||||
|
|
||||||
|
if (!$user || !password_verify($password, $user['password'])) {
|
||||||
|
|
||||||
|
$_SESSION['login_intentos']++;
|
||||||
|
$_SESSION['login_ultimo_intento'] = time();
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'Usuario o contraseña incorrectos';
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificamos el rol del usuario para cargar la vista correspondiente
|
||||||
|
|
||||||
|
if ($user['rol'] === 'operador') {
|
||||||
|
|
||||||
|
// Se resetean los intentos
|
||||||
|
|
||||||
|
$_SESSION['login_intentos'] = 0;
|
||||||
|
$_SESSION['login_ultimo_intento'] = time();
|
||||||
|
|
||||||
|
$_SESSION['user'] = $user; // Se define $user dentro del array asociativo $_SESSION para tenerlo disponible en todo el sistema
|
||||||
|
header("Location: index.php?opt=inicio_operador");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
} else if ($user['rol'] === 'admin') {
|
||||||
|
|
||||||
|
// Se resetean los intentos
|
||||||
|
|
||||||
|
$_SESSION['login_intentos'] = 0;
|
||||||
|
$_SESSION['login_ultimo_intento'] = time();
|
||||||
|
|
||||||
|
$_SESSION['user'] = $user; // Se define $user dentro del array asociativo $_SESSION para tenerlo disponible en todo el sistema
|
||||||
|
header("Location: index.php?opt=inicio_admin");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HOME OPERADOR
|
||||||
|
|
||||||
|
case 'home_operador':
|
||||||
|
|
||||||
|
// Validar sesión
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si el rol es admin, se redirigue al home_admin (en caso de ingresar por URL)
|
||||||
|
|
||||||
|
if ($_SESSION['user']['rol'] !== 'operador') {
|
||||||
|
header("Location: index.php?opt=home_admin");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se carga en caché el home operador y se envía el HTML
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/login/homeOperadorView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// HOME ADMIN
|
||||||
|
|
||||||
|
case 'home_admin':
|
||||||
|
|
||||||
|
// Validar sesión
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_admin");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si el rol es operador, se redirigue al home_operador (en caso de ingresar por URL)
|
||||||
|
|
||||||
|
if ($_SESSION['user']['rol'] !== 'admin') {
|
||||||
|
header("Location: index.php?opt=home_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se carga en caché el home admin y se envía el HTML
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/login/homeAdminView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// INICIO OPERADOR
|
||||||
|
|
||||||
|
case 'inicio_operador':
|
||||||
|
|
||||||
|
// Validar sesión
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si el rol es admin, se redirigue al inicio_admin (en caso de ingresar por URL)
|
||||||
|
|
||||||
|
if ($_SESSION['user']['rol'] !== 'operador') {
|
||||||
|
header("Location: index.php?opt=inicio_admin");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se carga en caché el inicio operador y se envía el HTML
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/login/inicioOperadorView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// INICIO ADMIN
|
||||||
|
|
||||||
|
case 'inicio_admin':
|
||||||
|
|
||||||
|
// Validar sesión
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_admin");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si el rol es operador, se redirigue al inicio_operador (en caso de ingresar por URL)
|
||||||
|
|
||||||
|
if ($_SESSION['user']['rol'] !== 'admin') {
|
||||||
|
header("Location: index.php?opt=inicio_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcular si mostrar alerta de backup (solo a admin)
|
||||||
|
|
||||||
|
if ($_SESSION['user']['rol'] === 'admin') {
|
||||||
|
|
||||||
|
require __DIR__ . '/../models/configuracionModel.php';
|
||||||
|
$configuracionmodel = new configuracionModel();
|
||||||
|
|
||||||
|
$fechaUltimoBackup = $configuracionmodel->obtenerFechaUltimoBackup();
|
||||||
|
|
||||||
|
if ($fechaUltimoBackup) {
|
||||||
|
|
||||||
|
$dias = (new DateTime($fechaUltimoBackup))->diff(new DateTime())->days;
|
||||||
|
|
||||||
|
if ($dias >= 7) {
|
||||||
|
|
||||||
|
header("Location: index.php?opt=generar_backup&from=inicio_admin&auto=1");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$ahora = time();
|
||||||
|
|
||||||
|
// Si nunca se mostró o ya pasó 1 hora
|
||||||
|
|
||||||
|
if (!isset($_SESSION['ultimo_aviso_backup']) || ($ahora - $_SESSION['ultimo_aviso_backup']) >= 3600) {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = "No se encontraron copias de seguridad. Por favor, verifique.";
|
||||||
|
|
||||||
|
// Guardamos cuándo mostramos el mensaje
|
||||||
|
|
||||||
|
$_SESSION['ultimo_aviso_backup'] = $ahora;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se carga en caché el inicio admin y se envía el HTML
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/login/inicioAdminView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// LOGOUT
|
||||||
|
|
||||||
|
case 'logout':
|
||||||
|
|
||||||
|
session_unset(); // Libera las variables almacenadas en $_SESSION
|
||||||
|
session_destroy(); // Destruye la sesión del servidor
|
||||||
|
setcookie(session_name(), '', time() - 3600, '/'); // Borra la cookie en el navegaodor
|
||||||
|
|
||||||
|
header("Location: index.php?opt=login");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// CARGAR VISTA README (AYUDA)
|
||||||
|
|
||||||
|
case 'readme':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/ayudas/readMe.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// RECUPERACIÓN DE CUENTA
|
||||||
|
|
||||||
|
case 'recuperar_cuenta':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/login/recuperarCuentaView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { // Recorre todos los caracteres (siempre) y retorna true or false
|
||||||
|
$_SESSION['flash_error'] = 'Acción inválida.';
|
||||||
|
header('Location: index.php?opt=login');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requiero modelos necesarios
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/usuarioModel.php';
|
||||||
|
$usuariomodel = new usuarioModel();
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/logInModel.php';
|
||||||
|
$loginmodel = new logInModel();
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../helpers/email_recuperar_cuenta.php';
|
||||||
|
|
||||||
|
$email = trim($_POST['email'] ?? $_SESSION['reset_email'] ?? '');
|
||||||
|
|
||||||
|
// Cooltime de envío de mails
|
||||||
|
|
||||||
|
if (isset($_SESSION['ultimo_envio_codigo'])) {
|
||||||
|
|
||||||
|
$segundosRestantes = 60 - (time() - $_SESSION['ultimo_envio_codigo']);
|
||||||
|
|
||||||
|
if ($segundosRestantes > 0) {
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Ya se envió un código recientemente. Espere {$segundosRestantes} segundos.";
|
||||||
|
|
||||||
|
header('Location: index.php?opt=verificar_codigo');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validación
|
||||||
|
|
||||||
|
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$_SESSION['flash_error'] = 'Email inválido';
|
||||||
|
header('Location: index.php?opt=recuperar_cuenta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buscar usuario
|
||||||
|
|
||||||
|
$usuario = $usuariomodel->obtenerPorEmail($email);
|
||||||
|
|
||||||
|
// Siempre respuesta genérica (seguridad)
|
||||||
|
|
||||||
|
if (!$usuario || $usuario['rol'] !== 'admin') {
|
||||||
|
$_SESSION['flash_error'] = 'Si el correo ingresado es válido, recibirás un código de verificación.';
|
||||||
|
header('Location: index.php?opt=recuperar_cuenta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generar código
|
||||||
|
|
||||||
|
$codigo = random_int(100000, 999999);
|
||||||
|
$codigoHash = password_hash($codigo, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
|
$expira = date('Y-m-d H:i:s', strtotime('+10 minutes'));
|
||||||
|
|
||||||
|
// Invalidar códigos anteriores
|
||||||
|
|
||||||
|
if (!$loginmodel->invalidarPorUsuario($usuario['id'])) {
|
||||||
|
$_SESSION['flash_error'] = 'Ocurrió un error inesperado.';
|
||||||
|
header('Location: index.php?opt=recuperar_cuenta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardar nuevo código
|
||||||
|
|
||||||
|
if (!$loginmodel->crear([
|
||||||
|
'id_usuario' => $usuario['id'],
|
||||||
|
'codigo' => $codigoHash,
|
||||||
|
'expira_a' => $expira
|
||||||
|
])) {
|
||||||
|
$_SESSION['flash_error'] = 'Ocurrió un error inesperado.';
|
||||||
|
header('Location: index.php?opt=recuperar_cuenta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardar email en sesión (UX)
|
||||||
|
|
||||||
|
$_SESSION['reset_email'] = $email;
|
||||||
|
|
||||||
|
// Enviar mail
|
||||||
|
|
||||||
|
if (!enviarCodigoRecuperacion($email, $codigo)) {
|
||||||
|
$_SESSION['flash_error'] = 'Ocurrió un error inesperado al enviar el email.';
|
||||||
|
header('Location: index.php?opt=recuperar_cuenta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['ultimo_envio_codigo'] = time();
|
||||||
|
|
||||||
|
// Redirigir a verificación
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = 'Si el correo ingresado es válido, recibirás un código de verificación.';
|
||||||
|
header('Location: index.php?opt=verificar_codigo');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'verificar_codigo':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
if (empty($_SESSION['reset_email'])) {
|
||||||
|
header('Location: index.php?opt=recuperar_cuenta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/login/verificarCodigoView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||||
|
$_SESSION['flash_error'] = 'Acción inválida.';
|
||||||
|
header('Location: index.php?opt=login');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicializamos contador
|
||||||
|
|
||||||
|
if (!isset($_SESSION['codigo_intentos'])) {
|
||||||
|
$_SESSION['codigo_intentos'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALIDAMOS BLOQUEO ACTIVO
|
||||||
|
|
||||||
|
// SI EL BLOQUEO YA EXPIRÓ, RESETEAMOS
|
||||||
|
|
||||||
|
if (isset($_SESSION['codigo_bloqueado_hasta']) && time() >= $_SESSION['codigo_bloqueado_hasta']) {
|
||||||
|
|
||||||
|
unset($_SESSION['codigo_bloqueado_hasta']);
|
||||||
|
$_SESSION['codigo_intentos'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SI SIGUE BLOQUEADO
|
||||||
|
|
||||||
|
if (isset($_SESSION['codigo_bloqueado_hasta']) && time() < $_SESSION['codigo_bloqueado_hasta']) {
|
||||||
|
|
||||||
|
$segundos = $_SESSION['codigo_bloqueado_hasta'] - time();
|
||||||
|
|
||||||
|
$minutos = ceil($segundos / 60);
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = "Demasiados intentos fallidos. Espere {$minutos} minuto(s).";
|
||||||
|
|
||||||
|
header('Location: index.php?opt=verificar_codigo');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/usuarioModel.php';
|
||||||
|
$usuariomodel = new usuarioModel();
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/logInModel.php';
|
||||||
|
$loginmodel = new logInModel();
|
||||||
|
|
||||||
|
$codigo = trim($_POST['codigo'] ?? '');
|
||||||
|
$email = $_SESSION['reset_email'] ?? null;
|
||||||
|
|
||||||
|
if (empty($codigo) || empty($email)) {
|
||||||
|
$_SESSION['flash_error'] = 'Debe ingresar el código.';
|
||||||
|
header('Location: index.php?opt=verificar_codigo');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$usuario = $usuariomodel->obtenerPorEmail($email);
|
||||||
|
|
||||||
|
if (!$usuario) {
|
||||||
|
$_SESSION['flash_error'] = 'Ocurrió un error.';
|
||||||
|
header('Location: index.php?opt=login');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$registro = $loginmodel->obtenerCodigoValido($usuario['id']);
|
||||||
|
|
||||||
|
if (!$registro) {
|
||||||
|
|
||||||
|
$_SESSION['codigo_intentos']++;
|
||||||
|
|
||||||
|
// BLOQUEO AUTOMÁTICO
|
||||||
|
|
||||||
|
if ($_SESSION['codigo_intentos'] >= 5) {
|
||||||
|
|
||||||
|
$_SESSION['codigo_bloqueado_hasta'] = time() + (15 * 60);
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'Demasiados intentos fallidos. Intente nuevamente en 15 minutos.';
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = 'El código no es válido.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=verificar_codigo');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPIRACIÓN
|
||||||
|
|
||||||
|
if (strtotime($registro['expira_a']) < time()) {
|
||||||
|
|
||||||
|
$loginmodel->marcarComoUsado($registro['id']);
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'El código ha expirado.';
|
||||||
|
|
||||||
|
header('Location: index.php?opt=verificar_codigo');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALIDACIÓN HASH
|
||||||
|
|
||||||
|
if (!password_verify($codigo, $registro['codigo'])) {
|
||||||
|
|
||||||
|
$_SESSION['codigo_intentos']++;
|
||||||
|
|
||||||
|
// BLOQUEAR SI LLEGA AL LÍMITE
|
||||||
|
|
||||||
|
if ($_SESSION['codigo_intentos'] >= 5) {
|
||||||
|
|
||||||
|
$_SESSION['codigo_bloqueado_hasta'] = time() + (15 * 60);
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'Demasiados intentos fallidos. Intente nuevamente en 15 minutos.';
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = 'Código incorrecto.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=verificar_codigo');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ÉXITO
|
||||||
|
|
||||||
|
$loginmodel->marcarComoUsado($registro['id']);
|
||||||
|
|
||||||
|
$_SESSION['reset_autorizado'] = $usuario['id'];
|
||||||
|
|
||||||
|
// Limpiar protección
|
||||||
|
|
||||||
|
unset($_SESSION['codigo_intentos']);
|
||||||
|
unset($_SESSION['codigo_bloqueado_hasta']);
|
||||||
|
|
||||||
|
header('Location: index.php?opt=restablecer_contrasenia');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESTABLECER CONTRASENIA
|
||||||
|
|
||||||
|
case 'restablecer_contrasenia':
|
||||||
|
|
||||||
|
if (!isset($_SESSION['reset_autorizado'])) {
|
||||||
|
header('Location: index.php?opt=login');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MOSTRAR VISTA
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/login/restablecerContraseniaView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GUARDAR NUEVA CONTRASENIA
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||||
|
$_SESSION['flash_error'] = 'Acción inválida.';
|
||||||
|
header('Location: index.php?opt=login');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/usuarioModel.php';
|
||||||
|
$usuariomodel = new usuarioModel();
|
||||||
|
|
||||||
|
$password = trim($_POST['password'] ?? '');
|
||||||
|
$confirmar = trim($_POST['confirmar_password'] ?? '');
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// VALIDACIONES
|
||||||
|
|
||||||
|
if (empty($password) || empty($confirmar)) {
|
||||||
|
$_SESSION['flash_error'] = 'Complete todos los campos.';
|
||||||
|
header('Location: index.php?opt=restablecer_contrasenia');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALIDACIONES DE CONTRASENIA
|
||||||
|
|
||||||
|
if ($password === '') {
|
||||||
|
$errores[] = "La contraseña no puede estar vacía.";
|
||||||
|
} elseif (strpos($password, ' ') !== false) {
|
||||||
|
$errores[] = "La contraseña no puede contener espacios.";
|
||||||
|
} elseif (!preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{6,}$/', $password)) {
|
||||||
|
$errores[] = "La contraseña debe tener al menos una mayúscula, una minúscula y un número, y mínimo 6 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($password !== $confirmar) {
|
||||||
|
$_SESSION['flash_error'] = 'Las contraseñas no coinciden.';
|
||||||
|
header('Location: index.php?opt=restablecer_contrasenia');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores);
|
||||||
|
header("Location: index.php?opt=restablecer_contrasenia");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HASH
|
||||||
|
|
||||||
|
$passwordHash = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
|
// ACTUALIZAR
|
||||||
|
|
||||||
|
if (!$usuariomodel->actualizarContrasenia (
|
||||||
|
$_SESSION['reset_autorizado'], $passwordHash
|
||||||
|
)) {
|
||||||
|
$_SESSION['flash_error'] = 'Ocurrió un error inesperado.';
|
||||||
|
header('Location: index.php?opt=restablecer_contrasenia');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// INVALIDAMOS TODOS LOS CODIGOS (PARA PREVENIR)
|
||||||
|
|
||||||
|
$loginmodel->invalidarPorUsuario($_SESSION['reset_autorizado']);
|
||||||
|
|
||||||
|
// Limpiar sesión temporal
|
||||||
|
|
||||||
|
unset($_SESSION['reset_autorizado']);
|
||||||
|
unset($_SESSION['reset_email']);
|
||||||
|
|
||||||
|
// REGENERACION DE SESSION ID
|
||||||
|
|
||||||
|
session_regenerate_id(true);
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = 'Contraseña actualizada correctamente.';
|
||||||
|
|
||||||
|
header('Location: index.php?opt=login');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEFAULT (404)
|
||||||
|
|
||||||
|
default:
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/404View.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
@@ -0,0 +1,324 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Inicio de sesión
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Generación de token (previene Cross-Site y Request Forgery)
|
||||||
|
|
||||||
|
if (empty($_SESSION['csrf_token'])) {
|
||||||
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evitar Back-Forward Cache en Chrome
|
||||||
|
|
||||||
|
header("Cache-Control: no-store");
|
||||||
|
|
||||||
|
// Funcion principal para obtener el body a mostrar
|
||||||
|
|
||||||
|
function getBodyContent() {
|
||||||
|
|
||||||
|
$opt = $_GET['opt'] ?? null; // Obtiene parámetro enviado desde URL (es el parámetro que maneja todas las acciones dentro del sistema)
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- REDIRECCIONES ----- */
|
||||||
|
|
||||||
|
|
||||||
|
// USUARIO LOGUEADO SIN OPT (en caso de ingresar localhost/ en la barra de direcciones)
|
||||||
|
|
||||||
|
if (isset($_SESSION['user']) && $opt === null) {
|
||||||
|
if ($_SESSION['user']['rol'] === 'admin') {
|
||||||
|
header("Location: index.php?opt=inicio_admin"); // Redirección HTTP
|
||||||
|
} else {
|
||||||
|
header("Location: index.php?opt=inicio_operador");
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OPCIONES DE RECUPERACION DE CUENTA (lo maneja logInController)
|
||||||
|
|
||||||
|
$opcionesRecuperacion = [
|
||||||
|
'recuperar_cuenta',
|
||||||
|
'verificar_codigo',
|
||||||
|
'restablecer_contrasenia'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcionesRecuperacion, true)) {
|
||||||
|
return require __DIR__ . '/../controllers/logInController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// USUARIO NO LOGUEADO
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
|
||||||
|
if ($opt === 'login') {
|
||||||
|
return require __DIR__ . '/../controllers/logInController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=login"); // Envía cabeceras http al navegador, obliga a mostrar el login
|
||||||
|
exit; // Importante usar exit para cortar ejecución
|
||||||
|
}
|
||||||
|
|
||||||
|
// SETEA EL USUARIO EN $_SESSION EN CASO DE QUE EL ROL SE CAMBIÓ. ADEMÁS HACE LOGOUT SI FUÉ DESACTIVADO
|
||||||
|
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/logInModel.php';
|
||||||
|
$loginmodel = new logInModel();
|
||||||
|
|
||||||
|
$usuario = $loginmodel->buscarPorUsuario($_SESSION['user']['usuario']);
|
||||||
|
|
||||||
|
if (!$usuario || !(bool)$usuario['activo']) {
|
||||||
|
session_unset();
|
||||||
|
session_destroy();
|
||||||
|
setcookie(session_name(), '', time() - 3600, '/');
|
||||||
|
|
||||||
|
header("Location: index.php?opt=login");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['user'] = $usuario;
|
||||||
|
|
||||||
|
if ($_SESSION['user']['activo'] !== true) {
|
||||||
|
header("Location:index.php?opt=logout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- OPCIONES DE ACCIONES EN SISTEMA ----- */
|
||||||
|
|
||||||
|
|
||||||
|
// USUARIO LOGUEADO (HOME / LOGOUT)
|
||||||
|
|
||||||
|
$opcioneslogueado = [
|
||||||
|
'inicio_admin',
|
||||||
|
'inicio_operador',
|
||||||
|
'home_operador',
|
||||||
|
'home_admin',
|
||||||
|
'logout',
|
||||||
|
'readme'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcioneslogueado, true)) { // Parámetro $strict en true, PHP compara el valor y también el tipo de dato
|
||||||
|
return require __DIR__ . '/../controllers/logInController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// OPCIONES DE USUARIO (PERFIL + ADMINISTRACION)
|
||||||
|
|
||||||
|
$opcionesusuarios = [
|
||||||
|
'datos_usuario',
|
||||||
|
'usuarios',
|
||||||
|
'crear_usuario',
|
||||||
|
'eliminar_usuario'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcionesusuarios, true)) {
|
||||||
|
return require __DIR__ . '/../controllers/usuarioController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLIENTES
|
||||||
|
|
||||||
|
$opcionesclientes = [
|
||||||
|
'clientes',
|
||||||
|
'crear_cliente',
|
||||||
|
'editar_cliente'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcionesclientes, true)) {
|
||||||
|
return require __DIR__ . '/../controllers/clienteController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROVEEDORES
|
||||||
|
|
||||||
|
$opcionesproveedores = [
|
||||||
|
'proveedores',
|
||||||
|
'crear_proveedor',
|
||||||
|
'editar_proveedor'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcionesproveedores, true)) {
|
||||||
|
return require __DIR__ . '/../controllers/proveedorController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRODUCTOS
|
||||||
|
|
||||||
|
$opcionesproductos = [
|
||||||
|
|
||||||
|
// Manejo de productos
|
||||||
|
|
||||||
|
'productos',
|
||||||
|
'productos_inactivos',
|
||||||
|
'crear_producto',
|
||||||
|
'editar_producto',
|
||||||
|
'cambiar_estado',
|
||||||
|
|
||||||
|
// Manejo de categoría de productos
|
||||||
|
|
||||||
|
'categorias',
|
||||||
|
'crear_categoria',
|
||||||
|
'editar_categoria'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcionesproductos, true)) {
|
||||||
|
return require __DIR__ . '/../controllers/productoController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// VENTAS
|
||||||
|
|
||||||
|
$opcionesventas = [
|
||||||
|
'listar_ventas',
|
||||||
|
'crear_venta',
|
||||||
|
'agregar_producto',
|
||||||
|
'seleccionar_cliente',
|
||||||
|
'eliminar_cliente',
|
||||||
|
'resumen_venta',
|
||||||
|
'guardar_datos_venta',
|
||||||
|
'generar_venta_pdf',
|
||||||
|
'continuar_sin_comprobante',
|
||||||
|
'confirmar_venta',
|
||||||
|
'detalle_venta'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcionesventas, true)) {
|
||||||
|
return require __DIR__ . '/../controllers/ventaController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// PEDIDOS
|
||||||
|
|
||||||
|
$opcionespedidos = [
|
||||||
|
'listar_pedidos',
|
||||||
|
'detalle_pedido',
|
||||||
|
'crear_pedido',
|
||||||
|
'agregar_producto_pedido',
|
||||||
|
'seleccionar_proveedor',
|
||||||
|
'eliminar_proveedor',
|
||||||
|
'generar_remito_pdf',
|
||||||
|
'continuar_sin_remito',
|
||||||
|
'confirmar_pedido'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcionespedidos, true)) {
|
||||||
|
return require __DIR__ . '/../controllers/pedidoController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// SERVICIOS
|
||||||
|
|
||||||
|
$opcionesservicios = [
|
||||||
|
'crear_servicio',
|
||||||
|
'guardar_servicio', // Guardado inicial del servicio (cuando viene el cliente al local)
|
||||||
|
'generar_servicio_pdf',
|
||||||
|
'seleccionar_cliente_servicio',
|
||||||
|
'eliminar_cliente_servicio',
|
||||||
|
'listar_servicios',
|
||||||
|
'actualizar_servicio', // Actualización del servicio (se cargan los datos a realizar e insumos a utilizar)
|
||||||
|
'agregar_insumo',
|
||||||
|
'finalizar_servicio', // Actualización del servicio (el cliente decidió llevar a cabo el servicio, al terminar se marca como finalizado en la base de datos)
|
||||||
|
'cancelar_servicio', // Según regla de negocio (caso en donde el cliente decide no realizar el servicio)
|
||||||
|
'detalle_servicio',
|
||||||
|
'detalle_insumos_servicio',
|
||||||
|
'resetear_datos_servicio', // Para resetar caja y datos en $_SESSION al ingresar a la vista de actualización
|
||||||
|
'agregar_mano_obra'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcionesservicios, true)) {
|
||||||
|
return require __DIR__ . '/../controllers/servicioController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// MANEJO GENERAL DE CAJA
|
||||||
|
|
||||||
|
$opcionescaja = [
|
||||||
|
|
||||||
|
// MANEJO DE VENTAS
|
||||||
|
|
||||||
|
'agregar_producto_caja',
|
||||||
|
'eliminar_producto',
|
||||||
|
'editar_cantidad',
|
||||||
|
'vaciar_caja_venta',
|
||||||
|
|
||||||
|
// MANEJO DE PEDIDOS
|
||||||
|
|
||||||
|
'agregar_producto_pedido_caja',
|
||||||
|
'eliminar_producto_pedido',
|
||||||
|
'editar_datos_producto',
|
||||||
|
'vaciar_caja_pedido',
|
||||||
|
|
||||||
|
// MANEJO DE SERVICIOS
|
||||||
|
|
||||||
|
'agregar_insumo_caja',
|
||||||
|
'vaciar_caja_servicio',
|
||||||
|
'eliminar_insumo',
|
||||||
|
'editar_cantidad_insumo'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcionescaja, true)) {
|
||||||
|
return require __DIR__ . '/../controllers/cajaController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRESUPUESTOS
|
||||||
|
|
||||||
|
$opcionespresupuestos = [
|
||||||
|
'crear_presupuesto',
|
||||||
|
'seleccionar_cliente_presupuesto',
|
||||||
|
'eliminar_cliente_presupuesto',
|
||||||
|
'agregar_producto_presupuesto',
|
||||||
|
'eliminar_producto_presupuesto',
|
||||||
|
'vaciar_caja_presupuesto',
|
||||||
|
'editar_cantidad_presupuesto',
|
||||||
|
'resumen_presupuesto',
|
||||||
|
'guardar_datos_presupuesto',
|
||||||
|
'generar_presupuesto_pdf'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcionespresupuestos, true)) {
|
||||||
|
return require __DIR__ . '/../controllers/presupuestoController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// INFORMES
|
||||||
|
|
||||||
|
$opcionesinformes = [
|
||||||
|
'generar_balance_anual',
|
||||||
|
'generar_cuentas_por_cobrar',
|
||||||
|
'informe'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcionesinformes, true)) {
|
||||||
|
return require __DIR__ . '/../controllers/informeController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONFIGURACIÓN DEL SISTEMA (controla configuración general y también backups)
|
||||||
|
|
||||||
|
$opcionesconfiguracion = [
|
||||||
|
'configuracion',
|
||||||
|
'actualizar_config',
|
||||||
|
'backups',
|
||||||
|
'generar_backup',
|
||||||
|
'descargar_backup',
|
||||||
|
'eliminar_backup',
|
||||||
|
'restaurar_backup'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcionesconfiguracion, true)) {
|
||||||
|
return require __DIR__ . '/../controllers/configuracionController.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHATBOT
|
||||||
|
|
||||||
|
$opcioneschatbot = [
|
||||||
|
'chatbot'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($opt, $opcioneschatbot, true)) {
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
require __DIR__ . '/../views/chatbot/chatbotView.php';
|
||||||
|
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEFAULT (404)
|
||||||
|
|
||||||
|
ob_start(); // Inicia búfer de salida en PHP, lo guarda en memoria
|
||||||
|
require __DIR__ . '/../views/404View.php';
|
||||||
|
return ob_get_clean(); // Devuelve el contenido almacenado en el búfer como string
|
||||||
|
}
|
||||||
@@ -0,0 +1,554 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Validacion de token
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { // Recorre todos los caracteres (siempre) y retorna true or false
|
||||||
|
$_SESSION['flash_error'] = 'Acción inválida.';
|
||||||
|
header('Location: index.php?opt=inicio_operador');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Librería domPDF
|
||||||
|
|
||||||
|
require __DIR__ . '/../libs/dompdf/autoload.inc.php';
|
||||||
|
|
||||||
|
use Dompdf\Dompdf;
|
||||||
|
use Dompdf\Options;
|
||||||
|
|
||||||
|
// Validar sesión
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$opt = $_GET['opt'] ?? null;
|
||||||
|
|
||||||
|
// Manejo de opt
|
||||||
|
|
||||||
|
switch ($opt) {
|
||||||
|
|
||||||
|
// LISTAR PEDIDOS
|
||||||
|
|
||||||
|
case 'listar_pedidos':
|
||||||
|
|
||||||
|
// Requiere el modelo de pedidos
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/pedidoModel.php';
|
||||||
|
$pedidomodel = new pedidoModel();
|
||||||
|
|
||||||
|
$anioActual = date('Y');
|
||||||
|
$anioSeleccionado = $_GET['anio'] ?? $anioActual;
|
||||||
|
|
||||||
|
$pedidos = $pedidomodel->listarPedidos($anioSeleccionado);
|
||||||
|
|
||||||
|
// Reordenamiento de fecha (para poder ordenar correctamente con dataTables)
|
||||||
|
|
||||||
|
foreach ($pedidos as $i => $p) {
|
||||||
|
|
||||||
|
if (!empty($p['fecha'])) {
|
||||||
|
|
||||||
|
$dt = new DateTime($p['fecha']);
|
||||||
|
|
||||||
|
// Para ordenar
|
||||||
|
|
||||||
|
$pedidos[$i]['fecha'] = $dt->format('Y-m-d');
|
||||||
|
|
||||||
|
// Para mostrar
|
||||||
|
|
||||||
|
$pedidos[$i]['fecha_formateada'] = $dt->format('d/m/Y');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$pedidos[$i]['fecha'] = '-';
|
||||||
|
$pedidos[$i]['fecha_formateada'] = '-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/pedidos/listaPedidosView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// MOSTRAR DETALLE DE PRODUCTOS DE UN PEDIDO
|
||||||
|
|
||||||
|
case 'detalle_pedido':
|
||||||
|
|
||||||
|
$idPedido = (int) ($_GET['id'] ?? 0);
|
||||||
|
|
||||||
|
if ($idPedido <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=listar_pedidos');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/pedidoModel.php';
|
||||||
|
$pedidomodel = new pedidoModel();
|
||||||
|
|
||||||
|
$pedido_detalle = $pedidomodel->getDetallePedido($idPedido);
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/pedidos/pedidoDetalleView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// CREAR PEDIDO
|
||||||
|
|
||||||
|
case 'crear_pedido':
|
||||||
|
|
||||||
|
// Se muestra vista de carga de pedidos
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
$condIVA = $_SESSION['pedido']['cond_iva'] ?? $_SESSION['pedido']['proveedor']['cond_iva'] ?? 'consumidor_final';
|
||||||
|
$cuit = $_SESSION['pedido']['cuit'] ?? $_SESSION['pedido']['proveedor']['cuit'] ?? '00000000000';
|
||||||
|
|
||||||
|
// Se obtienen los productos almacenados en caja (puede estar vacía)
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/cajaModel.php';
|
||||||
|
$cajamodel = new cajaModel();
|
||||||
|
|
||||||
|
$productos = $cajamodel->listarProductosPedido();
|
||||||
|
|
||||||
|
if (isset($_SESSION['pedido']['proveedor'])) {
|
||||||
|
$proveedor = $_SESSION['pedido']['proveedor'];
|
||||||
|
} else {
|
||||||
|
$proveedor = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cálculo de total
|
||||||
|
|
||||||
|
$total = $cajamodel->calcularPrecioTotalPedido() ?? 0;
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/pedidos/crearPedidoView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// AGREGAR PRODUCTO AL PEDIDO
|
||||||
|
|
||||||
|
case 'agregar_producto_pedido':
|
||||||
|
|
||||||
|
// Guardo datos en $_SESSION para persistencia
|
||||||
|
|
||||||
|
$_SESSION['pedido']['cond_iva'] = $_POST['cond_iva'] ?? '';
|
||||||
|
$_SESSION['pedido']['cuit'] = ($_POST['cuit_1'] ?? '') . ($_POST['cuit_2'] ?? '') . ($_POST['cuit_3'] ?? '');
|
||||||
|
|
||||||
|
// Requiero el modelo de producto
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/productoModel.php';
|
||||||
|
$productomodel = new productoModel();
|
||||||
|
|
||||||
|
$productos = $productomodel->listarProductosPedido();
|
||||||
|
|
||||||
|
foreach ($productos as $i => $p) {
|
||||||
|
|
||||||
|
if (!isset($p['id'])) {
|
||||||
|
unset($productos[$i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($p['stock'] === null || $p['stock'] === 0) {
|
||||||
|
$productos[$i]['stock'] = '-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/pedidos/agregarProductoPedidoView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// SELECCIONAR PROVEEDOR
|
||||||
|
|
||||||
|
case 'seleccionar_proveedor':
|
||||||
|
|
||||||
|
// Requiero el modelo de proveedor
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/proveedorModel.php';
|
||||||
|
$proveedormodel = new proveedorModel();
|
||||||
|
|
||||||
|
// Se muestra la vista de selección de cliente
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
$proveedores = $proveedormodel->listarProveedores();
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/pedidos/seleccionarProveedorView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$idProveedor = (int) ($_POST['id_proveedor'] ?? 0);
|
||||||
|
|
||||||
|
if ($idProveedor <= 0) {
|
||||||
|
$_SESSION['flash_error'] = "No se seleccionó ningún proveedor.";
|
||||||
|
header("Location: index.php?opt=seleccionar_proveedor");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$proveedor = $proveedormodel->obtenerPorId($idProveedor);
|
||||||
|
|
||||||
|
if (!$proveedor) {
|
||||||
|
$_SESSION['flash_error'] = "Proveedor inválido.";
|
||||||
|
header("Location: index.php?opt=seleccionar_proveedor");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardo proveedor en $_SESSION
|
||||||
|
|
||||||
|
$_SESSION['pedido']['proveedor'] = [
|
||||||
|
'id' => $proveedor['id'],
|
||||||
|
'razon_social' => $proveedor['razon_social'],
|
||||||
|
'telefono' => $proveedor['telefono'],
|
||||||
|
'email' => $proveedor['email'],
|
||||||
|
'cuit' => $proveedor['cuit'],
|
||||||
|
'cond_iva' => $proveedor['cond_iva']
|
||||||
|
];
|
||||||
|
|
||||||
|
$_SESSION['pedido']['cond_iva'] = $proveedor['cond_iva'];
|
||||||
|
$_SESSION['pedido']['cuit'] = $proveedor['cuit'];
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Proveedor agregado correctamente.";
|
||||||
|
header("Location: index.php?opt=crear_pedido");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELIMINAR PROVEEDOR
|
||||||
|
|
||||||
|
case 'eliminar_proveedor':
|
||||||
|
|
||||||
|
unset($_SESSION['pedido']['cond_iva']);
|
||||||
|
unset($_SESSION['pedido']['cuit']);
|
||||||
|
|
||||||
|
if (isset($_SESSION['pedido']['proveedor'])) {
|
||||||
|
unset($_SESSION['pedido']['proveedor']);
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Proveedor eliminado correctamente.";
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = "No había proveedor seleccionado.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=crear_pedido");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// GENERACION DE REMITO PDF MEDIANTE USO DE LIBRERIA DOMPDF
|
||||||
|
|
||||||
|
case 'generar_remito_pdf':
|
||||||
|
|
||||||
|
// CARGAMOS LA VISTA
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
$numeroRemito = $_SESSION['ultimo_pedido_id'];
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/pedidos/descargarRemito.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GENERACIÓN DEL PDF
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
// Requiero datos de empresa y configuración
|
||||||
|
|
||||||
|
require __DIR__ . '/../models/configuracionModel.php';
|
||||||
|
$configuracionmodel = new configuracionModel();
|
||||||
|
|
||||||
|
$datosEmpresa = $configuracionmodel->obtenerConfiguracion();
|
||||||
|
|
||||||
|
$mapCondIVA = [
|
||||||
|
'consumidor_final' => 'Consumidor Final',
|
||||||
|
'responsable_inscripto' => 'Responsable Inscripto',
|
||||||
|
'monotributo' => 'Monotributo',
|
||||||
|
];
|
||||||
|
|
||||||
|
$datosEmpresa['cond_iva'] = $mapCondIVA[$datosEmpresa['cond_iva']];
|
||||||
|
|
||||||
|
// Obtenemos en la variable los datos guardados en $_SESSION durante la confirmación del pedido
|
||||||
|
|
||||||
|
$datosRemito = $_SESSION['remito_pdf'] ?? null;
|
||||||
|
|
||||||
|
if (!$datosRemito) {
|
||||||
|
$_SESSION['flash_error'] = 'No hay datos disponibles para generar el remito.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preparamos las variables con los datos
|
||||||
|
|
||||||
|
$productos = $datosRemito['productos'];
|
||||||
|
$proveedor = $datosRemito['proveedor'];
|
||||||
|
$vendedor = $datosRemito['vendedor'];
|
||||||
|
$total = $datosRemito['total'];
|
||||||
|
$condIVA = $datosRemito['cond_iva'];
|
||||||
|
$cuit = $datosRemito['cuit'];
|
||||||
|
|
||||||
|
// VALIDACIONES BASE
|
||||||
|
|
||||||
|
if (!$proveedor || empty($productos)) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos incompletos para generar el remito.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$vendedor) {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo identificar el usuario.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALIDACIÓN CONDICIÓN IVA
|
||||||
|
|
||||||
|
$condIvaPermitidas = [
|
||||||
|
'consumidor_final',
|
||||||
|
'responsable_inscripto',
|
||||||
|
'monotributo'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!in_array($condIVA, $condIvaPermitidas, true)) {
|
||||||
|
$_SESSION['flash_error'] = 'Condición de IVA inválida.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALIDACIÓN CUIT
|
||||||
|
|
||||||
|
$cuit = trim($cuit);
|
||||||
|
|
||||||
|
if (!preg_match('/^\d{10,11}$/', $cuit)) {
|
||||||
|
$_SESSION['flash_error'] = 'CUIT inválido.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($total <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Total inválido.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MAPEOS PARA PDF
|
||||||
|
|
||||||
|
$mapCondIVA = [
|
||||||
|
'consumidor_final' => 'Consumidor Final',
|
||||||
|
'responsable_inscripto' => 'Responsable Inscripto',
|
||||||
|
'monotributo' => 'Monotributo',
|
||||||
|
];
|
||||||
|
|
||||||
|
$condIVALabel = $mapCondIVA[$condIVA];
|
||||||
|
|
||||||
|
// Obtenemos código ID del pedido generado
|
||||||
|
|
||||||
|
$numero = $_SESSION['ultimo_pedido_id'] ?? null;
|
||||||
|
|
||||||
|
if (!$numero) {
|
||||||
|
$_SESSION['flash_error'] = 'No se encontró el número de remito.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardamos en la vatiable para mostrar el código en la vista
|
||||||
|
|
||||||
|
$numeroRemito = '0001-' . str_pad($numero, 8, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
// RENDER PDF
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../pdf/pedidos/remitoPDF.php';
|
||||||
|
$html = ob_get_clean();
|
||||||
|
|
||||||
|
$cssPath = '/var/www/html/css/stylesRemitoPDF.css';
|
||||||
|
$css = file_get_contents($cssPath);
|
||||||
|
|
||||||
|
// Configuración de opciones
|
||||||
|
|
||||||
|
$options = new Options();
|
||||||
|
$options->set('isRemoteEnabled', true); // Permite cargar recursos externos (CSS, imágenes, etc.)
|
||||||
|
$options->set('chroot', '/var/www/html'); // Define directorios base para recursos locales
|
||||||
|
$options->set('defaultFont', 'DejaVu Sans');
|
||||||
|
|
||||||
|
// Creación de instancia Dompdf con las opciones seteadas
|
||||||
|
|
||||||
|
$dompdf = new Dompdf($options);
|
||||||
|
|
||||||
|
// Cargar el HTML que se va a convertir en PDF
|
||||||
|
|
||||||
|
$dompdf->loadHtml("
|
||||||
|
<style>$css</style>
|
||||||
|
$html
|
||||||
|
");
|
||||||
|
|
||||||
|
$dompdf->setPaper('A4', 'portrait'); // Define el tamanio de hoja y la orientación
|
||||||
|
$dompdf->render(); // Renderiza el PDF (procesa el HTML)
|
||||||
|
|
||||||
|
// Enviar el PDF al browser
|
||||||
|
|
||||||
|
$dompdf->stream(
|
||||||
|
'Remito N° ' . $numeroRemito . '.pdf',
|
||||||
|
['Attachment' => true]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Liberamos datos del array $_SESSION
|
||||||
|
|
||||||
|
unset($_SESSION['remito_pdf']);
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONTINUAR SIN DESCARGAR EL REMITO PDF
|
||||||
|
|
||||||
|
case 'continuar_sin_remito':
|
||||||
|
|
||||||
|
// Liberamos $_SESSION
|
||||||
|
|
||||||
|
unset($_SESSION['remito_pdf']);
|
||||||
|
unset($_SESSION['ultimo_pedido_id']);
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = 'Pedido finalizado correctamente.';
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// CONFIRMAR PEDIDO
|
||||||
|
|
||||||
|
case 'confirmar_pedido':
|
||||||
|
|
||||||
|
// Requiero modelos necesarios
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/cajaModel.php';
|
||||||
|
$cajamodel = new cajaModel();
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/proveedorModel.php';
|
||||||
|
$proveedormodel = new proveedorModel();
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/pedidoModel.php';
|
||||||
|
$pedidomodel = new pedidoModel();
|
||||||
|
|
||||||
|
// Condición IVA
|
||||||
|
|
||||||
|
$condIvaPermitidas = [
|
||||||
|
'consumidor_final',
|
||||||
|
'responsable_inscripto',
|
||||||
|
'monotributo'
|
||||||
|
];
|
||||||
|
|
||||||
|
$condIva = $_POST['cond_iva'] ?? 'consumidor_final';
|
||||||
|
|
||||||
|
if (!in_array($condIva, $condIvaPermitidas, true)) {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'Condición de IVA inválida.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CUIT
|
||||||
|
|
||||||
|
$cuit = ($_POST['cuit_1'] ?? '') . ($_POST['cuit_2'] ?? '') . ($_POST['cuit_3'] ?? '');
|
||||||
|
|
||||||
|
$cuit = trim($cuit);
|
||||||
|
|
||||||
|
if ($cuit === '') {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'Debe ingresar un CUIT.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/^\d{11}$/', $cuit)) {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'El CUIT debe contener 11 dígitos.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$idProveedor = $_SESSION['pedido']['proveedor']['id'] ?? null;
|
||||||
|
|
||||||
|
$vendedor = $_SESSION['user'] ?? null;
|
||||||
|
|
||||||
|
if ($idProveedor === null) {
|
||||||
|
$_SESSION['flash_error'] = 'No seleccionó un proveedor.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$vendedor) {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo identificar el usuario.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$productosFinales = $cajamodel->listarProductosPedido();
|
||||||
|
$proveedor = $proveedormodel->obtenerPorId($idProveedor);
|
||||||
|
$total = $cajamodel->calcularPrecioTotalPedido();
|
||||||
|
|
||||||
|
if (empty($productosFinales)) {
|
||||||
|
$_SESSION['flash_error'] = 'No hay productos en el pedido.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($productosFinales as $p) {
|
||||||
|
if (empty($p['id_producto']) || empty($p['cantidad']) || $p['cantidad'] <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Pedido con productos inválidos.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$proveedor) {
|
||||||
|
$_SESSION['flash_error'] = 'Proveedor inválido.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($total <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'El total del pedido es inválido.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardamos el pedido y obtenemos el ID autoincremental desde la base de datos
|
||||||
|
|
||||||
|
$idPedido = $pedidomodel->guardarPedido($productosFinales, $proveedor, $vendedor, $total);
|
||||||
|
|
||||||
|
// Verificamos si se obtuvo el ID o si ocurrió algún error
|
||||||
|
|
||||||
|
if ($idPedido !== false) {
|
||||||
|
|
||||||
|
// Guardamos datos para utilizar en la generación del PDF
|
||||||
|
|
||||||
|
$_SESSION['ultimo_pedido_id'] = $idPedido;
|
||||||
|
|
||||||
|
$_SESSION['remito_pdf'] = [
|
||||||
|
'productos' => $productosFinales,
|
||||||
|
'proveedor' => $proveedor,
|
||||||
|
'vendedor' => $vendedor,
|
||||||
|
'total' => $total,
|
||||||
|
'cond_iva' => $condIva,
|
||||||
|
'cuit' => $cuit
|
||||||
|
];
|
||||||
|
|
||||||
|
// Liberamos
|
||||||
|
|
||||||
|
$cajamodel->vaciarCajaPedido();
|
||||||
|
unset($_SESSION['pedido']);
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Pedido guardado correctamente.";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = $pedidomodel->getError() ?? 'No se pudo guardar el pedido.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=generar_remito_pdf");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// DEFAULT
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
header("Location: index.php?opt=404View.php");
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
@@ -0,0 +1,729 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Validacion de token
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { // Recorre todos los caracteres (siempre) y retorna true or false
|
||||||
|
$_SESSION['flash_error'] = 'Acción inválida.';
|
||||||
|
header('Location: index.php?opt=inicio_operador');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requiere el modelo de presupuesto
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/presupuestoModel.php';
|
||||||
|
$presupuestomodel = new presupuestoModel();
|
||||||
|
|
||||||
|
// Librería domPDF
|
||||||
|
|
||||||
|
require __DIR__ . '/../libs/dompdf/autoload.inc.php';
|
||||||
|
|
||||||
|
use Dompdf\Dompdf;
|
||||||
|
use Dompdf\Options;
|
||||||
|
|
||||||
|
// Validar sesion
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$opt = $_GET['opt'] ?? null;
|
||||||
|
|
||||||
|
// Switch para manejo de opt
|
||||||
|
|
||||||
|
switch ($opt) {
|
||||||
|
|
||||||
|
// MOSTRAR VISTA DE CREACIÓN DE PRESUPUESTO
|
||||||
|
|
||||||
|
case 'crear_presupuesto':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
// Obtenemos los productos de caja, pueder estar vacía o no
|
||||||
|
|
||||||
|
$productos = $presupuestomodel->listarProductos();
|
||||||
|
|
||||||
|
if (isset($_SESSION['presupuesto']['cliente'])) {
|
||||||
|
$cliente = $_SESSION['presupuesto']['cliente'];
|
||||||
|
} else {
|
||||||
|
$cliente = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Llamamos a los métodos del modelo para obtener calculos preliminares
|
||||||
|
|
||||||
|
$subtotal = $presupuestomodel->calcularSubtotal() ?? 0;
|
||||||
|
$descuento = $presupuestomodel->calcularDescuentoTotal() ?? 0;
|
||||||
|
$total = $presupuestomodel->calcularPrecioTotal() ?? 0;
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/presupuestos/crearPresupuestoView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// AGREGAR PRODUCTO AL PRESUPUESTO (en casos de presupuesto, el manejo de productos es manejado por modelo de presupuesto)
|
||||||
|
|
||||||
|
case 'agregar_producto_presupuesto':
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/productoModel.php';
|
||||||
|
$productomodel = new productoModel();
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
$productos = $productomodel->listarProductosVenta();
|
||||||
|
|
||||||
|
foreach ($productos as $i => $p) {
|
||||||
|
|
||||||
|
if (!isset($p['id'])) {
|
||||||
|
unset($productos[$i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($p['stock'] === null || $p['stock'] === 0) {
|
||||||
|
$productos[$i]['stock'] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
$productos[$i]['descuento_total'] = number_format($p['descuento_total'], 2, '.', '') . ' %';
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/presupuestos/agregarProductoView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$ids = $_POST['ids'] ?? [];
|
||||||
|
$cantidades = $_POST['cantidades'] ?? [];
|
||||||
|
|
||||||
|
if (empty($ids)) {
|
||||||
|
$_SESSION['flash_error'] = 'No se seleccionó ningún producto.';
|
||||||
|
header('Location: index.php?opt=agregar_producto_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$agregados = 0;
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Evitar duplicados
|
||||||
|
|
||||||
|
$ids = array_unique($ids);
|
||||||
|
|
||||||
|
foreach ($ids as $idProducto) {
|
||||||
|
|
||||||
|
$idProducto = (int) $idProducto;
|
||||||
|
$cantidad = isset($cantidades[$idProducto]) ? (int) $cantidades[$idProducto] : 1;
|
||||||
|
|
||||||
|
if ($idProducto <= 0 || $cantidad <= 0) {
|
||||||
|
$errores[] = 'Datos inválidos.';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$producto = $productomodel->obtenerPorIdParaCaja($idProducto);
|
||||||
|
|
||||||
|
if (!$producto) {
|
||||||
|
$errores[] = 'Producto inexistente.';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($presupuestomodel->agregarProducto($producto, $cantidad)) {
|
||||||
|
$agregados++;
|
||||||
|
} else {
|
||||||
|
$errores[] = $presupuestomodel->getError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($agregados === 1) {
|
||||||
|
$_SESSION['flash_success'] = "Se agregó un producto correctamente.";
|
||||||
|
} else if ($agregados > 1) {
|
||||||
|
$_SESSION['flash_success'] = "Se agregaron {$agregados} productos correctamente.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', array_unique($errores));
|
||||||
|
header('Location: index.php?opt=agregar_producto_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELIMINAR PRODUCTO DE CAJA
|
||||||
|
|
||||||
|
case 'eliminar_producto_presupuesto':
|
||||||
|
|
||||||
|
$idProducto = (int) ($_POST['id_producto'] ?? 0);
|
||||||
|
|
||||||
|
if ($idProducto <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=crear_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$presupuestomodel->eliminarProducto($idProducto)) {
|
||||||
|
$_SESSION['flash_error'] = $cajamodel->getError() ?? 'No se pudo eliminar el producto.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_success'] = 'Producto eliminado correctamente.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_presupuesto');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// EDITAR CANTIDAD DEL PRODUCTO
|
||||||
|
|
||||||
|
case 'editar_cantidad_presupuesto':
|
||||||
|
|
||||||
|
$idProducto = (int) ($_POST['id_producto'] ?? 0);
|
||||||
|
$nuevaCantidad = (int) ($_POST['cantidad'] ?? 0);
|
||||||
|
|
||||||
|
if ($idProducto <= 0 || $nuevaCantidad <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=crear_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$presupuestomodel->editarCantidad($idProducto, $nuevaCantidad)) {
|
||||||
|
$_SESSION['flash_error'] = $presupuestomodel->getError() ?? 'No se pudo actualizar la cantidad.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_success'] = 'Cantidad editada correctamente.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_presupuesto');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// SELECCIONAR CLIENTE PARA EL PRESUPUESTO
|
||||||
|
|
||||||
|
case 'seleccionar_cliente_presupuesto':
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/clienteModel.php';
|
||||||
|
|
||||||
|
$clientemodel = new clienteModel();
|
||||||
|
|
||||||
|
// Se muestra la vista de selección de cliente
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
$clientes = $clientemodel->listarClientes();
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/presupuestos/seleccionarClienteView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$idCliente = (int) ($_POST['id_cliente'] ?? 0);
|
||||||
|
|
||||||
|
if ($idCliente <= 0) {
|
||||||
|
$_SESSION['flash_error'] = "No se seleccionó ningún cliente.";
|
||||||
|
header("Location: index.php?opt=seleccionar_cliente_presupuesto");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cliente = $clientemodel->obtenerPorId($idCliente);
|
||||||
|
|
||||||
|
if (!$cliente) {
|
||||||
|
$_SESSION['flash_error'] = "Cliente inválido.";
|
||||||
|
header("Location: index.php?opt=seleccionar_cliente_presupuesto");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['presupuesto']['cliente'] = [
|
||||||
|
'id' => $cliente['id'],
|
||||||
|
'nombre' => $cliente['nombre'],
|
||||||
|
'apellido' => $cliente['apellido'],
|
||||||
|
'telefono' => $cliente['telefono'],
|
||||||
|
'email' => $cliente['email'],
|
||||||
|
'cuit' => $cliente['cuit'],
|
||||||
|
'cond_iva' => $cliente['cond_iva']
|
||||||
|
];
|
||||||
|
|
||||||
|
$_SESSION['presupuesto_extra']['cond_iva'] = $cliente['cond_iva'];
|
||||||
|
$_SESSION['presupuesto_extra']['cuit'] = $cliente['cuit'];
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Cliente agregado correctamente.";
|
||||||
|
header("Location: index.php?opt=crear_presupuesto");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELIMINAR CLIENTE YA SELECCIONADO
|
||||||
|
|
||||||
|
case 'eliminar_cliente_presupuesto':
|
||||||
|
|
||||||
|
if (isset($_SESSION['presupuesto']['cliente'])) {
|
||||||
|
unset($_SESSION['presupuesto']['cliente']);
|
||||||
|
|
||||||
|
// También se eliminan datos del presupuesto guardados desde vista resumen
|
||||||
|
|
||||||
|
if (isset($_SESSION['presupuesto_extra'])) {
|
||||||
|
unset($_SESSION['presupuesto_extra']);
|
||||||
|
unset($_SESSION['presupuesto_final']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Cliente eliminado correctamente.";
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = "No había cliente seleccionado.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=crear_presupuesto");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// VACIAR CAJA DE PRODUCTOS
|
||||||
|
|
||||||
|
case 'vaciar_caja_presupuesto':
|
||||||
|
|
||||||
|
if ($presupuestomodel->vaciarCajaPresupuesto()) {
|
||||||
|
$_SESSION['flash_success'] = 'Carrito vaciado.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo vaciar el carrito.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_presupuesto');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// GENERAR VISTA RESUMEN DE PRESUPUESTO
|
||||||
|
|
||||||
|
case 'resumen_presupuesto':
|
||||||
|
|
||||||
|
$cliente = ($_SESSION['presupuesto']['cliente'] ?? null);
|
||||||
|
|
||||||
|
$_SESSION['presupuesto_extra']['cond_iva'] = $_SESSION['presupuesto_extra']['cond_iva'] ?? $cliente['cond_iva'] ?? 'consumidor_final';
|
||||||
|
$_SESSION['presupuesto_extra']['cuit'] = $_SESSION['presupuesto_extra']['cuit'] ?? $cliente['cuit'] ?? '00000000000';
|
||||||
|
|
||||||
|
$productos = $presupuestomodel->listarProductos();
|
||||||
|
$descuento = $presupuestomodel->calcularDescuentoTotal();
|
||||||
|
|
||||||
|
if (!$cliente || empty($productos)) {
|
||||||
|
$_SESSION['flash_error'] = "Debe seleccionar el cliente y al menos un producto.";
|
||||||
|
header("Location: index.php?opt=crear_presupuesto");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
// 1. Subtotal SIEMPRE se recalcula (pueden haber cambiado productos/cantidades)
|
||||||
|
|
||||||
|
$subtotal = $presupuestomodel->calcularSubtotal() ?? 0;
|
||||||
|
|
||||||
|
// 2. Datos extra guardados en sesión (si existen)
|
||||||
|
|
||||||
|
$descuentoIngresado = isset($_SESSION['presupuesto_extra']['descuento']) ? (float) $_SESSION['presupuesto_extra']['descuento'] : 0;
|
||||||
|
|
||||||
|
$recargoIngresado = isset($_SESSION['presupuesto_extra']['recargo']) ? (float) $_SESSION['presupuesto_extra']['recargo'] : 0;
|
||||||
|
|
||||||
|
// 3. Recalculo siempre con subtotal actual
|
||||||
|
|
||||||
|
$datosProvisorios = $presupuestomodel->actualizarDatosProvisorio($descuentoIngresado, $recargoIngresado, $subtotal, $descuento);
|
||||||
|
|
||||||
|
$descuento = $datosProvisorios['descuento'] ?? 0;
|
||||||
|
$recargo = $datosProvisorios['recargo'] ?? 0;
|
||||||
|
$total = $datosProvisorios['total'] ?? 0;
|
||||||
|
|
||||||
|
// Guardo en SESSION
|
||||||
|
|
||||||
|
$_SESSION['presupuesto_final'] = [
|
||||||
|
'descuento' => $descuento,
|
||||||
|
'recargo' => $recargo,
|
||||||
|
'total' => $total
|
||||||
|
];
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/presupuestos/resumenPresupuestoView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GUARDAR DATOS DEL PRESUPUESTO (permite volver hacia atrás en el proceso y no perder datos)
|
||||||
|
|
||||||
|
case 'guardar_datos_presupuesto':
|
||||||
|
|
||||||
|
// Validaciones de reglas de negocio
|
||||||
|
|
||||||
|
// Condición IVA
|
||||||
|
|
||||||
|
$condIvaPermitidas = [
|
||||||
|
'consumidor_final',
|
||||||
|
'responsable_inscripto',
|
||||||
|
'monotributo'
|
||||||
|
];
|
||||||
|
|
||||||
|
$condIva = $_POST['cond_iva'] ?? 'consumidor_final';
|
||||||
|
|
||||||
|
if (!in_array($condIva, $condIvaPermitidas, true)) {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'Condición de IVA inválida.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CUIT
|
||||||
|
|
||||||
|
$cuit = ($_POST['cuit_1'] ?? '') . ($_POST['cuit_2'] ?? '') . ($_POST['cuit_3'] ?? '');
|
||||||
|
|
||||||
|
$cuit = trim($cuit);
|
||||||
|
|
||||||
|
if ($cuit === '') {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'Debe ingresar un CUIT.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/^\d{11}$/', $cuit)) {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'El CUIT debe contener 11 dígitos.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forma de pago + tipo de pago
|
||||||
|
|
||||||
|
$reglasPago = [
|
||||||
|
'efectivo' => ['pago_unico'],
|
||||||
|
'transferencia' => ['pago_unico'],
|
||||||
|
'echeq' => ['pago_unico'],
|
||||||
|
'debito' => ['pago_unico'],
|
||||||
|
'credito' => ['pago_unico', '3_cuotas', '6_cuotas', '12_cuotas'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$formaPago = $_POST['forma_pago'] ?? 'efectivo';
|
||||||
|
$tipoPago = $_POST['tipo_pago'] ?? 'pago_unico';
|
||||||
|
|
||||||
|
if (!isset($reglasPago[$formaPago]) || !in_array($tipoPago, $reglasPago[$formaPago], true)) {
|
||||||
|
$_SESSION['flash_error'] = 'Combinación de forma y tipo de pago inválida.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validación de descuento y recargo
|
||||||
|
|
||||||
|
$descuentoPresupuesto = isset($_POST['descuento']) ? (float) $_POST['descuento'] : 0;
|
||||||
|
$recargoPresupuesto = isset($_POST['recargo']) ? (float) $_POST['recargo'] : 0;
|
||||||
|
|
||||||
|
if ($descuentoPresupuesto < 0 || $descuentoPresupuesto > 100) {
|
||||||
|
$_SESSION['flash_error'] = 'El descuento debe estar entre 0 y 100%.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($recargoPresupuesto < 0 || $recargoPresupuesto > 100) {
|
||||||
|
$_SESSION['flash_error'] = 'El recargo debe estar entre 0 y 100%.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardado de datos
|
||||||
|
|
||||||
|
// Datos extra del presupuesto
|
||||||
|
|
||||||
|
$datosExtraPresupuesto = [
|
||||||
|
'cond_iva' => $condIva,
|
||||||
|
'cuit' => $cuit,
|
||||||
|
'forma_pago' => $formaPago,
|
||||||
|
'tipo_pago' => $tipoPago,
|
||||||
|
'descuento' => $descuentoPresupuesto,
|
||||||
|
'recargo' => $recargoPresupuesto
|
||||||
|
];
|
||||||
|
|
||||||
|
// También los persisto en sesión para mantener estado
|
||||||
|
|
||||||
|
$_SESSION['presupuesto_extra'] = $datosExtraPresupuesto;
|
||||||
|
|
||||||
|
// Datos para la vista
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/clienteModel.php';
|
||||||
|
$clientemodel = new clienteModel();
|
||||||
|
|
||||||
|
$idCliente = ($_SESSION['presupuesto']['cliente']['id'] ?? null);
|
||||||
|
$productos = $presupuestomodel->listarProductos();
|
||||||
|
|
||||||
|
if ($idCliente === null) {
|
||||||
|
$_SESSION['flash_error'] = 'No seleccionó un cliente.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$vendedor = $_SESSION['user'] ?? null;
|
||||||
|
$cliente = $clientemodel->obtenerPorId($idCliente);
|
||||||
|
|
||||||
|
if (!$cliente) {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo identificar el cliente.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$vendedor) {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo identificar el usuario.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtotal que no contiene descuentos.
|
||||||
|
(Al ingresar descuento o recargo, lo guardo en un array separado, para no sobreescribir datos) */
|
||||||
|
|
||||||
|
$subtotal = $presupuestomodel->calcularSubtotal() ?? 0;
|
||||||
|
$descuento = $presupuestomodel->calcularDescuentoTotal() ?? 0;
|
||||||
|
|
||||||
|
// Validación del subtotal
|
||||||
|
|
||||||
|
if ($subtotal <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'El presupuesto no contiene productos válidos.';
|
||||||
|
header('Location: index.php?opt=crear_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Llamamos a la función que devuelve los datos actualizados en un array nuevo
|
||||||
|
|
||||||
|
$datosProvisorios = $presupuestomodel->actualizarDatosProvisorio($descuentoPresupuesto, $recargoPresupuesto, $subtotal, $descuento);
|
||||||
|
|
||||||
|
$recargo = $datosProvisorios['recargo'] ?? 0;
|
||||||
|
$descuento = $datosProvisorios['descuento'] ?? 0;
|
||||||
|
$total = $datosProvisorios['total'] ?? 0;
|
||||||
|
|
||||||
|
$_SESSION['presupuesto_final'] = [
|
||||||
|
'recargo' => $recargo,
|
||||||
|
'descuento' => $descuento,
|
||||||
|
'total' => $total
|
||||||
|
];
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Datos guardados de manera provisoria.";
|
||||||
|
header("Location: index.php?opt=resumen_presupuesto");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// GENERACION DE PRESUPUESTO PDF MEDIANTE USO DE LIBRERIA DOMPDF
|
||||||
|
|
||||||
|
case 'generar_presupuesto_pdf':
|
||||||
|
|
||||||
|
// Requiero datos de empresa y configuración
|
||||||
|
|
||||||
|
require __DIR__ . '/../models/configuracionModel.php';
|
||||||
|
$configuracionmodel = new configuracionModel();
|
||||||
|
|
||||||
|
$datosEmpresa = $configuracionmodel->obtenerConfiguracion();
|
||||||
|
|
||||||
|
$mapCondIVA = [
|
||||||
|
'consumidor_final' => 'Consumidor Final',
|
||||||
|
'responsable_inscripto' => 'Responsable Inscripto',
|
||||||
|
'monotributo' => 'Monotributo',
|
||||||
|
];
|
||||||
|
|
||||||
|
$datosEmpresa['cond_iva'] = $mapCondIVA[$datosEmpresa['cond_iva']];
|
||||||
|
|
||||||
|
// Requiero modelos de presupuesto y cliente
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/presupuestoModel.php';
|
||||||
|
$presupuestomodel = new presupuestoModel();
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/clienteModel.php';
|
||||||
|
$clientemodel = new clienteModel();
|
||||||
|
|
||||||
|
$productos = $presupuestomodel->listarProductos();
|
||||||
|
$idCliente = $_SESSION['presupuesto']['cliente']['id'] ?? null;
|
||||||
|
|
||||||
|
if ($idCliente === null) {
|
||||||
|
$_SESSION['flash_error'] = 'No seleccionó un cliente.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$vendedor = $_SESSION['user'] ?? null;
|
||||||
|
$cliente = $clientemodel->obtenerPorId($idCliente);
|
||||||
|
|
||||||
|
// DATOS FORM (requiero todo de nuevo desde el formulario mediante POST, no utilizo los datos ya almacenados en $_SESSION (son unicamente para mantener estado))
|
||||||
|
|
||||||
|
$condIVA = $_POST['cond_iva'] ?? '';
|
||||||
|
$cuit = ($_POST['cuit_1'] ?? '') . ($_POST['cuit_2'] ?? '') . ($_POST['cuit_3'] ?? '');
|
||||||
|
$formaPago = $_POST['forma_pago'] ?? '';
|
||||||
|
$tipoPago = $_POST['tipo_pago'] ?? '';
|
||||||
|
|
||||||
|
$descuentoPresupuesto = $_POST['descuento'] ?? 0;
|
||||||
|
$recargoPresupuesto = $_POST['recargo'] ?? 0;
|
||||||
|
|
||||||
|
// VALIDACIONES BASE
|
||||||
|
|
||||||
|
if (!$cliente || empty($productos)) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos incompletos para generar el presupuesto.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$vendedor) {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo identificar el usuario.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALIDACIÓN CONDICIÓN IVA
|
||||||
|
|
||||||
|
$condIvaPermitidas = [
|
||||||
|
'consumidor_final',
|
||||||
|
'responsable_inscripto',
|
||||||
|
'monotributo'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!in_array($condIVA, $condIvaPermitidas, true)) {
|
||||||
|
$_SESSION['flash_error'] = 'Condición de IVA inválida.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALIDACIÓN CUIT
|
||||||
|
|
||||||
|
$cuit = trim($cuit);
|
||||||
|
|
||||||
|
if (!preg_match('/^\d{10,11}$/', $cuit)) {
|
||||||
|
$_SESSION['flash_error'] = 'CUIT inválido. Debe contener solo números.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALIDACIÓN FORMA + TIPO DE PAGO
|
||||||
|
|
||||||
|
$reglasPago = [
|
||||||
|
'efectivo' => ['pago_unico'],
|
||||||
|
'transferencia' => ['pago_unico'],
|
||||||
|
'echeq' => ['pago_unico'],
|
||||||
|
'debito' => ['pago_unico'],
|
||||||
|
'credito' => ['pago_unico', '3_cuotas', '6_cuotas', '12_cuotas'],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!isset($reglasPago[$formaPago]) || !in_array($tipoPago, $reglasPago[$formaPago], true)) {
|
||||||
|
$_SESSION['flash_error'] = 'Combinación de forma y tipo de pago inválida.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALIDACIÓN DESCUENTO / RECARGO
|
||||||
|
|
||||||
|
if ($descuentoPresupuesto < 0 || $descuentoPresupuesto > 100) {
|
||||||
|
$_SESSION['flash_error'] = 'El descuento debe estar entre 0 y 100%.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($recargoPresupuesto < 0 || $recargoPresupuesto > 100) {
|
||||||
|
$_SESSION['flash_error'] = 'El recargo debe estar entre 0 y 100%.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RECÁLCULO DE TOTALES (SIEMPRE)
|
||||||
|
|
||||||
|
$subtotal = $presupuestomodel->calcularSubtotal() ?? 0;
|
||||||
|
|
||||||
|
if ($subtotal <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'El presupuesto no contiene productos válidos.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$descuentoProductos = $presupuestomodel->calcularDescuentoTotal() ?? 0;
|
||||||
|
|
||||||
|
$datosProvisorios = $presupuestomodel->actualizarDatosProvisorio($descuentoPresupuesto, $recargoPresupuesto, $subtotal, $descuentoProductos);
|
||||||
|
|
||||||
|
$recargo = $datosProvisorios['recargo'] ?? 0;
|
||||||
|
$descuento = $datosProvisorios['descuento'] ?? 0;
|
||||||
|
$total = $datosProvisorios['total'] ?? 0;
|
||||||
|
|
||||||
|
if ($total <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Total inválido.';
|
||||||
|
header('Location: index.php?opt=resumen_presupuesto');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MAPEOS PARA PDF
|
||||||
|
|
||||||
|
$mapCondIVA = [
|
||||||
|
'consumidor_final' => 'Consumidor Final',
|
||||||
|
'responsable_inscripto' => 'Responsable Inscripto',
|
||||||
|
'monotributo' => 'Monotributo',
|
||||||
|
];
|
||||||
|
|
||||||
|
$mapFormaPago = [
|
||||||
|
'efectivo'=> 'Efectivo',
|
||||||
|
'transferencia' => 'Transferencia',
|
||||||
|
'debito' => 'Débito',
|
||||||
|
'credito' => 'Crédito',
|
||||||
|
'echeq' => 'Echeq',
|
||||||
|
];
|
||||||
|
|
||||||
|
$mapTipoPago = [
|
||||||
|
'pago_unico' => 'Pago Único',
|
||||||
|
'3_cuotas' => '3 Cuotas',
|
||||||
|
'6_cuotas' => '6 Cuotas',
|
||||||
|
'12_cuotas' => '12 Cuotas',
|
||||||
|
];
|
||||||
|
|
||||||
|
$condIVALabel = $mapCondIVA[$condIVA];
|
||||||
|
$formaPagoLabel = $mapFormaPago[$formaPago];
|
||||||
|
$tipoPagoLabel = $mapTipoPago[$tipoPago];
|
||||||
|
|
||||||
|
// CÁLCULO DE CUOTAS
|
||||||
|
|
||||||
|
if ($tipoPago !== 'pago_unico') {
|
||||||
|
$cuota = (int) explode('_', $tipoPago)[0];
|
||||||
|
$valorCuota = $total / $cuota;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NÚMERO DE PRESUPUESTO (RANDOM) (decidí dejarlo con código aleatorio, se podría modificar perfectamente)
|
||||||
|
|
||||||
|
$parte2 = str_pad((string) rand(0, 99999999), 8, '0', STR_PAD_LEFT);
|
||||||
|
$numeroPresupuesto = '0001' . '-' . $parte2;
|
||||||
|
|
||||||
|
// RENDER PDF
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../pdf/presupuestos/presupuestoPDF.php';
|
||||||
|
$html = ob_get_clean();
|
||||||
|
|
||||||
|
$cssPath = '/var/www/html/css/stylesPDF.css';
|
||||||
|
$css = file_get_contents($cssPath);
|
||||||
|
|
||||||
|
// Configuración de opciones
|
||||||
|
|
||||||
|
$options = new Options();
|
||||||
|
$options->set('isRemoteEnabled', true); // Permite cargar recursos externos (CSS, imágenes, etc.)
|
||||||
|
$options->set('chroot', '/var/www/html'); // Define directorios base para recursos locales
|
||||||
|
$options->set('defaultFont', 'DejaVu Sans');
|
||||||
|
|
||||||
|
// Creación de instancia Dompdf con las opciones seteadas
|
||||||
|
|
||||||
|
$dompdf = new Dompdf($options);
|
||||||
|
|
||||||
|
// Cargar el HTML que se va a convertir en PDF
|
||||||
|
|
||||||
|
$dompdf->loadHtml("
|
||||||
|
<style>$css</style>
|
||||||
|
$html
|
||||||
|
");
|
||||||
|
|
||||||
|
$dompdf->setPaper('A4', 'portrait'); // Define el tamanio de hoja y la orientación
|
||||||
|
$dompdf->render(); // Renderiza el PDF (procesa el HTML)
|
||||||
|
|
||||||
|
// Enviar el PDF al browser
|
||||||
|
|
||||||
|
$dompdf->stream(
|
||||||
|
'Presupuesto N° ' . $numeroPresupuesto . '.pdf',
|
||||||
|
['Attachment' => true] // false = se muestra en el navegador | true = descarga directa
|
||||||
|
);
|
||||||
|
|
||||||
|
// Eliminamos datos ingresados y vaciamos caja
|
||||||
|
|
||||||
|
$presupuestomodel->vaciarCajaPresupuesto();
|
||||||
|
unset(($_SESSION)['presupuesto']['cliente']);
|
||||||
|
unset($_SESSION['presupuesto_final']);
|
||||||
|
unset($_SESSION['presupuesto_extra']);
|
||||||
|
unset($_SESSION['prov_presupuesto']);
|
||||||
|
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// DEFAULT (404)
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
header("Location: index.php?opt=404View.php");
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
@@ -0,0 +1,541 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Validacion de token
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { // Recorre todos los caracteres (siempre) y retorna true or false
|
||||||
|
$_SESSION['flash_error'] = 'Acción inválida.';
|
||||||
|
header('Location: index.php?opt=inicio_operador');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requiere el modelo de producto
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/productoModel.php';
|
||||||
|
$productomodel = new productoModel;
|
||||||
|
|
||||||
|
// Validar sesón
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$opt = $_GET['opt'] ?? null;
|
||||||
|
|
||||||
|
// Manejo de opciones $opt
|
||||||
|
|
||||||
|
switch($opt) {
|
||||||
|
|
||||||
|
// LISTAR PRODUCTOS
|
||||||
|
|
||||||
|
case 'productos':
|
||||||
|
|
||||||
|
// Utilizamos el modelo para obtener la lista completa de productos
|
||||||
|
|
||||||
|
$productos = $productomodel->listarProductos();
|
||||||
|
|
||||||
|
// Saneamiento de datos para enviar a la view
|
||||||
|
|
||||||
|
foreach ($productos as $i => $p) {
|
||||||
|
|
||||||
|
if (($p['precio'] === null) || ($p['precio'] == 0)) {
|
||||||
|
$productos[$i]['precio'] = '-';
|
||||||
|
} else {
|
||||||
|
$productos[$i]['precio'] = '$ ' . number_format($p['precio'], 2, '.', ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($p['stock'] === null || $p['stock'] === 0) {
|
||||||
|
$productos[$i]['stock'] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
$productos[$i]['descuento_total'] = number_format($p['descuento_total'], 2, '.', '') . ' %';
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/productos/listaProductosView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// LISTAR PRODUCTOS INACTIVOS
|
||||||
|
|
||||||
|
case 'productos_inactivos':
|
||||||
|
|
||||||
|
// Utilizamos el modelo para obtener la lista completa de productos
|
||||||
|
|
||||||
|
$productos = $productomodel->listarProductosInactivos();
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/productos/listaProductosInactivosView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// CREAR PRODUCTO
|
||||||
|
|
||||||
|
case 'crear_producto':
|
||||||
|
|
||||||
|
// Mostramos la vista con los datos correspondientes
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
$categorias = $productomodel->listarCategorias();
|
||||||
|
$tipos = $productomodel->listarTipos();
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/productos/crearProductoView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// En caso de modificación de datos
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$marca = trim($_POST['marca'] ?? '');
|
||||||
|
$modelo = trim($_POST['modelo'] ?? '');
|
||||||
|
$id_categoria = trim($_POST['categoria'] ?? '');
|
||||||
|
$id_tipo = trim($_POST['tipo'] ?? '');
|
||||||
|
|
||||||
|
// Normalización de datos
|
||||||
|
|
||||||
|
$descuentoRaw = trim($_POST['descuento'] ?? '');
|
||||||
|
$descuento = $descuentoRaw === '' ? null : $descuentoRaw;
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
if ($marca === '') {
|
||||||
|
$errores[] = "La marca no puede estar vacía.";
|
||||||
|
} else if (!preg_match("/^[A-Za-zÁÉÍÓÚÑáéíóúñ0-9]+(?: [A-Za-zÁÉÍÓÚÑáéíóúñ0-9]+)*$/", $marca)) {
|
||||||
|
$errores[] = "La marca solo puede contener letras, números y espacios.";
|
||||||
|
} else if (strlen($marca) < 2) {
|
||||||
|
$errores[] = "La marca debe tener al menos 2 caracteres.";
|
||||||
|
} else if (strlen($marca) > 50) {
|
||||||
|
$errores[] = "La marca no puede superar los 50 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($modelo === '') {
|
||||||
|
$errores[] = "El modelo no puede estar vacío.";
|
||||||
|
} else if (!preg_match("/^[A-Za-zÁÉÍÓÚÑáéíóúñ0-9.\-]+(?: [A-Za-zÁÉÍÓÚÑáéíóúñ0-9.\-]+)*$/", $modelo)) {
|
||||||
|
$errores[] = "El modelo solo puede contener letras, números, espacios, guiones y puntos";
|
||||||
|
} else if (strlen($modelo) < 2) {
|
||||||
|
$errores[] = "El modelo debe tener al menos 2 caracteres.";
|
||||||
|
} else if (strlen($modelo) > 50) {
|
||||||
|
$errores[] = "El modelo no puede superar los 50 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($descuento !== null) {
|
||||||
|
|
||||||
|
if (!preg_match("/^\d{1,3}(\.\d{1,2})?$/", $descuento)) {
|
||||||
|
$errores[] = "El descuento debe ser un número válido con hasta 2 decimales.";
|
||||||
|
} else if ((float)$descuento <= 0 || (float)$descuento > 100) {
|
||||||
|
$errores[] = "El descuento debe estar entre 0 a 100.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($id_categoria === '') {
|
||||||
|
$errores[] = "Debe seleccionar una categoría.";
|
||||||
|
} else if (!filter_var($id_categoria, FILTER_VALIDATE_INT) || $id_categoria <= 0) {
|
||||||
|
$errores[] = "La categoría seleccionada no es válida.";
|
||||||
|
} else if (!$productomodel->existeCategoria($id_categoria)) {
|
||||||
|
$errores[] = "La categoría ingresada no existe.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($id_tipo === '') {
|
||||||
|
$errores[] = "Debe seleccionar un tipo.";
|
||||||
|
} else if (!filter_var($id_tipo, FILTER_VALIDATE_INT) || $id_tipo <= 0) {
|
||||||
|
$errores[] = "El tipo seleccionado no es válido.";
|
||||||
|
} else if (!$productomodel->existeTipo($id_tipo)) {
|
||||||
|
$errores[] = "El tipo ingresado no existe.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($id_tipo === 2) && ($descuento !== null)) {
|
||||||
|
$errores[] = "No se permite descuento si el tipo de producto es Servicio.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores);
|
||||||
|
header("Location: index.php?opt=crear_producto");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utilizamos el método del modelo para crear el producto
|
||||||
|
|
||||||
|
if ($productomodel->crearProducto([
|
||||||
|
'marca' => $marca,
|
||||||
|
'modelo' => $modelo,
|
||||||
|
'precio' => null,
|
||||||
|
'stock' => null,
|
||||||
|
'descuento' => $descuento !== null ? (float)$descuento : null,
|
||||||
|
'id_categoria' => $id_categoria,
|
||||||
|
'id_tipo' => $id_tipo
|
||||||
|
])) {
|
||||||
|
$_SESSION['flash_success'] = "Producto creado correctamente.";
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = "No se pudo crear el producto.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=productos");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EDITAR PRODUCTO
|
||||||
|
|
||||||
|
case 'editar_producto':
|
||||||
|
|
||||||
|
$idEditar = (int) ($_GET['id'] ?? 0);
|
||||||
|
|
||||||
|
if ($idEditar <= 0) {
|
||||||
|
header("Location: index.php?opt=productos");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Busco el producto en la base de datos mediante el modelo
|
||||||
|
|
||||||
|
$producto = $productomodel->obtenerPorId($idEditar);
|
||||||
|
|
||||||
|
if (!$producto) {
|
||||||
|
$_SESSION['flash_error'] = "No se encontró el producto.";
|
||||||
|
header("Location: index.php?opt=productos");
|
||||||
|
exit;
|
||||||
|
};
|
||||||
|
|
||||||
|
// (Para indicar que aún no existen pedidos que incluyan dicho producto (no existe ID de pedido))
|
||||||
|
|
||||||
|
if ($producto['id_pedido'] === null) {
|
||||||
|
$producto['id_pedido'] = 'Aún no existe pedido que incluya este producto';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si el descuento es null lo seteamos en 0
|
||||||
|
|
||||||
|
if ($producto['descuento'] === null) {
|
||||||
|
$producto['descuento'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mostrar datos de producto
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
$categorias = $productomodel->listarCategorias();
|
||||||
|
$tipos = $productomodel->listarTipos();
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/productos/editarProductoView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caso en donde se modifican los datos
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
// Normalización de datos
|
||||||
|
|
||||||
|
$descuentoRaw = trim($_POST['descuento'] ?? '');
|
||||||
|
$descuento = $descuentoRaw === '' ? null : $descuentoRaw;
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
if ($descuento !== null) {
|
||||||
|
|
||||||
|
if (!preg_match("/^\d{1,3}(\.\d{1,2})?$/", $descuento)) {
|
||||||
|
$errores[] = "El descuento debe ser un número válido con hasta 2 decimales.";
|
||||||
|
} else if ((float)$descuento < 0 || (float)$descuento > 100) {
|
||||||
|
$errores[] = "El descuento debe estar entre 0 a 100.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores);
|
||||||
|
header("Location: index.php?opt=editar_producto&id=" . $idEditar);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculoamos precio (en caso de que se modifique descuento)
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/pedidoModel.php';
|
||||||
|
$pedidomodel = new pedidoModel();
|
||||||
|
|
||||||
|
$costoUnitario = $pedidomodel->obtenerCosto($_GET['id']);
|
||||||
|
$categoria = $productomodel->obtenerPorIdCategoria($producto['id_categoria']);
|
||||||
|
|
||||||
|
// Antes de guardar los datos verificamos que la suma del descuento no sea mayor a 100
|
||||||
|
|
||||||
|
if (($categoria['porcentaje_ganancia'] + $descuento) > 100) {
|
||||||
|
$_SESSION['flash_error'] = "La suma entre el descuento ingresado y el descuento de la categoria supera el 100%. Verifique.";
|
||||||
|
header("Location:index.php?opt=editar_producto&id=$idEditar");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creamos el array con los datos correspondientes
|
||||||
|
|
||||||
|
$guardarData = [
|
||||||
|
'precio' => $costoUnitario !== null ? (100 + $categoria['porcentaje_ganancia']) * $costoUnitario / 100 : null,
|
||||||
|
'descuento' => $descuento !== null ? (float) $descuento : null,
|
||||||
|
'id_categoria' => $producto['id_categoria']
|
||||||
|
];
|
||||||
|
|
||||||
|
// Utilizamos la función del modelo para actualizar el producto
|
||||||
|
|
||||||
|
if ($productomodel->actualizarProducto($_GET['id'], $guardarData)) {
|
||||||
|
$_SESSION['flash_success'] = "Datos actualizados correctamente.";
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = "No se pudo actualizar los datos.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=productos");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAMBIAR ESTADO DEL PRODUCTO (ACTIVO / INACTIVO)
|
||||||
|
|
||||||
|
case 'cambiar_estado':
|
||||||
|
|
||||||
|
$idProducto = (int) ($_POST['id'] ?? 0);
|
||||||
|
$nuevoEstado = isset($_POST['estado']) ? (bool) $_POST['estado'] : null;
|
||||||
|
|
||||||
|
if ($idProducto <= 0 || !is_bool($nuevoEstado)) {
|
||||||
|
header("Location: index.php?opt=productos");
|
||||||
|
exit;
|
||||||
|
};
|
||||||
|
|
||||||
|
$productomodel->actualizarEstado($idProducto, $nuevoEstado);
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Estado actualizado correctamente.";
|
||||||
|
header("Location: index.php?opt=productos");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// LISTAR LAS CATEGORIAS DE PRODUCTOS
|
||||||
|
|
||||||
|
case 'categorias':
|
||||||
|
|
||||||
|
$categorias = $productomodel->listarCategorias();
|
||||||
|
|
||||||
|
foreach ($categorias as $i => $c) {
|
||||||
|
|
||||||
|
if (($categorias)[$i]['porcentaje_ganancia'] === null) {
|
||||||
|
$c['porcentaje_ganancia'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($categorias)[$i]['descuento'] === null) {
|
||||||
|
$c['descuento'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$categorias[$i]['porcentaje_ganancia'] = number_format($c['porcentaje_ganancia'], 2, '.', '') . ' %';
|
||||||
|
$categorias[$i]['descuento'] = number_format($c['descuento'], 2, '.', '') . ' %';
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/productos/listaCategoriasView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// CREAR NUEVA CATEGORIA DE PRODUCTOS
|
||||||
|
|
||||||
|
case 'crear_categoria':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/productos/crearCategoriaView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$nombre = trim($_POST['nombre'] ?? '');
|
||||||
|
|
||||||
|
// Normalización
|
||||||
|
|
||||||
|
$porcentaje_gananciaRaw = trim($_POST['porcentaje_ganancia'] ?? '');
|
||||||
|
$porcentaje_ganancia = $porcentaje_gananciaRaw === '' ? null : $porcentaje_gananciaRaw;
|
||||||
|
|
||||||
|
$descuentoRaw = trim($_POST['descuento'] ?? '');
|
||||||
|
$descuento = $descuentoRaw === '' ? null : $descuentoRaw;
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
if ($nombre === '') {
|
||||||
|
$errores[] = "El nombre no puede estar vacío.";
|
||||||
|
} else if (!preg_match("/^[A-Za-zÁÉÍÓÚÑáéíóúñ0-9]+(?: [A-Za-zÁÉÍÓÚÑáéíóúñ0-9]+)*$/", $nombre)) {
|
||||||
|
$errores[] = "El nombre solo puede contener letras, números y espacios.";
|
||||||
|
} else if (strlen($nombre) < 2) {
|
||||||
|
$errores[] = "El nombre debe tener al menos 2 caracteres.";
|
||||||
|
} else if (strlen($nombre) > 50) {
|
||||||
|
$errores[] = "El nombre no puede superar los 50 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($porcentaje_ganancia !== null) {
|
||||||
|
|
||||||
|
if (!preg_match("/^\d{1,3}(\.\d{1,2})?$/", $porcentaje_ganancia)) {
|
||||||
|
$errores[] = "La ganancia debe ser un número válido con hasta 2 decimales.";
|
||||||
|
} else if ((float)$porcentaje_ganancia <= 0 || (float)$porcentaje_ganancia > 100) {
|
||||||
|
$errores[] = "La ganancia debe estar entre 0 a 100.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($descuento !== null) {
|
||||||
|
|
||||||
|
if (!preg_match("/^\d{1,3}(\.\d{1,2})?$/", $descuento)) {
|
||||||
|
$errores[] = "El descuento debe ser un número válido con hasta 2 decimales.";
|
||||||
|
} else if ((float)$descuento < 0 || (float)$descuento > 100) {
|
||||||
|
$errores[] = "El descuento debe estar entre 0 a 100.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores);
|
||||||
|
header("Location: index.php?opt=crear_categoria");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($productomodel->crearCategoria([
|
||||||
|
'nombre' => $nombre,
|
||||||
|
'porcentaje_ganancia' => $porcentaje_ganancia !== null ? (float) $porcentaje_ganancia : null,
|
||||||
|
'descuento' => $descuento !== null ? (float) $descuento : null
|
||||||
|
])) {
|
||||||
|
$_SESSION['flash_success'] = "Categoría creada correctamente.";
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = "No se pudo crear la categoría.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=categorias");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EDITAR CATEGORIA
|
||||||
|
|
||||||
|
case 'editar_categoria':
|
||||||
|
|
||||||
|
$idEditar = (int) ($_GET['id'] ?? 0);
|
||||||
|
|
||||||
|
if ($idEditar <= 0) {
|
||||||
|
header("Location: index.php?opt=categorias");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Busco la categoría en la base de datos mediante el modelo
|
||||||
|
|
||||||
|
$categorias = $productomodel->obtenerPorIdCategoria($idEditar);
|
||||||
|
|
||||||
|
if (!$categorias) {
|
||||||
|
header("Location: index.php?opt=categorias");
|
||||||
|
exit;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Si el descuento o el porcentaje_ganancia es null lo seteamos en 0
|
||||||
|
|
||||||
|
if ($categorias['porcentaje_ganancia'] === null) {
|
||||||
|
$categorias['porcentaje_ganancia'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($categorias['descuento'] === null) {
|
||||||
|
$categorias['descuento'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mostrar datos de producto
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/productos/editarCategoriaView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caso en donde se modifican los datos
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$nombre = trim($_POST['nombre'] ?? '');
|
||||||
|
|
||||||
|
// Normalización
|
||||||
|
|
||||||
|
$porcentaje_gananciaRaw = trim($_POST['porcentaje_ganancia'] ?? '');
|
||||||
|
$porcentaje_ganancia = $porcentaje_gananciaRaw === '' ? null : $porcentaje_gananciaRaw;
|
||||||
|
|
||||||
|
$descuentoRaw = trim($_POST['descuento'] ?? '');
|
||||||
|
$descuento = $descuentoRaw === '' ? null : $descuentoRaw;
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
if ($nombre === '') {
|
||||||
|
$errores[] = "El nombre no puede estar vacío.";
|
||||||
|
} else if (!preg_match("/^[A-Za-zÁÉÍÓÚÑáéíóúñ0-9]+(?: [A-Za-zÁÉÍÓÚÑáéíóúñ0-9]+)*$/", $nombre)) {
|
||||||
|
$errores[] = "El nombre solo puede contener letras, números y espacios.";
|
||||||
|
} else if (strlen($nombre) < 2) {
|
||||||
|
$errores[] = "El nombre debe tener al menos 2 caracteres.";
|
||||||
|
} else if (strlen($nombre) > 50) {
|
||||||
|
$errores[] = "El nombre no puede superar los 50 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($porcentaje_ganancia !== null) {
|
||||||
|
|
||||||
|
if (!preg_match("/^\d{1,3}(\.\d{1,2})?$/", $porcentaje_ganancia)) {
|
||||||
|
$errores[] = "La ganancia debe ser un número válido con hasta 2 decimales.";
|
||||||
|
} else if ((float)$porcentaje_ganancia <= 0 || (float)$porcentaje_ganancia > 100) {
|
||||||
|
$errores[] = "La ganancia debe estar entre 0 a 100.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($descuento !== null) {
|
||||||
|
|
||||||
|
if (!preg_match("/^\d{1,3}(\.\d{1,2})?$/", $descuento)) {
|
||||||
|
$errores[] = "El descuento debe ser un número válido con hasta 2 decimales.";
|
||||||
|
} else if ((float)$descuento < 0 || (float)$descuento > 100) {
|
||||||
|
$errores[] = "El descuento debe estar entre 0 a 100.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores);
|
||||||
|
header("Location: index.php?opt=editar_categoria&id=" . $idEditar);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificamos que la suma entre los descuentos no supere el 100%
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/productoModel.php';
|
||||||
|
$productomodel = new productoModel();
|
||||||
|
|
||||||
|
$productosDescuentos = $productomodel->obtenerDescuento($idEditar);
|
||||||
|
|
||||||
|
foreach ($productosDescuentos as $i => $p) {
|
||||||
|
|
||||||
|
if (($productosDescuentos[$i]['descuento'] + $descuento) > 100) {
|
||||||
|
$_SESSION['flash_error'] = "Existe al menos un producto que supera el 100% de sumatoria en el descuento. Verifique.";
|
||||||
|
header("Location:index.php?opt=editar_categoria&id=$idEditar");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$guardarData = [
|
||||||
|
'nombre' => $nombre,
|
||||||
|
'porcentaje_ganancia' => $porcentaje_ganancia !== null ? (float) $porcentaje_ganancia : null,
|
||||||
|
'descuento' => $descuento !== null ? (float) $descuento : null
|
||||||
|
];
|
||||||
|
|
||||||
|
// Obtenemos productos con sus respectivos costos para recalcular el precio de venta
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/pedidoModel.php';
|
||||||
|
$pedidomodel = new pedidoModel();
|
||||||
|
|
||||||
|
$costosProductos = $pedidomodel->obtenerCostosPorCategoria($idEditar);
|
||||||
|
|
||||||
|
if ($productomodel->actualizarPreciosPorCategoria($idEditar, $guardarData, $costosProductos)) {
|
||||||
|
$_SESSION['flash_success'] = "Datos actualizados correctamente.";
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = "No se pudo actualizar la categoría.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=categorias");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEFAULT (404)
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/404View.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
@@ -0,0 +1,300 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Validacion de token
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { // Recorre todos los caracteres (siempre) y retorna true or false
|
||||||
|
$_SESSION['flash_error'] = 'Acción inválida.';
|
||||||
|
header('Location: index.php?opt=inicio_operador');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requiere modelo de proveedor
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/proveedorModel.php';
|
||||||
|
$proveedormodel = new proveedorModel;
|
||||||
|
|
||||||
|
// Validar sesión
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$opt = $_GET['opt'] ?? null;
|
||||||
|
|
||||||
|
// Manejo de opciones enviadaas por URL
|
||||||
|
|
||||||
|
switch ($opt) {
|
||||||
|
|
||||||
|
// LISTAR PROVEEDORES
|
||||||
|
|
||||||
|
case 'proveedores':
|
||||||
|
|
||||||
|
// Utilizamos el modelo para obtener la lista completa de proveedores
|
||||||
|
|
||||||
|
$proveedores = $proveedormodel->listarProveedores();
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/proveedores/listaProveedoresView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// CREAR PROVEEDOR
|
||||||
|
|
||||||
|
case 'crear_proveedor':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
$_SESSION['return_opt'] = $_GET['from'] ?? 'proveedores';
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/proveedores/crearProveedorView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$razon_social = trim($_POST['razon_social'] ?? '');
|
||||||
|
$telefono = trim($_POST['telefono'] ?? '');
|
||||||
|
$email = trim($_POST['email'] ?? '');
|
||||||
|
$condIVA = trim($_POST['cond_iva'] ?? 'consumidor_final');
|
||||||
|
|
||||||
|
$cuit1 = trim($_POST['cuit_1'] ?? '');
|
||||||
|
$cuit2 = trim($_POST['cuit_2'] ?? '');
|
||||||
|
$cuit3 = trim($_POST['cuit_3'] ?? '');
|
||||||
|
|
||||||
|
$cuit = $cuit1 . $cuit2 . $cuit3;
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
if ($razon_social === '') {
|
||||||
|
$errores[] = "La razón social no puede estar vacía.";
|
||||||
|
} else if (!preg_match("/^[A-Za-zÁÉÍÓÚÑáéíóúñ0-9&',.\-]+(?: [A-Za-zÁÉÍÓÚÑáéíóúñ0-9&',.\-]+)*(?: (S\.A\.|S\.R\.L\.|S\.C\.|S\.C\.P\.|S\.A\.S\.))?$/", $razon_social)) {
|
||||||
|
$errores[] = "La razón social contiene caracteres inválidos o la abreviatura legal final no es correcta.";
|
||||||
|
} else if (strlen($razon_social) < 2) {
|
||||||
|
$errores[] = "La razón social debe tener al menos 2 caracteres.";
|
||||||
|
} else if (strlen($razon_social) > 100) {
|
||||||
|
$errores[] = "La razón social no puede superar los 100 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($telefono === '') {
|
||||||
|
$errores[] = "El teléfono no puede estar vacío.";
|
||||||
|
} else if (!preg_match('/^[0-9]{10}$/', $telefono)) {
|
||||||
|
$errores[] = "El teléfono debe tener 10 dígitos.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($email === '') {
|
||||||
|
$errores[] = "El email no puede estar vacío.";
|
||||||
|
} else if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errores[] = "Formato de email inválido.";
|
||||||
|
} else if (strlen($email) > 100) {
|
||||||
|
$errores[] = "El email no puede superar los 100 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$condicionesPermitidas = [
|
||||||
|
'consumidor_final',
|
||||||
|
'monotributo',
|
||||||
|
'responsable_inscripto'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!in_array($condIVA, $condicionesPermitidas, true)) {
|
||||||
|
|
||||||
|
$errores[] = 'Condición de IVA inválida.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cuit !== '') {
|
||||||
|
|
||||||
|
if (!preg_match('/^\d{11}$/', $cuit)) {
|
||||||
|
|
||||||
|
$errores[] = 'El CUIT debe contener 11 dígitos.';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$cuit = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardamos errores para mostrarlos por pantalla
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores); // Se guarda en $_SESSION el error
|
||||||
|
header("Location: index.php?opt=crear_proveedor");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificación de existencia de proveedor
|
||||||
|
|
||||||
|
if ($proveedormodel->verificarExistencia([
|
||||||
|
'razon_social' => $razon_social,
|
||||||
|
'email' => $email,
|
||||||
|
'cuit' => $cuit
|
||||||
|
])) {
|
||||||
|
$_SESSION['flash_error'] = "El proveedor ya existe.";
|
||||||
|
|
||||||
|
header("Location: index.php?opt=crear_proveedor");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($proveedormodel->crearProveedor([
|
||||||
|
'razon_social' => $razon_social,
|
||||||
|
'telefono' => $telefono,
|
||||||
|
'email' => $email,
|
||||||
|
'cuit' => $cuit,
|
||||||
|
'cond_iva' => $condIVA
|
||||||
|
])) {
|
||||||
|
$_SESSION['flash_succes'] = "Proveedor creado correctamente.";
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = "Ocurrió un error al crear el proveedor.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$opt = $_SESSION['return_opt'];
|
||||||
|
|
||||||
|
header("Location: index.php?opt=$opt");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EDITAR PROVEEDOR
|
||||||
|
|
||||||
|
case 'editar_proveedor':
|
||||||
|
|
||||||
|
$idEditar = (int) ($_GET['id'] ?? 0);
|
||||||
|
|
||||||
|
if ($idEditar < 0) {
|
||||||
|
header("Location: index.php?opt=proveedores");
|
||||||
|
exit;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Busco el cliente en la base de datos mediante el modelo
|
||||||
|
|
||||||
|
$proveedor = $proveedormodel->obtenerPorId($idEditar);
|
||||||
|
|
||||||
|
if (!$proveedor) {
|
||||||
|
header("Location: index.php?opt=proveedores");
|
||||||
|
exit;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mostrar datos de proveedor
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/proveedores/editarProveedorView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caso en donde se modifican los datos
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$razon_social = trim($_POST['razon_social']) ?? '';
|
||||||
|
$telefono = trim($_POST['telefono']) ?? '';
|
||||||
|
$email = trim($_POST['email']) ?? '';
|
||||||
|
$condIVA = trim($_POST['cond_iva'] ?? 'consumidor_final');
|
||||||
|
|
||||||
|
$cuit1 = trim($_POST['cuit_1'] ?? '');
|
||||||
|
$cuit2 = trim($_POST['cuit_2'] ?? '');
|
||||||
|
$cuit3 = trim($_POST['cuit_3'] ?? '');
|
||||||
|
|
||||||
|
$cuit = $cuit1 . $cuit2 . $cuit3;
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
if ($razon_social === '') {
|
||||||
|
$errores[] = "La razón social no puede estar vacía.";
|
||||||
|
} else if (!preg_match("/^[A-Za-zÁÉÍÓÚÑáéíóúñ0-9&',.\-]+(?: [A-Za-zÁÉÍÓÚÑáéíóúñ0-9&',.\-]+)*(?: (S\.A\.|S\.R\.L\.|S\.C\.|S\.C\.P\.|S\.A\.S\.))?$/", $razon_social)) {
|
||||||
|
$errores[] = "La razón social contiene caracteres inválidos o la abreviatura legal final no es correcta.";
|
||||||
|
} else if (strlen($razon_social) < 2) {
|
||||||
|
$errores[] = "La razón social debe tener al menos 2 caracteres.";
|
||||||
|
} else if (strlen($razon_social) > 100) {
|
||||||
|
$errores[] = "La razón social no puede superar los 100 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($telefono === '') {
|
||||||
|
$errores[] = "El teléfono no puede estar vacío.";
|
||||||
|
} else if (!preg_match('/^[0-9]{10}$/', $telefono)) {
|
||||||
|
$errores[] = "El teléfono debe tener 10 dígitos.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($email === '') {
|
||||||
|
$errores[] = "El email no puede estar vacío.";
|
||||||
|
} else if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errores[] = "Formato de email inválido.";
|
||||||
|
} else if (strlen($email) > 100) {
|
||||||
|
$errores[] = "El email no puede superar los 100 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$condicionesPermitidas = [
|
||||||
|
'consumidor_final',
|
||||||
|
'monotributo',
|
||||||
|
'responsable_inscripto'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!in_array($condIVA, $condicionesPermitidas, true)) {
|
||||||
|
|
||||||
|
$errores[] = 'Condición de IVA inválida.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($condIVA !== 'consumidor_final') && ($cuit === '')) {
|
||||||
|
|
||||||
|
$errores[] = 'Debe ingresar el CUIT';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cuit !== '') {
|
||||||
|
|
||||||
|
if (!preg_match('/^\d{11}$/', $cuit)) {
|
||||||
|
|
||||||
|
$errores[] = 'El CUIT debe contener 11 dígitos.';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$cuit = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores); // Guardamos los errores en $_SESSION
|
||||||
|
header("Location: index.php?opt=editar_proveedor");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificación de existencia de proveedor
|
||||||
|
|
||||||
|
if ($proveedormodel->verificarExistencia([
|
||||||
|
'razon_social' => $razon_social,
|
||||||
|
'email' => $email,
|
||||||
|
'cuit' => $cuit
|
||||||
|
], $idEditar)) {
|
||||||
|
$_SESSION['flash_error'] = "El proveedor ya existe.";
|
||||||
|
|
||||||
|
header("Location: index.php?opt=editar_proveedor&id=$idEditar");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$guardarData = [
|
||||||
|
'razon_social' => $razon_social,
|
||||||
|
'telefono' => $telefono,
|
||||||
|
'email' => $email,
|
||||||
|
'cuit' => $cuit,
|
||||||
|
'cond_iva' => $condIVA
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($proveedormodel->actualizarProveedor($proveedor['id'], $guardarData)) {
|
||||||
|
$_SESSION['flash_success'] = "Datos actualizados correctamente."; // Guardo en $_SESSION el success
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = "No se pudo actualizar el proveedor.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=proveedores");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEFAULT (404)
|
||||||
|
|
||||||
|
default:
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/404View.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
@@ -0,0 +1,915 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Validacion de token
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { // Recorre todos los caracteres (siempre) y retorna true or false
|
||||||
|
$_SESSION['flash_error'] = 'Acción inválida.';
|
||||||
|
header('Location: index.php?opt=inicio_operador');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requiero helper que contiene métodos para encriptar y desencriptar contraseñas
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../helpers/crypto.php';
|
||||||
|
|
||||||
|
// Librería domPDF
|
||||||
|
|
||||||
|
require __DIR__ . '/../libs/dompdf/autoload.inc.php';
|
||||||
|
|
||||||
|
use Dompdf\Dompdf;
|
||||||
|
use Dompdf\Options;
|
||||||
|
|
||||||
|
// Validar sesión
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $_SESSION['user']; // Defino el array del usuario dentro de una variable
|
||||||
|
$rol = $user['rol']; // Defino el rol dentro de la variable
|
||||||
|
$esAdmin = ($rol === 'admin'); // Variable booleana, true = admin
|
||||||
|
|
||||||
|
$opt = $_GET['opt'] ?? null;
|
||||||
|
|
||||||
|
// Manejo de $opt
|
||||||
|
|
||||||
|
switch ($opt) {
|
||||||
|
|
||||||
|
// CREAR SERVICIO
|
||||||
|
|
||||||
|
case 'crear_servicio':
|
||||||
|
|
||||||
|
// Se muestra la vista de creación de servicios
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
if (isset($_SESSION['servicio']['cliente'])) {
|
||||||
|
$cliente = $_SESSION['servicio']['cliente'];
|
||||||
|
} else {
|
||||||
|
$cliente = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/productoModel.php';
|
||||||
|
|
||||||
|
$productomodel = new productoModel;
|
||||||
|
|
||||||
|
$productos = $productomodel->busquedaParaServicio();
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/servicios/crearServicioView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GUARDADO INICIAL DEL SERVICIO
|
||||||
|
|
||||||
|
case 'guardar_servicio':
|
||||||
|
|
||||||
|
// Cliente y vendedor
|
||||||
|
|
||||||
|
$cliente = $_SESSION['servicio']['cliente'] ?? null;
|
||||||
|
$vendedor = $_SESSION['user'] ?? '';
|
||||||
|
|
||||||
|
// Tomo los datos del form
|
||||||
|
|
||||||
|
$descripcionEquipo = trim($_POST['descripcion_equipo']) ?? '';
|
||||||
|
$contraseniaEquipo = trim($_POST['contrasenia_equipo']) ?? '';
|
||||||
|
$cuenta = trim($_POST['cuenta']) ?? '';
|
||||||
|
$contraseniaCuenta = trim($_POST['contrasenia_cuenta']) ?? '';
|
||||||
|
$descripcionProblema = trim($_POST['descripcion_problema']) ?? '';
|
||||||
|
$observacion = trim($_POST['observacion']) ?? '';
|
||||||
|
|
||||||
|
if (!$cliente) {
|
||||||
|
$_SESSION['flash_error'] = "Debe seleccionar el cliente.";
|
||||||
|
header("Location: index.php?opt=crear_servicio");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
if ($descripcionEquipo === '') {
|
||||||
|
$errores[] = "La descripción del equipo es obligatoria.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($descripcionProblema === '') {
|
||||||
|
$errores[] = "Debe describir el problema.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validacion de longitud
|
||||||
|
|
||||||
|
if (strlen($descripcionEquipo) > 100) {
|
||||||
|
$errores[] = "La descripción del equipo es demasiado larga.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($descripcionProblema) > 2000) {
|
||||||
|
$errores[] = "La descripción del problema es demasiada larga.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($contraseniaEquipo) > 100) {
|
||||||
|
$errores[] = "Contraseña demasiado larga.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($observacion) > 500) {
|
||||||
|
$errores[] = "La descripción de la observación es demasiada larga.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($cuenta) > 100) {
|
||||||
|
$errores[] = "Cuenta demasiado larga.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($contraseniaCuenta) > 100) {
|
||||||
|
$errores[] = "Contraseña de cuenta demasiado larga.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otras validaciones
|
||||||
|
|
||||||
|
if (!empty($cuenta) && !filter_var($cuenta, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errores[] = "El email de la cuenta no es válido.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($contraseniaEquipo !== '' && $descripcionEquipo === '') {
|
||||||
|
$errores[] = "Debe indicar el equipo si ingresa contraseña.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($contraseniaCuenta !== '' && $cuenta === '') {
|
||||||
|
$errores[] = "Debe indicar la cuenta si ingresa contraseña.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores);
|
||||||
|
header("Location:index.php?opt=crear_servicio");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardado de datos en array
|
||||||
|
|
||||||
|
$datosServicio = [
|
||||||
|
'descripcion_equipo' => $descripcionEquipo,
|
||||||
|
'cuenta' => $cuenta,
|
||||||
|
'descripcion_problema' => $descripcionProblema,
|
||||||
|
'observacion' => $observacion
|
||||||
|
];
|
||||||
|
|
||||||
|
// Encriptamos las contraseñas
|
||||||
|
|
||||||
|
if ($contraseniaEquipo !== '') {
|
||||||
|
$datosServicio['contrasenia_equipo'] = encryptData($contraseniaEquipo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($contraseniaCuenta !== '') {
|
||||||
|
$datosServicio['contrasenia_cuenta'] = encryptData($contraseniaCuenta);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Llamado a modelo de servicio
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/servicioModel.php';
|
||||||
|
$serviciomodel = new servicioModel();
|
||||||
|
|
||||||
|
// NÚMERO DE SERVICIO (OBTENIDO MEDIANTE SECUENCIA PREVIEW)
|
||||||
|
|
||||||
|
if (isset($_SESSION['servicio_numero'])) {
|
||||||
|
$numero = $_SESSION['servicio_numero'];
|
||||||
|
} else {
|
||||||
|
$numero = $serviciomodel->getNumeroServicioPreview();
|
||||||
|
$_SESSION['servicio_numero'] = $numero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($serviciomodel->guardarServicio($numero, $datosServicio, $cliente, $vendedor)) {
|
||||||
|
$_SESSION['flash_success'] = "Servicio guardado correctamente.";
|
||||||
|
|
||||||
|
unset($_SESSION['servicio']['cliente']);
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = "Hubo un error al crear el servicio.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location:index.php?opt=crear_servicio");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// SELECCIONAR CLIENTE PARA EL SERVICIO
|
||||||
|
|
||||||
|
case 'seleccionar_cliente_servicio':
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/clienteModel.php';
|
||||||
|
$clientemodel = new clienteModel();
|
||||||
|
|
||||||
|
// Se muestra la vista de selección de cliente
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
$clientes = $clientemodel->listarClientes();
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/servicios/seleccionarClienteView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$idCliente = (int) ($_POST['id_cliente'] ?? 0);
|
||||||
|
|
||||||
|
if ($idCliente <= 0) {
|
||||||
|
$_SESSION['flash_error'] = "No se seleccionó ningún cliente.";
|
||||||
|
header("Location: index.php?opt=seleccionar_cliente");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cliente = $clientemodel->obtenerPorId($idCliente);
|
||||||
|
|
||||||
|
if (!$cliente) {
|
||||||
|
$_SESSION['flash_error'] = "Cliente inválido.";
|
||||||
|
header("Location: index.php?opt=seleccionar_cliente");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['servicio']['cliente'] = [
|
||||||
|
'id' => $cliente['id'],
|
||||||
|
'nombre' => $cliente['nombre'],
|
||||||
|
'apellido' => $cliente['apellido'],
|
||||||
|
'telefono' => $cliente['telefono'],
|
||||||
|
'email' => $cliente['email']
|
||||||
|
];
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Cliente agregado correctamente.";
|
||||||
|
header("Location: index.php?opt=crear_servicio");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELIMINAR CLIENTE YA SELECCIONADO
|
||||||
|
|
||||||
|
case 'eliminar_cliente_servicio':
|
||||||
|
|
||||||
|
if (isset($_SESSION['servicio']['cliente'])) {
|
||||||
|
unset($_SESSION['servicio']['cliente']);
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Cliente eliminado correctamente.";
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = "No había cliente seleccionado.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=crear_servicio");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// GENERACION DE COMRPOBANTE DE SERVCIO PDF MEDIANTE USO DE LIBRERIA DOMPDF
|
||||||
|
|
||||||
|
case 'generar_servicio_pdf':
|
||||||
|
|
||||||
|
// Requiero datos de empresa y configuración
|
||||||
|
|
||||||
|
require __DIR__ . '/../models/configuracionModel.php';
|
||||||
|
$configuracionmodel = new configuracionModel();
|
||||||
|
|
||||||
|
$datosEmpresa = $configuracionmodel->obtenerConfiguracion();
|
||||||
|
|
||||||
|
$mapCondIVA = [
|
||||||
|
'consumidor_final' => 'Consumidor Final',
|
||||||
|
'responsable_inscripto' => 'Responsable Inscripto',
|
||||||
|
'monotributo' => 'Monotributo',
|
||||||
|
];
|
||||||
|
|
||||||
|
$datosEmpresa['cond_iva'] = $mapCondIVA[$datosEmpresa['cond_iva']];
|
||||||
|
|
||||||
|
$cliente = $_SESSION['servicio']['cliente'] ?? null;
|
||||||
|
$vendedor = $_SESSION['user'] ?? null;
|
||||||
|
|
||||||
|
// DATOS FORM
|
||||||
|
|
||||||
|
$descripcionEquipo = trim($_POST['descripcion_equipo']) ?? '';
|
||||||
|
$contraseniaEquipo = trim($_POST['contrasenia_equipo']) ?? '';
|
||||||
|
$cuenta = trim($_POST['cuenta']) ?? '';
|
||||||
|
$contraseniaCuenta = trim($_POST['contrasenia_cuenta']) ?? '';
|
||||||
|
$descripcionProblema = trim($_POST['descripcion_problema']) ?? '';
|
||||||
|
$observacion = trim($_POST['observacion']) ?? '';
|
||||||
|
|
||||||
|
// VALIDACIONES
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
if ($descripcionEquipo === '') {
|
||||||
|
$errores[] = "La descripción del equipo es obligatoria.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($descripcionProblema === '') {
|
||||||
|
$errores[] = "Debe describir el problema.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validacion de longitud
|
||||||
|
|
||||||
|
if (strlen($descripcionEquipo) > 100) {
|
||||||
|
$errores[] = "La descripción del equipo es demasiado larga.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($descripcionProblema) > 2000) {
|
||||||
|
$errores[] = "La descripción del problema es demasiada larga.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($contraseniaEquipo) > 100) {
|
||||||
|
$errores[] = "Contraseña demasiado larga.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($observacion) > 500) {
|
||||||
|
$errores[] = "La descripción de la observación es demasiada larga.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($cuenta) > 100) {
|
||||||
|
$errores[] = "Cuenta demasiado larga.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($contraseniaCuenta) > 100) {
|
||||||
|
$errores[] = "Contraseña de cuenta demasiado larga.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otras validaciones
|
||||||
|
|
||||||
|
if (!empty($cuenta) && !filter_var($cuenta, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errores[] = "El email de la cuenta no es válido.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($contraseniaEquipo !== '' && $descripcionEquipo === '') {
|
||||||
|
$errores[] = "Debe indicar el equipo si ingresa contraseña.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($contraseniaCuenta !== '' && $cuenta === '') {
|
||||||
|
$errores[] = "Debe indicar la cuenta si ingresa contraseña.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores);
|
||||||
|
header("Location:index.php?opt=crear_servicio");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardado de datos en array
|
||||||
|
|
||||||
|
$datosServicio = [
|
||||||
|
'descripcion_equipo' => $descripcionEquipo,
|
||||||
|
'contrasenia_equipo' => $contraseniaEquipo,
|
||||||
|
'cuenta' => $cuenta,
|
||||||
|
'contrasenia_cuenta' => $contraseniaCuenta,
|
||||||
|
'descripcion_problema' => $descripcionProblema,
|
||||||
|
'observacion' => $observacion
|
||||||
|
];
|
||||||
|
|
||||||
|
// NÚMERO DE SERVICIO (OBTENIDO MEDIANTE SECUENCIA PREVIEW)
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/servicioModel.php';
|
||||||
|
$serviciomodel = new servicioModel();
|
||||||
|
|
||||||
|
$numero = $serviciomodel->getNumeroServicioPreview();
|
||||||
|
$_SESSION['servicio_numero'] = $numero;
|
||||||
|
|
||||||
|
$numeroServicio = '0001-' . str_pad($numero, 8, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
// RENDER PDF
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../pdf/servicios/servicioPDF.php';
|
||||||
|
$html = ob_get_clean();
|
||||||
|
|
||||||
|
$cssPath = '/var/www/html/css/stylesPDF.css';
|
||||||
|
$css = file_get_contents($cssPath);
|
||||||
|
|
||||||
|
// Configuración de opciones
|
||||||
|
|
||||||
|
$options = new Options();
|
||||||
|
$options->set('isRemoteEnabled', true); // Permite cargar recursos externos (CSS, imágenes, etc.)
|
||||||
|
$options->set('chroot', '/var/www/html'); // Define directorios base para recursos locales
|
||||||
|
$options->set('defaultFont', 'DejaVu Sans');
|
||||||
|
|
||||||
|
// Creación de instancia Dompdf con las opciones seteadas
|
||||||
|
|
||||||
|
$dompdf = new Dompdf($options);
|
||||||
|
|
||||||
|
// Cargar el HTML que se va a convertir en PDF
|
||||||
|
|
||||||
|
$dompdf->loadHtml("
|
||||||
|
<style>$css</style>
|
||||||
|
$html
|
||||||
|
");
|
||||||
|
|
||||||
|
$dompdf->setPaper('A4', 'portrait'); // Define el tamanio de hoja y la orientación
|
||||||
|
$dompdf->render(); // Renderiza el PDF (procesa el HTML)
|
||||||
|
|
||||||
|
// Enviar el PDF al browser
|
||||||
|
|
||||||
|
$dompdf->stream(
|
||||||
|
'Servicio N° ' . $numeroServicio . '.pdf',
|
||||||
|
['Attachment' => true] // false = se muestra en el navegador | true = descarga directa
|
||||||
|
);
|
||||||
|
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// OBTENER LISTADO DE SERVICIOS
|
||||||
|
|
||||||
|
case 'listar_servicios':
|
||||||
|
|
||||||
|
// Requiero el modelo de servicio
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/servicioModel.php';
|
||||||
|
$serviciomodel = new servicioModel();
|
||||||
|
|
||||||
|
$anioActual = date('Y');
|
||||||
|
$anioSeleccionado = $_GET['anio'] ?? $anioActual;
|
||||||
|
|
||||||
|
$servicios = $serviciomodel->listarServicios($anioSeleccionado);
|
||||||
|
|
||||||
|
foreach ($servicios as $i => $s) {
|
||||||
|
|
||||||
|
// Saneamiento de fecha (para poder ordenar correctamente el campo con dataTables)
|
||||||
|
|
||||||
|
if (!empty($s['fecha_registro'])) {
|
||||||
|
|
||||||
|
$dt = new DateTime($s['fecha_registro']);
|
||||||
|
|
||||||
|
// Para ordenar
|
||||||
|
|
||||||
|
$servicios[$i]['fecha_registro'] = $dt->format('Y-m-d');
|
||||||
|
|
||||||
|
// Para mostrar
|
||||||
|
|
||||||
|
$servicios[$i]['fecha_formateada'] = $dt->format('d/m/Y');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$servicios[$i]['fecha_registro'] = '-';
|
||||||
|
$servicios[$i]['fecha_formateada'] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($s['fecha_terminado'])) {
|
||||||
|
|
||||||
|
$dt = new DateTime($s['fecha_terminado']);
|
||||||
|
|
||||||
|
// Para ordenar
|
||||||
|
|
||||||
|
$servicios[$i]['fecha_terminado'] = $dt->format('Y-m-d');
|
||||||
|
|
||||||
|
// Para mostrar
|
||||||
|
|
||||||
|
$servicios[$i]['fecha_formateada_term'] = $dt->format('d/m/Y');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$servicios[$i]['fecha_terminado'] = '-';
|
||||||
|
$servicios[$i]['fecha_formateada_term'] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saneamiento de tipo de pago
|
||||||
|
|
||||||
|
$servicios[$i]['estado'] = ucwords($servicios[$i]['estado']);
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/servicios/listaServiciosView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// RESETEAR DATOS DE SERVICIO (al querer actualizar un servicio) (básicamente limpia todo para que los campos del form y la caja estén vacíos)
|
||||||
|
|
||||||
|
case 'resetear_datos_servicio':
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
// Verifico si es admin
|
||||||
|
|
||||||
|
if (!$esAdmin) {
|
||||||
|
header("Location: index.php?opt=inicio_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$idServicio = (int) ($_GET['id'] ?? 0);
|
||||||
|
|
||||||
|
if ($idServicio <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=listar_servicios');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se resetean los campos
|
||||||
|
|
||||||
|
$manoObra = 0;
|
||||||
|
$detalleRealizado = '';
|
||||||
|
$total = 0;
|
||||||
|
|
||||||
|
$_SESSION['servicio']['mano_obra'] = $manoObra;
|
||||||
|
$_SESSION['servicio']['detalle_realizado'] = $detalleRealizado;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/cajaModel.php';
|
||||||
|
$cajamodel = new cajaModel();
|
||||||
|
|
||||||
|
// Se vacia la caja
|
||||||
|
|
||||||
|
$cajamodel->vaciarCajaServicio();
|
||||||
|
$insumos = $cajamodel->listarInsumos();
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/servicios/actualizarServicioView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACTUALIZACIÓN DEL SERVICIO
|
||||||
|
|
||||||
|
case 'actualizar_servicio':
|
||||||
|
|
||||||
|
// Verifico si es admin
|
||||||
|
|
||||||
|
if (!$esAdmin) {
|
||||||
|
header("Location: index.php?opt=inicio_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/servicioModel.php';
|
||||||
|
$serviciomodel = new servicioModel();
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
$idServicio = (int) ($_GET['id'] ?? 0);
|
||||||
|
|
||||||
|
if ($idServicio <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=listar_servicios');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validamos si el servicio se encuentra en estado 'pendiente'
|
||||||
|
|
||||||
|
if (!($serviciomodel->getEstadoServicio($idServicio))) {
|
||||||
|
$_SESSION['flash_error'] = "El servicio no se encuentra en el estado correspondiente.";
|
||||||
|
header("Location: index.php?opt=listar_servicios");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$manoObra = isset($_SESSION['servicio']['mano_obra']) ? (float) $_SESSION['servicio']['mano_obra'] : 0;
|
||||||
|
$detalleRealizado = isset($_SESSION['servicio']['detalle_realizado']) ? trim($_SESSION['servicio']['detalle_realizado']) : '';
|
||||||
|
|
||||||
|
// Requiero modelo de caja
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/cajaModel.php';
|
||||||
|
$cajamodel = new cajaModel();
|
||||||
|
|
||||||
|
$insumos = $cajamodel->listarInsumos();
|
||||||
|
|
||||||
|
$total = $cajamodel->calcularPrecioTotalServicio() ?? 0;
|
||||||
|
|
||||||
|
if (isset($_SESSION['servicio']['mano_obra'])) {
|
||||||
|
$total = $total + $_SESSION['servicio']['mano_obra'];
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/servicios/actualizarServicioView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$idServicio = (int) ($_GET['id'] ?? 0);
|
||||||
|
$detalleRealizado = trim($_POST['detalle_realizado'] ?? '');
|
||||||
|
$manoObra = (float) ($_POST['mano_obra'] ?? 0);
|
||||||
|
|
||||||
|
// VALIDACIONES
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
if ($idServicio <= 0 || $manoObra < 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=actualizar_servicio&id=' . $idServicio);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($detalleRealizado === '') {
|
||||||
|
$errores[] = "El detalle es obligatorio.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($detalleRealizado) > 2000) {
|
||||||
|
$errores[] = "El detalle es demasiado largo.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores);
|
||||||
|
header("Location:index.php?opt=actualizar_servicio&id=" . $idServicio);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requiero modelo de caja
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/cajaModel.php';
|
||||||
|
$cajamodel = new cajaModel();
|
||||||
|
|
||||||
|
$insumos = $cajamodel->listarInsumos();
|
||||||
|
$total = $cajamodel->calcularPrecioTotalServicio();
|
||||||
|
|
||||||
|
$totalFinal = $total + $manoObra;
|
||||||
|
|
||||||
|
if ($totalFinal < 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Total inválido.';
|
||||||
|
header('Location: index.php?opt=actualizar_servicio&id=' . $idServicio);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$datosServicio = [
|
||||||
|
'id_servicio' => $idServicio,
|
||||||
|
'detalle_realizado' => $detalleRealizado,
|
||||||
|
'mano_obra' => $manoObra,
|
||||||
|
'total_final' => $totalFinal
|
||||||
|
];
|
||||||
|
|
||||||
|
// REQUERIMOS EL MODELO SERVICIO Y EL HELPER DEL MAIL
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/servicioModel.php';
|
||||||
|
require_once __DIR__ . '/../helpers/email_actualizar.php';
|
||||||
|
|
||||||
|
$serviciomodel = new servicioModel();
|
||||||
|
|
||||||
|
if ($serviciomodel->actualizarServicio($datosServicio, $insumos)) {
|
||||||
|
|
||||||
|
$success = [];
|
||||||
|
|
||||||
|
$success[] = "Servicio actualizado correctamente.";
|
||||||
|
|
||||||
|
unset($_SESSION['servicio']['mano_obra']);
|
||||||
|
unset($_SESSION['servicio']['detalle_realizado']);
|
||||||
|
$cajamodel->vaciarCajaServicio();
|
||||||
|
|
||||||
|
// OBTENEMOS DATOS DEL SERVICIO (SERVICIO, INSUMO UTILIZADOS Y CLIENTE)
|
||||||
|
|
||||||
|
$servicio = $serviciomodel->getDetalleServicio($idServicio);
|
||||||
|
$insumos = $serviciomodel->getDetalleInsumos($idServicio);
|
||||||
|
$cliente = $serviciomodel->getClienteServicio($idServicio);
|
||||||
|
|
||||||
|
// LLAMO MÉTODO PARA ENVÍO DEL EMAIL
|
||||||
|
|
||||||
|
$resultadoEnvioMail = enviarMailServicioFinalizado($servicio, $insumos, $cliente);
|
||||||
|
|
||||||
|
// OBTENGO DATO DE CONFIGURACIÓN PARA DECIDIR QUE MENSAJE MOSTRAR
|
||||||
|
|
||||||
|
$configuracionmodel = new configuracionModel();
|
||||||
|
$datosConfig = $configuracionmodel->obtenerDatosEnvioMail();
|
||||||
|
|
||||||
|
if (((int) $datosConfig['mail_automatico'] === 1) && $resultadoEnvioMail) {
|
||||||
|
$success[] = "Email automático enviado correctamente.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$resultadoEnvioMail) {
|
||||||
|
$success[] = "No fué posible enviar el email automático, por favor verifique manualmente.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = implode('<br>', $success);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = $serviciomodel->getError() ?? 'Hubo un error al actualizar el servicio.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location:index.php?opt=listar_servicios");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AGREGAR INSUMO AL SERVICIO (el post es atendido por CAJA)
|
||||||
|
|
||||||
|
case 'agregar_insumo':
|
||||||
|
|
||||||
|
// Verifico si es admin
|
||||||
|
|
||||||
|
if (!$esAdmin) {
|
||||||
|
header("Location: index.php?opt=inicio_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardo datos en $_SESSION para mantener persistencia de los datos
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$_SESSION['servicio']['detalle_realizado'] = $_POST['detalle_realizado'] ?? '';
|
||||||
|
$_SESSION['servicio']['mano_obra'] = (float) $_POST['mano_obra'] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$idServicio = (int) ($_GET['id'] ?? 0);
|
||||||
|
|
||||||
|
// Requerimos modelo de producto
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/productoModel.php';
|
||||||
|
$productomodel = new productoModel();
|
||||||
|
|
||||||
|
$insumos = $productomodel->listarInsumosServicio();
|
||||||
|
|
||||||
|
foreach ($insumos as $j => $i) {
|
||||||
|
|
||||||
|
if (!isset($i['id'])) {
|
||||||
|
unset($insumos[$j]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($i['stock'] === null || $i['stock'] === 0) {
|
||||||
|
$insumos[$j]['stock'] = '-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/servicios/agregarInsumoServicioView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// FINALIZACIÓN DE SERVICIO
|
||||||
|
|
||||||
|
case 'finalizar_servicio':
|
||||||
|
|
||||||
|
// Verifico si es admin
|
||||||
|
|
||||||
|
if (!$esAdmin) {
|
||||||
|
header("Location: index.php?opt=inicio_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$idServicio = (int) ($_POST['id'] ?? 0);
|
||||||
|
|
||||||
|
// VALIDACIONES
|
||||||
|
|
||||||
|
if ($idServicio <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=actualizar_servicio&id=' . $idServicio);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// REQUERIMOS EL MODELO SERVICIO Y EL HELPER DEL MAIL
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/servicioModel.php';
|
||||||
|
require_once __DIR__ . '/../helpers/email_finalizado.php';
|
||||||
|
|
||||||
|
$serviciomodel = new servicioModel();
|
||||||
|
|
||||||
|
if ($serviciomodel->finalizarServicio($idServicio)) {
|
||||||
|
|
||||||
|
$success = [];
|
||||||
|
|
||||||
|
$success[] = "Servicio actualizado correctamente.";
|
||||||
|
|
||||||
|
// OBTENEMOS DATOS DEL SERVICIO (SERVICIO, INSUMO UTILIZADOS Y CLIENTE)
|
||||||
|
|
||||||
|
$servicio = $serviciomodel->getDetalleServicio($idServicio);
|
||||||
|
$insumos = $serviciomodel->getDetalleInsumos($idServicio);
|
||||||
|
$cliente = $serviciomodel->getClienteServicio($idServicio);
|
||||||
|
|
||||||
|
// LLAMADA A MÉTODO PARA ENVÍO DE EMAIL
|
||||||
|
|
||||||
|
$resultadoEnvioMail = enviarMailServicioFinalizado($servicio, $insumos, $cliente);
|
||||||
|
|
||||||
|
// OBTENGO DATO DE CONFIGURACIÓN PARA DECIDIR QUE MENSAJE MOSTRAR
|
||||||
|
|
||||||
|
$configuracionmodel = new configuracionModel();
|
||||||
|
$datosConfig = $configuracionmodel->obtenerDatosEnvioMail();
|
||||||
|
|
||||||
|
if (((int) $datosConfig['mail_automatico'] === 1) && $resultadoEnvioMail) {
|
||||||
|
$success[] = "Email automático enviado correctamente.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$resultadoEnvioMail) {
|
||||||
|
$success[] = "No fué posible enviar el email automático, por favor verifique manualmente.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = implode('<br>', $success);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = $serviciomodel->getError() ?? 'Hubo un error al finalizar el servicio.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location:index.php?opt=listar_servicios");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// CANCELAR SERVICIO
|
||||||
|
|
||||||
|
case 'cancelar_servicio':
|
||||||
|
|
||||||
|
// Verifico si es admin
|
||||||
|
|
||||||
|
if (!$esAdmin) {
|
||||||
|
header("Location: index.php?opt=inicio_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$idServicio = (int) ($_POST['id'] ?? 0);
|
||||||
|
|
||||||
|
if ($idServicio <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=listar_servicios');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OBTENGO EL CARGO POR CANCELACIÓN DE SERVICIO (desde configuración)
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/configuracionModel.php';
|
||||||
|
$configuracionmodel = new configuracionModel();
|
||||||
|
|
||||||
|
$cargoCancelacion = $configuracionmodel->obtenerCargoCancelacion();
|
||||||
|
|
||||||
|
// Requiero modelo de servicio y llamo al método de cancelación
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/servicioModel.php';
|
||||||
|
$serviciomodel = new servicioModel();
|
||||||
|
|
||||||
|
if ($serviciomodel->cancelarServicio($idServicio, $cargoCancelacion)) {
|
||||||
|
$_SESSION['flash_success'] = 'Servicio cancelado correctamente.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo cancelar el servicio.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location:index.php?opt=listar_servicios");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// VER EL DETALLE TOTAL DEL SERVICIO REALIZADO
|
||||||
|
|
||||||
|
case 'detalle_servicio':
|
||||||
|
|
||||||
|
$idServicio = (int) ($_GET['id'] ?? 0);
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/servicioModel.php';
|
||||||
|
$serviciomodel = new servicioModel();
|
||||||
|
|
||||||
|
$datosServicio = $serviciomodel->getDetalleServicio($idServicio);
|
||||||
|
|
||||||
|
if (empty($datosServicio)) {
|
||||||
|
$_SESSION['flash_error'] = "Hubo un error al cargar el detalle del servicio.";
|
||||||
|
header("Location:index.php?opt=listar_servicios");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saneamiento de datos
|
||||||
|
|
||||||
|
if ($datosServicio['detalle_realizado'] === null) {
|
||||||
|
$datosServicio['detalle_realizado'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($datosServicio['mano_obra'] === null) {
|
||||||
|
$datosServicio['mano_obra'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($datosServicio['total'] === null) {
|
||||||
|
$datosServicio['total'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Desencriptacion de contraseñas
|
||||||
|
|
||||||
|
if ($datosServicio['contrasenia_equipo'] !== null) {
|
||||||
|
$datosServicio['contrasenia_equipo'] = decryptData($datosServicio['contrasenia_equipo']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($datosServicio['contrasenia_cuenta'] !== null) {
|
||||||
|
$datosServicio['contrasenia_cuenta'] = decryptData($datosServicio['contrasenia_cuenta']);
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require_once __DIR__ . '/../views/servicios/detalleServicioView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// MOSTRAR EL DETALLE DE INSUMOS
|
||||||
|
|
||||||
|
case 'detalle_insumos_servicio':
|
||||||
|
|
||||||
|
$idServicio = (int) ($_GET['id'] ?? 0);
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/servicioModel.php';
|
||||||
|
$serviciomodel = new servicioModel();
|
||||||
|
|
||||||
|
$servicio_detalle = $serviciomodel->getDetalleInsumos($idServicio);
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require_once __DIR__ . '/../views/servicios/detalleInsumosServicioView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// ACTUALIZAR EL TOTAL AGREGANDO LA MANO DE OBRA
|
||||||
|
|
||||||
|
case 'agregar_mano_obra':
|
||||||
|
|
||||||
|
// Verifico si es admin
|
||||||
|
|
||||||
|
if (!$esAdmin) {
|
||||||
|
header("Location: index.php?opt=inicio_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$idServicio = (int) ($_GET['id'] ?? 0);
|
||||||
|
|
||||||
|
// Obtengo datos
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$detalleRealizado = $_POST['detalle_realizado'] ?? '';
|
||||||
|
$manoObra = (float) $_POST['mano_obra'] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($idServicio <= 0 || $manoObra < 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=actualizar_servicio&id=' . $idServicio);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['servicio']['detalle_realizado'] = $detalleRealizado;
|
||||||
|
$_SESSION['servicio']['mano_obra'] = $manoObra;
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = 'Mano de obra agregada correctamente.';
|
||||||
|
header('Location: index.php?opt=actualizar_servicio&id=' . $idServicio);
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// DEFAULT (404)
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
header("Location: index.php?opt=404View.php");
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
@@ -0,0 +1,386 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Validacion de token
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { // Recorre todos los caracteres (siempre) y retorna true or false
|
||||||
|
$_SESSION['flash_error'] = 'Acción inválida.';
|
||||||
|
header('Location: index.php?opt=inicio_operador');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requiere modelo de usuario
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/usuarioModel.php';
|
||||||
|
$usuariomodel = new usuarioModel();
|
||||||
|
|
||||||
|
// Validar sesión
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $_SESSION['user']; // Defino el array del usuario dentro de una variable
|
||||||
|
$rol = $user['rol']; // Defino el rol dentro de la variable
|
||||||
|
$esAdmin = ($rol === 'admin'); // Variable booleana, true = admin
|
||||||
|
|
||||||
|
$opt = $_GET['opt'] ?? null;
|
||||||
|
|
||||||
|
// Manejo de opciones enviadas por URL
|
||||||
|
|
||||||
|
switch ($opt) {
|
||||||
|
|
||||||
|
// USUARIO REQUIERE SUS DATOS, O ADMIN BUSCA EDITAR UNO
|
||||||
|
|
||||||
|
case 'datos_usuario':
|
||||||
|
|
||||||
|
$idEditar = null;
|
||||||
|
|
||||||
|
$idEditar = (int) ($_GET['id'] ?? $user['id']);
|
||||||
|
|
||||||
|
if ($idEditar <= 0) {
|
||||||
|
header("Location: index.php?opt=usuarios");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buscamos el usuario en la base mediante el ID
|
||||||
|
|
||||||
|
$usuario = $usuariomodel->obtenerPorId($idEditar);
|
||||||
|
|
||||||
|
if (!$usuario) {
|
||||||
|
$_SESSION['flash_error'] = "No se encontró el usuario.";
|
||||||
|
header("Location: index.php?opt=usuarios"); // Si no se encontró al usuario se redirige a la lista de usuarios
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
if ($esAdmin) {
|
||||||
|
$return = 'inicio_admin';
|
||||||
|
} else {
|
||||||
|
$return = 'inicio_operador';
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['return_opt'] = $_GET['from'] ?? "$return";
|
||||||
|
$return = $_SESSION['return_opt'];
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/usuarios/datosUsuarioView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// En caso de modificar datos de un usuario
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
// Si no es admin no puede editar usuarios
|
||||||
|
|
||||||
|
if (!$esAdmin) {
|
||||||
|
header("Location: index.php?opt=home_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nombre = trim($_POST['nombre'] ?? '');
|
||||||
|
$apellido = trim($_POST['apellido'] ?? '');
|
||||||
|
$email = trim($_POST['email'] ?? '');
|
||||||
|
$telefono = trim($_POST['telefono'] ?? '');
|
||||||
|
$password = trim($_POST['password'] ?? '');
|
||||||
|
$usuarioInput = $usuario['usuario'] ?? '';
|
||||||
|
$rol = trim($_POST['rol']) ?? 'operador';
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
if ($nombre === '') {
|
||||||
|
$errores[] = "El nombre no puede estar vacío.";
|
||||||
|
} elseif (!preg_match("/^[A-ZÁÉÍÓÚÑ][a-záéíóúñ]+(?: [A-ZÁÉÍÓÚÑ][a-záéíóúñ]+)*$/", $nombre)) {
|
||||||
|
$errores[] = "El nombre debe comenzar con mayúscula y solo contener letras y espacios.";
|
||||||
|
} elseif (strlen($nombre) < 2) {
|
||||||
|
$errores[] = "El nombre debe tener al menos 2 caracteres.";
|
||||||
|
} elseif (strlen($nombre) > 50) {
|
||||||
|
$errores[] = "El nombre no puede superar los 50 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($apellido === '') {
|
||||||
|
$errores[] = "El apellido no puede estar vacío.";
|
||||||
|
} elseif (!preg_match("/^[A-ZÁÉÍÓÚÑ][a-záéíóúñ]+(?: [A-ZÁÉÍÓÚÑ][a-záéíóúñ]+)*$/", $apellido)) {
|
||||||
|
$errores[] = "El apellido debe comenzar con mayúscula y solo contener letras y espacios.";
|
||||||
|
} elseif (strlen($apellido) < 2) {
|
||||||
|
$errores[] = "El apellido debe tener al menos 2 caracteres.";
|
||||||
|
} elseif (strlen($apellido) > 50) {
|
||||||
|
$errores[] = "El apellido no puede superar los 50 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($email === '') {
|
||||||
|
$errores[] = "El email no puede estar vacío.";
|
||||||
|
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errores[] = "Formato de email inválido.";
|
||||||
|
} elseif (strlen($email) > 100) {
|
||||||
|
$errores[] = "El email no puede superar los 100 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($telefono === '') {
|
||||||
|
$errores[] = "El teléfono no puede estar vacío.";
|
||||||
|
} else if (!preg_match('/^[0-9]{10}$/', $telefono)) {
|
||||||
|
$errores[] = "El teléfono debe tener 10 dígitos.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Password (si se ingresa)
|
||||||
|
|
||||||
|
if (!empty($password)) {
|
||||||
|
if ($password === '') {
|
||||||
|
$errores[] = "La contraseña no puede estar vacía.";
|
||||||
|
} elseif (strpos($password, ' ') !== false) {
|
||||||
|
$errores[] = "La contraseña no puede contener espacios.";
|
||||||
|
} elseif (!preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{6,}$/', $password)) {
|
||||||
|
$errores[] = "La contraseña debe tener al menos una mayúscula, una minúscula y un número, y mínimo 6 caracteres.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($rol, ['admin', 'operador'], true)) {
|
||||||
|
$errores[] = "El rol es incorrecto.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Un admin no puede modificar su rol
|
||||||
|
|
||||||
|
if (($idEditar === $user['id']) && ($rol === 'operador')) {
|
||||||
|
$_SESSION['flash_error'] = 'No puede modificar su propio rol';
|
||||||
|
header("Location: index.php?opt=datos_usuario&id=$idEditar&from=usuarios");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores); // Se guarda en $_SESSION el error
|
||||||
|
header("Location: index.php?opt=datos_usuario&id=$idEditar&from=usuarios");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificación de existencia de usuario
|
||||||
|
|
||||||
|
if ($usuariomodel->verificarExistencia([
|
||||||
|
'nombre' => $nombre,
|
||||||
|
'apellido' => $apellido,
|
||||||
|
'email' => $email,
|
||||||
|
'usuario' => $usuarioInput
|
||||||
|
], $idEditar)) {
|
||||||
|
$_SESSION['flash_error'] = "El usuario ya existe o el mail ya está registrado.";
|
||||||
|
|
||||||
|
header("Location: index.php?opt=datos_usuario&id=$idEditar&from=usuarios");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$guardarData = [
|
||||||
|
'nombre' => $nombre,
|
||||||
|
'apellido' => $apellido,
|
||||||
|
'email' => $email,
|
||||||
|
'telefono' => $telefono,
|
||||||
|
'usuario' => $usuarioInput,
|
||||||
|
'rol' => $rol
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!empty($password)) {
|
||||||
|
$guardarData['password'] = password_hash($password, PASSWORD_DEFAULT); // Hasheo de contraseña
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualiza los datos del usuario
|
||||||
|
|
||||||
|
if ($usuariomodel->actualizarUsuario($idEditar, $guardarData)) {
|
||||||
|
$_SESSION['flash_success'] = 'Datos actualizados correctamente.'; // Se guarda en $_SESSION el success
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo actualizar los datos.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($idEditar === $user['id']) {
|
||||||
|
$_SESSION['user'] = $usuariomodel->obtenerPorId($idEditar);
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=usuarios");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// LISTAR USUARIOS
|
||||||
|
|
||||||
|
case 'usuarios':
|
||||||
|
|
||||||
|
if (!$esAdmin) {
|
||||||
|
header("Location: index.php?opt=home_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utilizamos el modelo para obtener la lista completa de usuarios
|
||||||
|
|
||||||
|
$usuarios = $usuariomodel->listarUsuarios();
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/usuarios/listaUsuariosView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// CREACIÓN DE NUEVO USUARIO
|
||||||
|
|
||||||
|
case 'crear_usuario':
|
||||||
|
|
||||||
|
if (!$esAdmin) {
|
||||||
|
header("Location: index.php?opt=home_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se requiere la view del form
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/usuarios/crearUsuarioView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Datos recibidos del POST del formulario
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$nombre = trim($_POST['nombre'] ?? '');
|
||||||
|
$apellido = trim($_POST['apellido'] ?? '');
|
||||||
|
$email = trim($_POST['email'] ?? '');
|
||||||
|
$telefono = trim($_POST['telefono'] ?? '');
|
||||||
|
$usuarioInput = trim($_POST['usuario'] ?? '');
|
||||||
|
$password = trim($_POST['password'] ?? '');
|
||||||
|
$rol = trim($_POST['rol']) ?? 'operador';
|
||||||
|
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
// Validaciones
|
||||||
|
|
||||||
|
if ($nombre === '') {
|
||||||
|
$errores[] = "El nombre no puede estar vacío.";
|
||||||
|
} elseif (!preg_match("/^[A-ZÁÉÍÓÚÑ][a-záéíóúñ]+(?: [A-ZÁÉÍÓÚÑ][a-záéíóúñ]+)*$/", $nombre)) {
|
||||||
|
$errores[] = "El nombre debe comenzar con mayúscula y solo contener letras y espacios.";
|
||||||
|
} elseif (strlen($nombre) < 2) {
|
||||||
|
$errores[] = "El nombre debe tener al menos 2 caracteres.";
|
||||||
|
} elseif (strlen($nombre) > 50) {
|
||||||
|
$errores[] = "El nombre no puede superar los 50 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($apellido === '') {
|
||||||
|
$errores[] = "El apellido no puede estar vacío.";
|
||||||
|
} elseif (!preg_match("/^[A-ZÁÉÍÓÚÑ][a-záéíóúñ]+(?: [A-ZÁÉÍÓÚÑ][a-záéíóúñ]+)*$/", $apellido)) {
|
||||||
|
$errores[] = "El apellido debe comenzar con mayúscula y solo contener letras y espacios.";
|
||||||
|
} elseif (strlen($apellido) < 2) {
|
||||||
|
$errores[] = "El apellido debe tener al menos 2 caracteres.";
|
||||||
|
} elseif (strlen($apellido) > 50) {
|
||||||
|
$errores[] = "El apellido no puede superar los 50 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($email === '') {
|
||||||
|
$errores[] = "El email no puede estar vacío.";
|
||||||
|
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errores[] = "Formato de email inválido.";
|
||||||
|
} elseif (strlen($email) > 100) {
|
||||||
|
$errores[] = "El email no puede superar los 100 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($telefono === '') {
|
||||||
|
$errores[] = "El teléfono no puede estar vacío.";
|
||||||
|
} else if (!preg_match('/^[0-9]{10}$/', $telefono)) {
|
||||||
|
$errores[] = "El teléfono debe tener 10 dígitos.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($usuarioInput === '') {
|
||||||
|
$errores[] = "El usuario no puede estar vacío.";
|
||||||
|
} elseif (!preg_match('/^[a-zA-Z0-9._]+$/', $usuarioInput)) {
|
||||||
|
$errores[] = "El usuario solo puede contener letras, números, puntos y guiones bajos.";
|
||||||
|
} elseif (strlen($usuarioInput) < 5) {
|
||||||
|
$errores[] = "El usuario debe tener al menos 5 caracteres.";
|
||||||
|
} elseif (strlen($usuarioInput) > 30) {
|
||||||
|
$errores[] = "El usuario no puede superar los 30 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($password === '') {
|
||||||
|
$errores[] = "La contraseña no puede estar vacía.";
|
||||||
|
} elseif (strpos($password, ' ') !== false) {
|
||||||
|
$errores[] = "La contraseña no puede contener espacios.";
|
||||||
|
} elseif (!preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{6,}$/', $password)) {
|
||||||
|
$errores[] = "La contraseña debe tener al menos una mayúscula, una minúscula y un número, y mínimo 6 caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($rol, ['admin', 'operador'], true)) {
|
||||||
|
$rol = 'operador';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errores)) {
|
||||||
|
$_SESSION['flash_error'] = implode('<br>', $errores);
|
||||||
|
header("Location: index.php?opt=crear_usuario");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificación de existencia de usuario
|
||||||
|
|
||||||
|
if ($usuariomodel->verificarExistencia([
|
||||||
|
'nombre' => $nombre,
|
||||||
|
'apellido' => $apellido,
|
||||||
|
'email' => $email,
|
||||||
|
'usuario' => $usuarioInput
|
||||||
|
])) {
|
||||||
|
$_SESSION['flash_error'] = "El usuario ya existe o el mail ya está registrado.";
|
||||||
|
|
||||||
|
header("Location: index.php?opt=crear_usuario");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se llama a la función para crear el usuario
|
||||||
|
|
||||||
|
if ($usuariomodel->crearUsuario([
|
||||||
|
'nombre' => $nombre,
|
||||||
|
'apellido' => $apellido,
|
||||||
|
'email' => $email,
|
||||||
|
'telefono' => $telefono,
|
||||||
|
'usuario' => $usuarioInput,
|
||||||
|
'password' => password_hash($password, PASSWORD_DEFAULT),
|
||||||
|
'rol' => $rol
|
||||||
|
])) {
|
||||||
|
$_SESSION['flash_success'] = 'Usuario creado correctamente';
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = 'Ocurrió un error al crear el usuario.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=usuarios");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELIMINAR USUARIO (BAJA LÓGICA)
|
||||||
|
|
||||||
|
case 'eliminar_usuario':
|
||||||
|
|
||||||
|
$idEliminar = $_POST['id'] ?? 0;
|
||||||
|
|
||||||
|
if ($idEliminar <= 0) {
|
||||||
|
header("Location: index.php?opt=usuarios");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if ($idEliminar == $user['id']) {
|
||||||
|
$_SESSION['flash_error'] = "No puede eliminar su usuario.";
|
||||||
|
header("Location:index.php?opt=usuarios");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($usuariomodel->eliminarUsuario($idEliminar)) {
|
||||||
|
$_SESSION['flash_success'] = "Usuario eliminado correctamente.";
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = "Ocurrió un error al eliminar el usuario.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location:index.php?opt=usuarios");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEFAULT (404)
|
||||||
|
|
||||||
|
default:
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/404View.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
@@ -0,0 +1,944 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { // Recorre todos los caracteres (siempre) y retorna true or false
|
||||||
|
$_SESSION['flash_error'] = 'Acción inválida.';
|
||||||
|
header('Location: index.php?opt=inicio_operador');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Librería domPDF
|
||||||
|
|
||||||
|
require __DIR__ . '/../libs/dompdf/autoload.inc.php';
|
||||||
|
|
||||||
|
use Dompdf\Dompdf;
|
||||||
|
use Dompdf\Options;
|
||||||
|
|
||||||
|
// Validar sesión
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Location: index.php?opt=login_operador");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$opt = $_GET['opt'] ?? null;
|
||||||
|
|
||||||
|
// Manejo de $opt
|
||||||
|
|
||||||
|
switch ($opt) {
|
||||||
|
|
||||||
|
// CREAR VENTA
|
||||||
|
|
||||||
|
case 'crear_venta':
|
||||||
|
|
||||||
|
// Se muestra la vista de creación de ventas
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
// Requiero el modelo de caja (puede estar vacía)
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/cajaModel.php';
|
||||||
|
$cajamodel = new cajaModel();
|
||||||
|
|
||||||
|
$productos = $cajamodel->listarProductos();
|
||||||
|
|
||||||
|
if (isset($_SESSION['venta']['cliente'])) {
|
||||||
|
$cliente = $_SESSION['venta']['cliente'];
|
||||||
|
} else {
|
||||||
|
$cliente = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtengo cálculos preliminares
|
||||||
|
|
||||||
|
$subtotal = $cajamodel->calcularSubtotal() ?? 0;
|
||||||
|
$descuento = $cajamodel->calcularDescuentoTotal() ?? 0;
|
||||||
|
$total = $cajamodel->calcularPrecioTotal() ?? 0;
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/ventas/crearVentaView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// AGREGAR PRODUCTO A LA VENTA (el post es atendido por CAJA)
|
||||||
|
|
||||||
|
case 'agregar_producto':
|
||||||
|
|
||||||
|
// Requiero el productoModel
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/productoModel.php';
|
||||||
|
$productomodel = new productoModel();
|
||||||
|
|
||||||
|
$productos = $productomodel->listarProductosVenta();
|
||||||
|
|
||||||
|
foreach ($productos as $i => $p) {
|
||||||
|
|
||||||
|
if (!isset($p['id'])) {
|
||||||
|
unset($productos[$i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($p['stock'] === null || $p['stock'] === 0) {
|
||||||
|
$productos[$i]['stock'] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
$productos[$i]['descuento_total'] = number_format($p['descuento_total'], 2, '.', '') . ' %';
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/ventas/agregarProductoView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// SELECCIONAR CLIENTE PARA LA VENTA
|
||||||
|
|
||||||
|
case 'seleccionar_cliente':
|
||||||
|
|
||||||
|
// Requiero el modelo de cliente
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/clienteModel.php';
|
||||||
|
$clientemodel = new clienteModel();
|
||||||
|
|
||||||
|
// Se muestra la vista de selección de cliente
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
$clientes = $clientemodel->listarClientes();
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/ventas/seleccionarClienteView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
$idCliente = (int) ($_POST['id_cliente'] ?? 0);
|
||||||
|
|
||||||
|
if ($idCliente <= 0) {
|
||||||
|
$_SESSION['flash_error'] = "No se seleccionó ningún cliente.";
|
||||||
|
header("Location: index.php?opt=seleccionar_cliente");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cliente = $clientemodel->obtenerPorId($idCliente);
|
||||||
|
|
||||||
|
if (!$cliente) {
|
||||||
|
$_SESSION['flash_error'] = "Cliente inválido.";
|
||||||
|
header("Location: index.php?opt=seleccionar_cliente");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['venta']['cliente'] = [
|
||||||
|
'id' => $cliente['id'],
|
||||||
|
'nombre' => $cliente['nombre'],
|
||||||
|
'apellido' => $cliente['apellido'],
|
||||||
|
'telefono' => $cliente['telefono'],
|
||||||
|
'email' => $cliente['email'],
|
||||||
|
'cuit' => $cliente['cuit'],
|
||||||
|
'cond_iva' => $cliente['cond_iva']
|
||||||
|
];
|
||||||
|
|
||||||
|
$_SESSION['venta_extra']['cond_iva'] = $cliente['cond_iva'];
|
||||||
|
$_SESSION['venta_extra']['cuit'] = $cliente['cuit'];
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Cliente agregado correctamente.";
|
||||||
|
header("Location: index.php?opt=crear_venta");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELIMINAR CLIENTE YA SELECCIONADO
|
||||||
|
|
||||||
|
case 'eliminar_cliente':
|
||||||
|
|
||||||
|
if (isset($_SESSION['venta']['cliente'])) {
|
||||||
|
unset($_SESSION['venta']['cliente']);
|
||||||
|
|
||||||
|
// También se eliminan datos de la venta guardados desde vista resumen
|
||||||
|
|
||||||
|
if (isset($_SESSION['venta_extra'])) {
|
||||||
|
unset($_SESSION['venta_extra']);
|
||||||
|
unset($_SESSION['venta_final']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Cliente eliminado correctamente.";
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = "No había cliente seleccionado.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=crear_venta");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// GENERAR VISTA RESUMEN DE VENTA
|
||||||
|
|
||||||
|
case 'resumen_venta':
|
||||||
|
|
||||||
|
$cliente = ($_SESSION['venta']['cliente'] ?? null);
|
||||||
|
|
||||||
|
$_SESSION['venta_extra']['cond_iva'] = $_SESSION['venta_extra']['cond_iva'] ?? $cliente['cond_iva'] ?? 'consumidor_final';
|
||||||
|
$_SESSION['venta_extra']['cuit'] = $_SESSION['venta_extra']['cuit'] ?? $cliente['cuit'] ?? '00000000000';
|
||||||
|
|
||||||
|
// Requiero el modelo de caja
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/cajaModel.php';
|
||||||
|
$cajamodel = new cajaModel();
|
||||||
|
|
||||||
|
$productos = $cajamodel->listarProductos();
|
||||||
|
$descuento = $cajamodel->calcularDescuentoTotal();
|
||||||
|
|
||||||
|
if (!$cliente || empty($productos)) {
|
||||||
|
$_SESSION['flash_error'] = "Debe seleccionar el cliente y al menos un producto.";
|
||||||
|
header("Location: index.php?opt=crear_venta");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/ventaModel.php';
|
||||||
|
|
||||||
|
$ventamodel = new ventaModel();
|
||||||
|
|
||||||
|
// 1. Subtotal SIEMPRE se recalcula (pueden haber cambiado productos/cantidades)
|
||||||
|
|
||||||
|
$subtotal = $cajamodel->calcularSubtotal() ?? 0;
|
||||||
|
|
||||||
|
// 2. Datos extra guardados en sesión (si existen)
|
||||||
|
|
||||||
|
$descuentoIngresado = isset($_SESSION['venta_extra']['descuento']) ? (float) $_SESSION['venta_extra']['descuento'] : 0;
|
||||||
|
|
||||||
|
$recargoIngresado = isset($_SESSION['venta_extra']['recargo']) ? (float) $_SESSION['venta_extra']['recargo'] : 0;
|
||||||
|
|
||||||
|
// 3. Recalculo siempre con subtotal actual
|
||||||
|
|
||||||
|
$datosProvisorios = $ventamodel->actualizarDatosProvisorio($descuentoIngresado, $recargoIngresado, $subtotal, $descuento);
|
||||||
|
|
||||||
|
$descuento = $datosProvisorios['descuento'] ?? 0;
|
||||||
|
$recargo = $datosProvisorios['recargo'] ?? 0;
|
||||||
|
$total = $datosProvisorios['total'] ?? 0;
|
||||||
|
|
||||||
|
// Guardo en SESSION
|
||||||
|
|
||||||
|
$_SESSION['venta_final'] = [
|
||||||
|
'descuento' => $descuento,
|
||||||
|
'recargo' => $recargo,
|
||||||
|
'total' => $total
|
||||||
|
];
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/ventas/resumenVentaView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GUARDAR DATOS DE VENTA (permite volver hacia atrás en el proceso y no perder datos)
|
||||||
|
|
||||||
|
case 'guardar_datos_venta':
|
||||||
|
|
||||||
|
// Validaciones de reglas de negocio
|
||||||
|
|
||||||
|
// Condición IVA
|
||||||
|
|
||||||
|
$condIvaPermitidas = [
|
||||||
|
'consumidor_final',
|
||||||
|
'responsable_inscripto',
|
||||||
|
'monotributo'
|
||||||
|
];
|
||||||
|
|
||||||
|
$condIva = $_POST['cond_iva'] ?? 'consumidor_final';
|
||||||
|
|
||||||
|
if (!in_array($condIva, $condIvaPermitidas, true)) {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'Condición de IVA inválida.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CUIT
|
||||||
|
|
||||||
|
$cuit = ($_POST['cuit_1'] ?? '') . ($_POST['cuit_2'] ?? '') . ($_POST['cuit_3'] ?? '');
|
||||||
|
|
||||||
|
$cuit = trim($cuit);
|
||||||
|
|
||||||
|
if ($cuit === '') {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'Debe ingresar un CUIT.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/^\d{11}$/', $cuit)) {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'El CUIT debe contener 11 dígitos.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forma de pago + tipo de pago
|
||||||
|
|
||||||
|
$reglasPago = [
|
||||||
|
'efectivo' => ['pago_unico'],
|
||||||
|
'transferencia' => ['pago_unico'],
|
||||||
|
'echeq' => ['pago_unico'],
|
||||||
|
'debito' => ['pago_unico'],
|
||||||
|
'credito' => ['pago_unico', '3_cuotas', '6_cuotas', '12_cuotas'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$formaPago = $_POST['forma_pago'] ?? 'efectivo';
|
||||||
|
$tipoPago = $_POST['tipo_pago'] ?? 'pago_unico';
|
||||||
|
|
||||||
|
if (!isset($reglasPago[$formaPago]) || !in_array($tipoPago, $reglasPago[$formaPago], true)) {
|
||||||
|
$_SESSION['flash_error'] = 'Combinación de forma y tipo de pago inválida.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validación de descuento y recargo
|
||||||
|
|
||||||
|
$descuentoVenta = isset($_POST['descuento']) ? (float) $_POST['descuento'] : 0;
|
||||||
|
$recargoVenta = isset($_POST['recargo']) ? (float) $_POST['recargo'] : 0;
|
||||||
|
|
||||||
|
if ($descuentoVenta < 0 || $recargoVenta > 100) {
|
||||||
|
$_SESSION['flash_error'] = 'El descuento debe estar entre 0 y 100%.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($descuentoVenta < 0 || $recargoVenta > 100) {
|
||||||
|
$_SESSION['flash_error'] = 'El recargo debe estar entre 0 y 100%.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardado de datos
|
||||||
|
|
||||||
|
// Guardo datos extra de la venta para ser reutilizados en la vista
|
||||||
|
|
||||||
|
$datosExtraVenta = [
|
||||||
|
'cond_iva' => $condIva,
|
||||||
|
'cuit' => $cuit,
|
||||||
|
'forma_pago' => $formaPago,
|
||||||
|
'tipo_pago' => $tipoPago,
|
||||||
|
'descuento' => $descuentoVenta,
|
||||||
|
'recargo' => $recargoVenta
|
||||||
|
];
|
||||||
|
|
||||||
|
// También los persisto en sesión para mantener estado
|
||||||
|
|
||||||
|
$_SESSION['venta_extra'] = $datosExtraVenta;
|
||||||
|
|
||||||
|
// Datos para la vista
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/cajaModel.php';
|
||||||
|
$cajamodel = new cajaModel();
|
||||||
|
|
||||||
|
$cliente = ($_SESSION['venta']['cliente'] ?? null);
|
||||||
|
$productos = $cajamodel->listarProductos();
|
||||||
|
|
||||||
|
$vendedor = $_SESSION['user'] ?? null;
|
||||||
|
|
||||||
|
// VALIDACIONES BASE
|
||||||
|
|
||||||
|
if (!$cliente || empty($productos)) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos incompletos para generar la venta.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$vendedor) {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo identificar el usuario.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtotal que no contiene descuentos.
|
||||||
|
(Al ingresar descuento o recargo, lo guardo en un array separado, para no sobreescribir datos) */
|
||||||
|
|
||||||
|
$subtotal = $cajamodel->calcularSubtotal() ?? 0;
|
||||||
|
$descuento = $cajamodel->calcularDescuentoTotal() ?? 0;
|
||||||
|
|
||||||
|
// Validación del subtotal
|
||||||
|
|
||||||
|
if ($subtotal <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'La venta no contiene productos válidos.';
|
||||||
|
header('Location: index.php?opt=crear_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Llamamos a la función que devuelve los datos actualizados en un array nuevo
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/ventaModel.php';
|
||||||
|
|
||||||
|
$ventamodel = new ventaModel();
|
||||||
|
|
||||||
|
$datosProvisorios = $ventamodel->actualizarDatosProvisorio($descuentoVenta, $recargoVenta, $subtotal, $descuento);
|
||||||
|
|
||||||
|
$recargo = $datosProvisorios['recargo'] ?? 0;
|
||||||
|
$descuento = $datosProvisorios['descuento'] ?? 0;
|
||||||
|
$total = $datosProvisorios['total'] ?? 0;
|
||||||
|
|
||||||
|
$_SESSION['venta_final'] = [
|
||||||
|
'recargo' => $recargo,
|
||||||
|
'descuento' => $descuento,
|
||||||
|
'total' => $total
|
||||||
|
];
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Datos guardados de manera provisoria.";
|
||||||
|
header("Location: index.php?opt=resumen_venta");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// GENERACION DE VENTA PDF MEDIANTE USO DE LIBRERIA DOMPDF
|
||||||
|
|
||||||
|
case 'generar_venta_pdf':
|
||||||
|
|
||||||
|
// OBTENEMOS LA VISTA
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
$numeroVenta = $_SESSION['ultima_venta_id'];
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/ventas/descargarComprobante.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GENERACIÓN DEL PDF
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
// Requiero datos de empresa y configuración
|
||||||
|
|
||||||
|
require __DIR__ . '/../models/configuracionModel.php';
|
||||||
|
$configuracionmodel = new configuracionModel();
|
||||||
|
|
||||||
|
$datosEmpresa = $configuracionmodel->obtenerConfiguracion();
|
||||||
|
|
||||||
|
$mapCondIVA = [
|
||||||
|
'consumidor_final' => 'Consumidor Final',
|
||||||
|
'responsable_inscripto' => 'Responsable Inscripto',
|
||||||
|
'monotributo' => 'Monotributo',
|
||||||
|
];
|
||||||
|
|
||||||
|
$datosEmpresa['cond_iva'] = $mapCondIVA[$datosEmpresa['cond_iva']];
|
||||||
|
|
||||||
|
// Obtenemos array con datos necesarios, que fueron guardados durante la confirmación de venta en $_SESSION
|
||||||
|
|
||||||
|
$datosVenta = $_SESSION['venta_pdf'] ?? null;
|
||||||
|
|
||||||
|
if (!$datosVenta) {
|
||||||
|
$_SESSION['flash_error'] = 'No existen datos para generar el comprobante.';
|
||||||
|
header('Location: index.php?opt=crear_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asignamos datos a variables y validamos
|
||||||
|
|
||||||
|
$cliente = $datosVenta['cliente'];
|
||||||
|
$vendedor = $datosVenta['vendedor'];
|
||||||
|
$productos = $datosVenta['productos'];
|
||||||
|
|
||||||
|
// VALIDACIONES BASE
|
||||||
|
|
||||||
|
if (!$cliente || empty($productos)) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos incompletos para generar la venta.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$vendedor) {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo identificar el usuario.';
|
||||||
|
header('Location: index.php?opt=crear_pedido');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asignamos datos a variables y validamos
|
||||||
|
|
||||||
|
$condIVA = $datosVenta['cond_iva'];
|
||||||
|
$cuit = $datosVenta['cuit'];
|
||||||
|
|
||||||
|
$formaPago = $datosVenta['forma_pago'];
|
||||||
|
$tipoPago = $datosVenta['tipo_pago'];
|
||||||
|
|
||||||
|
$descuentoVenta = $datosVenta['descuentoVenta'];
|
||||||
|
$recargoVenta = $datosVenta['recargoVenta'];
|
||||||
|
|
||||||
|
// VALIDACIÓN CONDICIÓN IVA
|
||||||
|
|
||||||
|
$condIvaPermitidas = [
|
||||||
|
'consumidor_final',
|
||||||
|
'responsable_inscripto',
|
||||||
|
'monotributo'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!in_array($condIVA, $condIvaPermitidas, true)) {
|
||||||
|
$_SESSION['flash_error'] = 'Condición de IVA inválida.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validación de CUIT
|
||||||
|
|
||||||
|
$cuit = trim($cuit);
|
||||||
|
|
||||||
|
if (!preg_match('/^\d{10,11}$/', $cuit)) {
|
||||||
|
$_SESSION['flash_error'] = 'CUIT inválido.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALIDACIÓN FORMA + TIPO DE PAGO
|
||||||
|
|
||||||
|
$reglasPago = [
|
||||||
|
'efectivo' => ['pago_unico'],
|
||||||
|
'transferencia' => ['pago_unico'],
|
||||||
|
'echeq' => ['pago_unico'],
|
||||||
|
'debito' => ['pago_unico'],
|
||||||
|
'credito' => ['pago_unico', '3_cuotas', '6_cuotas', '12_cuotas'],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!isset($reglasPago[$formaPago]) || !in_array($tipoPago, $reglasPago[$formaPago], true)) {
|
||||||
|
$_SESSION['flash_error'] = 'Combinación de forma y tipo de pago inválida.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALIDACIÓN DESCUENTO / RECARGO
|
||||||
|
|
||||||
|
if ($descuentoVenta < 0 || $descuentoVenta > 100) {
|
||||||
|
$_SESSION['flash_error'] = 'El descuento debe estar entre 0 y 100%.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($recargoVenta < 0 || $recargoVenta > 100) {
|
||||||
|
$_SESSION['flash_error'] = 'El recargo debe estar entre 0 y 100%.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$subtotal = $datosVenta['subtotal'];
|
||||||
|
|
||||||
|
if ($subtotal <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'La venta no contiene productos válidos.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asignamos datos a variables y validamos
|
||||||
|
|
||||||
|
$descuento = $datosVenta['descuento'];
|
||||||
|
$recargo = $datosVenta['recargo'];
|
||||||
|
$total = $datosVenta['total'];
|
||||||
|
|
||||||
|
if ($total <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Total inválido.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MAPEOS PARA PDF
|
||||||
|
|
||||||
|
$mapCondIVA = [
|
||||||
|
'consumidor_final' => 'Consumidor Final',
|
||||||
|
'responsable_inscripto' => 'Responsable Inscripto',
|
||||||
|
'monotributo' => 'Monotributo',
|
||||||
|
];
|
||||||
|
|
||||||
|
$mapFormaPago = [
|
||||||
|
'efectivo'=> 'Efectivo',
|
||||||
|
'transferencia' => 'Transferencia',
|
||||||
|
'debito' => 'Débito',
|
||||||
|
'credito' => 'Crédito',
|
||||||
|
'echeq' => 'Echeq',
|
||||||
|
];
|
||||||
|
|
||||||
|
$mapTipoPago = [
|
||||||
|
'pago_unico' => 'Pago Único',
|
||||||
|
'3_cuotas' => '3 Cuotas',
|
||||||
|
'6_cuotas' => '6 Cuotas',
|
||||||
|
'12_cuotas' => '12 Cuotas',
|
||||||
|
];
|
||||||
|
|
||||||
|
$condIVALabel = $mapCondIVA[$condIVA];
|
||||||
|
$formaPagoLabel = $mapFormaPago[$formaPago];
|
||||||
|
$tipoPagoLabel = $mapTipoPago[$tipoPago];
|
||||||
|
|
||||||
|
// CÁLCULO DE CUOTAS
|
||||||
|
|
||||||
|
if ($tipoPago !== 'pago_unico') {
|
||||||
|
$cuota = (int) explode('_', $tipoPago)[0];
|
||||||
|
$valorCuota = $total / $cuota;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtenemos el número de venta que se obtuvo desde la base de datos al momento de guardar la venta
|
||||||
|
|
||||||
|
$numero = $_SESSION['ultima_venta_id'] ?? null;
|
||||||
|
|
||||||
|
if (!$numero) {
|
||||||
|
$_SESSION['flash_error'] = 'No se encontró el número de venta.';
|
||||||
|
header('Location: index.php?opt=crear_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable para mostrar el código en la vista
|
||||||
|
|
||||||
|
$numeroVenta = '0001-' . str_pad($numero, 8, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
// RENDER PDF
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../pdf/ventas/ventaPDF.php';
|
||||||
|
$html = ob_get_clean();
|
||||||
|
|
||||||
|
$cssPath = '/var/www/html/css/stylesPDF.css';
|
||||||
|
$css = file_get_contents($cssPath);
|
||||||
|
|
||||||
|
// Configuración de opciones
|
||||||
|
|
||||||
|
$options = new Options();
|
||||||
|
$options->set('isRemoteEnabled', true); // Permite cargar recursos externos (CSS, imágenes, etc.)
|
||||||
|
$options->set('chroot', '/var/www/html'); // Define directorios base para recursos locales
|
||||||
|
$options->set('defaultFont', 'DejaVu Sans');
|
||||||
|
|
||||||
|
// Creación de instancia Dompdf con las opciones seteadas
|
||||||
|
|
||||||
|
$dompdf = new Dompdf($options);
|
||||||
|
|
||||||
|
// Cargar el HTML que se va a convertir en PDF
|
||||||
|
|
||||||
|
$dompdf->loadHtml("
|
||||||
|
<style>$css</style>
|
||||||
|
$html
|
||||||
|
");
|
||||||
|
|
||||||
|
$dompdf->setPaper('A4', 'portrait'); // Define el tamanio de hoja y la orientación
|
||||||
|
$dompdf->render(); // Renderiza el PDF (procesa el HTML)
|
||||||
|
|
||||||
|
// Enviar el PDF al browser
|
||||||
|
|
||||||
|
$dompdf->stream(
|
||||||
|
'Venta N° ' . $numeroVenta . '.pdf',
|
||||||
|
['Attachment' => true] // false = se muestra en el navegador | true = descarga directa
|
||||||
|
);
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONTINUAR SIN DESCARGAR EL COMPROBANTE PDF
|
||||||
|
|
||||||
|
case 'continuar_sin_comprobante':
|
||||||
|
|
||||||
|
// Liberamos $_SESSION
|
||||||
|
|
||||||
|
unset($_SESSION['venta_pdf']);
|
||||||
|
unset($_SESSION['ultima_venta_id']);
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = 'Venta finalizada correctamente.';
|
||||||
|
|
||||||
|
header('Location: index.php?opt=crear_venta');
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// CONFIRMAR VENTA
|
||||||
|
|
||||||
|
case 'confirmar_venta':
|
||||||
|
|
||||||
|
// Validaciones de regla de negocio
|
||||||
|
|
||||||
|
// Condición IVA
|
||||||
|
|
||||||
|
$condIvaPermitidas = [
|
||||||
|
'consumidor_final',
|
||||||
|
'responsable_inscripto',
|
||||||
|
'monotributo'
|
||||||
|
];
|
||||||
|
|
||||||
|
$condIva = $_POST['cond_iva'] ?? 'consumidor_final';
|
||||||
|
|
||||||
|
if (!in_array($condIva, $condIvaPermitidas, true)) {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'Condición de IVA inválida.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CUIT
|
||||||
|
|
||||||
|
$cuit = ($_POST['cuit_1'] ?? '') . ($_POST['cuit_2'] ?? '') . ($_POST['cuit_3'] ?? '');
|
||||||
|
|
||||||
|
$cuit = trim($cuit);
|
||||||
|
|
||||||
|
if ($cuit === '') {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'Debe ingresar un CUIT.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/^\d{11}$/', $cuit)) {
|
||||||
|
|
||||||
|
$_SESSION['flash_error'] = 'El CUIT debe contener 11 dígitos.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forma de pago + tipo de pago
|
||||||
|
|
||||||
|
$reglasPago = [
|
||||||
|
'efectivo' => ['pago_unico'],
|
||||||
|
'transferencia' => ['pago_unico'],
|
||||||
|
'echeq' => ['pago_unico'],
|
||||||
|
'debito' => ['pago_unico'],
|
||||||
|
'credito' => ['pago_unico', '3_cuotas', '6_cuotas', '12_cuotas'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$formaPago = $_POST['forma_pago'] ?? 'efectivo';
|
||||||
|
$tipoPago = $_POST['tipo_pago'] ?? 'pago_unico';
|
||||||
|
|
||||||
|
if (!isset($reglasPago[$formaPago]) || !in_array($tipoPago, $reglasPago[$formaPago], true)) {
|
||||||
|
$_SESSION['flash_error'] = 'Combinación de forma y tipo de pago inválida.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validación de descuento y recargo
|
||||||
|
|
||||||
|
$descuentoVenta = isset($_POST['descuento']) ? (float) $_POST['descuento'] : 0;
|
||||||
|
$recargoVenta = isset($_POST['recargo']) ? (float) $_POST['recargo'] : 0;
|
||||||
|
|
||||||
|
if ($descuentoVenta < 0 || $descuentoVenta > 100) {
|
||||||
|
$_SESSION['flash_error'] = 'El descuento debe estar entre 0 y 100%.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($recargoVenta < 0 || $recargoVenta > 100) {
|
||||||
|
$_SESSION['flash_error'] = 'El recargo debe estar entre 0 y 100%.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardado de datos de la venta
|
||||||
|
|
||||||
|
$datosExtraVenta = [
|
||||||
|
'forma_pago' => $formaPago,
|
||||||
|
'tipo_pago' => $tipoPago,
|
||||||
|
'descuento' => $descuentoVenta,
|
||||||
|
'recargo' => $recargoVenta,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Requiero los modelos necesarios
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/cajaModel.php';
|
||||||
|
$cajamodel = new cajaModel();
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/clienteModel.php';
|
||||||
|
$clientemodel = new clienteModel();
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/ventaModel.php';
|
||||||
|
$ventamodel = new ventaModel();
|
||||||
|
|
||||||
|
$idCliente = $_SESSION['venta']['cliente']['id'] ?? null;
|
||||||
|
|
||||||
|
if ($idCliente === null) {
|
||||||
|
$_SESSION['flash_error'] = 'No seleccionó un cliente.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$vendedor = $_SESSION['user'] ?? null;
|
||||||
|
$cliente = $clientemodel->obtenerPorId($idCliente);
|
||||||
|
|
||||||
|
if (!$cliente) {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo identificar el cliente.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$vendedor) {
|
||||||
|
$_SESSION['flash_error'] = 'No se pudo identificar el usuario.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$productosFinales = $cajamodel->listarProductos();
|
||||||
|
|
||||||
|
if (empty($productosFinales)) {
|
||||||
|
$_SESSION['flash_error'] = 'No hay productos en la venta.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($productosFinales as $p) {
|
||||||
|
if (empty($p['id_producto']) || empty($p['cantidad']) || $p['cantidad'] <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Venta con productos inválidos.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtotal que no contiene descuentos.
|
||||||
|
(Al ingresar descuento o recargo, lo guardo en un array separado, para no sobreescribir datos) */
|
||||||
|
|
||||||
|
$subtotal = $cajamodel->calcularSubtotal() ?? 0;
|
||||||
|
$descuento = $cajamodel->calcularDescuentoTotal() ?? 0;
|
||||||
|
|
||||||
|
// Validación del subtotal
|
||||||
|
|
||||||
|
if ($subtotal <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'La venta no contiene productos válidos.';
|
||||||
|
header('Location: index.php?opt=crear_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Llamamos a la función que devuelve los datos actualizados en un array nuevo
|
||||||
|
|
||||||
|
$datosProvisorios = $ventamodel->actualizarDatosProvisorio($descuentoVenta, $recargoVenta, $subtotal, $descuento);
|
||||||
|
|
||||||
|
if ($datosProvisorios['total'] <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'El total de la venta es inválido.';
|
||||||
|
header('Location: index.php?opt=resumen_venta');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtenemos el ID de venta desde la base de datos
|
||||||
|
|
||||||
|
$idVenta = $ventamodel->guardarVenta($productosFinales, $vendedor, $cliente, $datosExtraVenta, $datosProvisorios);
|
||||||
|
|
||||||
|
// Verificamos si se obtuvo el ID desde la base o si ocurrió un error
|
||||||
|
|
||||||
|
if ($idVenta !== false) {
|
||||||
|
|
||||||
|
// Guardamos en $_SESSION los datos necesarios para la generación del PDF
|
||||||
|
|
||||||
|
$_SESSION['ultima_venta_id'] = $idVenta;
|
||||||
|
|
||||||
|
$_SESSION['venta_pdf'] = [
|
||||||
|
|
||||||
|
'cliente' => $cliente,
|
||||||
|
'vendedor' => $vendedor,
|
||||||
|
'productos' => $productosFinales,
|
||||||
|
|
||||||
|
'cond_iva' => $condIva,
|
||||||
|
'cuit' => $cuit,
|
||||||
|
|
||||||
|
'forma_pago' => $formaPago,
|
||||||
|
'tipo_pago' => $tipoPago,
|
||||||
|
|
||||||
|
'descuentoVenta' => $descuentoVenta,
|
||||||
|
'recargoVenta' => $recargoVenta,
|
||||||
|
|
||||||
|
'descuento' => $datosProvisorios['descuento'],
|
||||||
|
'recargo' => $datosProvisorios['recargo'],
|
||||||
|
|
||||||
|
'subtotal' => $subtotal,
|
||||||
|
'total' => $datosProvisorios['total']
|
||||||
|
];
|
||||||
|
|
||||||
|
// Liberamos
|
||||||
|
|
||||||
|
$cajamodel->vaciarCajaVenta();
|
||||||
|
unset($_SESSION['venta']['cliente']);
|
||||||
|
unset($_SESSION['venta_extra']);
|
||||||
|
unset($_SESSION['venta_final']);
|
||||||
|
unset($_SESSION['prov_venta']);
|
||||||
|
|
||||||
|
$_SESSION['flash_success'] = "Venta guardada correctamente.";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$_SESSION['flash_error'] = $ventamodel->getError() ?? 'No se pudo guardar la venta.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php?opt=generar_venta_pdf");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
// LISTAR VENTAS
|
||||||
|
|
||||||
|
case 'listar_ventas':
|
||||||
|
|
||||||
|
// Requiero el modelo de venta
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/ventaModel.php';
|
||||||
|
$ventamodel = new ventaModel();
|
||||||
|
|
||||||
|
$anioActual = date('Y');
|
||||||
|
$anioSeleccionado = $_GET['anio'] ?? $anioActual;
|
||||||
|
|
||||||
|
$ventas = $ventamodel->listarVentas($anioSeleccionado);
|
||||||
|
|
||||||
|
foreach ($ventas as $i => $v) {
|
||||||
|
|
||||||
|
// Saneamiento de fecha (para poder ordenar correctamente el campo con dataTables)
|
||||||
|
|
||||||
|
if (!empty($v['fecha'])) {
|
||||||
|
|
||||||
|
$dt = new DateTime($v['fecha']);
|
||||||
|
|
||||||
|
// Para ordenar
|
||||||
|
|
||||||
|
$ventas[$i]['fecha'] = $dt->format('Y-m-d');
|
||||||
|
|
||||||
|
// Para mostrar
|
||||||
|
|
||||||
|
$ventas[$i]['fecha_formateada'] = $dt->format('d/m/Y');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$ventas[$i]['fecha'] = '-';
|
||||||
|
$ventas[$i]['fecha_formateada'] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mapeos de forma y tipo de pago
|
||||||
|
|
||||||
|
$mapFormaPago = [
|
||||||
|
'efectivo'=> 'Efectivo',
|
||||||
|
'transferencia' => 'Transferencia',
|
||||||
|
'debito' => 'Débito',
|
||||||
|
'credito' => 'Crédito',
|
||||||
|
'echeq' => 'Echeq',
|
||||||
|
];
|
||||||
|
|
||||||
|
$mapTipoPago = [
|
||||||
|
'pago_unico' => 'Pago Único',
|
||||||
|
'3_cuotas' => '3 Cuotas',
|
||||||
|
'6_cuotas' => '6 Cuotas',
|
||||||
|
'12_cuotas' => '12 Cuotas',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Saneamiento de forma de pago
|
||||||
|
|
||||||
|
$ventas[$i]['forma_pago'] = $mapFormaPago[$ventas[$i]['forma_pago']];
|
||||||
|
|
||||||
|
// Saneamiento de tipo de pago
|
||||||
|
|
||||||
|
$ventas[$i]['tipo_pago'] = $mapTipoPago[$ventas[$i]['tipo_pago']];
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/ventas/listaVentasView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// MOSTRAR DETALLE DE PRODUCTOS DE UNA VENTA
|
||||||
|
|
||||||
|
case 'detalle_venta':
|
||||||
|
|
||||||
|
$idVenta = (int) ($_GET['id'] ?? 0);
|
||||||
|
|
||||||
|
if ($idVenta <= 0) {
|
||||||
|
$_SESSION['flash_error'] = 'Datos inválidos.';
|
||||||
|
header('Location: index.php?opt=listar_ventas');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../models/ventaModel.php';
|
||||||
|
$ventamodel = new ventaModel();
|
||||||
|
|
||||||
|
$venta_detalle = $ventamodel->getDetalleVenta($idVenta);
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require __DIR__ . '/../views/ventas/ventaDetalleView.php';
|
||||||
|
return ob_get_clean();
|
||||||
|
|
||||||
|
// DEFAULT (404)
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
header("Location: index.php?opt=404View.php");
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user