Carpeta views

This commit is contained in:
Axel Axt
2026-08-04 19:24:53 -03:00
parent 6aaff2ffe4
commit d95e4012a4
54 changed files with 10640 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
<div class="container d-flex align-items-center justify-content-center" style="min-height: 100vh;">
<div class="card card-rounded card-shadow text-center p-5">
<h1 class="display-1 fw-bold text-danger">404</h1>
<h4 class="mb-3">Página no encontrada</h4>
<p class="text-muted mb-4">
La página que estás buscando no existe o no tienes permisos para acceder.
</p>
</div>
</div>
+798
View File
@@ -0,0 +1,798 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5 readme-container">
<div class="row justify-content-center">
<div class="col-xl-12 col-lg-11">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap">
<h4 class="mb-0 title-shadow">
<i class="fas fa-book me-2"></i>
Ayuda
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=inicio_operador" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body readme-scroll px-4 py-4">
<div class="readme-index mb-3">
<h5>Índice</h5>
<ul>
<li>
<a href="#modulos-comunes">Módulos Comunes (Administrador y Operador)</a>
<ul>
<li><a href="#clientes">Clientes</a></li>
<li><a href="#ventas">Ventas</a></li>
<li><a href="#servicios">Servicios</a></li>
<li><a href="#productos">Productos</a></li>
<li><a href="#presupuestos">Presupuestos</a></li>
<li><a href="#proveedores">Proveedores</a></li>
<li><a href="#pedidos">Pedidos</a></li>
</ul>
</li>
<li>
<a href="#modulos-admin">Módulos Exclusivos (Administrador)</a>
<ul>
<li><a href="#usuarios">Usuarios</a></li>
<li><a href="#informes">Informes</a></li>
<li><a href="#copias_seguridad">Copias de Seguridad</a></li>
</ul>
</li>
</ul>
</div>
<h1>MÓDULOS PRINCIPALES DEL SISTEMA</h1>
<p>
El sistema se organiza en distintos módulos que permiten gestionar la información, las operaciones del negocio y distintos aspectos del sistema.
Los mismos están distribuidos entre el Panel Principal y el Panel de Negocio, en donde cada uno se compone de distintas pantallas y acciones a poder realizar:
</p>
<h2 id="modulos-comunes">MÓDULOS COMUNES (Administrador y Operador)</h2>
<h2 id="clientes"> <i class="fas fa-users me-2"> </i> CLIENTES</h2>
<h3>OBJETIVO</h3>
<p>Gestionar la información de los clientes registrados en el sistema.</p>
<h3>PANTALLA PRINCIPAL</h3>
<p>
Al ingresar al módulo “Clientes”, se visualizará un listado completo de los clientes registrados en el sistema,
junto con filtros de búsqueda que permiten localizar registros según distintos campos.
</p>
<h3>CREAR UN NUEVO CLIENTE</h3>
<ul>
<li>Hacer clic en el botón “Nuevo Cliente”, ubicado en la esquina superior derecha.</li>
<li>Completar los datos solicitados en el formulario, en base a las restricciones del sistema para garantizar la consistencia.</li>
<li>Una vez completado, presionar en el botón “Crear Cliente” para guardar el registro.</li>
</ul>
<h3>EDITAR UN CLIENTE EXISTENTE</h3>
<ul>
<li>Ubicar el cliente buscado dentro del listado de registros.</li>
<li>Presionar el botón “Editar” correspondientemente.</li>
<li>Se abrirá otra pantalla en donde puede modificar los datos del mismo.</li>
<li>Hacer clic en el botón “Guardar Cambios”.</li>
</ul>
<h2 id="ventas"> <i class="fas fa-shopping-cart me-2"> </i> VENTAS</h2>
<h3>OBJETIVO</h3>
<p>
Generar ventas asociadas a clientes, permitiendo calcular totales, aplicar descuentos, recargos, formas de pagos y mucho más, pudiendo emitir el comprobante correspondiente, y guardando de manera permanente la venta y todos los datos asociados.
</p>
<h3>PANTALLA PRINCIPAL</h3>
<p>
La pantalla se compone de: Selección de cliente, Gestión de productos y Cálculo preliminar (subtotal, descuento y total).
</p>
<h3>CLIENTE</h3>
<p>
En la parte superior se visualiza la sección correspondiente al cliente.
</p>
<h4>SELECCIONAR CLIENTE</h4>
<ul>
<li>Hacer clic en “Seleccionar Cliente”.</li>
<li>Se abrirá una pantalla con el listado de clientes disponibles.</li>
<li>Utilizar los filtros si es necesario.</li>
<li>Seleccionar la casilla correspondiente al cliente deseado.</li>
<li>Presionar el botón “Finalizar”.</li>
<li>El sistema redirigirá automáticamente a la pantalla de creación de venta con el cliente seleccionado.</li>
</ul>
<h4>ELIMINAR CLIENTE SELECCIONADO</h4> <p>Permite quitar el cliente seleccionado y dejar los campos vacíos.</p>
<h3>GESTION DE PRODUCTOS DE LA VENTA</h3>
<p>
En la parte central se encuentra el gestor de productos que formarán parte de la venta.
</p>
<h4>AGREGAR PRODUCTO</h4>
<ul>
<li>Hacer clic en “Agregar Producto”.</li>
<li>Se abrirá una pantalla con el listado de productos disponibles.</li>
<li>Para cada producto seleccionado se debe indicar la cantidad correspondiente a vender.</li>
<li>Presionar el botón “Finalizar Selección”.</li>
<li>Los productos seleccionados se agregarán a la lista principal de la venta.</li>
</ul>
<h4>VACIAR LISTA DE PRODUCTOS</h4> <p>Permite eliminar todos los productos agregados a la venta, dejando la lista vacía.</p>
<h4>EDITAR CANTIDAD DE PRODUCTO SELECCIONADO</h4>
<ul>
<li>Modificar directamente la cantidad.</li>
<li>Presionar el botón de edición ubicado en la columna “Acciones”.</li>
</ul>
<h4>ELIMINAR PRODUCTO INDIVIDUAL</h4> <p>En la columna “Acciones” además se encuentra un botón para eliminar productos de forma individual sin afectar el resto de la lista.</p>
<h3>CALCULO PRELIMINAR</h3>
<p>
En la parte inferior de la pantalla se visualizan los siguientes campos: Subtotal, Descuento y Total.
</p>
<p>
Estos valores se actualizan automáticamente en base a los productos y cantidades ingresadas.
</p>
<h3>ACCEDER A RESUMEN DE VENTA</h3>
<p>
Al hacer clic en “Siguiente”, el sistema redirige a una pantalla resumen donde se visualiza:
</p>
<ul>
<li>Datos del cliente.</li>
<li>Listado de productos seleccionados.</li>
<li>Resumen de importes.</li>
</ul>
<p>
Además, se deben completar o modificar los siguientes datos de pago:
</p>
<ul>
<li>Condición de IVA del cliente. (si desea)</li>
<li>CUIT del cliente. (si desea)</li>
<li>Forma de pago.</li>
<li>Tipo de pago.</li>
<li>Descuento.</li>
<li>Recargo.</li>
</ul>
<h3>ACCIONES FINALES</h3>
<p>
En la pantalla resumen se encuentran dos botones principales:
</p>
<h4>GUARDAR COMO BORRADOR</h4>
<ul>
<li>Guarda los datos de venta de forma provisoria.</li>
<li>Permite salir del módulo y realizar otras acciones en el sistema.</li>
<li>Al volver, los datos permanecen intactos.</li>
</ul>
<h4>CONFIRMAR VENTA</h4>
<p>
Guarda de manera permanente en el sistema la venta realizada, junto a todos los datos ingresados y asociados.
Una vez realizada la venta, el sistema lo redirigirá a una pantalla preguntando si desea descargar el comprobante o simplemente continuar,
en caso de querer descargar el comprobante hacer click en "Descargar Comprobante" y puede guardarlo donde desee, caso contrario clickear en
botón "Continuar" y finalizará totalmente el proceso de venta.
</p>
<h3>VENTAS REGISTRADAS</h3>
<p>
En la parte superior de la pantalla de creación de venta se encuentra el botón “Ventas Registras”, el cual abre un listado de los registros que posee el sistema; sobre cada uno de ellos existe un botón “Detalle Productos” que permite visualizar los productos involucrados en la venta.
</p>
<h2 id="servicios"> <i class="fas fa-wrench me-2"> </i> SERVICIOS</h2>
<h3>OBJETIVO</h3>
<p>
Registrar servicios técnicos realizados a equipos de clientes, documentar el diagnóstico y gestionar el proceso hasta su finalización o cancelación.
</p>
<h3>CREACION DE UN SERVICIO</h3>
<p>
La pantalla de creación de servicio inicial se compone de tres secciones: Selección de cliente, Datos del servicio y Acciones finales.
</p>
<h3>CLIENTE</h3>
<p>
En la parte superior se visualiza la sección correspondiente al cliente.
</p>
<h4>SELECCIONAR CLIENTE</h4>
<ul>
<li>Hacer clic en “Seleccionar Cliente”.</li>
<li>Se abrirá una pantalla con el listado de clientes disponibles.</li>
<li>Utilizar los filtros si es necesario.</li>
<li>Seleccionar la casilla correspondiente al cliente deseado.</li>
<li>Presionar el botón “Finalizar”.</li>
<li>El sistema redirigirá automáticamente a la pantalla de creación de servicio con el cliente seleccionado.</li>
</ul>
<h4>ELIMINAR CLIENTE SELECCIONADO</h4> <p>Permite quitar el cliente seleccionado y dejar los campos vacíos.</p>
<h3>DATOS DEL SERVICIO</h3>
<p>
En la sección central se deben completar los datos relacionados al equipo ingresado.
</p>
<p>Los campos principales son:</p>
<ul>
<li>Descripción del equipo.</li>
<li>Contraseña del equipo (si corresponde).</li>
<li>Cuenta asociada (Gmail o Apple).</li>
<li>Contraseña de la cuenta (si corresponde).</li>
</ul>
<p>Luego se encuentran los siguientes campos descriptivos:</p>
<ul>
<li>Problema reportado.</li>
<li>Observaciones generales.</li>
</ul>
<p>
NOTA: Estos campos permiten registrar información relevante para el diagnóstico y reparación del equipo.
</p>
<h3>ACCIONES FINALES</h3>
<p>
En la parte inferior de la pantalla se encuentran dos botones principales.
</p>
<h4>GENERAR SERVICIO</h4>
<ul>
<li>Genera el documento del servicio en formato PDF.</li>
<li>El documento contiene el detalle de los datos registrados.</li>
<li>Se abre automáticamente en una nueva pestaña para su descarga o impresión.</li>
</ul>
<h4>CONFIRMAR SERVICIO</h4>
<ul>
<li>Guarda el servicio en el sistema.</li>
<li>El registro queda disponible para su posterior seguimiento.</li>
</ul>
<h3>SERVICIOS REGISTRADOS</h3>
<p>
En la parte superior de la pantalla se encuentra el botón “Servicios Registrados”, al acceder, se visualiza un listado con todos los servicios registrados en el sistema, cada servicio cuenta con diferentes acciones disponibles según su estado.
</p>
<h4>DETALLE SERVICIO</h4>
<p>Permite visualizar toda la información registrada del servicio.</p>
<h4>ACTUALIZAR SERVICIO</h4>
<p>
Conduce a una pantalla donde se debe registrar:
</p>
<ul>
<li>El detalle de la reparación a realizar.</li>
<li>Los insumos necesarios.</li>
<li>El costo de mano de obra.</li>
</ul>
<p>
Una vez completados estos datos, el sistema envía automáticamente un correo electrónico al cliente para informar el diagnóstico (si la opción esta activada en configuración) y permitirle decidir si desea continuar con la reparación.
</p>
<h4>FINALIZAR SERVICIO</h4>
<p>
Permite marcar el servicio como Finalizado.
</p>
<ul>
<li>Se registra el ingreso de dinero correspondiente al servicio, y demas asociaciones necesarias como el descuento de stock.</li>
<li>El servicio queda cerrado dentro del sistema.</li>
<li>Se envía automáticamente un email al cliente detallando el servicio e indicando que el mismo fue finalizado y ya puede retirar el equipo (si la opción esta activada en configuración).</li>
</ul>
<h4>CANCELAR SERVICIO</h4>
<p>
Permite marcar el servicio como cancelado en caso de que no se continúe con la reparación.
</p>
<h4>DETALLE INSUMOS</h4>
<p>
Permite visualizar los insumos asociados al servicio.
</p>
<h3>ESTADOS DEL SERVICIO</h3>
<ul>
<li>En cualquier estado, el botón de detalle siempre estará visible.</li>
<li>Un servicio Pendiente solo puede actualizarse, ingresando los datos correspondientes a realizar.</li>
<li>Un servicio Pendiente - Enviado permite ser Cancelado o Finalizado, y visualizar el detalle de insumos ingresados (si corresponde).</li>
<li>Un servicio Finalizado o Cancelado permite ver el detalle de los insumos.</li>
</ul>
<h2 id="productos"> <i class="fas fa-box-open me-2"> </i> PRODUCTOS</h2>
<h3>OBJETIVO</h3>
<p>
Gestionar los productos comercializados por el negocio, tanto para venta directa como para uso en servicios (insumos).
</p>
<h3>PANTALLA PRINCIPAL</h3>
<p>
Al ingresar al módulo “Productos”, se visualizará un listado completo de productos registrados,
junto con información relevante asociada a cada uno de ellos y las acciones disponibles a realizar.
</p>
<h3>EDITAR UN PRODUCTO EXISTENTE</h3>
<ul>
<li>Ubicar el producto buscado dentro del listado de registros.</li>
<li>Presionar el botón “Editar” correspondientemente.</li>
<li>Se abrirá otra pantalla en donde puede modificar el descuento aplicado al mismo.</li>
<li>Hacer clic en el botón “Guardar Cambios”.</li>
</ul>
<h3>OCULTAR UN PRODUCTO</h3>
<p>
Al presionar el botón “Desactivar Producto” se permite ocultar el producto, principalmente en caso de que deje de
comercializarse, esta acción no elimina el producto del sistema, sino que lo marca como inactivo.
</p>
<h3>GESTIÓN DE PRODUCTOS INACTIVOS</h3>
<ul>
<li>Acceder al listado de productos inactivos.</li>
<li>Volver a activar productos previamente ocultados.</li>
</ul>
<p>
Esto permite mantener la integridad histórica de los datos sin perder registros.
</p>
<h3>CREAR UN NUEVO PRODUCTO</h3>
<ul>
<li>En la parte superior derecha se encuentra el botón “Nuevo Producto”.</li>
<li>Hacer clic en el botón “Nuevo Producto”.</li>
<li>Completar los datos solicitados en base a restricciones indicadas por el sistema, para garantizar consistencias.</li>
<li>Hacer clic en botón “Crear Producto”.</li>
</ul>
<h3>CATEGORÍAS DE PRODUCTOS</h3>
<p>
Junto al botón de creación de producto, se encuentra el botón “Categorías”, el mismo permite:
</p>
<ul>
<li>Visualizar el listado de categorías existentes.</li>
<li>Editar categorías.</li>
<li>Crear una nueva categoría mediante el botón correspondiente.</li>
</ul>
<p>
Las categorías determinan ciertos parámetros importantes, como el margen de ganancia aplicado al producto
(con el cual se determina el precio de venta) o el descuento aplicado a todos los productos pertenecientes a la misma.
</p>
<h3>CONSIDERACIONES IMPORTANTES</h3>
<h4>PRECIO DEL PRODUCTO</h4>
<p>
Al crear un producto el precio no se ingresa manualmente, sino que se calcula automáticamente en base al último
Pedido registrado que contenga dicho producto, es decir, para cargar el precio, primero se debe realizar un Pedido,
modelando así la lógica de negocio; además de esto, a dicho precio se le aplica la ganancia ingresada en la
categoría correspondiente al producto.
</p>
<p>
Esto garantiza coherencia en la estructura de costos y márgenes.
</p>
<h4>STOCK</h4>
<p>
El stock del producto no se carga manualmente al crearlo, sino que está vinculado a los Pedidos realizados que
incluyan a dicho producto. En cada Venta o Servicio realizado la contabilización de stock se descuenta automáticamente.
</p>
<h4>TIPO DE PRODUCTO</h4>
<ul>
<li>Venta: Producto destinado a comercialización directa.</li>
<li>Servicio: Producto utilizado como insumo en la prestación de servicios.</li>
</ul>
<h4>DESCUENTOS DE CATEGORÍA</h4>
<p>
Este descuento se aplica a todos los productos que pertenezcan a una misma categoría, a parte del descuento
que se puede aplicar a cada uno de ellos.
</p>
<p>
Esto permite libertad al poder aplicar descuentos generales o aplicados a ciertos productos.
</p>
<h2 id="presupuestos"> <i class="fas fa-file-invoice-dollar me-2"> </i> PRESUPUESTOS</h2>
<h3>OBJETIVO</h3>
<p>
Generar presupuestos para clientes, permitiendo calcular totales, aplicar descuentos, recargos, formas de pagos y mucho más, emitiendo así el documento correspondiente.
</p>
<h3>PANTALLA PRINCIPAL</h3>
<p>
La pantalla de creación de presupuestos se compone de: Selección de cliente, Gestión de productos y Cálculo preliminar (subtotal, descuento y total).
</p>
<h3>CLIENTE</h3>
<h4>SELECCIONAR CLIENTE</h4>
<ul>
<li>Hacer clic en “Seleccionar Cliente”.</li>
<li>Se abrirá una pantalla con el listado de clientes disponibles.</li>
<li>Utilizar los filtros si es necesario.</li>
<li>Seleccionar la casilla correspondiente al cliente deseado.</li>
<li>Presionar el botón “Finalizar”.</li>
<li>El sistema redirigirá automáticamente a la pantalla de creación del presupuesto con el cliente seleccionado.</li>
</ul>
<h4>ELIMINAR CLIENTE SELECCIONADO</h4> <p>Permite quitar el cliente seleccionado y dejar los campos vacíos.</p>
<h3>GESTION DE PRODUCTOS DEL PRESUPUESTO</h3>
<p>
En la parte central se encuentra el gestor de productos que formarán parte del presupuesto.
</p>
<h4>AGREGAR PRODUCTO</h4>
<ul>
<li>Hacer clic en “Agregar Producto”.</li>
<li>Se abrirá una pantalla con el listado de productos disponibles.</li>
<li>Para cada producto seleccionado se debe indicar la cantidad correspondiente a agregar.</li>
<li>Presionar el botón “Finalizar Selección”.</li>
<li>Los productos seleccionados se agregarán a la lista principal del presupuesto.</li>
</ul>
<h4>VACIAR LISTA DE PRODUCTOS</h4> <p>Permite eliminar todos los productos agregados al presupuesto.</p>
<h4>EDITAR CANTIDAD DE PRODUCTO SELECCIONADO</h4>
<ul>
<li>Modificar directamente la cantidad.</li>
<li>Presionar el botón de edición ubicado en la columna “Acciones”.</li>
</ul>
<h4>ELIMINAR PRODUCTO INDIVIDUAL</h4> <p>Permite eliminar productos de forma individual sin afectar el resto de la lista.</p>
<h3>CALCULO PRELIMINAR</h3>
<p>
En la parte inferior de la pantalla se visualizan los siguientes campos: Subtotal, Descuento y Total.
</p>
<p>
Estos valores se actualizan automáticamente en base a los productos y cantidades ingresadas.
</p>
<h3>ACCEDER A RESUMEN DE PRESUPUESTO</h3>
<p>
Al hacer clic en “Siguiente”, el sistema redirige a una pantalla resumen donde se visualiza:
</p>
<ul>
<li>Datos del cliente.</li>
<li>Listado de productos seleccionados.</li>
<li>Resumen de importes.</li>
</ul>
<p>
Además, se deben completar o modificar los siguientes datos de pago:
</p>
<ul>
<li>Condición de IVA del cliente.</li>
<li>CUIT del cliente.</li>
<li>Forma de pago.</li>
<li>Tipo de pago.</li>
<li>Descuento.</li>
<li>Recargo.</li>
</ul>
<h3>ACCIONES FINALES</h3>
<h4>GENERAR PRESUPUESTO</h4>
<ul>
<li>Genera el documento del presupuesto.</li>
<li>Se abre automáticamente en una nueva pestaña.</li>
<li>Permite descargarlo o guardarlo según necesidad.</li>
</ul>
<h4>GUARDAR COMO BORRADOR</h4>
<ul>
<li>Guarda los datos del presupuesto de forma provisoria.</li>
<li>Permite salir del módulo y realizar otras acciones en el sistema.</li>
<li>Al volver, los datos permanecen intactos.</li>
</ul>
<h2 id="proveedores"> <i class="fas fa-truck me-2"> </i> PROVEEDORES</h2>
<h3>OBJETIVO</h3>
<p>Administrar los proveedores asociados al negocio.</p>
<h3>PANTALLA PRINCIPAL</h3>
<p>
Al ingresar al módulo “Proveedores” se visualizará una lista con los proveedores registrados,
además de filtros de búsqueda para localizar registros específicos.
</p>
<h3>CREAR UN NUEVO PROVEEDOR</h3>
<ul>
<li>Hacer clic en el botón “Nuevo Proveedor”, ubicado en la esquina superior derecha.</li>
<li>Completar los datos solicitados en el formulario, en base a las restricciones del sistema para garantizar la consistencia.</li>
<li>Una vez completado, presionar en el botón “Crear Proveedor” para guardar el registro.</li>
</ul>
<h3>EDITAR UN PROVEEDOR EXISTENTE</h3>
<ul>
<li>Ubicar el proveedor buscado dentro del listado de registros.</li>
<li>Presionar el botón “Editar” correspondientemente.</li>
<li>Se abrirá otra pantalla en donde puede modificar los datos del mismo.</li>
<li>Hacer clic en el botón “Guardar Cambios”.</li>
</ul>
<h2 id="pedidos"> <i class="fas fa-clipboard-list me-2"> </i> PEDIDOS</h2>
<h3>OBJETIVO</h3>
<p>
Registrar pedidos realizados a proveedores, actualizar el stock de productos y mantener trazabilidad de costos.
</p>
<h3>PANTALLA PRINCIPAL</h3>
<p>
La pantalla de “Pedidos” está organizada en tres secciones principales: Datos del proveedor, el Listado de productos y los Datos finales de pedido.
</p>
<h3>PROVEEDOR</h3>
<h4>SELECCIONAR PROVEEDOR</h4>
<ul>
<li>Hacer clic en “Seleccionar Proveedor”.</li>
<li>Se abrirá una pantalla con el listado de proveedores disponibles.</li>
<li>Utilizar los filtros si es necesario.</li>
<li>Seleccionar la casilla correspondiente al proveedor deseado.</li>
<li>Presionar el botón “Finalizar”.</li>
<li>El sistema redirigirá automáticamente a la pantalla de creación del pedido con el proveedor seleccionado.</li>
</ul>
<h4>ELIMINAR PROVEEDOR SELECCIONADO</h4> <p>Permite quitar el proveedor seleccionado y dejar los campos vacíos.</p>
<h3>GESTION DE LISTADO DE PRODUCTOS</h3>
<p>
En la parte central se encuentra el gestor de productos que formarán parte del pedido.
</p>
<h4>AGREGAR PRODUCTO</h4>
<ul>
<li>Hacer clic en “Agregar Producto”.</li>
<li>Se abrirá una pantalla con el listado de productos disponibles.</li>
<li>Para cada producto seleccionado se debe Ingresar el costo unitario e Indicar la cantidad correspondiente.</li>
<li>Marcar la casilla de selección.</li>
<li>Presionar el botón “Finalizar Selección”.</li>
<li>Los productos seleccionados se agregarán a la lista principal del pedido.</li>
</ul>
<h4>VACIAR LISTA DE PRODUCTOS</h4> <p>El botón “Vaciar Lista” permite eliminar todos los productos agregados al pedido, dejando la lista vacía.</p>
<h4>EDITAR DATOS DE PRODUCTO SELECCIONADO</h4>
<ul>
<li>Modificar directamente el costo o la cantidad.</li>
<li>Presionar el botón de edición ubicado en la columna “Acciones”.</li>
</ul>
<h4>ELIMINAR PRODUCTO INDIVIDUAL</h4> <p>En la columna “Acciones” también se encuentra un botón para eliminar productos de forma individual sin afectar el resto de la lista.</p>
<h3>ACCIONES FINALES DEL PEDIDO</h3>
<p>
En la parte inferior de la vista, a parte del monto final y la fecha, se encuentran un botón:
</p>
<h4>CONFIRMAR PEDIDO</h4>
<p>
Este botón permite guardar de manera definitiva el pedido en el sistema, actualizando el stock y precio de cada producto involucrado en el pedido,
y cargar la vista final en donde el sistema consulta si desea descargar el remito o no, en caso de que desee debe presionar el botón "Generar Remito" y guardarlo donde desee,
sino, puede hacer click en "Continuar" y así finaliza definitivamente el circuito de creación de pedido.
</p>
<h3>
PEDIDOS REGISTRADOS</h4> <p>Al hacer clic en el botón “Pedidos Registrados” que se encuentra en la parte superior derecha podrá visualizar el listado completo de todos los pedidos registrados.</p>
</h3>
<h2 id="modulos-admin">MÓDULOS EXCLUSIVOS (Administrador)</h2>
<h2 id="usuarios"> <i class="fas fa-users-cog me-2"> </i> USUARIOS</h2>
<h3>OBJETIVO</h3>
<p>Gestionar los usuarios con distintos tipos de acceso al sistema.</p>
<h3>PANTALLA PRINCIPAL</h3>
<p>
Al ingresar al módulo “Usuarios”, se visualizará un listado completo de los usuarios registrados,
junto con filtros de búsqueda en base a campos seleccionados, y acciones a realizar sobre cada usuario.
</p>
<h3>EDITAR UN USUARIO EXISTENTE</h3>
<ul>
<li>Ubicar el usuario buscado dentro de los registros.</li>
<li>Presionar el botón “Editar” correspondientemente.</li>
<li>Se abrirá otra pantalla en donde puede modificar los datos del mismo.</li>
<li>Hacer clic en el botón “Guardar Cambios”.</li>
</ul>
<p>NOTA: La contraseña actual no se muestra por motivos de seguridad.</p>
<h3>ELIMINAR USUARIO</h3>
<ul>
<li>Ubicar el usuario buscado dentro del listado de registros.</li>
<li>Hacer clic en el botón que se encuentra ubicado del lado derecho para eliminar el usuario.</li>
</ul>
<p>NOTA: Las acciones vinculadas a dicho usuario no serán desvinculadas ni eliminadas.</p>
<h3>CREAR UN NUEVO USUARIO</h3>
<ul>
<li>Hacer clic en el botón “Nuevo Usuario”, ubicado en la esquina superior derecha.</li>
<li>Completar los datos solicitados.</li>
<li>Seleccionar el rol correspondiente, ya sea Operador o Administrador.</li>
<li>Presionar el botón “Crear Usuario” para finalizar.</li>
</ul>
<h2 id="informes"> <i class="fas fa-chart-line me-2"> </i> INFORMES</h2>
<h3>OBJETIVO</h3>
<p>
Visualizar información financiera anual del negocio mediante gráficos dinámicos.
</p>
<p>
El módulo permite alternar entre distintos tipos de gráficos utilizando un selector superior para elegir
el tipo de informe y otro selector de año para definir el periodo a analizar.
</p>
<p>Existen dos informes disponibles:</p>
<ul>
<li>Balance Anual</li>
<li>Dinero a Cobrar</li>
</ul>
<h3>BALANCE ANUAL</h3>
<p>
Este informe permite analizar el rendimiento económico mensual del negocio durante el año seleccionado.
</p>
<ul>
<li>Ingresos mensuales por Ventas y Servicios.</li>
<li>Egresos correspondientes a Pedidos realizados.</li>
<li>Un gráfico de línea que representa la utilidad mensual.</li>
</ul>
<h4>TARJETAS INFORMATIVAS SUPERIORES</h4>
<ul>
<li>Ingresos totales.</li>
<li>Egresos totales.</li>
<li>Resultado neto anual.</li>
<li>Mejor mes del año.</li>
</ul>
<p>NOTA: Estas tarjetas se actualizan automáticamente al cambiar el año seleccionado.</p>
<h3>DINERO A COBRAR</h3>
<p>
Este informe permite visualizar el dinero pendiente a cobrar por servicios marcados como Pendiente - Enviado, en base al año seleccionado.
</p>
<ul>
<li>Una barra que suma el monto de servicio a cobrar.</li>
</ul>
<p>
Representa el total de dinero que todavía no ha sido cobrado por el negocio.
</p>
<p>
Las tarjetas de egresos, resultado neto y mejor mes se desactivan.
Solo permanece activa la tarjeta de ingresos, mostrando el total pendiente de cobro.
</p>
<h2 id="copias_seguridad"> <i class="fas fa-book me-2"> </i> COPIAS DE SEGURIDAD</h2>
<h3>OBJETIVO</h3>
<p>Gestionar las copias de seguridad realizadas sobre los datos contenidos en la base de datos.</p>
<h3>PANTALLA PRINCIPAL</h3>
<p>
Al ingresar al módulo “Copias de Seguridad”, se visualizará un listado con las copias realizadas que actualmente estan almacenadas,
junto con acciones que se puede realizar sobre cada una de ellas. Además en la parte inferior se visualiza el botón para generar una nueva copia.
</p>
<h3>ACCIONES SOBRE CADA COPIA</h3>
<p>
Existen tres botones, uno para descargar de manera local el archivo que contiene la copia de seguridad, el segundo que permite eliminarlo permanentemente,
y el tercero que sirve para restaurar los datos del sistema a partir de la copia seleccionada.
</p>
<p>NOTA: Se recomienda precaución sobre el uso de estas acciones, ya que si bien se crea una copia automática al restaurar el sistema, se trata de datos sensibles
que pueden afectar su negocio.
</p>
<h3>CREAR COPIA DE SEGURIDAD</h3>
<p>En la zona inferior derecha de la pantalla se encuentra un botón "Generar Copia", que permite generar y guardar un nuevo backup, para
poder restaurar el sistema en un futuro a partir del mismo.
</p>
<p>NOTA GENERALES SOBRE EL FUNCIONAMIENTO: Al iniciar sesión y durante una cierta cantidad de tiempo, si el sistema no detecta copias de seguridad almacenadas,
el mismo dará aviso para que usted verifique el estado de las mismas, esto puede deberse a que no encuentra el dispositivo externo
(si asi se setea al momento de la instalación y configuración del sistema) o porque no existen copias almacenadas,
se pide que verifique cual de estos casos está ocurriendo y si es necesario, generar un backup.
<br>
Por otro lado, el sistema puede generar copias automáticas cada 7 días, esto siempre sucederá en la pantalla de Panel Principal (al iniciar sesión o acceder a ella),
y será informado con un mensaje de éxito o fracaso. Para que esto suceda debe estar conectado el dispositivo externo (si así fué configurado).
</p>
</div>
</div>
</div>
</div>
</div>
+120
View File
@@ -0,0 +1,120 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<link rel="stylesheet" href="css/chatbot.css">
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-9 col-lg-10">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<div>
<h4 class="mb-0 title-shadow">
<i class="fas fa-comment-dots me-"></i>
Asistente ByteCenter
</h4>
<small class="text-light opacity-75">
Asistente interno del sistema
</small>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body p-0 bg-white">
<!-- CHAT -->
<div id="chat-box" class="chatbot-body">
<?php if (empty($_SESSION['chat_historial'])): ?>
<!-- MENSAJE INICIAL -->
<div class="mensaje-bot">
<div class="mensaje-contenido">
¡Hola! 👋<br><br>
Soy el asistente interno de ByteCenter.<br>
Puedo ayudarte a utilizar los módulos del sistema,
explicar funcionalidades y guiarte en distintas acciones.
</div>
</div>
<?php else: ?>
<?php foreach ($_SESSION['chat_historial'] as $msg): ?>
<div class="<?= $msg['role'] === 'user' ? 'mensaje-user' : 'mensaje-bot' ?>">
<div class="mensaje-contenido">
<?= nl2br(htmlspecialchars($msg['content'])) ?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<!-- FOOTER -->
<div class="chatbot-footer border-top p-3">
<form id="chat-form" novalidate>
<div class="input-group">
<input
type="text"
id="mensaje"
class="form-control"
placeholder="Escriba una consulta..."
autocomplete="off"
required>
<button
type="submit"
class="btn btn-success">
<i class="fas fa-paper-plane"></i>
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="js/chatbot.js"></script>
+203
View File
@@ -0,0 +1,203 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-8 col-lg-9 col-md-10">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 tittle-shadow">
<i class="fas fa-user-plus me-2"></i>
Crear Cliente
</h4>
<a href="index.php?opt=<?= htmlspecialchars($_SESSION['return_opt'] ?? 'clientes') ?>" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i> Volver
</a>
</div>
</div>
<!-- BODY -->
<div class="card-body p-4 bg-white">
<form id="form-crear-cliente" method="POST" action="?opt=crear_cliente" novalidate>
<!-- CAMPO OCULTO (para saber desde que pagina se solicito la creacion del cliente -->
<input type="hidden" name="redirect_opt" value="<?= htmlspecialchars($_SESSION['return_opt'] ?? 'clientes') ?>">
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="row g-3">
<!-- NOMBRE -->
<div class="col-md-6">
<label class="form-label">Nombre</label>
<input type="text" name="nombre" class="form-control">
</div>
<!-- APELLIDO -->
<div class="col-md-6">
<label class="form-label">Apellido</label>
<input type="text" name="apellido" class="form-control">
</div>
<!-- TELÉFONO -->
<div class="col-md-6">
<label class="form-label">Teléfono</label>
<input type="text" name="telefono" class="form-control">
</div>
<!-- EMAIL -->
<div class="col-md-6">
<label class="form-label">Email</label>
<input type="email" name="email" class="form-control">
</div>
<!-- CONDICIÓN IVA -->
<div class="col-md-6">
<label class="form-label">Condición IVA</label>
<select
name="cond_iva"
class="form-select">
<option value="consumidor_final">
Consumidor Final
</option>
<option value="monotributo">
Monotributo
</option>
<option value="responsable_inscripto">
Responsable Inscripto
</option>
</select>
</div>
<!-- CUIT -->
<div class="col-md-6">
<label class="form-label">CUIT</label>
<div class="d-flex gap-2">
<input
type="text"
name="cuit_1"
maxlength="2"
class="form-control text-center"
data-bs-toggle="tooltip"
title="Tipo de persona">
<input
type="text"
name="cuit_2"
maxlength="8"
class="form-control text-center"
data-bs-toggle="tooltip"
title="DNI">
<input
type="text"
name="cuit_3"
maxlength="1"
class="form-control text-center"
data-bs-toggle="tooltip"
title="Dígito verificador">
</div>
</div>
</div>
<!-- BOTON SUBMIT -->
<div class="d-flex justify-content-between align-items-center mt-4 pt-4 border-top">
<button type="submit" class="btn btn-success">
<i class="fas fa-user-plus me-1"></i> Crear Cliente
</button>
</div>
</form>
<!-- Manejo de errores provinientes de clienteController -->
<!-- ALERT -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/clientes/clienteValidator.js"></script>
<!-- Pasar al siguiente input en CUIT -->
<script>
document.querySelectorAll("input[name^='cuit_']").forEach((input, index, inputs) => {
input.addEventListener("input", () => {
if (input.value.length === input.maxLength && inputs[index + 1]) {
inputs[index + 1].focus();
}
});
});
</script>
<!-- Tooltip -->
<script>
document.addEventListener("DOMContentLoaded", () => {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipTriggerList.forEach(el => new bootstrap.Tooltip(el));
});
</script>
+224
View File
@@ -0,0 +1,224 @@
<?php require __DIR__ . '/../../includes/navBar.php'?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-8 col-lg-9 col-md-10">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-user-circle me-2"></i>
Datos del Cliente
</h4>
<a href="?opt=clientes" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
<!-- BODY -->
<div class="card-body p-4 bg-white">
<form id="form-editar-cliente" method="POST" action="index.php?opt=editar_cliente&id=<?= $cliente['id'] ?>" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="row g-3">
<!-- NOMBRE -->
<div class="col-md-6">
<label class="form-label">Nombre</label>
<input type="text"
name="nombre"
class="form-control"
value="<?= htmlspecialchars($cliente['nombre'] ?? '') ?>"
>
</div>
<!-- APELLIDO -->
<div class="col-md-6">
<label class="form-label">Apellido</label>
<input type="text"
name="apellido"
class="form-control"
value="<?= htmlspecialchars($cliente['apellido'] ?? '') ?>"
>
</div>
<!-- TELÉFONO -->
<div class="col-md-6">
<label class="form-label">Teléfono</label>
<input type="text"
name="telefono"
class="form-control"
value="<?= htmlspecialchars($cliente['telefono'] ?? '') ?>"
>
</div>
<!-- EMAIL -->
<div class="col-md-6">
<label class="form-label">Email</label>
<input type="email"
name="email"
class="form-control"
value="<?= htmlspecialchars($cliente['email'] ?? '') ?>"
>
</div>
<!-- CONDICIÓN IVA -->
<div class="col-md-6">
<label class="form-label">Condición IVA</label>
<select
name="cond_iva"
class="form-select">
<option
value="consumidor_final"
<?= ($cliente['cond_iva'] ?? '') === 'consumidor_final' ? 'selected' : '' ?>>
Consumidor Final
</option>
<option
value="monotributo"
<?= ($cliente['cond_iva'] ?? '') === 'monotributo' ? 'selected' : '' ?>>
Monotributo
</option>
<option
value="responsable_inscripto"
<?= ($cliente['cond_iva'] ?? '') === 'responsable_inscripto' ? 'selected' : '' ?>>
Responsable Inscripto
</option>
</select>
</div>
<!-- CUIT -->
<div class="col-md-6">
<label class="form-label">CUIT</label>
<div class="d-flex gap-2">
<input
type="text"
name="cuit_1"
maxlength="2"
class="form-control text-center"
data-bs-toggle="tooltip"
title="Tipo de persona"
value="<?= htmlspecialchars(substr($cliente['cuit'] ?? '', 0, 2)) ?>">
<input
type="text"
name="cuit_2"
maxlength="8"
class="form-control text-center"
data-bs-toggle="tooltip"
title="DNI"
value="<?= htmlspecialchars(substr($cliente['cuit'] ?? '', 2, 8)) ?>">
<input
type="text"
name="cuit_3"
maxlength="1"
class="form-control text-center"
data-bs-toggle="tooltip"
title="Dígito verificador"
value="<?= htmlspecialchars(substr($cliente['cuit'] ?? '', 10, 1)) ?>">
</div>
</div>
<!-- BOTON SUMBIT -->
<div class="d-flex justify-content-between mt-4 pt-3 border-top">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i> Guardar cambios
</button>
</div>
</div>
</form>
<!-- Errores provinientes de clienteController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/clientes/clienteValidator.js"></script>
<!-- Pasar al siguiente input en CUIT -->
<script>
document.querySelectorAll("input[name^='cuit_']").forEach((input, index, inputs) => {
input.addEventListener("input", () => {
if (input.value.length === input.maxLength && inputs[index + 1]) {
inputs[index + 1].focus();
}
});
});
</script>
<!-- Tooltip -->
<script>
document.addEventListener("DOMContentLoaded", () => {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipTriggerList.forEach(el => new bootstrap.Tooltip(el));
});
</script>
+144
View File
@@ -0,0 +1,144 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-users icon-xl me-2"></i>
Gestión de Clientes
</h4>
<div class="d-flex gap-2">
<a href="?opt=crear_cliente" class="btn btn-success">
<i class="fas fa-plus me-1"></i>
Nuevo Cliente
</a>
<a href="?opt=home_operador" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($clientes)): ?>
<div class="text-center text-muted py-5">
No hay clientes registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm ms-2"
style="width: auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Nombre</option>
<option value="2">Apellido</option>
<option value="3">Teléfono</option>
<option value="4">Email</option>
</select>
</div>
<div class="table-responsive">
<table id="tablaClientes" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Apellido</th>
<th>Teléfono</th>
<th>Email</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($clientes as $c): ?>
<tr>
<td><?= $c['id'] ?? '' ?></td>
<td><?= htmlspecialchars($c['nombre'] ?? '') ?></td>
<td><?= htmlspecialchars($c['apellido'] ?? '') ?></td>
<td><?= htmlspecialchars($c['telefono'] ?? '') ?></td>
<td><?= htmlspecialchars($c['email'] ?? '') ?></td>
<td class="text-center">
<div class="d-inline-flex align-items-center gap-2">
<!-- EDITAR -->
<a href="?opt=editar_cliente&id=<?= $c['id'] ?>"
class="btn btn-primary btn-sm"
title="Editar Cliente">
<i class="fas fa-pen fa-sm fa-fw"></i>
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- Errores provinientes de clienteController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/clientes/clienteValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/clientes/clienteTableLista.js"></script>
+205
View File
@@ -0,0 +1,205 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-11 col-lg-11">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 tittle-shadow">
<i class="fas fa-database me-2"></i>
Copias de Seguridad
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="index.php?opt=inicio_operador" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i> Volver
</a>
</div>
</div>
</div>
<div class="card-body p-4 d-flex flex-column">
<!-- RESUMEN -->
<div class="mb-2 mx-2 flex-shrink-0">
<strong>Total de Backups:</strong> <?= count($backups) ?><br>
<strong>Última Copia Realizada: </strong><?= $ultimoBackup ?? 'No hay backups aún' ?><br>
<strong>Espacio Utilizado:</strong> <?= $totalSize ?> MB
</div>
<?php if (empty($backups)): ?>
<div class="text-center text-muted py-5">
No se encontraron backups guardados.
</div>
<?php else: ?>
<div class="table-scroll-backup my-3 mx-2">
<table class="table table-fixed table-hover align-middle mb-0 table-resumen-venta">
<thead class="table-dark">
<tr>
<th>Archivo</th>
<th class="text-left">Fecha</th>
<th class="text-left">Tamaño</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($backups as $b): ?>
<tr>
<td>
<i class="fas fa-database text-secondary me-2"></i>
<?= htmlspecialchars($b['nombre']) ?>
</td>
<td class="text-left">
<?= $b['fecha'] ?>
</td>
<td class="text-left">
<?= $b['size'] ?>
</td>
<td class="text-center">
<div class="d-inline-flex gap-2">
<!-- DESCARGAR BACKUP -->
<a href="index.php?opt=descargar_backup&file=<?= urlencode($b['nombre']) ?>"
title="Descargar Backup"
class="btn btn-success btn-sm">
<i class="fas fa-download fa-fw"></i>
</a>
<!-- ELIMINAR BACKUP -->
<button
class="btn btn-danger btn-sm btnConfirmar"
title="Eliminar Backup"
data-action="eliminar_backup"
data-file="<?= $b['nombre'] ?>"
data-mensaje="¿Seguro que quieres eliminar este backup?"
data-titulo="Eliminar Backup"
data-bs-toggle="modal"
data-bs-target="#modalConfirmacion">
<i class="fas fa-trash fa-fw"></i>
</button>
<!-- RESTAURAR BACKUP -->
<button
class="btn btn-warning btn-sm btnConfirmar"
title="Restaurar Backup"
data-action="restaurar_backup"
data-file="<?= $b['nombre'] ?>"
data-mensaje="¿Seguro que quieres restaurar el sistema a partir de esta copia de seguridad?"
data-titulo="Restaurar Backup"
data-bs-toggle="modal"
data-bs-target="#modalConfirmacion">
<i class="fas fa-rotate-left fa-fw text-white"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- BOTÓN PARA GENERAR COPIA -->
<div class="d-flex justify-content-end mt-1 pt-3 mb-1 flex-shrink-0">
<form method="POST" action="index.php?opt=generar_backup">
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<button class="btn btn-primary text-white">
<i class="fas fa-plus me-1"></i>
Generar Copia
</button>
</form>
</div>
</div>
</div>
<!-- Errores provenientes de configuracionController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/configuracion/configuracionValidator.js"></script>
@@ -0,0 +1,380 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-11 col-lg-11">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 tittle-shadow">
<i class="fa-solid fa-store me-2"></i>
Datos del Negocio
</h4>
<a href="index.php?opt=inicio_operador" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i> Volver
</a>
</div>
</div>
<!-- BODY -->
<div class="card-body p-4 bg-white">
<form id="form-config-empresa" method="POST" action="?opt=actualizar_config" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<!-- DATOS DEL NEGOCIO -->
<h6 class="text-muted fw-semibold mb-1">Datos Generales</h6>
<div class="row g-3 mb-4">
<!-- NOMBRE -->
<div class="col-md-3">
<label class="form-label fw-semibold fw-semibold">Nombre</label>
<input type="text" name="nombre" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Nombre del negocio o razón social"
value="<?= htmlspecialchars($configuracion['nombre'] ?? '') ?>">
</div>
<!-- DIRECCIÓN -->
<div class="col-md-3">
<label class="form-label fw-semibold">Dirección</label>
<input type="text" name="direccion" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Dirección física del negocio"
value="<?= htmlspecialchars($configuracion['direccion'] ?? '') ?>">
</div>
<!-- CIUDAD -->
<div class="col-md-3">
<label class="form-label fw-semibold">Ciudad</label>
<input type="text" name="ciudad" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Ciudad donde se encuentra ubicado el negocio"
value="<?= htmlspecialchars($configuracion['ciudad'] ?? '') ?>">
</div>
<!-- PROVINCIA -->
<div class="col-md-3">
<label class="form-label fw-semibold">Provincia</label>
<input type="text" name="provincia" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Provincia donde se encuentra ubicado el negocio"
value="<?= htmlspecialchars($configuracion['provincia'] ?? '') ?>">
</div>
<!-- CÓDIGO POSTAL -->
<div class="col-md-3">
<label class="form-label fw-semibold">Código Postal</label>
<input type="text" name="codigo_postal" class="form-control"
value="<?= htmlspecialchars($configuracion['codigo_postal'] ?? '') ?>">
</div>
<!-- TELÉFONO -->
<div class="col-md-3">
<label class="form-label fw-semibold">Teléfono</label>
<input type="text" name="telefono" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Teléfono de contacto del negocio"
value="<?= htmlspecialchars($configuracion['telefono'] ?? '') ?>">
</div>
<!-- EMAIL -->
<div class="col-md-3">
<label class="form-label fw-semibold">Email</label>
<input type="email" name="email" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Email de contacto del negocio"
value="<?= htmlspecialchars($configuracion['email'] ?? '') ?>">
</div>
</div>
<!-- DATOS FISCALES -->
<h6 class="text-muted fw-semibold mb-1">Datos Fiscales</h6>
<div class="row g-3">
<!-- COND. IVA -->
<div class="col-md-3">
<label class="form-label fw-semibold">Cond. IVA</label>
<select class="form-select" name="cond_iva"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Condición fiscal frente al IVA del negocio">
<option value="consumidor_final"
<?= $configuracion['cond_iva'] === 'consumidor_final' ? 'selected' : '' ?>>
Consumidor Final
</option>
<option value="responsable_inscripto"
<?= $configuracion['cond_iva'] === 'responsable_inscripto' ? 'selected' : '' ?>>
Responsable Inscripto
</option>
<option value="monotributo"
<?= $configuracion['cond_iva'] === 'monotributo' ? 'selected' : '' ?>>
Monotributo
</option>
</select>
</div>
<!-- CUIT -->
<div class="col-md-3">
<label class="form-label fw-semibold">CUIT</label>
<div class="d-flex gap-2">
<input type="text" name="cuit_1" maxlength="2" class="form-control text-center"
data-bs-toggle="tooltip"
title="Tipo de persona"
value="<?= substr($configuracion['cuit'] ?? '', 0, 2) ?>"
placeholder="00">
<input type="text" name="cuit_2" maxlength="8" class="form-control text-center"
data-bs-toggle="tooltip"
title="DNI"
value="<?= substr($configuracion['cuit'] ?? '', 2, 8) ?>"
placeholder="00000000">
<input type="text" name="cuit_3" maxlength="1" class="form-control text-center"
data-bs-toggle="tooltip"
title="Dígito verificador"
value="<?= substr($configuracion['cuit'] ?? '', 10, 1) ?>"
placeholder="0">
</div>
</div>
<!-- INICIO DE ACTIVIDADES -->
<div class="col-md-3">
<label class="form-label fw-semibold">Inicio de Actividades</label>
<input type="text" name="inicio_act" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Fecha de inicio de actividades del negocio"
value="<?= htmlspecialchars($configuracion['inicio_act_formateada'] ?? '') ?>" readonly>
</div>
<!-- IVA (INFORMATIVO EN PDFs) -->
<div class="col-md-3">
<label class="form-label fw-semibold">IVA (%) Informativo</label>
<input type="number" step="0.01" name="iva" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Porcentaje informativo para comprobantes"
value="<?= htmlspecialchars($configuracion['iva'] ?? '0') ?>">
</div>
</div>
<h6 class="text-muted fw-semibold mt-4 mb-1">Automatizaciones</h6>
<div class="row g-3">
<!-- HORARIO APERTURA MAÑANA-->
<div class="col-md-3">
<label class="form-label fw-semibold">Horario de Apertura (mañana)</label>
<input type="time" name="hora_apertura_maniana" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Horario en que el negocio abre por la mañana"
value="<?= htmlspecialchars($configuracion['hora_apertura_maniana'] ?? '') ?>">
</div>
<!-- HORARIO CIERRE MAÑANA -->
<div class="col-md-3">
<label class="form-label fw-semibold">Horario de Cierre (mañana)</label>
<input type="time" name="hora_cierre_maniana" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Horario en el que el negocio cierra al mediodía"
value="<?= htmlspecialchars($configuracion['hora_cierre_maniana'] ?? '') ?>">
</div>
<!-- HORARIO APERTURA TARDE-->
<div class="col-md-3">
<label class="form-label fw-semibold">Horario de Apertura (tarde)</label>
<input type="time" name="hora_apertura_tarde" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Horario en que el negocio abre por la tarde"
value="<?= htmlspecialchars($configuracion['hora_apertura_tarde'] ?? '') ?>">
</div>
<!-- HORARIO CIERRE TARDE -->
<div class="col-md-3">
<label class="form-label fw-semibold">Horario de Cierre (tarde)</label>
<input type="time" name="hora_cierre_tarde" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Horario en que el negocio cierra al finalizar el día"
value="<?= htmlspecialchars($configuracion['hora_cierre_tarde'] ?? '') ?>">
</div>
<!-- CARGO POR CANCELACIÓN DE SERVICIO -->
<div class="col-md-3">
<label class="form-label fw-semibold">Cargo por Cancelación (Servicios)</label>
<input type="number" step="0.01" name="cargo_cancelacion_servicio" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Monto cobrado si el cliente cancela un servicio"
value="<?= htmlspecialchars($configuracion['cargo_cancelacion_servicio'] ?? '0') ?>">
</div>
<!-- EMAIL EMISOR -->
<div class="col-md-3">
<label class="form-label fw-semibold">Email Emisor</label>
<input type="text" name="email_emisor" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Correo desde el cual se enviarán los emails del sistema"
value="<?= htmlspecialchars($configuracion['email_emisor'] ?? '') ?>">
</div>
<!-- CONTRASEÑA EMAIL EMISOR -->
<div class="col-md-3">
<label class="form-label fw-semibold">Contraseña Email Emisor</label>
<input type="text" name="contrasenia_emisor" class="form-control"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Contraseña de aplicación"
value="<?= htmlspecialchars($configuracion['contrasenia_emisor_formateada'] ?? '') ?>">
</div>
<!-- ENVÍO AUTOMÁTICO DE MAIL -->
<div class="col-md-3">
<label class="form-label fw-semibold">Correos al Finalizar Servicio</label>
<div class="form-check form-switch mt-2">
<input class="form-check-input"
type="checkbox"
name="mail_automatico"
value="1"
<?= !empty($configuracion['mail_automatico']) ? 'checked' : '' ?>>
<label class="form-check-label">
Habilitar envío automático
</label>
</div>
</div>
</div>
<!-- BOTÓN -->
<div class="d-flex justify-content-end mt-2 pt-3">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i>
Guardar Configuración
</button>
</div>
</form>
<!-- Errores provenientes de configuracionController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCESS -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/configuracion/configuracionValidator.js"></script>
<!-- Tooltip -->
<script>
document.addEventListener("DOMContentLoaded", () => {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipTriggerList.forEach(el => new bootstrap.Tooltip(el));
});
</script>
<!-- Pasar al siguiente input en CUIT -->
<script>
document.querySelectorAll("input[name^='cuit_']").forEach((input, index, inputs) => {
input.addEventListener("input", () => {
if (input.value.length === input.maxLength && inputs[index + 1]) {
inputs[index + 1].focus();
}
});
});
</script>
+114
View File
@@ -0,0 +1,114 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-16 col-lg-16">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap">
<!-- IZQUIERDA -->
<div class="d-flex align-items-center gap-4">
<h4 class="mb-0 tittle-shadow" id="tituloInforme">
<i class="fas fa-chart-line me-2"></i> Balance Anual
</h4>
<div class="d-flex align-items-center gap-3">
<!-- AÑO -->
<div class="d-flex align-items-center gap-2 select-group">
<i class="fas fa-calendar-alt text-light"></i>
<select id="anio" class="form-select custom-select select-informe">
<?php for ($i = date('Y'); $i >= 2020; $i--): ?>
<option value="<?= $i ?>" <?= $i == date('Y') ? 'selected' : '' ?>>
<?= $i ?>
</option>
<?php endfor; ?>
</select>
</div>
<!-- TIPO INFORME -->
<div class="d-flex align-items-center gap-2 select-group">
<i class="fas fa-chart-pie text-light"></i>
<select id="tipoInforme" class="form-select custom-select select-informe">
<option value="balance">Balance Anual</option>
<option value="flujo">Dinero a Cobrar</option>
</select>
</div>
</div>
</div>
<!-- DERECHA -->
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="index.php?opt=inicio_operador" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i> Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body p-4 bg-white">
<!-- KPI ROW -->
<div class="row mb-4 g-3">
<div class="col-md-3">
<div class="card shadow-sm border-0 text-center p-3">
<h6 class="text-muted mb-1">Ingresos</h6>
<h4 class="fw-bold text-success" id="kpiIngresos">$0</h4>
</div>
</div>
<div class="col-md-3">
<div class="card shadow-sm border-0 text-center p-3">
<h6 class="text-muted mb-1">Egresos</h6>
<h4 class="fw-bold text-danger" id="kpiEgresos">$0</h4>
</div>
</div>
<div class="col-md-3">
<div class="card shadow-sm border-0 text-center p-3">
<h6 class="text-muted mb-1">Resultado Neto</h6>
<h4 class="fw-bold" id="kpiResultado">$0</h4>
</div>
</div>
<div class="col-md-3">
<div class="card shadow-sm border-0 text-center p-3">
<h6 class="text-muted mb-1">Mejor Mes</h6>
<h4 class="fw-bold text-primary" id="kpiMejorMes">-</h4>
</div>
</div>
</div>
<!-- GRÁFICO -->
<canvas id="graficoBalance" height="90"></canvas>
</div>
</div>
</div>
</div>
</div>
<script src="js/informes.js"></script>
+150
View File
@@ -0,0 +1,150 @@
<?php require __DIR__ . '/../../includes/navbar.php'; ?>
<div class="container mb-5 custom-margin-top">
<h2 class="text-center mb-0 text-white title-presence">
Panel de Negocio
</h2>
<p class="text-center text-white-50 mb-5">
Administra las operaciones diarias.
</p>
<div class="row g-4 justify-content-center">
<!-- CLIENTES -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-clientes mb-2">
<i class="fas fa-users"></i>
</div>
<h5 class="card-title mt-2">Clientes</h5>
<p class="card-text text-muted small">Registra, consulta y administra la información de tus clientes.</p>
<a href="?opt=clientes" class="stretched-link"></a>
</div>
</div>
</div>
<!-- VENTAS -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-ventas mb-2">
<i class="fas fa-shopping-cart"></i>
</div>
<h5 class="card-title mt-2">Ventas</h5>
<p class="card-text text-muted small">Registra, consulta el historial y lleva el control de cada venta.</p>
<a href="?opt=crear_venta" class="stretched-link"></a>
</div>
</div>
</div>
<!-- SERVICIOS -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-servicios mb-2">
<i class="fas fa-wrench"></i>
</div>
<h5 class="card-title mt-2">Servicios</h5>
<p class="card-text text-muted small">Crea y administra los servicios con toda la información necesaria.</p>
<a href="?opt=crear_servicio" class="stretched-link"></a>
</div>
</div>
</div>
<!-- PRESUPUESTOS -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-presupuestos mb-2">
<i class="fas fa-file-invoice-dollar"></i>
</div>
<h5 class="card-title mt-2">Presupuestos</h5>
<p class="card-text text-muted small">Genera presupuestos detallados para tus clientes.</p>
<a href="?opt=crear_presupuesto" class="stretched-link"></a>
</div>
</div>
</div>
<!-- PRODUCTOS -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-productos mb-2">
<i class="fas fa-box-open"></i>
</div>
<h5 class="card-title mt-2">Productos</h5>
<p class="card-text text-muted small">Controla el stock y administra cada producto.</p>
<a href="?opt=productos" class="stretched-link"></a>
</div>
</div>
</div>
<!-- PROVEEDORES -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-proveedores mb-2">
<i class="fas fa-truck"></i>
</div>
<h5 class="card-title mt-2">Proveedores</h5>
<p class="card-text text-muted small">Gestiona proveedores y sus datos correspondientes.</p>
<a href="?opt=proveedores" class="stretched-link"></a>
</div>
</div>
</div>
<!-- PEDIDOS -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-pedidos mb-2">
<i class="fas fa-clipboard-list"></i>
</div>
<h5 class="card-title mt-2">Pedidos</h5>
<p class="card-text text-muted small">Crea y gestiona órdenes de compra a proveedores</p>
<a href="?opt=crear_pedido" class="stretched-link"></a>
</div>
</div>
</div>
</div>
</div>
<script>
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
</script>
+150
View File
@@ -0,0 +1,150 @@
<?php require __DIR__ . '/../../includes/navbar.php'; ?>
<div class="container mb-5 custom-margin-top">
<h2 class="text-center mb-0 text-white title-presence">
Panel de Negocio
</h2>
<p class="text-center text-white-50 mb-5">
Administra las operaciones diarias.
</p>
<div class="row g-4 justify-content-center">
<!-- CLIENTES -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-clientes mb-2">
<i class="fas fa-users"></i>
</div>
<h5 class="card-title mt-2">Clientes</h5>
<p class="card-text text-muted small">Registra, consulta y administra la información de tus clientes.</p>
<a href="?opt=clientes" class="stretched-link"></a>
</div>
</div>
</div>
<!-- VENTAS -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-ventas mb-2">
<i class="fas fa-shopping-cart"></i>
</div>
<h5 class="card-title mt-2">Ventas</h5>
<p class="card-text text-muted small">Registra, consulta el historial y lleva el control de cada venta.</p>
<a href="?opt=crear_venta" class="stretched-link"></a>
</div>
</div>
</div>
<!-- SERVICIOS -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-servicios mb-2">
<i class="fas fa-wrench"></i>
</div>
<h5 class="card-title mt-2">Servicios</h5>
<p class="card-text text-muted small">Crea y administra los servicios con toda la información necesaria.</p>
<a href="?opt=crear_servicio" class="stretched-link"></a>
</div>
</div>
</div>
<!-- PRESUPUESTOS -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-presupuestos mb-2">
<i class="fas fa-file-invoice-dollar"></i>
</div>
<h5 class="card-title mt-2">Presupuestos</h5>
<p class="card-text text-muted small">Genera presupuestos detallados para tus clientes.</p>
<a href="?opt=crear_presupuesto" class="stretched-link"></a>
</div>
</div>
</div>
<!-- PRODUCTOS -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-productos mb-2">
<i class="fas fa-box-open"></i>
</div>
<h5 class="card-title mt-2">Productos</h5>
<p class="card-text text-muted small">Controla el stock y administra cada producto.</p>
<a href="?opt=productos" class="stretched-link"></a>
</div>
</div>
</div>
<!-- PROVEEDORES -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-proveedores mb-2">
<i class="fas fa-truck"></i>
</div>
<h5 class="card-title mt-2">Proveedores</h5>
<p class="card-text text-muted small">Gestiona proveedores y sus datos correspondientes.</p>
<a href="?opt=proveedores" class="stretched-link"></a>
</div>
</div>
</div>
<!-- PEDIDOS -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body card-body-compact d-flex flex-column align-items-center justify-content-center">
<div class="icon-box bg-pedidos mb-2">
<i class="fas fa-clipboard-list"></i>
</div>
<h5 class="card-title mt-2">Pedidos</h5>
<p class="card-text text-muted small">Crea y gestiona órdenes de compra a proveedores</p>
<a href="?opt=crear_pedido" class="stretched-link"></a>
</div>
</div>
</div>
</div>
</div>
<script>
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
</script>
+198
View File
@@ -0,0 +1,198 @@
<?php require __DIR__ . '/../../includes/navbar.php'; ?>
<div class="container mb-5 custom-margin-top">
<h2 class="text-center mb-0 text-white title-presence">
Panel Principal
</h2>
<p class="text-center text-white-50 mb-5">
Bienvenido/a <?= $_SESSION['user']['nombre'] ?>. Gestiona el sistema desde aquí.
</p>
<div class="row g-4 justify-content-center">
<!-- MÓDULOS DEL SISTEMA -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body d-flex flex-column align-items-center justify-content-center">
<div class="icon-box-soft bg-primary-soft text-primary mb-2">
<i class="fas fa-layer-group"></i>
</div>
<h5 class="card-title">Panel de Negocio</h5>
<p class="card-text text-muted">Administra todas las áreas del negocio desde un solo lugar.</p>
<a href="?opt=home_admin" class="stretched-link"></a>
</div>
</div>
</div>
<!-- INFORMES -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body d-flex flex-column align-items-center justify-content-center">
<div class="icon-box-soft bg-success-soft text-success mb-2">
<i class="fas fa-chart-line"></i>
</div>
<h5 class="card-title">Informes</h5>
<p class="card-text text-muted">Accede a reportes sobre el rendimiento de tu negocio.</p>
<a href="index.php?opt=informe" class="stretched-link"></a>
</div>
</div>
</div>
<!-- BACKUPS -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body d-flex flex-column align-items-center justify-content-center">
<div class="icon-box-soft bg-warning-soft text-warning mb-2">
<i class="fas fa-database"></i>
</div>
<h5 class="card-title">Copias de Seguridad</h5>
<p class="card-text text-muted">Respaldo y recuperación de información del sistema.</p>
<a href="?opt=backups" class="stretched-link"></a>
</div>
</div>
</div>
<!-- USUARIOS -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body d-flex flex-column align-items-center justify-content-center">
<div class="icon-box-soft bg-danger-soft text-danger mb-2">
<i class="fas fa-users-cog"></i>
</div>
<h5 class="card-title">Usuarios</h5>
<p class="card-text text-muted">Administra los datos de los usuarios y permisos asociado a cada uno.</p>
<a href="index.php?opt=usuarios" class="stretched-link"></a>
</div>
</div>
</div>
<!-- README -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body d-flex flex-column align-items-center justify-content-center">
<div class="icon-box-soft bg-info-soft text-info mb-2">
<i class="fas fa-book"></i>
</div>
<h5 class="card-title">Ayuda</h5>
<p class="card-text text-muted">Léelo en caso de tener dudas respecto al funcionamiento del sistema.</p>
<a href="?opt=readme" class="stretched-link"></a>
</div>
</div>
</div>
<!-- CONFIGURACIÓN -->
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body d-flex flex-column align-items-center justify-content-center">
<div class="icon-box-soft bg-secondary-soft text-secondary mb-2">
<i class="fa-solid fa-store"></i>
</div>
<h5 class="card-title">Datos del Negocio</h5>
<p class="card-text text-muted">Administra parámetros del sistema y datos de la empresa.</p>
<a href="?opt=configuracion" class="stretched-link"></a>
</div>
</div>
</div>
<!-- VERSIÓN -->
<footer class="text-end text-white-50 position-fixed bottom-0 start-50 translate-middle-x mb-2">
<a href="#"
class="text-white-50 text-decoration-none"
data-bs-toggle="modal"
data-bs-target="#modalAcerca"
title="Acerca del sistema">
Versión Beta 0.1.3
</a>
</footer>
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
<script src="js/login/logInValidator.js"></script>
<!-- Evita que Chrome use BFCache -->
<script>
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
</script>
<!-- Modal Acerca De -->
<div class="modal fade" id="modalAcerca" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Acerca del Sistema</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body text-center">
<h6 class="fw-bold">ByteCenter App</h6>
<p class="mb-1">Versión Beta 0.1.3</p>
<p class="text-muted small">
Sistema de gestión interna para administración de ventas, servicios, stock y mucho más.
</p>
<hr>
<p class="mb-1"><strong>Desarrollado por:</strong> Axel Axt</p>
<p class="mb-1"><strong>Año:</strong> 2026</p>
</div>
</div>
</div>
</div>
+100
View File
@@ -0,0 +1,100 @@
<?php require __DIR__ . '/../../includes/navbar.php'; ?>
<div class="container mb-5 custom-margin-top">
<h2 class="text-center mb-0 text-white title-presence">
Panel Principal
</h2>
<p class="text-center text-white-50 mb-5">
Bienvenido/a <?= $_SESSION['user']['nombre'] ?>. Gestiona el sistema desde aquí.
</p>
<div class="row g-4 justify-content-center">
<!-- MÓDULOS DEL SISTEMA -->
<div class="col-xl-mb-2 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body d-flex flex-column align-items-center justify-content-center">
<div class="icon-box-soft bg-primary-soft text-primary mb-mb-2">
<i class="fas fa-layer-group"></i>
</div>
<h5 class="card-title">Panel de Negocio</h5>
<p class="card-text text-muted">Administra todas las áreas del negocio desde un solo lugar.</p>
<a href="?opt=home_admin" class="stretched-link"></a>
</div>
</div>
</div>
<!-- README -->
<div class="col-xl-mb-2 col-lg-4 col-md-6">
<div class="card action-card text-center h-100">
<div class="card-body d-flex flex-column align-items-center justify-content-center">
<div class="icon-box-soft bg-info-soft text-info mb-mb-2">
<i class="fas fa-book"></i>
</div>
<h5 class="card-title">Ayuda</h5>
<p class="card-text text-muted">Léelo en caso de tener dudas respecto al funcionamiento del sistema.</p>
<a href="?opt=readme" class="stretched-link"></a>
</div>
</div>
</div>
<!-- VERSIÓN -->
<footer class="text-end text-white-50 position-fixed bottom-0 start-50 translate-middle-x mb-2">
<a href="#"
class="text-white-50 text-decoration-none"
data-bs-toggle="modal"
data-bs-target="#modalAcerca"
title="Acerca del sistema">
Versión Beta 0.1.3
</a>
</footer>
</div>
</div>
<!-- Modal Acerca De -->
<div class="modal fade" id="modalAcerca" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Acerca del Sistema</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body text-center">
<h6 class="fw-bold">ByteCenter App</h6>
<p class="mb-1">Versión Beta 0.1.3</p>
<p class="text-muted small">
Sistema de gestión interna para administración de ventas, servicios, stock y mucho más.
</p>
<hr>
<p class="mb-1"><strong>Desarrollado por:</strong> Axel Axt</p>
<p class="mb-1"><strong>Año:</strong> 2026</p>
</div>
</div>
</div>
</div>
<!-- Evita que Chrome use BFCache -->
<script>
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
</script>
+149
View File
@@ -0,0 +1,149 @@
<div class="container-fluid vh-100 d-flex align-items-center justify-content-center">
<div class="row mx-0 login-box">
<!-- COLUMNA IZQUIERDA -->
<div class="col-lg-6 col-md-6 d-flex flex-column justify-content-center text-white p-5"
style="background: linear-gradient(135deg, #0f172a, #1e3a5f, #3b82b8); border-radius: 16px 0 0 16px;">
<div class="text-center mb-4">
<img src="imgs/logo.png" alt="ByteCenter" style="width: 120px;">
</div>
<h1 class="fw-bold text-center text-white">
<a href="?opt=login" class="text-decoration-none fw-semibold text-white">
ByteCenter
</a>
</h1>
<p class="mt-3 fs-5 text-center">Bienvenido de nuevo!
<br>Inicie sesión para continuar.
</p>
</div>
<!-- COLUMNA DERECHA (FORMULARIO) -->
<div class="col-lg-6 col-md-6 d-flex align-items-center p-5 bg-white" style="border-radius: 0 16px 16px 0;">
<div class="w-100">
<h4 class="text-center mb-5">Iniciar Sesión</h4>
<!-- FORMULARIO -->
<form method="POST" id="form-login" action="?opt=login" novalidate>
<div class="form-floating mb-4">
<input type="text" name="usuario"
class="form-control input-lg"
id="floatingUserInput"
placeholder="Nombre de Usuario">
<label for="floatingUserInput">Usuario</label>
</div>
<div class="position-relative mb-3">
<div class="form-floating">
<input
type="password"
name="password"
class="form-control input-lg pe-5"
id="floatingPasswordInput"
placeholder="Contraseña"
>
<label for="floatingPasswordInput">
Contraseña
</label>
</div>
<!-- BOTÓN OJO -->
<button
type="button"
class="btn-toggle-password"
data-target="floatingPasswordInput"
>
<i class="fas fa-eye"></i>
</button>
</div>
<div class="text-center mt-3">
<a href="?opt=recuperar_cuenta" class="text-decoration-none fw-semibold">
Olvidé mi contraseña
</a>
</div>
<button type="submit" class="btn btn-primary w-100 btn-lg mt-3">
Entrar
</button>
</form>
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/login/logInValidator.js"></script>
<!-- Ojo de contrasenia -->
<script>
document.querySelectorAll(".btn-toggle-password").forEach(button => {
button.addEventListener("click", () => {
const input = document.getElementById(button.dataset.target);
const icon = button.querySelector("i");
if (input.type === "password") {
input.type = "text";
icon.classList.remove("fa-eye");
icon.classList.add("fa-eye-slash");
} else {
input.type = "password";
icon.classList.remove("fa-eye-slash");
icon.classList.add("fa-eye");
}
});
});
</script>
+103
View File
@@ -0,0 +1,103 @@
<div class="container-fluid vh-100 d-flex align-items-center justify-content-center">
<div class="row mx-0 login-box">
<!-- COLUMNA IZQUIERDA -->
<div class="col-lg-6 col-md-6 d-flex flex-column justify-content-center text-white p-5"
style="background: linear-gradient(135deg, #0f172a, #1e3a5f, #3b82b8); border-radius: 16px 0 0 16px;">
<div class="text-center mb-4">
<img src="imgs/logo.png" alt="ByteCenter" style="width: 120px;">
</div>
<h1 class="fw-bold text-center text-white">
<a href="?opt=login" class="text-decoration-none fw-semibold text-white">
ByteCenter
</a>
</h1>
<p class="mt-3 fs-5 text-center">
¿Éres administrador y olvidaste tu contraseña?
<br>Te enviaremos un código para recuperarla.
</p>
</div>
<!-- COLUMNA DERECHA -->
<div class="col-lg-6 col-md-6 d-flex align-items-center p-5 bg-white"
style="border-radius: 0 16px 16px 0;">
<div class="w-100">
<h4 class="text-center mb-4">Recuperar Contraseña</h4>
<p class="text-muted text-center mb-4">
Ingresá tu email y te enviaremos un código de verificación.
</p>
<!-- FORMULARIO -->
<form method="POST" action="?opt=recuperar_cuenta" novalidate>
<!-- CSRF -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<!-- EMAIL -->
<div class="form-floating mb-4">
<input type="email" name="email" class="form-control input-lg" placeholder="nombre@mail.com" required>
<label for="floatingEmailInput">Correo electrónico</label>
</div>
<!-- BOTONES -->
<div class="d-flex flex-column gap-2">
<button type="submit" class="btn btn-primary btn-lg w-100">
Enviar código
</button>
<a href="?opt=login" class="btn btn-secondary text-center mt-4">
Volver al login
</a>
</div>
</form>
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
</div>
<script src="js/login/logInValidator.js"></script>
@@ -0,0 +1,166 @@
<?php require __DIR__ . '/../../includes/navbar.php'; ?>
<div class="container-fluid vh-100 d-flex align-items-center justify-content-center">
<div class="row mx-0 login-box">
<!-- IZQUIERDA -->
<div class="col-lg-6 col-md-6 d-flex flex-column justify-content-center text-white p-5"
style="background: linear-gradient(135deg, #0f172a, #1e3a5f, #3b82b8); border-radius: 16px 0 0 16px;">
<div class="text-center mb-4">
<img src="imgs/logo.png" style="width: 120px;">
</div>
<h1 class="fw-bold text-center text-white">
<a href="?opt=login" class="text-decoration-none fw-semibold text-white">
ByteCenter
</a>
</h1>
<p class="mt-3 fs-5 text-center">
Establezca una nueva contraseña.
</p>
</div>
<!-- DERECHA -->
<div class="col-lg-6 col-md-6 d-flex align-items-center p-5 bg-white"
style="border-radius: 0 16px 16px 0;">
<div class="w-100">
<h4 class="text-center mb-4">Nueva Contraseña</h4>
<form method="POST" action="?opt=restablecer_contrasenia" novalidate>
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<!-- PASSWORD -->
<div class="position-relative mb-3">
<div class="form-floating">
<input
type="password"
name="password"
class="form-control pe-5"
id="passwordInput"
placeholder="Nueva contraseña"
required
>
<label for="passwordInput">
Nueva contraseña
</label>
</div>
<button
type="button"
class="btn-toggle-password"
data-target="passwordInput"
>
<i class="fas fa-eye"></i>
</button>
</div>
<!-- CONFIRMAR -->
<div class="position-relative mb-4">
<div class="form-floating">
<input
type="password"
name="confirmar_password"
class="form-control pe-5"
id="confirmPasswordInput"
placeholder="Confirmar contraseña"
required
>
<label for="confirmPasswordInput">
Confirmar contraseña
</label>
</div>
<button
type="button"
class="btn-toggle-password"
data-target="confirmPasswordInput"
>
<i class="fas fa-eye"></i>
</button>
</div>
<!-- BOTON -->
<button type="submit" class="btn btn-success btn-lg w-100">
Guardar nueva contraseña
</button>
</form>
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
</div>
<script src="js/login/logInValidator.js"></script>
<script>
document.querySelectorAll(".btn-toggle-password").forEach(button => {
button.addEventListener("click", () => {
const input = document.getElementById(button.dataset.target);
const icon = button.querySelector("i");
if (input.type === "password") {
input.type = "text";
icon.classList.remove("fa-eye");
icon.classList.add("fa-eye-slash");
} else {
input.type = "password";
icon.classList.remove("fa-eye-slash");
icon.classList.add("fa-eye");
}
});
});
</script>
+112
View File
@@ -0,0 +1,112 @@
<?php require __DIR__ . '/../../includes/navbar.php'; ?>
<div class="container-fluid vh-100 d-flex align-items-center justify-content-center">
<div class="row mx-0 login-box">
<!-- IZQUIERDA -->
<div class="col-lg-6 col-md-6 d-flex flex-column justify-content-center text-white p-5"
style="background: linear-gradient(135deg, #0f172a, #1e3a5f, #3b82b8); border-radius: 16px 0 0 16px;">
<div class="text-center mb-4">
<img src="imgs/logo.png" style="width: 120px;">
</div>
<h1 class="fw-bold text-center text-white">
<a href="?opt=login" class="text-decoration-none fw-semibold text-white">
ByteCenter
</a>
</h1>
<p class="mt-3 fs-5 text-center">
Te enviamos un código a tu correo.
<br>Ingresalo para continuar.
</p>
</div>
<!-- DERECHA -->
<div class="col-lg-6 col-md-6 d-flex align-items-center p-5 bg-white"
style="border-radius: 0 16px 16px 0;">
<div class="w-100">
<h4 class="text-center mb-4">Verificar Código</h4>
<form method="POST" action="?opt=verificar_codigo" novalidate>
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<!-- CODIGO -->
<div class="form-floating mb-4">
<input
type="text"
name="codigo"
class="form-control input-lg"
maxlength="6"
placeholder="Código de verificación"
required
>
<label>Código de verificación</label>
</div>
<!-- BOTONES -->
<div class="d-flex flex-column gap-2">
<button type="submit" class="btn btn-primary btn-lg w-100">
Verificar código
</button>
<!-- REENVIAR -->
<button type="submit"
formaction="?opt=recuperar_cuenta"
formmethod="POST"
class="btn btn-success text-center mt-4">
Reenviar código
</button>
<a href="?opt=login" class="btn btn-secondary text-center mt-0">
Volver al login
</a>
</div>
</form>
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
</div>
<script src="js/login/logInValidator.js"></script>
@@ -0,0 +1,192 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-box-open icon-xl me-2"></i>
Selección de Productos
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a href="?opt=crear_producto" class="btn btn-success">
<i class="fas fa-plus me-1"></i>
Nuevo Producto
</a>
<a href="?opt=crear_pedido" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($productos)): ?>
<div class="text-center text-muted py-5">
No hay productos registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex justify-content-between align-items-center mb-1">
<div id="filtrosIzquierda" class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm"
style="width:auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Productos</option>
<option value="5">Stock Actual</option>
</select>
</div>
</div>
<form method="POST" action="index.php?opt=agregar_producto_pedido_caja" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="table-responsive">
<table id="tablaProductos" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Productos</th>
<th>Costo Unit.</th>
<th>Cantidad</th>
<th>Stock Actual</th>
<th>Seleccionar</th>
</tr>
</thead>
<tbody>
<?php foreach ($productos as $p): ?>
<tr>
<!-- ID PRODUCTO -->
<td><?= $p['id'] ?></td>
<!-- MARCA Y MODELO -->
<td><?= htmlspecialchars($p['marca'] . ' ' . $p['modelo']) ?></td>
<!-- INPUT DE COSTO -->
<td class="text-center">
<div class="input-group input-group-sm input-precio" style="width: 150px;">
<span class="input-group-text">$</span>
<input
type="number"
name="costos[<?= $p['id'] ?>]"
value="0.00"
min="5"
step="0.01"
class="form-control text-append"
>
</div>
</td>
<!-- INPUT DE CANTIDAD -->
<td class="text-center">
<input type="number"
name="cantidades[<?= $p['id'] ?>]"
value="1"
min="1"
class="form-control form-control-sm"
style="width: 60px;">
</td>
<!-- STOCK ACTUAL -->
<td><?= htmlspecialchars($p['stock']) ?></td>
<!-- CHECKBOX PARA SELECCIONAR PRODUCTO -->
<td class="text-left">
<input type="checkbox"
name="ids[]"
value="<?= $p['id'] ?>"
class="form-check-input">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- BOTÓN FINALIZAR SELECCIÓN -->
<div class="mt-3 text-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-check me-1"></i> Finalizar Selección
</button>
</div>
</form>
<?php endif; ?>
<!-- Errores provenientes de pedidoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/pedidos/pedidoValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/pedidos/pedidoTableProducto.js"></script>
+415
View File
@@ -0,0 +1,415 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0 card-viewport">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-clipboard-list icon-xl me-2"></i>
Nuevo Pedido
</h4>
<div class="d-flex gap-2">
<a href="?opt=listar_pedidos" class="btn btn-primary">
<i class="fas fa-folder-open me-1"></i>
Pedidos Registrados
</a>
<a href="?opt=crear_proveedor&from=crear_pedido" class="btn btn-success">
<i class="fas fa-plus me-1"></i>
Nuevo Proveedor
</a>
<a href="?opt=home_operador" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<form method='POST' action="index.php?opt=confirmar_pedido" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="card-body p-4 d-flex flex-column card-body-scroll">
<!-- PROVEEDOR -->
<div class="mb-4 flex-shrink-0">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="text-muted mb-0">Proveedor</h6>
<div class="ms-4 d-flex gap-2">
<a href="?opt=seleccionar_proveedor" class="btn btn-primary btn-sm">
<i class="fas fa-user fa-fw me-1"></i>
Seleccionar Proveedor
</a>
<button
type="submit"
formaction="?opt=eliminar_proveedor"
formmethod="POST"
class="btn btn-danger btn-sm">
<i class="fas fa-times fa-fw me-1"></i>
Eliminar Proveedor
</button>
</div>
</div>
<div class="row g-3">
<div class="col-md-2">
<label class="label-formventa mb-1">ID</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($proveedor['id'] ?? '') ?>" readonly>
</div>
<div class="col-md-2">
<label class="label-formventa mb-1">Razón Social</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($proveedor['razon_social'] ?? '') ?>" readonly>
</div>
<div class="col-md-2">
<label class="label-formventa mb-1">Teléfono</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($proveedor['telefono'] ?? '') ?>" readonly>
</div>
<div class="col-md-2">
<label class="label-formventa mb-1">Email</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($proveedor['email'] ?? '') ?>" readonly>
</div>
<!-- COND. IVA -->
<div class="col">
<label class="label-formventa mb-1">Cond. IVA</label>
<select class="form-select form-select-sm" name="cond_iva" id="condIva">
<option value="consumidor_final"
<?= (($condIVA ?? '') === 'consumidor_final') ? 'selected' : '' ?>>
Consumidor Final
</option>
<option value="responsable_inscripto"
<?= (($condIVA ?? '') === 'responsable_inscripto') ? 'selected' : '' ?>>
Responsable Inscripto
</option>
<option value="monotributo"
<?= (($condIVA ?? '') === 'monotributo') ? 'selected' : '' ?>>
Monotributo
</option>
</select>
</div>
<!-- CUIT -->
<div class="col-md-2">
<label class="form-label">CUIT</label>
<div class="d-flex align-items-center justify-content-center gap-2">
<input
type="text"
name="cuit_1"
maxlength="2"
class="form-control text-center"
style="max-width: 50px;"
data-bs-toggle="tooltip"
title="Tipo de persona"
value="<?= substr($cuit ?? '', 0, 2) ?>">
<input
type="text"
name="cuit_2"
maxlength="8"
class="form-control text-center flex-grow-1"
style="max-width: 120px;"
data-bs-toggle="tooltip"
title="DNI"
value="<?= substr($cuit ?? '', 2, 8) ?>">
<input
type="text"
name="cuit_3"
maxlength="1"
class="form-control text-center"
style="max-width: 40px;"
data-bs-toggle="tooltip"
title="Dígito verificador"
value="<?= substr($cuit ?? '', 10, 1) ?>">
</div>
</div>
</div>
</div>
<!-- LISTADO -->
<div class="d-flex justify-content-between align-items-center mb-2 flex-shrink-0">
<h6 class="text-muted mb-0">Listado de Productos</h6>
<div class="ms-4 d-flex gap-2">
<button
type="submit"
formaction="index.php?opt=agregar_producto_pedido"
formmethod="POST"
class="btn btn-success btn-sm">
<i class="fas fa-plus fa-fw me-1"></i>
Agregar Producto
</button>
<button
type="button"
class="btn btn-danger btn-sm btnConfirmar"
title="Vaciar Lista"
data-action="vaciar_caja_pedido"
data-cuit="<?= htmlspecialchars($cuit) ?>"
data-cond_iva="<?= $condIVA ?>"
data-mensaje="Todos los productos serán retirados de la lista, ¿estás seguro?"
data-titulo="Vaciar Lista"
data-bs-toggle="modal"
data-bs-target="#modalConfirmacion">
<i class="fas fa-times fa-fw me-1"></i>
Vaciar Lista
</button>
</div>
</div>
<!-- TABLA -->
<div class="table-scroll-crear my-2">
<table id="tablaProductosPedido" class="table table-fixed table-hover align-middle mb-0 table-resumen-venta">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Producto</th>
<th class="text-left">Costo Unit.</th>
<th class="text-left">Cantidad</th>
<th class="text-left">Precio Unit.</th>
<th class="text-left">Stock Final</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php if (empty($productos)): ?>
<tr>
<td colspan="7" class="text-center">-</td>
</tr>
<?php else: ?>
<?php foreach ($productos as $p): ?>
<tr>
<!-- ID PRODUCTO -->
<td><?= $p['id_producto'] ?></td>
<!-- MARCA Y MODELO -->
<td>
<?= htmlspecialchars($p['marca'] . ' ' . $p['modelo']) ?>
</td>
<!-- COSTO -->
<td class="text-left">
<div class="input-group input-group-sm input-precio" style="width: 150px;">
<span class="input-group-text">$</span>
<input
type="number"
name="costo[<?= $p['id_producto'] ?>]"
value="<?= number_format($p['costo'], 2, '.', '') ?>"
min="1"
step="0.01"
class="form-control">
</div>
</td>
<!-- CANTIDAD -->
<td class="text-center">
<input type="number"
name="cantidad[<?= $p['id_producto'] ?>]"
value="<?= htmlspecialchars($p['cantidad']) ?>"
min="1"
class="form-control form-control-sm"
style="width: 60px;">
</td>
<!-- PRECIO -->
<td class="text-left">
$ <?= number_format($p['precio'], 2) ?>
</td>
<!-- STOCK FINAL -->
<td class="text-left">
<?= htmlspecialchars($p['stock_final']) ?>
</td>
<!-- ACCIONES -->
<td class="text-center">
<div class="d-inline-flex gap-2">
<button
type="submit"
name="id_producto"
value="<?= $p['id_producto'] ?>"
formaction="index.php?opt=editar_datos_producto"
formmethod="POST"
class="btn btn-primary btn-sm">
<i class="fas fa-pen fa-fw"></i>
</button>
<button
type="submit"
name="id_producto"
value="<?= $p['id_producto'] ?>"
formaction="?opt=eliminar_producto_pedido"
formmethod="POST"
class="btn btn-danger btn-sm">
<i class="fas fa-times fa-fw"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- TOTALES -->
<div class="flex-shrink-0 mt-3">
<h6 class="text-muted mb-3">Datos de Pedido</h6>
<div class="row g-3">
<div class="col-md-2">
<label class="label-formventa">Total</label>
<input class="form-control campo-readonly fw-semibold"
value="$ <?= number_format($total, 2) ?>" readonly>
</div>
<!-- FECHA -->
<div class="col-md-2">
<label class="label-formventa">Fecha</label>
<input class="form-control campo-readonly form-control-sm" readonly
value="<?= date('d/m/Y') ?>">
</div>
</div>
<div class="mt-3 text-end">
<button type="submit" class="btn btn-primary py-2">
<i class="fas fa-save me-1"></i> Confirmar Pedido
</button>
</div>
</div>
<!-- Errores provenients de pedidoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</form>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/pedidos/pedidoValidator.js"></script>
<!-- Evita que Chrome use BFCache -->
<script>
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
</script>
<!-- Tooltip -->
<script>
document.addEventListener("DOMContentLoaded", () => {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipTriggerList.forEach(el => new bootstrap.Tooltip(el));
});
</script>
<!-- Pasar al siguiente input en CUIT -->
<script>
document.querySelectorAll("input[name^='cuit_']").forEach((input, index, inputs) => {
input.addEventListener("input", () => {
if (input.value.length === input.maxLength && inputs[index + 1]) {
inputs[index + 1].focus();
}
});
});
</script>
+107
View File
@@ -0,0 +1,107 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card card-rounded card-shadow border-0">
<div class="card-header header-dark px-4 py-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-file-alt me-2"></i>
Pedido Confirmado
</h4>
</div>
<div class="card-body text-center p-5">
<i class="fas fa-check-circle text-success fa-4x mb-4"></i>
<h4>Pedido guardado correctamente</h4>
<p class="text-muted">
Remito N°
<strong><?= $numeroRemito ?></strong>
</p>
<hr>
<p>
¿Desea descargar el remito generado?
</p>
<div class="d-flex justify-content-center gap-3 mt-5">
<form method="POST" action="?opt=generar_remito_pdf">
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<button class="btn btn-success">
<i class="fas fa-download me-1 my-2"></i>
Descargar Remito
</button>
</form>
<a href="?opt=continuar_sin_remito" class="btn btn-secondary">
<i class="fas fa-arrow-right me-1 my-2"></i>
Continuar
</a>
</div>
</div>
</div>
</div>
</div>
<!-- Errores provenients de pedidoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
<!-- Validar mediante js -->
<script src="js/pedidos/pedidoValidator.js"></script>
<!-- Evita que Chrome use BFCache -->
<script>
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
</script>
+174
View File
@@ -0,0 +1,174 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<!-- IZQUIERDA -->
<div class="d-flex align-items-center gap-4">
<h4 class="mb-0 tittle-shadow">
<i class="fas fa-folder-open icon-xl me-2"></i> Listado de Pedidos
</h4>
<div class="d-flex align-items-center gap-3">
<!-- AÑO -->
<form method="GET" action="index.php" class="d-flex align-items-center gap-2 select-group mb-0">
<input type="hidden" name="opt" value="listar_pedidos">
<i class="fas fa-calendar-alt text-light"></i>
<select name="anio" class="form-select custom-select select-informe" onchange="this.form.submit()">
<?php for ($i = $anioActual; $i >= 2020; $i--): ?>
<option value="<?= $i ?>" <?= $i == $anioSeleccionado ? 'selected' : '' ?>>
<?= $i ?>
</option>
<?php endfor; ?>
</select>
</form>
</div>
</div>
<!-- DERECHA -->
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=crear_pedido" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($pedidos)): ?>
<div class="text-center text-muted py-5">
No hay pedidos registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm ms-2"
style="width: auto;">
<option value="0">ID Pedido</option>
<option value="1">Fecha</option>
<option value="3">Proveedor</option>
<option value="4">Operador</option>
</select>
</div>
<div class="table-responsive">
<table id="tablaPedidos" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Fecha</th>
<th>Total</th>
<th>Proveedor</th>
<th>Operador</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($pedidos as $p): ?>
<tr>
<td><?= $p['id'] ?></td>
<td data-order="<?= $p['fecha'] ?>">
<?= htmlspecialchars($p['fecha_formateada']) ?>
</td>
<td>$ <?= number_format($p['total'], 2, '.', ',') ?></td>
<td><?= htmlspecialchars($p['razon_social'] ?? '') ?></td>
<td><?= htmlspecialchars($p['vendedor_nombre'] . ' ' . $p['vendedor_apellido']) ?></td>
<td class="text-center">
<div class="d-inline-flex align-items-center gap-2">
<!-- VER DETALLE PRODUCTOS -->
<a href="?opt=detalle_pedido&id=<?= $p['id'] ?>"
class="btn btn-primary btn-sm"
title="Detalle Productos">
<i class="fas fa-box-open fa-sm fa-fw"></i>
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- Errores provenientes de pedidoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/pedidos/pedidoValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/pedidos/pedidoTableLista.js"></script>
+93
View File
@@ -0,0 +1,93 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-clipboard-list icon-xl me-2"></i>
Detalle de Pedido N° <?= htmlspecialchars($idPedido) ?>
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=listar_pedidos" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($pedido_detalle)): ?>
<div class="text-center text-muted py-5">
No existe el detalle del pedido.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm ms-2"
style="width: auto;">
<option value="0">ID Producto</option>
<option value="1">Producto</option>
</select>
</div>
<div class="table-responsive">
<table id="tablaDetallePedido" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID Producto</th>
<th>Producto</th>
<th>Costo Unit.</th>
<th>Cantidad</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody>
<?php foreach ($pedido_detalle as $pd): ?>
<tr>
<td><?= $pd['id_producto'] ?? '' ?></td>
<td><?= htmlspecialchars($pd['marca'] . ' ' . $pd['modelo']) ?></td>
<td>$ <?= number_format($pd['precio_unitario'], 2, '.', ',') ?></td>
<td><?= htmlspecialchars($pd['cantidad'] ?? '') ?></td>
<td>$ <?= number_format($pd['subtotal'], 2, '.', ',') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/pedidos/pedidoTableDetalle.js"></script>
@@ -0,0 +1,158 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-users icon-xl me-2"></i>
Selección de Proveedor
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=crear_pedido" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($proveedores)): ?>
<div class="text-center text-muted py-5">
No hay proveedores registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex justify-content-between align-items-center mb-1">
<div id="filtrosIzquierda" class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm"
style="width:auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Razón Social</option>
<option value="2">Teléfono</option>
<option value="3">Email</option>
</select>
</div>
</div>
<form method="POST" action="index.php?opt=seleccionar_proveedor" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="table-responsive">
<table id="tablaProveedores" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Razón Social</th>
<th>Teléfono</th>
<th>Email</th>
<th>Seleccionar</th>
</tr>
</thead>
<tbody>
<?php foreach ($proveedores as $p): ?>
<tr>
<td><?= $p['id'] ?></td>
<td><?= htmlspecialchars($p['razon_social'] ?? '') ?></td>
<td><?= htmlspecialchars($p['telefono'] ?? '') ?></td>
<td><?= htmlspecialchars($p['email'] ?? '') ?></td>
<!-- CHECKBOX PARA SELECCIONAR PROVEEDOR -->
<td class="text-left">
<input type="checkbox"
name="id_proveedor"
value="<?= $p['id'] ?>"
class="form-check-input">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- BOTÓN FINALIZAR SELECCIÓN -->
<div class="mt-3 text-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-check me-1"></i> Finalizar
</button>
</div>
</form>
<?php endif; ?>
<!-- Manejo de errores proveniente de pedidoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/pedidos/pedidoValidator.js"></script>
<!-- Búsqueda y filtro por columnas -->
<script src="js/pedidos/pedidoTableProveedor.js"></script>
@@ -0,0 +1,172 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-box-open icon-xl me-2"></i>
Selección de Productos
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=crear_presupuesto" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($productos)): ?>
<div class="text-center text-muted py-5">
No hay productos registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex justify-content-between align-items-center mb-1">
<div id="filtrosIzquierda" class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm"
style="width:auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Marca</option>
<option value="2">Modelo</option>
</select>
</div>
</div>
<form method="POST" action="index.php?opt=agregar_producto_presupuesto" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="table-responsive">
<table id="tablaProductos" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Marca</th>
<th>Modelo</th>
<th>Precio Unit. Fin.</th>
<th>Descuento Tot. (%)</th>
<th>Stock</th>
<th>Seleccionar</th>
<th>Cantidad</th>
</tr>
</thead>
<tbody>
<?php foreach ($productos as $p): ?>
<tr>
<td><?= $p['id'] ?? '' ?></td>
<td><?= htmlspecialchars($p['marca'] ?? '') ?></td>
<td><?= htmlspecialchars($p['modelo'] ?? '') ?></td>
<td>$ <?= number_format($p['precio_final'], 2, '.', ',') ?></td>
<td><?= htmlspecialchars($p['descuento_total'] ?? '') ?></td>
<td><?= htmlspecialchars($p['stock'] ?? '') ?></td>
<!-- CHECKBOX PARA SELECCIONAR PRODUCTO -->
<td class="text-left">
<input type="checkbox"
name="ids[]"
value="<?= $p['id'] ?>"
class="form-check-input">
</td>
<!-- INPUT DE CANTIDAD -->
<td class="text-center">
<input type="number"
name="cantidades[<?= $p['id'] ?>]"
value="1"
min="1"
max="<?= $p['stock'] ?>"
class="form-control form-control-sm"
style="width: 60px;">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- BOTÓN FINALIZAR SELECCIÓN -->
<div class="mt-3 text-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-check me-1"></i> Finalizar Selección
</button>
</div>
</form>
<?php endif; ?>
<!-- Manejo de errores provenientes de presupuestoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/presupuestos/presupuestoValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/presupuestos/presupuestoTableProducto.js"></script>
@@ -0,0 +1,270 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0 card-viewport">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-file-invoice-dollar icon-xl me-2"></i>
Nuevo Presupuesto
</h4>
<div class="d-flex gap-2">
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=crear_cliente&from=crear_presupuesto" class="btn btn-success">
<i class="fas fa-plus me-1"></i>
Nuevo Cliente
</a>
<a href="?opt=home_operador" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<div class="card-body p-4 d-flex flex-column card-body-scroll">
<!-- CLIENTE -->
<div class="mb-4 flex-shrink-0">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="text-muted mb-0">Cliente</h6>
<div class="ms-4 d-flex gap-2">
<a href="?opt=seleccionar_cliente_presupuesto" class="btn btn-primary btn-sm">
<i class="fas fa-user fa-fw me-1"></i>
Seleccionar Cliente
</a>
<form method="POST" action="?opt=eliminar_cliente_presupuesto" class="d-inline">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<button class="btn btn-danger btn-sm">
<i class="fas fa-times fa-fw me-1"></i>
Eliminar Cliente
</button>
</form>
</div>
</div>
<div class="row g-3">
<div class="col-md-3">
<label class="label-formventa mb-1">ID</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($cliente['id'] ?? '') ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa mb-1">Nombre y Apellido</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars(($cliente['nombre'] ?? '') . ' ' . ($cliente['apellido'] ?? '')) ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa mb-1">Teléfono</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($cliente['telefono'] ?? '') ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa mb-1">Email</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($cliente['email'] ?? '') ?>" readonly>
</div>
</div>
</div>
<!-- LISTADO -->
<div class="d-flex justify-content-between align-items-center mb-2 flex-shrink-0">
<h6 class="text-muted mb-0">Listado de Productos</h6>
<div class="ms-4 d-flex gap-2">
<a href="?opt=agregar_producto_presupuesto" class="btn btn-success btn-sm">
<i class="fas fa-plus fa-fw me-1"></i>
Agregar Producto
</a>
<button
type="button"
class="btn btn-danger btn-sm btnConfirmar"
title="Vaciar Lista"
data-action="vaciar_caja_presupuesto"
data-mensaje="Todos los productos serán retirados de la lista, ¿estás seguro?"
data-titulo="Vaciar Lista"
data-bs-toggle="modal"
data-bs-target="#modalConfirmacion">
<i class="fas fa-times fa-fw me-1"></i>
Vaciar Lista
</button>
</div>
</div>
<!-- TABLA -->
<div class="table-scroll-crear my-2">
<table id="tablaProductosPresupuesto" class="table table-fixed table-hover align-middle mb-0 table-resumen-venta">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Producto</th>
<th class="text-left">Precio Unit. Fin.</th>
<th class="text-left">Descuento (%)</th>
<th class="text-left">Cantidad</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php if (empty($productos)): ?>
<tr>
<td colspan="6" class="text-center">-</td>
</tr>
<?php else: ?>
<?php foreach ($productos as $p): ?>
<tr>
<form method="POST" action="index.php?opt=editar_cantidad_presupuesto" novalidate>
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="id_producto" value="<?= $p['id_producto'] ?>">
<td><?= $p['id_producto'] ?></td>
<td>
<?= htmlspecialchars($p['marca'] . ' ' . $p['modelo']) ?>
</td>
<td class="text-left">
$ <?= number_format($p['precio'], 2) ?>
</td>
<td class="text-left">
<?= htmlspecialchars($p['descuento_total']) ?>%
</td>
<td class="text-left">
<input type="number"
name="cantidad"
value="<?= $p['cantidad'] ?>"
min="1"
max="<?= $p['stock'] ?>"
class="form-control form-control-sm"
style="width: 60px;">
</td>
<td class="text-center">
<button type="submit" class="btn btn-primary btn-sm">
<i class="fas fa-pen fa-fw"></i>
</button>
<button
type="submit"
formaction="?opt=eliminar_producto_presupuesto"
class="btn btn-danger btn-sm">
<i class="fas fa-times fa-fw"></i>
</button>
</td>
</form>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- TOTALES -->
<div class="flex-shrink-0 mt-3">
<h6 class="text-muted mb-3">Datos de Presupuesto</h6>
<div class="row g-3">
<div class="col-md-3">
<label class="label-formventa">Subtotal</label>
<input class="form-control campo-readonly"
value="$ <?= number_format($subtotal, 2) ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa">Descuento</label>
<input class="form-control campo-readonly"
value="$ <?= number_format($descuento, 2) ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa">Total</label>
<input class="form-control campo-readonly fw-semibold"
value="$ <?= number_format($total, 2) ?>" readonly>
</div>
</div>
<div class="mt-3 text-end">
<a href="?opt=resumen_presupuesto" class="btn btn-primary py-2">
Siguiente
<i class="fas fa-arrow-right ms-1"></i>
</a>
</div>
</div>
<!-- Errores provinientes de presupuestoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/presupuestos/presupuestoValidator.js"></script>
@@ -0,0 +1,418 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<?php
// Datos desde back end (si existen)
$presupuesto = $_SESSION['presupuesto_extra'] ?? [];
$condIva = $presupuesto['cond_iva'] ?? 'consumidor_final';
$cuit = $presupuesto['cuit'] ?? '';
$formaPago = $presupuesto['forma_pago'] ?? 'efectivo';
$tipoPago = $presupuesto['tipo_pago'] ?? 'pago_unico';
$descuentoIngresado = $presupuesto['descuento'] ?? 0;
$recargoIngresado = $presupuesto['recargo'] ?? 0;
?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0 card-viewport">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-receipt icon-xl me-2"></i>
Resumen de Presupuesto
</h4>
<a href="?opt=crear_presupuesto" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
<!-- BODY -->
<form id="form-crear_presupuesto" method='POST' action="index.php?opt=guardar_datos_presupuesto" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="card-body p-4 d-flex flex-column card-body-scroll">
<!-- CLIENTE -->
<div class="mb-4 flex-shrink-0">
<h6 class="text-muted mb-2">Datos Del Cliente</h6>
<div class="row g-3">
<!-- NOMBRE Y APELLIDO -->
<div class="col-md-3">
<label class="label-formventa">Nombre y Apellido</label>
<input class="form-control campo-readonly" readonly
value="<?= htmlspecialchars($cliente['nombre']) . ' ' . $cliente['apellido'] ?? '' ?>">
</div>
<!-- TELÉFONO -->
<div class="col-md-3">
<label class="label-formventa">Teléfono Cliente</label>
<input class="form-control campo-readonly" readonly
value="<?= htmlspecialchars($cliente['telefono'] ?? '') ?>">
</div>
<!-- COND. IVA -->
<div class="col-md-3">
<label class="label-formventa">Cond. IVA</label>
<select class="form-select form-select-sm" name="cond_iva" id="condIva">
<option value="consumidor_final" <?= $condIva === 'consumidor_final' ? 'selected' : '' ?>>
Consumidor Final
</option>
<option value="responsable_inscripto" <?= $condIva === 'responsable_inscripto' ? 'selected' : '' ?>>
Responsable Inscripto
</option>
<option value="monotributo" <?= $condIva === 'monotributo' ? 'selected' : '' ?>>
Monotributo
</option>
</select>
</div>
<!-- CUIT -->
<div class="col-md-3">
<label class="form-label">CUIT</label>
<div class="d-flex gap-2">
<input type="text" name="cuit_1" maxlength="2" class="form-control text-center"
data-bs-toggle="tooltip"
title="Tipo de persona"
value="<?= substr($cuit ?? '', 0, 2) ?>">
<input type="text" name="cuit_2" maxlength="8" class="form-control text-center"
data-bs-toggle="tooltip"
title="DNI"
value="<?= substr($cuit ?? '', 2, 8) ?>">
<input type="text" name="cuit_3" maxlength="1" class="form-control text-center"
data-bs-toggle="tooltip"
title="Dígito verificador"
value="<?= substr($cuit ?? '', 10, 1) ?>">
</div>
</div>
</div>
</div>
<!-- PRODUCTOS -->
<h6 class="text-muted mb-2">Productos</h6>
<div class="table-scroll my-3">
<table class="table table-fixed align-middle table-hover mb-0 table-resumen-venta">
<thead class="table-dark">
<tr>
<th>Producto</th>
<th class="text-end">Precio Unit. Fin.</th>
<th class="text-end">Descuento (%)</th>
<th class="text-end">Cantidad</th>
<th class="text-end">Subtotal</th>
</tr>
</thead>
<tbody>
<?php foreach ($productos as $p): ?>
<tr>
<td>
<?= htmlspecialchars($p['marca'] . ' ' . $p['modelo']) ?>
</td>
<td class="text-end">
$ <?= number_format($p['precio_final'], 2) ?>
</td>
<td class="text-end">
<?= htmlspecialchars($p['descuento_total']) ?> %
</td>
<td class="text-end">
<?= $p['cantidad'] ?>
</td>
<td class="text-end">
$ <?= number_format($p['precio_final'] * $p['cantidad'], 2) ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- DATOS DE PAGO -->
<div class="flex-shrink-0 mt-0">
<h6 class="text-muted mb-2">Datos De Pago</h6>
<div class="row g-3">
<!-- FORMA DE PAGO -->
<div class="col-md-3">
<label class="label-formventa">Forma de pago</label>
<select class="form-select form-select-sm" name="forma_pago" id="formaPago">
<option value="efectivo" <?= $formaPago === 'efectivo' ? 'selected' : '' ?>>Efectivo</option>
<option value="transferencia" <?= $formaPago === 'transferencia' ? 'selected' : '' ?>>Transferencia</option>
<option value="debito" <?= $formaPago === 'debito' ? 'selected' : '' ?>>Débito</option>
<option value="credito" <?= $formaPago === 'credito' ? 'selected' : '' ?>>Crédito</option>
<option value="echeq" <?= $formaPago === 'echeq' ? 'selected' : '' ?>>Echeq</option>
</select>
</div>
<!-- TIPO DE PAGO -->
<div class="col-md-3">
<label class="label-formventa">Tipo de pago</label>
<select class="form-select form-select-sm" name="tipo_pago" id="tipoPago">
<option value="pago_unico" <?= $tipoPago === 'pago_unico' ? 'selected' : '' ?>>Pago Único</option>
<option value="3_cuotas" <?= $tipoPago === '3_cuotas' ? 'selected' : '' ?>>3 Cuotas</option>
<option value="6_cuotas" <?= $tipoPago === '6_cuotas' ? 'selected' : '' ?>>6 Cuotas</option>
<option value="12_cuotas" <?= $tipoPago === '12_cuotas' ? 'selected' : '' ?>>12 Cuotas</option>
</select>
</div>
<!-- DESCUENTO -->
<div class="col-md-3">
<label class="label-formventa">Descuento (%)</label>
<div class="input-porcentaje">
<input
type="number"
name="descuento"
id="descuentoPago"
value="<?= number_format($descuentoIngresado ?? 0, 2, '.', '') ?>"
min="0"
step="0.01"
class="form-control form-control-sm text-end"
>
<span class="input-group-text">%</span>
</div>
</div>
<!-- RECARGO -->
<div class="col-md-3">
<label class="label-formventa">Recargo (%)</label>
<div class="input-porcentaje">
<input
type="number"
name="recargo"
id="recargoPago"
value="<?= number_format($recargoIngresado ?? 0, 2, '.', '') ?>"
min="0"
step="0.01"
class="form-control form-control-sm text-end"
>
<span class="input-group-text">%</span>
</div>
</div>
</div>
</div>
<!-- TOTALES -->
<div class="flex-shrink-0 mt-3">
<div class="d-flex align-items-center gap-2 mb-2">
<h6 class="text-muted mb-0">Totales</h6>
</div>
<div class="row g-3">
<!-- SUBTOTAL -->
<div class="col-md-3">
<label class="label-formventa">Subtotal</label>
<input class="form-control campo-readonly" readonly
value="$ <?= number_format($subtotal, 2) ?>">
</div>
<!-- DESCUENTO TOTAL -->
<div class="col-md-3">
<label class="label-formventa">Descuento</label>
<input class="form-control campo-readonly" readonly
value="$ <?= number_format($descuento, 2) ?>">
</div>
<!-- RECARGO TOTAL -->
<div class="col-md-3">
<label class="label-formventa">Recargo</label>
<input class="form-control campo-readonly" readonly
value="$ <?= number_format($recargo, 2) ?>">
</div>
<!-- TOTAL -->
<div class="col-md-3">
<label class="label-formventa fw-semibold">Total</label>
<input class="form-control campo-readonly fw-semibold" readonly
value="$ <?= number_format($total, 2) ?>">
</div>
</div>
</div>
<!-- ACCIONES -->
<div class="mt-4 pt- d-flex justify-content-between align-items-center flex-wrap gap-3">
<!-- ACCIÓN SECUNDARIA (BORRADOR) -->
<button
type="submit"
class="btn btn-soft-secondary text-white py-2"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Guarda el presupuesto sin confirmarlo para continuar más tarde">
<i class="fas fa-save me-1"></i>
Guardar como borrador
</button>
<!-- ACCION PRINCIPAL -->
<div class="d-flex gap-2">
<button
type="submit"
formaction="index.php?opt=generar_presupuesto_pdf"
formmethod="POST"
class="btn btn-success py-2">
<i class="fas fa-file-alt me-1"></i>
Generar comprobante
</button>
</div>
</div>
<!-- Errores provenientes de presupuestoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</form>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/presupuestos/presupuestoValidator.js"></script>
<!-- LÓGICA DE FORMA DE PAGOS -->
<script>
document.addEventListener("DOMContentLoaded", () => {
const formaPago = document.getElementById("formaPago");
const tipoPago = document.getElementById("tipoPago");
const recargoPago = document.getElementById("recargoPago");
const reglasPago = {
efectivo: ['pago_unico'],
transferencia: ['pago_unico'],
echeq: ['pago_unico'],
debito: ['pago_unico', '3_cuotas', '6_cuotas', '12_cuotas'],
credito: ['pago_unico', '3_cuotas', '6_cuotas', '12_cuotas']
};
function actualizarTipoPago() {
const forma = formaPago.value;
const permitidos = reglasPago[forma] ?? ['pago_unico'];
Array.from(tipoPago.options).forEach(opt => {
opt.disabled = !permitidos.includes(opt.value);
});
// Si el valor actual no está permitido, forzamos uno válido
if (!permitidos.includes(tipoPago.value)) {
tipoPago.value = permitidos[0];
}
}
// Eventos
formaPago.addEventListener("change", actualizarTipoPago);
// Init
actualizarTipoPago();
});
</script>
<!-- Evita que Chrome use BFCache -->
<script>
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
</script>
<!-- Tooltip -->
<script>
document.addEventListener("DOMContentLoaded", () => {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipTriggerList.forEach(el => new bootstrap.Tooltip(el));
});
</script>
<!-- Pasar al siguiente input en CUIT -->
<script>
document.querySelectorAll("input[name^='cuit_']").forEach((input, index, inputs) => {
input.addEventListener("input", () => {
if (input.value.length === input.maxLength && inputs[index + 1]) {
inputs[index + 1].focus();
}
});
});
</script>
@@ -0,0 +1,162 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-users icon-xl me-2"></i>
Selección de Cliente
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=crear_presupuesto" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($clientes)): ?>
<div class="text-center text-muted py-5">
No hay clientes registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex justify-content-between align-items-center mb-1">
<div id="filtrosIzquierda" class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm"
style="width:auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Nombre</option>
<option value="2">Apellido</option>
<option value="3">Teléfono</option>
<option value="4">Email</option>
</select>
</div>
</div>
<form method="POST" action="index.php?opt=seleccionar_cliente_presupuesto" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="table-responsive">
<table id="tablaClientes" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Apellido</th>
<th>Teléfono</th>
<th>Email</th>
<th>Seleccionar</th>
</tr>
</thead>
<tbody>
<?php foreach ($clientes as $c): ?>
<tr>
<td><?= $c['id'] ?></td>
<td><?= htmlspecialchars($c['nombre'] ?? '') ?></td>
<td><?= htmlspecialchars($c['apellido'] ?? '') ?></td>
<td><?= htmlspecialchars($c['telefono'] ?? '') ?></td>
<td><?= htmlspecialchars($c['email'] ?? '') ?></td>
<!-- CHECKBOX PARA SELECCIONAR CLIENTE -->
<td class="text-left">
<input type="checkbox"
name="id_cliente"
value="<?= $c['id'] ?>"
class="form-check-input">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- BOTÓN FINALIZAR SELECCIÓN -->
<div class="mt-3 text-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-check me-1"></i> Finalizar
</button>
</div>
</form>
<?php endif; ?>
<!-- Errores provenientes de presupuestoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/presupuestos/presupuestoValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/presupuestos/presupuestoTableCliente.js"></script>
+123
View File
@@ -0,0 +1,123 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-8 col-lg-9 col-md-10">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 tittle-shadow">
<i class="fas fa-plus-square me-2"></i>
Crear Categoría
</h4>
<a href="index.php?opt=categorias" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i> Volver
</a>
</div>
</div>
<!-- BODY -->
<div class="card-body p-4 bg-white">
<form id="form-crear-categoria" method="POST" action="?opt=crear_categoria" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="row g-3">
<!-- NOMBRE -->
<div class="col-md-6">
<label class="form-label">Nombre</label>
<input type="text" name="nombre" class="form-control">
</div>
<!-- PORCENTAJE DE GANANCIA -->
<div class="col-md-6">
<label class="form-label">Ganancia</label>
<input type="number" step="0.01" min="0" name="porcentaje_ganancia" placeholder="0.00" class="form-control">
<small class="text-muted">
Admite valor sin asignar
</small>
</div>
<!-- DESCUENTO -->
<div class="col-md-6">
<label class="form-label">Descuento</label>
<input type="number" step="0.01" min="0" name="descuento" placeholder="0.00" class="form-control">
<small class="text-muted">
Admite valor sin asignar
</small>
</div>
</div>
<!-- BOTON SUBMIT -->
<div class="d-flex justify-content-between align-items-center mt-4 pt-4 border-top">
<button type="submit" class="btn btn-success">
<i class="fas fa-plus-square me-1"></i> Crear Categoría
</button>
</div>
</form>
<!-- Errores provinientes de productoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/productos/categoriValidator.js"></script>
+172
View File
@@ -0,0 +1,172 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-8 col-lg-9 col-md-10">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 tittle-shadow">
<i class="fas fa-plus-square me-2"></i>
Crear Producto
</h4>
<a href="index.php?opt=productos" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i> Volver
</a>
</div>
</div>
<!-- BODY -->
<div class="card-body p-4 bg-white">
<form id="form-crear-producto" method="POST" action="?opt=crear_producto" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="row g-3">
<!-- MARCA -->
<div class="col-md-6">
<label class="form-label">Marca</label>
<input type="text" name="marca" class="form-control">
</div>
<!-- MODELO -->
<div class="col-md-6">
<label class="form-label">Modelo</label>
<input type="text" name="modelo" class="form-control">
</div>
<!-- CATEGORÍA -->
<div class="col-md-6">
<label class="form-label">Categoría</label>
<select name="categoria" class="form-select">
<option value="">Seleccione una categoría</option>
<?php foreach ($categorias as $c): ?>
<option value="<?= $c['id'] ?>">
<?= htmlspecialchars($c['nombre']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<!-- TIPO -->
<div class="col-md-6">
<label class="form-label">Tipo</label>
<select name="tipo" class="form-select">
<option value="">Seleccione un tipo</option>
<?php foreach ($tipos as $t): ?>
<option value="<?= $t['id'] ?>">
<?= htmlspecialchars($t['nombre']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<!-- DESCUENTO -->
<div class="col-md-6">
<label class="form-label">Descuento</label>
<input type="number" step="0.01" min="0" name="descuento" placeholder="0.00" class="form-control">
<small class="text-muted">
Admite valor sin asignar
</small>
</div>
<!-- PRECIO -->
<div class="col-md-6">
<label class="form-label">Precio</label>
<input type="text" placeholder="No ingresar" class="form-control" readonly>
<small class="text-muted">
Obtenido en base a ganancia de categoria
</small>
</div>
<!-- STOCK -->
<div class="col-md-6">
<label class="form-label">Stock</label>
<input type="text" placeholder="No ingresar" class="form-control" readonly>
<small class="text-muted">
Se debe ingresar en Pedidos
</small>
</div>
</div>
<!-- BOTON SUBMIT -->
<div class="d-flex justify-content-between align-items-center mt-4 pt-4 border-top">
<button type="submit" class="btn btn-success">
<i class="fas fa-plus-square me-1"></i> Crear Producto
</button>
</div>
</form>
<!-- Errores provinientes de productoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/productos/productoValidator.js"></script>
+143
View File
@@ -0,0 +1,143 @@
<?php require __DIR__ . '/../../includes/navBar.php'?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-8 col-lg-9 col-md-10">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-edit me-2"></i>
Datos de la Categoría
</h4>
<a href="?opt=categorias" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
<!-- BODY -->
<div class="card-body p-4 bg-white">
<form id="form-editar-categoria" method="POST" action="index.php?opt=editar_categoria&id=<?= $categorias['id'] ?>" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="row g-3">
<!-- NOMBRE -->
<div class="col-md-6">
<label class="form-label">Nombre</label>
<input type="text"
name="nombre"
class="form-control"
value="<?= htmlspecialchars($categorias['nombre']) ?>"
>
</div>
<!-- PORCENTAJE DE GANANCIA -->
<div class="col-md-6">
<label class="form-label">Ganancia (%)</label>
<input type="number"
step="0.01"
min="0"
name="porcentaje_ganancia"
placeholder="0.00"
class="form-control"
value="<?= number_format($categorias['porcentaje_ganancia'], 2, '.', '') ?>"
>
<small class="text-muted">
Admite valor sin asignar
</small>
</div>
<!-- DESCUENTO -->
<div class="col-md-6">
<label class="form-label">Descuento (%)</label>
<input type="number"
step="0.01"
min="0"
name="descuento"
class="form-control"
value="<?= number_format($categorias['descuento'], 2, '.', '') ?>"
>
<small class="text-muted">
Admite valor sin asignar
</small>
</div>
<!-- BOTON SUMBIT -->
<div class="d-flex justify-content-between mt-4 pt-3 border-top">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i> Guardar cambios
</button>
</div>
</div>
</form>
<!-- Errores provinientes de productoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/productos/categoriaValidator.js"></script>
+220
View File
@@ -0,0 +1,220 @@
<?php require __DIR__ . '/../../includes/navBar.php'?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-8 col-lg-9 col-md-10">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-edit me-2"></i>
Datos del Producto
</h4>
<a href="?opt=productos" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
<!-- BODY -->
<div class="card-body p-4 bg-white">
<form id="form-editar-producto" method="POST" action="index.php?opt=editar_producto&id=<?= $producto['id'] ?>" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="row g-3">
<!-- MARCA -->
<div class="col-md-6">
<label class="form-label">Marca</label>
<input type="text"
name="marca"
class="form-control bg-light text-muted"
style="cursor: not-allowed;"
value="<?= htmlspecialchars($producto['marca']) ?>" readonly>
</div>
<!-- MODELO -->
<div class="col-md-6">
<label class="form-label">Modelo</label>
<input type="text"
name="modelo"
class="form-control bg-light text-muted"
style="cursor: not-allowed;"
value="<?= htmlspecialchars($producto['modelo']) ?>" readonly>
</div>
<!-- PRECIO -->
<div class="col-md-6">
<label class="form-label">Precio ($)</label>
<input type="number"
class="form-control bg-light text-muted"
style="cursor: not-allowed;"
value="<?= number_format($producto['precio'], 2, '.', '') ?>"
readonly>
<small class="text-muted">
Obtenido en base a ganancia de categoria
</small>
</div>
<!-- STOCK -->
<div class="col-md-6">
<label class="form-label">Stock</label>
<input type="number"
class="form-control bg-light text-muted"
style="cursor: not-allowed;"
value="<?= htmlspecialchars($producto['stock']) ?>"
readonly>
<small class="text-muted">
Se debe ingresar en Pedidos
</small>
</div>
<!-- CATEGORÍA -->
<div class="col-md-6">
<label class="form-label">Categoría</label>
<select name="categoria" class="form-select bg-light text-muted" style="cursor: not-allowed; opacity: 1;">
<?php foreach ($categorias as $c): ?>
<option
value="<?= $c['id'] ?>"
<?= ($c['id'] == ($producto['id_categoria'] ?? null)) ? 'selected' : '' ?>
>
<?= htmlspecialchars($c['nombre']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<!-- TIPO -->
<div class="col-md-6">
<label class="form-label">Tipo</label>
<select name="tipo" class="form-select bg-light text-muted" style="cursor: not-allowed; opacity: 1;">
<?php foreach ($tipos as $t): ?>
<option
value="<?= $t['id'] ?>"
<?= ($t['id'] == ($producto['id_tipo'] ?? null)) ? 'selected' : '' ?>
>
<?= htmlspecialchars($t['nombre']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<!-- ID DE PEDIDO -->
<div class="col-md-6">
<label class="form-label">ID Pedido</label>
<input type="text"
class="form-control bg-light text-muted"
style="cursor: not-allowed;"
value="<?= htmlspecialchars($producto['id_pedido']) ?>"
readonly>
<small class="text-muted">
Solo con fines informativos
</small>
</div>
<!-- DESCUENTO -->
<div class="col-md-6">
<label class="form-label">Descuento (%)</label>
<input type="number"
step="0.01"
min="0"
name="descuento"
placeholder="0.00"
class="form-control campo-readonly"
value="<?= number_format($producto['descuento'], 2, '.', '') ?>"
>
<small class="text-muted">
Admite valor sin asignar
</small>
</div>
<!-- BOTON SUMBIT -->
<div class="d-flex justify-content-between mt-4 pt-3 border-top">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i> Guardar cambios
</button>
</div>
</div>
</form>
<!-- Errores provinientes de productoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/productos/productoValidator.js"></script>
+141
View File
@@ -0,0 +1,141 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title shadow">
<i class="fas fa-tags icon-xl me-2"></i>
Gestión de Categorías
</h4>
<div class="d-flex gap-2">
<a href="?opt=crear_categoria" class="btn btn-success">
<i class="fas fa-plus me-1"></i>
Nueva Categoría
</a>
<a href="?opt=productos" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($categorias)): ?>
<div class="text-center text-muted py-5">
No hay categorías registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex align-items-center gap-2" id="filtrosIzquierda">
<select id="columnFilter"
class="form-select form-select-sm"
style="width: auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Nombre</option>
<option value="2">Ganancia</option>
<option value="3">Descuento</option>
</select>
</div>
<div class="table-responsive">
<table id="tablaCategorias" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Ganancia (%)</th>
<th>Descuento (%)</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($categorias as $c): ?>
<tr>
<td><?= $c['id'] ?? '' ?></td>
<td><?= htmlspecialchars($c['nombre'] ?? '') ?></td>
<td><?= htmlspecialchars($c['porcentaje_ganancia'] ?? '') ?></td>
<td><?= htmlspecialchars($c['descuento'] ?? '') ?></td>
<td class="text-center">
<div class="d-inline-flex align-items-center gap-2">
<!-- EDITAR -->
<a href="?opt=editar_categoria&id=<?= $c['id'] ?>"
class="btn btn-primary btn-sm"
title="Editar Categoría">
<i class="fas fa-pen fa-sm fa-fw"></i>
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!--Errores provinientes de productoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/productos/categoriaValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/productos/categoriaTableLista.js"></script>
@@ -0,0 +1,165 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-box-open icon-xl me-2"></i>
Gestión de Productos Inactivos
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<!-- BOTÓN REAL -->
<a href="?opt=productos" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($productos)): ?>
<div class="text-center text-muted py-5">
No hay productos registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex justify-content-between align-items-center mb-1">
<div id="filtrosIzquierda" class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm"
style="width: auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Marca</option>
<option value="2">Modelo</option>
<option value="4">Categoría</option>
<option value="5">Tipo</option>
</select>
</div>
</div>
<div class="table-responsive">
<table id="tablaProductosInactivos" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Marca</th>
<th>Modelo</th>
<th>Categoría</th>
<th>Tipo</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($productos as $p): ?>
<tr>
<td><?= $p['id'] ?? '' ?></td>
<td><?= htmlspecialchars($p['marca'] ?? '') ?></td>
<td><?= htmlspecialchars($p['modelo'] ?? '') ?></td>
<td><?= htmlspecialchars($p['categoria'] ?? '') ?></td>
<td><?= htmlspecialchars($p['tipo'] ?? '') ?></td>
<td class="text-center">
<div class="d-inline-flex align-items-center gap-2">
<!-- CAMBIAR ESTADO -->
<button
class="btn btn-success btn-sm btnConfirmar"
title="Activar Producto"
data-action="cambiar_estado"
data-id="<?= $p['id'] ?>"
data-estado="1"
data-mensaje="¿Seguro que quieres activar este producto?"
data-titulo="Activar Producto"
data-bs-toggle="modal"
data-bs-target="#modalConfirmacion">
<i class="fas fa-eye"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- Errores provinientes de productoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/productos/productoValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/productos/productoTableListaInactivos.js"></script>
+187
View File
@@ -0,0 +1,187 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-box-open icon-xl me-2"></i>
Gestión de Productos
</h4>
<div class="d-flex gap-2">
<a href="?opt=crear_producto" class="btn btn-success">
<i class="fas fa-plus me-1"></i>
Nuevo Producto
</a>
<a href="?opt=categorias" class="btn btn-primary">
<i class="fas fa-tags me-1"></i>
Categorías
</a>
<a href="?opt=home_operador" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($productos)): ?>
<div class="text-center text-muted py-5">
No hay productos registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex justify-content-between align-items-center mb-1">
<div id="filtrosIzquierda" class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm"
style="width:auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Marca</option>
<option value="2">Modelo</option>
<option value="6">Categoría</option>
<option value="7">Tipo</option>
</select>
</div>
<div>
<a href="?opt=productos_inactivos" class="btn btn-primary btn-sm">
<i class="fas fa-box-open me-1"></i>
Productos Inactivos
</a>
</div>
</div>
<div class="table-responsive">
<table id="tablaProductos" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Marca</th>
<th>Modelo</th>
<th>Precio Unit.</th>
<th>Stock</th>
<th>Desc. Tot. (%)</th>
<th>Categoría</th>
<th>Tipo</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($productos as $p): ?>
<tr>
<td><?= $p['id'] ?? '' ?></td>
<td><?= htmlspecialchars($p['marca'] ?? '') ?></td>
<td><?= htmlspecialchars($p['modelo'] ?? '') ?></td>
<td><?= htmlspecialchars($p['precio'] ?? '') ?></td>
<td><?= htmlspecialchars($p['stock'] ?? '') ?></td>
<td><?= htmlspecialchars($p['descuento_total'] ?? '') ?></td>
<td><?= htmlspecialchars($p['categoria'] ?? '') ?></td>
<td><?= htmlspecialchars($p['tipo'] ?? '') ?></td>
<td class="text-center">
<div class="d-inline-flex align-items-center gap-2">
<!-- EDITAR -->
<a href="?opt=editar_producto&id=<?= $p['id'] ?>"
class="btn btn-primary btn-sm"
title="Editar Producto">
<i class="fas fa-pen fa-sm fa-fw"></i>
</a>
<!-- CAMBIAR ESTADO -->
<button
class="btn btn-danger btn-sm btnConfirmar"
title="Desactivar Producto"
data-action="cambiar_estado"
data-id="<?= $p['id'] ?>"
data-estado="0"
data-mensaje="¿Seguro que quieres desactivar este producto?"
data-titulo="Desactivar Producto"
data-bs-toggle="modal"
data-bs-target="#modalConfirmacion">
<i class="fas fa-eye-slash"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- Errores provinientes de productoController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/productos/productoValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/productos/productoTableLista.js"></script>
@@ -0,0 +1,196 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-8 col-lg-9 col-md-10">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 tittle-shadow">
<i class="fas fa-user-plus me-2"></i>
Crear Proveedor
</h4>
<a href="index.php?opt=<?= htmlspecialchars($_SESSION['return_opt'] ?? 'proveedores') ?>" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i> Volver
</a>
</div>
</div>
<!-- BODY -->
<div class="card-body p-4 bg-white">
<form id="form-crear-proveedor" method="POST" action="?opt=crear_proveedor" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<!-- CAMPO OCULTO (para saber desde que pagina se solicito la creacion del cliente -->
<input type="hidden" name="redirect_opt" value="<?= htmlspecialchars($_SESSION['return_opt'] ?? 'proveedores') ?>">
<div class="row g-3">
<!-- RAZÓN SOCIAL -->
<div class="col-md-6">
<label class="form-label">Razón social</label>
<input type="text" name="razon_social" class="form-control">
</div>
<!-- TELÉFONO -->
<div class="col-md-6">
<label class="form-label">Teléfono</label>
<input type="text" name="telefono" class="form-control">
</div>
<!-- EMAIL -->
<div class="col-md-6">
<label class="form-label">Email</label>
<input type="text" name="email" class="form-control">
</div>
<!-- CONDICIÓN IVA -->
<div class="col-md-6">
<label class="form-label">Condición IVA</label>
<select
name="cond_iva"
class="form-select">
<option value="consumidor_final">
Consumidor Final
</option>
<option value="monotributo">
Monotributo
</option>
<option value="responsable_inscripto">
Responsable Inscripto
</option>
</select>
</div>
<!-- CUIT -->
<div class="col-md-6">
<label class="form-label">CUIT</label>
<div class="d-flex gap-2">
<input
type="text"
name="cuit_1"
maxlength="2"
class="form-control text-center"
data-bs-toggle="tooltip"
title="Tipo de persona">
<input
type="text"
name="cuit_2"
maxlength="8"
class="form-control text-center"
data-bs-toggle="tooltip"
title="DNI">
<input
type="text"
name="cuit_3"
maxlength="1"
class="form-control text-center"
data-bs-toggle="tooltip"
title="Dígito verificador">
</div>
</div>
</div>
<!-- BOTON SUBMIT -->
<div class="d-flex justify-content-between align-items-center mt-4 pt-4 border-top">
<button type="submit" class="btn btn-success">
<i class="fas fa-user-plus me-1"></i> Crear Proveedor
</button>
</div>
</form>
<!-- Errores provinientes de proveedorController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/proveedores/proveedorValidator.js"></script>
<!-- Pasar al siguiente input en CUIT -->
<script>
document.querySelectorAll("input[name^='cuit_']").forEach((input, index, inputs) => {
input.addEventListener("input", () => {
if (input.value.length === input.maxLength && inputs[index + 1]) {
inputs[index + 1].focus();
}
});
});
</script>
<!-- Tooltip -->
<script>
document.addEventListener("DOMContentLoaded", () => {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipTriggerList.forEach(el => new bootstrap.Tooltip(el));
});
</script>
@@ -0,0 +1,217 @@
<?php require __DIR__ . '/../../includes/navBar.php'?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-8 col-lg-9 col-md-10">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-user-circle me-2"></i>
Datos del Proveedor
</h4>
<a href="?opt=proveedores" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
<!-- BODY -->
<div class="card-body p-4 bg-white">
<form id="form-editar-proveedor" method="POST" action="index.php?opt=editar_proveedor&id=<?= $proveedor['id'] ?>" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="row g-3">
<!-- RAZÓN SOCIAL -->
<div class="col-md-6">
<label class="form-label">Razón social</label>
<input type="text"
name="razon_social"
class="form-control"
value="<?= htmlspecialchars($proveedor['razon_social'] ?? '') ?>"
>
</div>
<!-- TELÉFONO -->
<div class="col-md-6">
<label class="form-label">Teléfono</label>
<input type="text"
name="telefono"
class="form-control"
value="<?= htmlspecialchars($proveedor['telefono'] ?? '') ?>"
>
</div>
<!-- EMAIL -->
<div class="col-md-6">
<label class="form-label">Email</label>
<input type="text"
name="email"
class="form-control"
value="<?= htmlspecialchars($proveedor['email'] ?? '') ?>"
>
</div>
<!-- CONDICIÓN IVA -->
<div class="col-md-6">
<label class="form-label">Condición IVA</label>
<select
name="cond_iva"
class="form-select">
<option
value="consumidor_final"
<?= ($proveedor['cond_iva'] ?? '') === 'consumidor_final' ? 'selected' : '' ?>>
Consumidor Final
</option>
<option
value="monotributo"
<?= ($proveedor['cond_iva'] ?? '') === 'monotributo' ? 'selected' : '' ?>>
Monotributo
</option>
<option
value="responsable_inscripto"
<?= ($proveedor['cond_iva'] ?? '') === 'responsable_inscripto' ? 'selected' : '' ?>>
Responsable Inscripto
</option>
</select>
</div>
<!-- CUIT -->
<div class="col-md-6">
<label class="form-label">CUIT</label>
<div class="d-flex gap-2">
<input
type="text"
name="cuit_1"
maxlength="2"
class="form-control text-center"
data-bs-toggle="tooltip"
title="Tipo de persona"
value="<?= htmlspecialchars(substr($proveedor['cuit'] ?? '', 0, 2)) ?>">
<input
type="text"
name="cuit_2"
maxlength="8"
class="form-control text-center"
data-bs-toggle="tooltip"
title="DNI"
value="<?= htmlspecialchars(substr($proveedor['cuit'] ?? '', 2, 8)) ?>">
<input
type="text"
name="cuit_3"
maxlength="1"
class="form-control text-center"
data-bs-toggle="tooltip"
title="Dígito verificador"
value="<?= htmlspecialchars(substr($proveedor['cuit'] ?? '', 10, 1)) ?>">
</div>
</div>
<!-- BOTON SUMBIT -->
<div class="d-flex justify-content-between mt-4 pt-3 border-top">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i> Guardar cambios
</button>
</div>
</div>
</form>
<!-- Errores provinientes de proveedorController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/proveedores/proveedorValidator.js"></script>
<!-- Pasar al siguiente input en CUIT -->
<script>
document.querySelectorAll("input[name^='cuit_']").forEach((input, index, inputs) => {
input.addEventListener("input", () => {
if (input.value.length === input.maxLength && inputs[index + 1]) {
inputs[index + 1].focus();
}
});
});
</script>
<!-- Tooltip -->
<script>
document.addEventListener("DOMContentLoaded", () => {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipTriggerList.forEach(el => new bootstrap.Tooltip(el));
});
</script>
@@ -0,0 +1,141 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center felx-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-truck icon-xl me-2"></i>
Gestión de Proveedores
</h4>
<div class="d-flex gap-2">
<a href="?opt=crear_proveedor" class="btn btn-success">
<i class="fas fa-plus me-1"></i>
Nuevo Proveedor
</a>
<a href="?opt=home_operador" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($proveedores)): ?>
<div class="text-center text-muted py-5">
No hay proveedores registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex align-items-center gap-2" id="filtrosIzquierda">
<select id="columnFilter"
class="form-select form-select-sm ms-2"
style="width: auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Razón social</option>
<option value="2">Teléfono</option>
<option value="3">Email</option>
</select>
</div>
<div class="table-responsive">
<table id="tablaProveedores" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Razón social</th>
<th>Teléfono</th>
<th>Email</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($proveedores as $p): ?>
<tr>
<td><?= $p['id'] ?? '' ?></td>
<td><?= htmlspecialchars($p['razon_social'] ?? '') ?></td>
<td><?= htmlspecialchars($p['telefono'] ?? '') ?></td>
<td><?= htmlspecialchars($p['email'] ?? '') ?></td>
<td class="text-center">
<div class="d-infline-flex align-items-center gap-2">
<!-- EDITAR -->
<a href="?opt=editar_proveedor&id=<?= $p['id'] ?>"
class="btn btn-primary btn-sm"
title="Editar Proveedor">
<i class="fas fa-pen fa-sm fa-fw"></i>
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- Errores provinientes de proveedorController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/proveedores/proveedorValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/proveedores/proveedorTableLista.js"></script>
@@ -0,0 +1,295 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0 card-viewport">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-wrench icon-xl me-2"></i>
Actualizar Servicio <?= htmlspecialchars($idServicio) ?>
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=listar_servicios" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<form method='POST' action="index.php?opt=actualizar_servicio&id= <?= $idServicio ?>" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="card-body p-4 d-flex flex-column card-body-scroll">
<!-- DETALLE DE LO REALIZADO -->
<div class="mb-4 flex-shrink-0">
<div class="row g-3">
<div class="col-md-12">
<label class="label-formventa mb-1">Detalle a Realizar</label>
<textarea class="form-control" rows="3"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Detalle del trabajo a realizar en el equipo"
name="detalle_realizado"><?= htmlspecialchars($detalleRealizado) ?></textarea>
</div>
</div>
</div>
<!-- LISTADO -->
<div class="d-flex justify-content-between align-items-center mb-2 flex-shrink-0">
<h6 class="text-muted mb-0">Listado de Insumos</h6>
<div class="ms-4 d-flex gap-2">
<button
type="submit"
formaction="index.php?opt=agregar_insumo&id=<?= $idServicio ?>"
formmethod="POST"
class="btn btn-success btn-sm">
<i class="fas fa-plus fa-fw me-1"></i>
Agregar Insumo
</button>
<button
type="button"
class="btn btn-danger btn-sm btnConfirmar"
title="Vaciar Lista"
data-action="vaciar_caja_servicio"
data-id="<?= $idServicio ?>"
data-detalle_realizado="<?= htmlspecialchars($detalleRealizado) ?>"
data-mano_obra="<?= $manoObra ?>"
data-mensaje="Todos los productos serán retirados de la lista, ¿estás seguro?"
data-titulo="Vaciar Lista"
data-bs-toggle="modal"
data-bs-target="#modalConfirmacion">
<i class="fas fa-times fa-fw me-1"></i>
Vaciar Lista
</button>
</div>
</div>
<!-- TABLA -->
<div class="table-scroll-crear my-2">
<table id="tablaInsumosServicio" class="table table-fixed table-hover align-middle mb-0 table-resumen-venta">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Producto</th>
<th class="text-left">Precio Unit.</th>
<th class="text-left">Cantidad</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php if (empty($insumos)): ?>
<tr>
<td colspan="6" class="text-center">-</td>
</tr>
<?php else: ?>
<?php foreach ($insumos as $i): ?>
<tr>
<td><?= $i['id_producto'] ?></td>
<td>
<?= htmlspecialchars($i['marca'] . ' ' . $i['modelo']) ?>
</td>
<td class="text-left">
$ <?= number_format($i['precio'], 2) ?>
</td>
<td class="text-left">
<input type="number"
name="cantidad[<?= $i['id_producto'] ?>]"
value="<?= $i['cantidad'] ?>"
min="1"
max="<?= $i['stock'] ?>"
class="form-control form-control-sm"
style="width: 60px;">
</td>
<td class="text-center">
<div class="d-inline-flex gap-2">
<button
type="submit"
name="id_producto"
value="<?= $i['id_producto'] ?>"
formaction="index.php?opt=editar_cantidad_insumo&id=<?= $idServicio ?>"
class="btn btn-primary btn-sm">
<i class="fas fa-pen fa-fw"></i>
</button>
<button
type="submit"
name="id_producto"
value="<?= $i['id_producto'] ?>"
formaction="?opt=eliminar_insumo&id=<?= $idServicio ?>"
class="btn btn-danger btn-sm">
<i class="fas fa-times fa-fw"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- TOTALES -->
<div class="flex-shrink-0 mt-3">
<div class="row g-3 align-items-end">
<div class="col-md-3">
<label class="label-formventa">Mano de Obra</label>
<div class="input-group input-moneda input-group-sm">
<span class="input-group-text">$</span>
<input class="form-control"
name="mano_obra"
value="<?= $manoObra ?>">
<button
type="submit"
formaction="index.php?opt=agregar_mano_obra&id= <?= $idServicio ?>"
formmethod="POST"
class="btn btn-primary">
<i class="fas fa-check"></i>
</button>
</div>
</div>
<div class="col-md-3">
<label class="label-formventa">Total</label>
<input class="form-control campo-readonly fw-semibold"
value="$ <?= number_format($total, 2) ?>"
readonly>
</div>
</div>
<div class="mt-3 text-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1 py-1"></i> Actualizar Servicio
</button>
</div>
</div>
<!-- Errores provinientes de servicioController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</form>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/servicios/servicioValidator.js"></script>
<!-- Evita que Chrome use BFCache -->
<script>
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
</script>
<!-- Tooltip -->
<script>
document.addEventListener('DOMContentLoaded', function () {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipTriggerList.forEach(el => {
const tooltip = new bootstrap.Tooltip(el);
let timeout;
el.addEventListener('shown.bs.tooltip', () => {
timeout = setTimeout(() => tooltip.hide(), 2000);
});
el.addEventListener('hide.bs.tooltip', () => {
clearTimeout(timeout);
});
});
});
</script>
@@ -0,0 +1,170 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-box-open icon-xl me-2"></i>
Selección de Insumos
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=actualizar_servicio&id=<?= $idServicio ?>" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($insumos)): ?>
<div class="text-center text-muted py-5">
No hay insumos registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex justify-content-between align-items-center mb-1">
<div id="filtrosIzquierda" class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm"
style="width:auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Marca</option>
<option value="2">Modelo</option>
</select>
</div>
</div>
<form method="POST" action="index.php?opt=agregar_insumo_caja&id=<?= $idServicio ?>" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="table-responsive">
<table id="tablaInsumos" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Marca</th>
<th>Modelo</th>
<th>Precio Unit</th>
<th>Stock</th>
<th>Seleccionar</th>
<th>Cantidad</th>
</tr>
</thead>
<tbody>
<?php foreach ($insumos as $i): ?>
<tr>
<td><?= $i['id'] ?? '' ?></td>
<td><?= htmlspecialchars($i['marca'] ?? '') ?></td>
<td><?= htmlspecialchars($i['modelo'] ?? '') ?></td>
<td>$ <?= number_format($i['precio'], 2, '.', ',') ?></td>
<td><?= htmlspecialchars($i['stock'] ?? '') ?></td>
<!-- CHECKBOX PARA SELECCIONAR PRODUCTO -->
<td class="text-left">
<input type="checkbox"
name="ids[]"
value="<?= $i['id'] ?>"
class="form-check-input">
</td>
<!-- INPUT DE CANTIDAD -->
<td class="text-center">
<input type="number"
name="cantidades[<?= $i['id'] ?>]"
value="1"
min="1"
max="<?= $i['stock'] ?>"
class="form-control form-control-sm"
style="width: 60px;">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- BOTÓN FINALIZAR SELECCIÓN -->
<div class="mt-3 text-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-check me-1"></i> Finalizar Selección
</button>
</div>
</form>
<?php endif; ?>
<!-- Errores provenientes de servicioController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/servicios/servicioValidator.js"></script>
<!-- Búsqueda y filtro por columnas -->
<script src="js/servicios/servicioTableInsumo.js"></script>
+292
View File
@@ -0,0 +1,292 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0 card-viewport">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-wrench icon-xl me-2"></i>
Nuevo Servicio
</h4>
<div class="d-flex gap-2">
<a href="?opt=listar_servicios" class="btn btn-primary">
<i class="fas fa-folder-open me-1"></i>
Servicios Registrados
</a>
<a href="?opt=crear_cliente&from=crear_servicio" class="btn btn-success">
<i class="fas fa-plus me-1"></i>
Nuevo Cliente
</a>
<a href="?opt=home_operador" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<form id="form_crear_servicio" method="POST" action="index.php?opt=guardar_servicio" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="card-body p-4 d-flex flex-column card-body-scroll">
<!-- CLIENTE -->
<div class="mb-4 flex-shrink-0">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="text-muted mb-0">Cliente</h6>
<div class="ms-4 d-flex gap-2">
<a href="?opt=seleccionar_cliente_servicio" class="btn btn-primary btn-sm">
<i class="fas fa-user fa-fw me-1"></i>
Seleccionar Cliente
</a>
<button
type="submit"
formaction="?opt=eliminar_cliente_servicio"
formmethod="POST"
class="btn btn-danger btn-sm">
<i class="fas fa-times fa-fw me-1"></i>
Eliminar Cliente
</button>
</div>
</div>
<div class="row g-3">
<div class="col-md-3">
<label class="label-formventa mb-1">ID</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($cliente['id'] ?? '') ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa mb-1">Nombre y Apellido</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars(($cliente['nombre'] ?? '') . ' ' . ($cliente['apellido'] ?? '')) ?>">
</div>
<div class="col-md-3">
<label class="label-formventa mb-1">Teléfono</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($cliente['telefono'] ?? '') ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa mb-1">Email</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($cliente['email'] ?? '') ?>" readonly>
</div>
</div>
</div>
<!-- DESCRIPCION DEL EQUIPO + CUENTAS Y CONTRASEÑAS -->
<div class="mb-4 flex-shrink-0">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="text-muted mb-1">Datos del Servicio</h6>
</div>
<div class="row g-3">
<div class="col-md-3">
<label class="label-formventa mb-1">
Descripción del Equipo
</label>
<input
class="form-control"
name="descripcion_equipo"
list="equipos_sugeridos"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Marca y modelo del equipo"
value="<?= htmlspecialchars($datosServicio['descripcion_equipo'] ?? '') ?>">
<datalist id="equipos_sugeridos">
<?php foreach ($productos as $producto): ?>
<option value="<?= htmlspecialchars($producto['marca'] . ' ' . $producto['modelo']) ?>">
<?php endforeach; ?>
</datalist>
</div>
<div class="col-md-3">
<label class="label-formventa mb-1">Contraseña del Equipo</label>
<input class="form-control" name="contrasenia_equipo"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Ingresar solo si corresponde"
value="<?= htmlspecialchars(($datosServicio['contrasenia_equipo'] ?? '')) ?>">
</div>
<div class="col-md-3">
<label class="label-formventa mb-1">Cuenta Gmail/Apple</label>
<input class="form-control" name="cuenta"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Ingresar solo si corresponde"
value="<?= htmlspecialchars($datosServicio['cuenta'] ?? '') ?>">
</div>
<div class="col-md-3">
<label class="label-formventa mb-1">Contraseña Gmail/Apple</label>
<input class="form-control" name="contrasenia_cuenta"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Ingresar solo si corresponde"
value="<?= htmlspecialchars($datosServicio['contrasenia_cuenta'] ?? '') ?>">
</div>
</div>
</div>
<!-- DESCRIPCION DEL PROBLEMA -->
<div class="mb-4 flex-shrink-0">
<div class="row g-3">
<div class="col-md-12">
<label class="label-formventa mb-1">Descripcion Del Problema</label>
<textarea class="form-control" rows="3"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Detalle el problema o lo que se debe realizar en el equipo"
name="descripcion_problema"><?= htmlspecialchars($datosServicio['descripcion_problema'] ?? '') ?></textarea>
</div>
</div>
</div>
<!-- OBSERVACIONES EXTRA -->
<div class="flex-shrink-0 mt-0">
<div class="row g-3">
<div class="col-md-12">
<label class="label-formventa mb-1">Observaciones/Detalles</label>
<textarea class="form-control" rows="3"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Ingrese cualquier detalle extra que considere importante"
name="observacion"><?= htmlspecialchars($datosServicio['observacion'] ?? '') ?></textarea>
</div>
</div>
<div class="mt-4 text-end">
<button
type="submit"
formaction="index.php?opt=generar_servicio_pdf"
formmethod="POST"
class="btn btn-success py-2">
<i class="fas fa-file-alt me-1"></i>
Generar Servicio
</button>
<button type="submit" class="btn btn-primary py-2">
<i class="fas fa-save me-1"></i> Confirmar Pedido
</button>
</div>
</div>
</div>
</form>
<!-- Errores provinientes de servicioController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/servicios/servicioValidator.js"></script>
<!-- Evita que Chrome use BFCache -->
<script>
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
</script>
<!-- Tooltip -->
<script>
document.addEventListener('DOMContentLoaded', function () {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipTriggerList.forEach(el => {
const tooltip = new bootstrap.Tooltip(el);
let timeout;
el.addEventListener('shown.bs.tooltip', () => {
timeout = setTimeout(() => tooltip.hide(), 2000);
});
el.addEventListener('hide.bs.tooltip', () => {
clearTimeout(timeout);
});
});
});
</script>
@@ -0,0 +1,95 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-wrench icon-xl me-2"></i>
Insumos de Servicio <?= htmlspecialchars($idServicio) ?>
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=listar_servicios" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($servicio_detalle)): ?>
<div class="text-center text-muted py-5">
No existe el detalle del servicio o no se utilizaron insumos.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm ms-2"
style="width: auto;">
<option value="0">ID Insumo</option>
<option value="1">Insumo</option>
</select>
</div>
<div class="table-responsive">
<table id="tablaDetalleVenta" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Insumo</th>
<th>Precio Unit.</th>
<th>Cantidad</th>
<th>Subtotal</th>
<th>ID Pedido</th>
</tr>
</thead>
<tbody>
<?php foreach ($servicio_detalle as $sd): ?>
<tr>
<td><?= $sd['id_insumo'] ?? '' ?></td>
<td><?= htmlspecialchars($sd['marca'] . ' ' . $sd['modelo']) ?? '' ?></td>
<td>$ <?= number_format($sd['precio_unitario'], 2, '.', ',') ?></td>
<td><?= htmlspecialchars($sd['cantidad'] ?? '') ?></td>
<td>$ <?= number_format($sd['subtotal'], 2, '.', ',') ?></td>
<td><?= htmlspecialchars($sd['id_pedido'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/servicios/servicioTableDetalleInsumos.js"></script>
+181
View File
@@ -0,0 +1,181 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0 card-viewport">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-wrench icon-xl me-2"></i>
Detalle de Servicio <?= htmlspecialchars($idServicio) ?>
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=listar_servicios" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body p-4 d-flex flex-column card-body-scroll">
<!-- DETALLES DEL EQUIPO -->
<div class="mb-4 flex-shrink-0">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="text-muted mb-1">Datos del Servicio</h6>
</div>
<div class="row g-3">
<div class="col-md-3">
<label class="label-formventa mb-1 fw-semibold">Descripcion del Equipo</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($datosServicio['equipo'] ?? '') ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa mb-1 fw-semibold">Contraseña del Equipo</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars(($datosServicio['contrasenia_equipo'] ?? '')) ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa mb-1 fw-semibold">Cuenta Gmail/Apple</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($datosServicio['cuenta'] ?? '') ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa mb-1 fw-semibold">Contraseña Gmail/Apple</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($datosServicio['contrasenia_cuenta'] ?? '') ?>" readonly>
</div>
</div>
</div>
<!-- DESCRIPCION DEL PROBLEMA -->
<div class="mb-4 flex-shrink-0">
<div class="row g-3">
<div class="col-md-12">
<label class="label-formventa mb-1 fw-semibold">Descripcion Del Problema</label>
<textarea class="form-control campo-readonly"
rows="3" readonly
><?= htmlspecialchars($datosServicio['problema'] ?? '') ?> </textarea>
</div>
</div>
</div>
<!-- OBSERVACIONES EXTRA -->
<div class="mb-4 flex-shrink-0">
<div class="row g-3">
<div class="col-md-12">
<label class="label-formventa mb-1 fw-semibold">Observaciones/Detalles</label>
<textarea class="form-control campo-readonly"
rows="2" readonly
><?= htmlspecialchars($datosServicio['observacion'] ?? '') ?></textarea>
</div>
</div>
</div>
<!-- DETALLE DE LO REALIZADO -->
<div class="mb-4 flex-shrink-0">
<div class="row g-3">
<div class="col-md-12">
<label class="label-formventa mb-1 fw-semibold">Detalle de lo Realizado</label>
<textarea class="form-control campo-readonly"
rows="3" readonly
><?= htmlspecialchars($datosServicio['detalle_realizado']) ?></textarea>
</div>
</div>
</div>
<!-- DATOS FINALES -->
<div class="mb-2 flex-shrink-0">
<div class="row g-3 align-items-end">
<div class="col-md-3">
<label class="label-formventa fw-semibold">Mano de Obra</label>
<input class="form-control campo-readonly fw-semibold"
value="$ <?= number_format($datosServicio['mano_obra'], 2) ?>"
readonly>
</div>
<div class="col-md-3">
<label class="label-formventa fw-semibold">Total</label>
<input class="form-control campo-readonly fw-semibold"
value="$ <?= number_format($datosServicio['total'], 2) ?>"
readonly>
</div>
</div>
</div>
<!-- Errores provinientes de servicioController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/servicios/servicioValidator.js"></script>
+291
View File
@@ -0,0 +1,291 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<!-- IZQUIERDA -->
<div class="d-flex align-items-center gap-4">
<h4 class="mb-0 tittle-shadow">
<i class="fas fa-folder-open icon-xl me-2"></i> Listado de Servicios
</h4>
<div class="d-flex align-items-center gap-3">
<!-- AÑO -->
<form method="GET" action="index.php" class="d-flex align-items-center gap-2 select-group mb-0">
<input type="hidden" name="opt" value="listar_servicios">
<i class="fas fa-calendar-alt text-light"></i>
<select name="anio" class="form-select custom-select select-informe" onchange="this.form.submit()">
<?php for ($i = $anioActual; $i >= 2020; $i--): ?>
<option value="<?= $i ?>" <?= $i == $anioSeleccionado ? 'selected' : '' ?>>
<?= $i ?>
</option>
<?php endfor; ?>
</select>
</form>
</div>
</div>
<!-- DERECHA -->
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=crear_servicio" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($servicios)): ?>
<div class="text-center text-muted py-5">
No hay servicios registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm ms-2"
style="width: auto;">
<option value="0">ID Servicio</option>
<option value="1">Fecha Registro</option>
<option value="2">Fecha Finalizado</option>
<option value="3">Equipo</option>
<option value="4">Estado</option>
<option value="5">Cliente</option>
<option value="6">Operador</option>
</select>
</div>
<div class="table-responsive">
<table id="tablaServicios" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>F. Registro</th>
<th>F. Finalizado</th>
<th>Equipo</th>
<th>Estado</th>
<th>Cliente</th>
<th>Operador</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($servicios as $s): ?>
<tr>
<td><?= $s['id'] ?></td>
<td data-order="<?= $s['fecha_registro'] ?>">
<?= htmlspecialchars($s['fecha_formateada']) ?>
</td>
<td data-order="<?= $s['fecha_terminado'] ?>">
<?= htmlspecialchars($s['fecha_formateada_term']) ?>
</td>
<td><?= htmlspecialchars($s['equipo']) ?></td>
<td><?= htmlspecialchars($s['estado']) ?></td>
<td><?= htmlspecialchars($s['cliente_nombre'] . ' ' . $s['cliente_apellido']) ?></td>
<td><?= htmlspecialchars($s['vendedor_nombre'] . ' ' . $s['vendedor_apellido']) ?></td>
<td class="text-center">
<div class="d-inline-flex align-items-center gap-2">
<!-- BOTONES SEGÚN ESTADO DE SERVICIO -->
<?php if ($s['estado'] === 'Pendiente'): ?>
<!-- DETALLE DEL SERVICIO -->
<a href="?opt=detalle_servicio&id=<?= $s['id'] ?>"
class="btn btn-primary btn-sm"
title="Detalle Servicio">
<i class="fas fa-file-alt fa-sm fa-fw"></i>
</a>
<!-- ACTUALIZAR SERVICIO -->
<?php if ($esAdmin): ?>
<a href="?opt=resetear_datos_servicio&id=<?= $s['id'] ?>"
class="btn btn-success btn-sm"
title="Actualizar Servicio">
<i class="fas fa-edit fa-sm fa-fw"></i>
</a>
<?php endif; ?>
<?php elseif ($s['estado'] === 'Pendiente - Enviado'): ?>
<!-- DETALLE DEL SERVICIO -->
<a href="?opt=detalle_servicio&id=<?= $s['id'] ?>"
class="btn btn-primary btn-sm"
title="Detalle Servicio">
<i class="fas fa-file-alt fa-sm fa-fw"></i>
</a>
<!-- DETALLE INSUMOS UTILIZADOS -->
<a href="?opt=detalle_insumos_servicio&id=<?= $s['id'] ?>"
class="btn btn-primary btn-sm"
title="Detalle Insumos">
<i class="fas fa-box-open fa-sm fa-fw"></i>
</a>
<!-- FINALIZAR SERVICIO -->
<?php if ($esAdmin): ?>
<button
class="btn btn-success btn-sm btnConfirmar"
title="Finalizar Servicio"
data-action="finalizar_servicio"
data-id="<?= $s['id'] ?>"
data-mensaje="¿Estás seguro que deseas finalizar este servicio?"
data-titulo="Finalizar Servicio"
data-bs-toggle="modal"
data-bs-target="#modalConfirmacion">
<i class="fas fa-check fa-sm fa-fw"></i>
</button>
<?php endif; ?>
<!-- CANCELAR SERVICIO -->
<?php if ($esAdmin): ?>
<button
class="btn btn-danger btn-sm btnConfirmar"
title="Cancelar Servicio"
data-action="cancelar_servicio"
data-id="<?= $s['id'] ?>"
data-mensaje="¿Estás seguro de que deseas cancelar este servicio?"
data-titulo="Cancelar Servicio"
data-bs-toggle="modal"
data-bs-target="#modalConfirmacion">
<i class="fas fa-times fa-fw"></i>
</button>
<?php endif; ?>
<?php elseif ($s['estado'] === 'Finalizado'): ?>
<!-- DETALLE DEL SERVICIO -->
<a href="?opt=detalle_servicio&id=<?= $s['id'] ?>"
class="btn btn-primary btn-sm"
title="Detalle Servicio">
<i class="fas fa-file-alt fa-sm fa-fw"></i>
</a>
<!-- DETALLE INSUMOS UTILIZADOS -->
<a href="?opt=detalle_insumos_servicio&id=<?= $s['id'] ?>"
class="btn btn-primary btn-sm"
title="Detalle Insumos">
<i class="fas fa-box-open fa-sm fa-fw"></i>
</a>
<?php elseif ($s['estado'] === 'Cancelado'): ?>
<!-- DETALLE SERVICIO -->
<a href="?opt=detalle_servicio&id=<?= $s['id'] ?>"
class="btn btn-primary btn-sm"
title="Detalle Servicio">
<i class="fas fa-file-alt fa-sm fa-fw"></i>
</a>
<!-- DETALLE INSUMOS UTILIZADOS -->
<a href="?opt=detalle_insumos_servicio&id=<?= $s['id'] ?>"
class="btn btn-primary btn-sm"
title="Detalle Insumos">
<i class="fas fa-box-open fa-sm fa-fw"></i>
</a>
<?php endif; ?>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- Errores provenientes de servicioController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/servicios/servicioValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/servicios/servicioTableLista.js"></script>
@@ -0,0 +1,162 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-users icon-xl me-2"></i>
Selección de Cliente
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=crear_servicio" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($clientes)): ?>
<div class="text-center text-muted py-5">
No hay clientes registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex justify-content-between align-items-center mb-1">
<div id="filtrosIzquierda" class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm"
style="width:auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Nombre</option>
<option value="2">Apellido</option>
<option value="3">Teléfono</option>
<option value="4">Email</option>
</select>
</div>
</div>
<form method="POST" action="index.php?opt=seleccionar_cliente_servicio" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="table-responsive">
<table id="tablaClientes" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Apellido</th>
<th>Teléfono</th>
<th>Email</th>
<th>Seleccionar</th>
</tr>
</thead>
<tbody>
<?php foreach ($clientes as $c): ?>
<tr>
<td><?= $c['id'] ?? '' ?></td>
<td><?= htmlspecialchars($c['nombre'] ?? '') ?></td>
<td><?= htmlspecialchars($c['apellido'] ?? '') ?></td>
<td><?= htmlspecialchars($c['telefono'] ?? '') ?></td>
<td><?= htmlspecialchars($c['email'] ?? '') ?></td>
<!-- CHECKBOX PARA SELECCIONAR CLIENTE -->
<td class="text-left">
<input type="checkbox"
name="id_cliente"
value="<?= $c['id'] ?>"
class="form-check-input">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- BOTÓN FINALIZAR SELECCIÓN -->
<div class="mt-3 text-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-check me-1"></i> Finalizar
</button>
</div>
</form>
<?php endif; ?>
<!-- Errores provenientes de servicioController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/servicios/servicioValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/servicios/servicioTableCliente.js"></script>
+140
View File
@@ -0,0 +1,140 @@
<?php require __DIR__ . '/../../includes/navbar.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-8 col-lg-9 col-md-10">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-user-plus me-2"></i>
Crear Usuario
</h4>
<a href="?opt=usuarios" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i> Volver
</a>
</div>
</div>
<!-- BODY -->
<div class="card-body p-4 bg-white">
<form id="form-crear-usuario" method="POST" action="?opt=crear_usuario" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="row g-3">
<!-- NOMBRE -->
<div class="col-md-6">
<label class="form-label">Nombre</label>
<input type="text" name="nombre" class="form-control">
</div>
<!-- APELLIDO -->
<div class="col-md-6">
<label class="form-label">Apellido</label>
<input type="text" name="apellido" class="form-control">
</div>
<!-- EMAIL -->
<div class="col-md-6">
<label class="form-label">Email</label>
<input type="email" name="email" class="form-control">
</div>
<!-- TELÉFONO -->
<div class="col-md-6">
<label class="form-label">Teléfono</label>
<input type="text" name="telefono" class="form-control">
</div>
<!-- USUARIO -->
<div class="col-md-6">
<label class="form-label">Usuario</label>
<input type="text" name="usuario" class="form-control">
</div>
<!-- PASSWORD -->
<div class="col-md-6">
<label class="form-label">Contraseña</label>
<input type="password" name="password" class="form-control">
</div>
<!-- ROL -->
<div class="col-md-6">
<label class="form-label">Rol</label>
<select name="rol" class="form-select">
<option value="operador">Operador</option>
<option value="admin">Administrador</option>
</select>
</div>
</div>
<!-- BOTON SUBMIT -->
<div class="d-flex justify-content-between mt-4 pt-3 border-top">
<button type="submit" class="btn btn-success">
<i class="fas fa-user-plus me-1"></i> Crear Usuario
</button>
</div>
</form>
<!-- Errores provinientes de usuarioController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/usuarios/usuarioValidator.js"></script>
+232
View File
@@ -0,0 +1,232 @@
<?php require __DIR__ . '/../../includes/navbar.php'; ?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-xl-8 col-lg-9 col-md-10">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-user-circle me-2"></i>
Datos del Usuario
</h4>
<a href="index.php?opt=<?= htmlspecialchars($_SESSION['return_opt']) ?>" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
<!-- BODY -->
<div class="card-body p-4 bg-white">
<!-- MENSAJE SI ES OPERADOR -->
<?php if (!$esAdmin): ?>
<div class="alert alert-info d-flex align-items-center mb-4" role="alert">
<i class="fas fa-info-circle me-3 fs-5"></i>
<div>
Para modificar sus datos personales, por favor comuníquese con un administrador.
</div>
</div>
<?php endif; ?>
<form id="form-datos-usuario" method="POST" action="?opt=<?= 'datos_usuario&id=' . $usuario['id'] ?>" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="row g-3">
<!-- NOMBRE -->
<div class="col-md-6">
<label class="form-label">Nombre</label>
<input
type="text"
class="form-control"
name="nombre"
value="<?= htmlspecialchars($usuario['nombre']) ?>"
<?= $esAdmin ? '' : 'readonly' ?>
>
</div>
<!-- APELLIDO -->
<div class="col-md-6">
<label class="form-label">Apellido</label>
<input
type="text"
class="form-control"
name="apellido"
value="<?= htmlspecialchars($usuario['apellido']) ?>"
<?= $esAdmin ? '' : 'readonly' ?>
>
</div>
<!-- EMAIL -->
<div class="col-md-6">
<label class="form-label">Email</label>
<input
type="email"
class="form-control"
name="email"
value="<?= htmlspecialchars($usuario['email']) ?>"
<?= $esAdmin ? '' : 'readonly' ?>
>
</div>
<!-- TELÉFONO -->
<div class="col-md-6">
<label class="form-label">Teléfono</label>
<input
type="text"
class="form-control"
name="telefono"
value="<?= htmlspecialchars($usuario['telefono']) ?>"
<?= $esAdmin ? '' : 'readonly' ?>
>
</div>
<!-- USUARIO -->
<div class="col-md-6">
<label class="form-label">Usuario</label>
<input
type="text"
class="form-control"
value="<?= htmlspecialchars($usuario['usuario']) ?>"
readonly
>
<?php if ($esAdmin): ?>
<small class="text-muted">
El nombre de usuario no puede modificarse
</small>
<?php endif; ?>
</div>
<!-- PASSWORD -->
<div class="col-6">
<label class="form-label">Contraseña</label>
<input
type="password"
class="form-control"
name="password"
placeholder="No es mostrada por motivos de seguridad"
<?= $esAdmin ? '' : 'readonly' ?>
>
<?php if ($esAdmin): ?>
<small class="text-muted">
Si no desea modificarla, dejar la casilla en blanco
</small>
<?php endif; ?>
</div>
<!-- ROL -->
<div class="col-md-6">
<label class="form-label">Rol</label>
<select name="rol" id="rolSelect" class="form-select"
<?= $_SESSION['user']['rol'] === 'operador' ? 'disabled' : '' ?>>
<option value="operador" <?= $usuario['rol'] === 'operador' ? 'selected' : '' ?>>
Operador
</option>
<option value="admin" <?= $usuario['rol'] === 'admin' ? 'selected' : '' ?>>
Administrador
</option>
</select>
</div>
</div>
<!-- BOTON SUBMIT -->
<div class="d-flex justify-content-between mt-4 pt-3 border-top">
<?php if ($esAdmin): ?>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i> Guardar cambios
</button>
<?php endif; ?>
</div>
</form>
<!-- Errores provinientes de usuarioController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</div>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/usuarios/usuarioValidator.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const readonlyInputs = document.querySelectorAll('input[readonly], textarea[readonly]');
readonlyInputs.forEach(el => {
el.classList.add('campo-readonly');
});
});
</script>
+177
View File
@@ -0,0 +1,177 @@
<?php require __DIR__ . '/../../includes/navbar.php'; ?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-users-cog icon-xl me-2"></i>
Gestión de Usuarios
</h4>
<div class="d-flex gap-2">
<a href="?opt=crear_usuario" class="btn btn-success">
<i class="fas fa-plus me-1"></i>
Nuevo Usuario
</a>
<a href="?opt=inicio_admin" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($usuarios)): ?>
<div class="text-center text-muted py-5">
No hay usuarios registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm ms-2"
style="width: auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Nombre</option>
<option value="2">Apellido</option>
<option value="3">Email</option>
<option value="4">Teléfono</option>
<option value="5">Usuario</option>
<option value="6">Rol</option>
</select>
</div>
<div class="table-responsive">
<table id="tablaUsuarios" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Apellido</th>
<th>Email</th>
<th>Teléfono</th>
<th>Usuario</th>
<th>Rol</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($usuarios as $u): ?>
<tr>
<td><?= $u['id'] ?? '' ?></td>
<td><?= htmlspecialchars($u['nombre'] ?? '') ?></td>
<td><?= htmlspecialchars($u['apellido'] ?? '') ?></td>
<td><?= htmlspecialchars($u['email'] ?? '') ?></td>
<td><?= htmlspecialchars($u['telefono'] ?? '-') ?></td>
<td><?= htmlspecialchars($u['usuario'] ?? '') ?></td>
<td>
<?php if ($u['rol'] === 'admin'): ?>
<span class="badge bg-danger">Admin</span>
<?php else: ?>
<span class="badge bg-secondary">Operador</span>
<?php endif; ?>
</td>
<td class="text-center">
<div class="d-inline-flex align-items-center gap-2">
<!-- EDITAR -->
<a href="?opt=datos_usuario&id=<?= $u['id'] ?>&from=usuarios"
class="btn btn-primary btn-sm"
title="Editar Usuario">
<i class="fas fa-pen fa-sm fa-fw"></i>
</a>
<!-- ELIMINAR USUARIO (BAJA LÓGICA) -->
<button
class="btn btn-danger btn-sm btnConfirmar"
title="Eliminar Usuario"
data-action="eliminar_usuario"
data-id="<?= $u['id'] ?>"
data-mensaje="¿Seguro que quieres eliminar el usuario?"
data-titulo="Eliminar Usuario"
data-bs-toggle="modal"
data-bs-target="#modalConfirmacion">
<i class="fas fa-user-slash fa-sm fa-fw"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- Errores provinientes de usuarioController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/usuarios/usuarioValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/usuarios/usuarioTableLista.js"></script>
+172
View File
@@ -0,0 +1,172 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-box-open icon-xl me-2"></i>
Selección de Productos
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=crear_venta" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($productos)): ?>
<div class="text-center text-muted py-5">
No hay productos registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex justify-content-between align-items-center mb-1">
<div id="filtrosIzquierda" class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm"
style="width:auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Marca</option>
<option value="2">Modelo</option>
</select>
</div>
</div>
<form method="POST" action="index.php?opt=agregar_producto_caja" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="table-responsive">
<table id="tablaProductos" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Marca</th>
<th>Modelo</th>
<th>Precio Unit. Fin.</th>
<th>Descuento Tot. (%)</th>
<th>Stock</th>
<th>Seleccionar</th>
<th>Cantidad</th>
</tr>
</thead>
<tbody>
<?php foreach ($productos as $p): ?>
<tr>
<td><?= $p['id'] ?? '' ?></td>
<td><?= htmlspecialchars($p['marca'] ?? '') ?></td>
<td><?= htmlspecialchars($p['modelo'] ?? '') ?></td>
<td>$ <?= number_format($p['precio_final'], 2, '.', ',') ?></td>
<td><?= htmlspecialchars($p['descuento_total'] ?? '') ?></td>
<td><?= htmlspecialchars($p['stock'] ?? '') ?></td>
<!-- CHECKBOX PARA SELECCIONAR PRODUCTO -->
<td class="text-left">
<input type="checkbox"
name="ids[]"
value="<?= $p['id'] ?>"
class="form-check-input">
</td>
<!-- INPUT DE CANTIDAD -->
<td class="text-center">
<input type="number"
name="cantidades[<?= $p['id'] ?>]"
value="1"
min="1"
max="<?= $p['stock'] ?>"
class="form-control form-control-sm"
style="width: 60px;">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- BOTÓN FINALIZAR SELECCIÓN -->
<div class="mt-3 text-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-check me-1"></i> Finalizar Selección
</button>
</div>
</form>
<?php endif; ?>
<!-- Errores provenientes de ventaController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/ventas/ventaValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/ventas/ventaTableProducto.js"></script>
+269
View File
@@ -0,0 +1,269 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0 card-viewport">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-shopping-cart icon-xl me-2"></i>
Nueva Venta
</h4>
<div class="d-flex gap-2">
<a href="?opt=listar_ventas" class="btn btn-primary">
<i class="fas fa-folder-open me-1"></i>
Ventas Registradas
</a>
<a href="?opt=crear_cliente&from=crear_venta" class="btn btn-success">
<i class="fas fa-plus me-1"></i>
Nuevo Cliente
</a>
<a href="?opt=home_operador" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<div class="card-body p-4 d-flex flex-column card-body-scroll">
<!-- CLIENTE -->
<div class="mb-4 flex-shrink-0">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="text-muted mb-0">Cliente</h6>
<div class="ms-4 d-flex gap-2">
<a href="?opt=seleccionar_cliente" class="btn btn-primary btn-sm">
<i class="fas fa-user fa-fw me-1"></i>
Seleccionar Cliente
</a>
<form method="POST" action="?opt=eliminar_cliente" class="d-inline">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<button class="btn btn-danger btn-sm">
<i class="fas fa-times fa-fw me-1"></i>
Eliminar Cliente
</button>
</form>
</div>
</div>
<div class="row g-3">
<div class="col-md-3">
<label class="label-formventa mb-1">ID</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($cliente['id'] ?? '') ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa mb-1">Nombre y Apellido</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars(($cliente['nombre'] ?? '') . ' ' . ($cliente['apellido'] ?? '')) ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa mb-1">Teléfono</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($cliente['telefono'] ?? '') ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa mb-1">Email</label>
<input class="form-control campo-readonly"
value="<?= htmlspecialchars($cliente['email'] ?? '') ?>" readonly>
</div>
</div>
</div>
<!-- LISTADO -->
<div class="d-flex justify-content-between align-items-center mb-2 flex-shrink-0">
<h6 class="text-muted mb-0">Listado de Productos</h6>
<div class="ms-4 d-flex gap-2">
<a href="?opt=agregar_producto" class="btn btn-success btn-sm">
<i class="fas fa-plus fa-fw me-1"></i>
Agregar Producto
</a>
<button
type="button"
class="btn btn-danger btn-sm btnConfirmar"
title="Vaciar Lista"
data-action="vaciar_caja_venta"
data-mensaje="Todos los productos serán retirados de la lista, ¿estás seguro?"
data-titulo="Vaciar Lista"
data-bs-toggle="modal"
data-bs-target="#modalConfirmacion">
<i class="fas fa-times fa-fw me-1"></i>
Vaciar Lista
</button>
</div>
</div>
<!-- TABLA -->
<div class="table-scroll-crear my-2">
<table id="tablaProductosVenta" class="table table-fixed table-hover align-middle mb-0 table-resumen-venta">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Producto</th>
<th class="text-left">Precio Unit. Fin.</th>
<th class="text-left">Descuento (%)</th>
<th class="text-left">Cantidad</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php if (empty($productos)): ?>
<tr>
<td colspan="6" class="text-center">-</td>
</tr>
<?php else: ?>
<?php foreach ($productos as $p): ?>
<tr>
<form method="POST" action="index.php?opt=editar_cantidad" novalidate>
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="id_producto" value="<?= $p['id_producto'] ?>">
<td><?= $p['id_producto'] ?></td>
<td>
<?= htmlspecialchars($p['marca'] . ' ' . $p['modelo']) ?>
</td>
<td>$ <?= number_format($p['precio_final'], 2) ?></td>
<td><?= htmlspecialchars($p['descuento_total']) ?> %</td>
<td>
<input type="number"
name="cantidad"
value="<?= $p['cantidad'] ?>"
min="1"
max="<?= $p['stock'] ?>"
class="form-control form-control-sm"
style="width: 60px;">
</td>
<td class="text-center">
<button type="submit" class="btn btn-primary btn-sm">
<i class="fas fa-pen fa-fw"></i>
</button>
<button
type="submit"
formaction="?opt=eliminar_producto"
class="btn btn-danger btn-sm">
<i class="fas fa-times fa-fw"></i>
</button>
</td>
</form>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- TOTALES -->
<div class="flex-shrink-0 mt-3">
<h6 class="text-muted mb-3">Datos de Venta</h6>
<div class="row g-3">
<div class="col-md-3">
<label class="label-formventa">Subtotal</label>
<input class="form-control campo-readonly"
value="$ <?= number_format($subtotal, 2) ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa">Descuento</label>
<input class="form-control campo-readonly"
value="$ <?= number_format($descuento, 2) ?>" readonly>
</div>
<div class="col-md-3">
<label class="label-formventa">Total</label>
<input class="form-control campo-readonly fw-semibold"
value="$ <?= number_format($total, 2) ?>" readonly>
</div>
</div>
<div class="mt-3 text-end">
<a href="?opt=resumen_venta" class="btn btn-primary py-2">
Siguiente
<i class="fas fa-arrow-right ms-1"></i>
</a>
</div>
</div>
<!-- Errores provinientes de ventaController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/ventas/ventaValidator.js"></script>
+107
View File
@@ -0,0 +1,107 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card card-rounded card-shadow border-0">
<div class="card-header header-dark px-4 py-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-file-alt me-2"></i>
Venta Confirmada
</h4>
</div>
<div class="card-body text-center p-5">
<i class="fas fa-check-circle text-success fa-4x mb-4"></i>
<h4>Venta guardada correctamente</h4>
<p class="text-muted">
Comprobante
<strong><?= $numeroVenta ?></strong>
</p>
<hr>
<p>
¿Desea descargar el comprobante generado?
</p>
<div class="d-flex justify-content-center gap-3 mt-5">
<form method="POST" action="?opt=generar_venta_pdf">
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<button class="btn btn-success">
<i class="fas fa-download me-1 my-2"></i>
Descargar Comprobante
</button>
</form>
<a href="?opt=continuar_sin_comprobante" class="btn btn-secondary">
<i class="fas fa-arrow-right me-1 my-2"></i>
Continuar
</a>
</div>
</div>
</div>
</div>
</div>
<!-- Errores provenients de ventaController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
<!-- Validar mediante js -->
<script src="js/ventas/ventaValidator.js"></script>
<!-- Evita que Chrome use BFCache -->
<script>
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
</script>
+180
View File
@@ -0,0 +1,180 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<!-- IZQUIERDA -->
<div class="d-flex align-items-center gap-4">
<h4 class="mb-0 tittle-shadow">
<i class="fas fa-folder-open icon-xl me-2"></i> Listado de Ventas
</h4>
<div class="d-flex align-items-center gap-3">
<!-- AÑO -->
<form method="GET" action="index.php" class="d-flex align-items-center gap-2 select-group mb-0">
<input type="hidden" name="opt" value="listar_ventas">
<i class="fas fa-calendar-alt text-light"></i>
<select name="anio" class="form-select custom-select select-informe" onchange="this.form.submit()">
<?php for ($i = $anioActual; $i >= 2020; $i--): ?>
<option value="<?= $i ?>" <?= $i == $anioSeleccionado ? 'selected' : '' ?>>
<?= $i ?>
</option>
<?php endfor; ?>
</select>
</form>
</div>
</div>
<!-- DERECHA -->
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=crear_venta" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($ventas)): ?>
<div class="text-center text-muted py-5">
No hay ventas registradas.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm ms-2"
style="width: auto;">
<option value="0">ID Venta</option>
<option value="1">Fecha</option>
<option value="6">Cliente</option>
<option value="7">Operador</option>
</select>
</div>
<div class="table-responsive">
<table id="tablaVentas" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Fecha</th>
<th>Total</th>
<th>Descuento (%)</th>
<th>Recargo (%)</th>
<th>Forma de Pago</th>
<th>Cliente</th>
<th>Operador</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($ventas as $v): ?>
<tr>
<td><?= $v['id'] ?></td>
<td data-order="<?= $v['fecha'] ?>">
<?= htmlspecialchars($v['fecha_formateada']) ?>
</td>
<td>$ <?= number_format($v['total'], 2, '.', ',') ?></td>
<td><?= number_format($v['descuento_pct'], 2, '.', ',') ?> %</td>
<td><?= number_format($v['recargo_pct'], 2, '.', ',') ?> %</td>
<td><?= htmlspecialchars($v['forma_pago']) . ' (' . $v['tipo_pago'] ?>)</td>
<td><?= htmlspecialchars($v['cliente_nombre'] . ' ' . $v['cliente_apellido']) ?></td>
<td><?= htmlspecialchars($v['vendedor_nombre'] . ' ' . $v['vendedor_apellido']) ?></td>
<td class="text-center">
<div class="d-inline-flex align-items-center gap-2">
<!-- VER DETALLE PRODUCTOS -->
<a href="?opt=detalle_venta&id=<?= $v['id'] ?>"
class="btn btn-primary btn-sm"
title="Detalle Productos">
<i class="fas fa-box-open fa-sm fa-fw"></i>
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- Errores provenientes de ventaController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/ventas/ventaValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/ventas/ventaTableLista.js"></script>
+423
View File
@@ -0,0 +1,423 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<?php require __DIR__ . '/../../includes/modal.php'; ?>
<?php
// Datos desde back end (si existen)
$venta = $_SESSION['venta_extra'] ?? [];
$condIva = $venta['cond_iva'] ?? 'consumidor_final';
$cuit = $venta['cuit'] ?? '';
$formaPago = $venta['forma_pago'] ?? 'efectivo';
$tipoPago = $venta['tipo_pago'] ?? 'pago_unico';
$descuentoIngresado = $venta['descuento'] ?? 0;
$recargoIngresado = $venta['recargo'] ?? 0;
?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0 card-viewport">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-receipt icon-xl me-2"></i>
Resumen de Venta
</h4>
<a href="?opt=crear_venta" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
<!-- BODY -->
<form id="form_crear_venta" method='POST' action="index.php?opt=guardar_datos_venta" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="card-body p-4 d-flex flex-column card-body-scroll">
<!-- CLIENTE -->
<div class="mb-4 flex-shrink-0">
<h6 class="text-muted mb-2">Datos Del Cliente</h6>
<div class="row g-3">
<!-- NOMBRE Y APELLIDO -->
<div class="col-md-3">
<label class="label-formventa">Nombre y Apellido</label>
<input class="form-control campo-readonly" readonly
value="<?= htmlspecialchars($cliente['nombre'] . ' ' . $cliente['apellido']) ?? '' ?>">
</div>
<!-- TELÉFONO -->
<div class="col-md-3">
<label class="label-formventa">Teléfono Cliente</label>
<input class="form-control campo-readonly" readonly
value="<?= htmlspecialchars($cliente['telefono'] ?? '') ?>">
</div>
<!-- COND. IVA -->
<div class="col-md-3">
<label class="label-formventa">Cond. IVA</label>
<select class="form-select form-select-sm" name="cond_iva" id="condIva">
<option value="consumidor_final" <?= $condIva === 'consumidor_final' ? 'selected' : '' ?>>
Consumidor Final
</option>
<option value="responsable_inscripto" <?= $condIva === 'responsable_inscripto' ? 'selected' : '' ?>>
Responsable Inscripto
</option>
<option value="monotributo" <?= $condIva === 'monotributo' ? 'selected' : '' ?>>
Monotributo
</option>
</select>
</div>
<!-- CUIT -->
<div class="col-md-3">
<label class="form-label">CUIT</label>
<div class="d-flex gap-2">
<input type="text" name="cuit_1" maxlength="2" class="form-control text-center"
data-bs-toggle="tooltip"
title="Tipo de persona"
value="<?= substr($cuit ?? '', 0, 2) ?>">
<input type="text" name="cuit_2" maxlength="8" class="form-control text-center"
data-bs-toggle="tooltip"
title="DNI"
value="<?= substr($cuit ?? '', 2, 8) ?>">
<input type="text" name="cuit_3" maxlength="1" class="form-control text-center"
data-bs-toggle="tooltip"
title="Dígito verificador"
value="<?= substr($cuit ?? '', 10, 1) ?>">
</div>
</div>
</div>
</div>
<!-- PRODUCTOS -->
<h6 class="text-muted mb-2">Productos</h6>
<div class="table-scroll my-3">
<table class="table table-fixed table-hover align-middle mb-0 table-resumen-venta">
<thead class="table-dark">
<tr>
<th>Producto</th>
<th class="text-end">Precio Unit. Fin.</th>
<th class="text-end">Descuento (%)</th>
<th class="text-end">Cantidad</th>
<th class="text-end">Subtotal</th>
</tr>
</thead>
<tbody>
<?php foreach ($productos as $p): ?>
<tr>
<td>
<?= htmlspecialchars($p['marca'] . ' ' . $p['modelo']) ?>
</td>
<td class="text-end">
$ <?= number_format($p['precio_final'], 2) ?>
</td>
<td class="text-end">
<?= htmlspecialchars($p['descuento_total']) ?> %
</td>
<td class="text-end">
<?= $p['cantidad'] ?>
</td>
<td class="text-end">
$ <?= number_format($p['precio_final'] * $p['cantidad'], 2) ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- DATOS DE PAGO -->
<div class="flex-shrink-0 mt-0">
<h6 class="text-muted mb-2">Datos De Pago</h6>
<div class="row g-3">
<!-- FORMA DE PAGO -->
<div class="col-md-3">
<label class="label-formventa">Forma de pago</label>
<select class="form-select form-select-sm" name="forma_pago" id="formaPago">
<option value="efectivo" <?= $formaPago === 'efectivo' ? 'selected' : '' ?>>Efectivo</option>
<option value="transferencia" <?= $formaPago === 'transferencia' ? 'selected' : '' ?>>Transferencia</option>
<option value="debito" <?= $formaPago === 'debito' ? 'selected' : '' ?>>Débito</option>
<option value="credito" <?= $formaPago === 'credito' ? 'selected' : '' ?>>Crédito</option>
<option value="echeq" <?= $formaPago === 'echeq' ? 'selected' : '' ?>>Echeq</option>
</select>
</div>
<!-- TIPO DE PAGO -->
<div class="col-md-3">
<label class="label-formventa">Tipo de pago</label>
<select class="form-select form-select-sm" name="tipo_pago" id="tipoPago">
<option value="pago_unico" <?= $tipoPago === 'pago_unico' ? 'selected' : '' ?>>Pago Único</option>
<option value="3_cuotas" <?= $tipoPago === '3_cuotas' ? 'selected' : '' ?>>3 Cuotas</option>
<option value="6_cuotas" <?= $tipoPago === '6_cuotas' ? 'selected' : '' ?>>6 Cuotas</option>
<option value="12_cuotas" <?= $tipoPago === '12_cuotas' ? 'selected' : '' ?>>12 Cuotas</option>
</select>
</div>
<!-- DESCUENTO -->
<div class="col-md-3">
<label class="label-formventa">Descuento (%)</label>
<div class="input-porcentaje">
<input
type="number"
name="descuento"
id="descuentoPago"
value="<?= number_format($descuentoIngresado ?? 0, 2, '.', '') ?>"
min="0"
step="0.01"
class="form-control form-control-sm text-end"
>
<span class="input-group-text">%</span>
</div>
</div>
<!-- RECARGO -->
<div class="col-md-3">
<label class="label-formventa">Recargo (%)</label>
<div class="input-porcentaje">
<input
type="number"
name="recargo"
id="recargoPago"
value="<?= number_format($recargoIngresado ?? 0, 2, '.', '') ?>"
min="0"
step="0.01"
class="form-control form-control-sm text-end"
>
<span class="input-group-text">%</span>
</div>
</div>
</div>
</div>
<!-- TOTALES -->
<div class="flex-shrink-0 mt-3">
<div class="d-flex align-items-center gap-2 mb-2">
<h6 class="text-muted mb-0">Totales</h6>
</div>
<div class="row g-3">
<!-- SUBTOTAL -->
<div class="col-md-3">
<label class="label-formventa">Subtotal</label>
<input class="form-control campo-readonly" readonly
value="$ <?= number_format($subtotal, 2) ?>">
</div>
<!-- DESCUENTO TOTAL -->
<div class="col-md-3">
<label class="label-formventa">Descuento</label>
<input class="form-control campo-readonly" readonly
value="$ <?= number_format($descuento, 2) ?>">
</div>
<!-- RECARGO TOTAL -->
<div class="col-md-3">
<label class="label-formventa">Recargo</label>
<input class="form-control campo-readonly" readonly
value="$ <?= number_format($recargo, 2) ?>">
</div>
<!-- TOTAL -->
<div class="col-md-3">
<label class="label-formventa fw-semibold">Total</label>
<input class="form-control campo-readonly fw-semibold" readonly
value="$ <?= number_format($total, 2) ?>">
</div>
</div>
</div>
<!-- ACCIONES -->
<div class="mt-4 pt- d-flex justify-content-between align-items-center flex-wrap gap-3">
<!-- ACCIÓN SECUNDARIA (BORRADOR) -->
<button
type="submit"
class="btn btn-soft-secondary text-white py-2"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Guarda la venta sin confirmarla para continuar más tarde">
<i class="fas fa-save me-1"></i>
Guardar como borrador
</button>
<!-- ACCIONES PRINCIPALES -->
<div class="d-flex gap-2">
<button
type="submit"
formaction="index.php?opt=confirmar_venta"
formmethod="POST"
class="btn btn-primary py-2">
<i class="fas fa-check me-1"></i>
Confirmar venta
</button>
</div>
</div>
<!-- Errores provenientes de ventaController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
<!-- MODAL -->
<script src="js/globalModal.js"></script>
</div>
</form>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/ventas/ventaValidator.js"></script>
<!-- LÓGICA DE FORMA DE PAGOS -->
<script>
document.addEventListener("DOMContentLoaded", () => {
const formaPago = document.getElementById("formaPago");
const tipoPago = document.getElementById("tipoPago");
const recargoPago = document.getElementById("recargoPago");
const reglasPago = {
efectivo: ['pago_unico'],
transferencia: ['pago_unico'],
echeq: ['pago_unico'],
debito: ['pago_unico'],
credito: ['pago_unico', '3_cuotas', '6_cuotas', '12_cuotas']
};
function actualizarTipoPago() {
const forma = formaPago.value;
const permitidos = reglasPago[forma] ?? ['pago_unico'];
Array.from(tipoPago.options).forEach(opt => {
opt.disabled = !permitidos.includes(opt.value);
});
// Si el valor actual no está permitido, forzamos uno válido
if (!permitidos.includes(tipoPago.value)) {
tipoPago.value = permitidos[0];
}
}
// Eventos
formaPago.addEventListener("change", actualizarTipoPago);
// Init
actualizarTipoPago();
});
</script>
<!-- Evita que Chrome use BFCache -->
<script>
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
</script>
<!-- Tooltip -->
<script>
document.addEventListener("DOMContentLoaded", () => {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipTriggerList.forEach(el => new bootstrap.Tooltip(el));
});
</script>
<!-- Pasar al siguiente input en CUIT -->
<script>
document.querySelectorAll("input[name^='cuit_']").forEach((input, index, inputs) => {
input.addEventListener("input", () => {
if (input.value.length === input.maxLength && inputs[index + 1]) {
inputs[index + 1].focus();
}
});
});
</script>
+162
View File
@@ -0,0 +1,162 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-users icon-xl me-2"></i>
Selección de Cliente
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=crear_venta" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($clientes)): ?>
<div class="text-center text-muted py-5">
No hay clientes registrados.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex justify-content-between align-items-center mb-1">
<div id="filtrosIzquierda" class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm"
style="width:auto;">
<option value="">Todas las columnas</option>
<option value="0">ID</option>
<option value="1">Nombre</option>
<option value="2">Apellido</option>
<option value="3">Teléfono</option>
<option value="4">Email</option>
</select>
</div>
</div>
<form method="POST" action="index.php?opt=seleccionar_cliente" novalidate>
<!-- CAMPO OCULTO (para verificación de token) -->
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="table-responsive">
<table id="tablaClientes" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Apellido</th>
<th>Teléfono</th>
<th>Email</th>
<th>Seleccionar</th>
</tr>
</thead>
<tbody>
<?php foreach ($clientes as $c): ?>
<tr>
<td><?= $c['id'] ?? '' ?></td>
<td><?= htmlspecialchars($c['nombre'] ?? '') ?></td>
<td><?= htmlspecialchars($c['apellido'] ?? '') ?></td>
<td><?= htmlspecialchars($c['telefono'] ?? '') ?></td>
<td><?= htmlspecialchars($c['email'] ?? '') ?></td>
<!-- CHECKBOX PARA SELECCIONAR CLIENTE -->
<td class="text-left">
<input type="checkbox"
name="id_cliente"
value="<?= $c['id'] ?>"
class="form-check-input">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- BOTÓN FINALIZAR SELECCIÓN -->
<div class="mt-3 text-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-check me-1"></i> Finalizar
</button>
</div>
</form>
<?php endif; ?>
<!-- Errores provenientes de ventaController -->
<!-- ERROR -->
<script>
const serverErrors = <?php
if (!empty($_SESSION['flash_error'])) {
$errorsArray = explode('<br>', $_SESSION['flash_error']);
unset($_SESSION['flash_error']);
echo json_encode($errorsArray);
} else {
echo '[]';
}
?>;
</script>
<!-- SUCCES -->
<script>
const serverSuccess = <?php
if (!empty($_SESSION['flash_success'])) {
$successArray = explode('<br>', $_SESSION['flash_success']);
unset($_SESSION['flash_success']);
echo json_encode($successArray);
} else {
echo '[]';
}
?>;
</script>
</div>
</div>
</div>
<!-- Validar mediante js -->
<script src="js/ventas/ventaValidator.js"></script>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/ventas/ventaTableCliente.js"></script>
+97
View File
@@ -0,0 +1,97 @@
<?php require __DIR__ . '/../../includes/navBar.php'; ?>
<div class="container my-5">
<div class="card card-rounded card-shadow border-0">
<!-- HEADER -->
<div class="card-header header-dark px-4 py-3">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<h4 class="mb-0 title-shadow">
<i class="fas fa-shopping-cart icon-xl me-2"></i>
Detalle de Venta <?= htmlspecialchars($idVenta) ?>
</h4>
<div class="d-flex gap-2">
<!-- BOTÓN FANTASMA (mantiene el layout) -->
<a class="btn btn-success invisible">
<i class="fas fa-plus me-1"></i>
</a>
<a href="?opt=listar_ventas" class="btn btn-soft-secondary text-white">
<i class="fas fa-arrow-left me-1"></i>
Volver
</a>
</div>
</div>
</div>
<!-- BODY -->
<div class="card-body px-4 py-4">
<?php if (empty($venta_detalle)): ?>
<div class="text-center text-muted py-5">
No existe el detalle de la venta.
</div>
<?php else: ?>
<!-- FILTRO POR COLUMNA + BUSCADOR -->
<!-- Input de búsqueda añadido automáticamente por DataTables -->
<div class="d-flex align-items-center gap-2">
<select id="columnFilter"
class="form-select form-select-sm ms-2"
style="width: auto;">
<option value="0">ID Producto</option>
<option value="1">Producto</option>
</select>
</div>
<div class="table-responsive">
<table id="tablaDetalleVenta" class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Producto</th>
<th>Precio Unit.</th>
<th>Cantidad</th>
<th>Descuento (%)</th>
<th>Subtotal</th>
<th>ID Pedido</th>
</tr>
</thead>
<tbody>
<?php foreach ($venta_detalle as $vd): ?>
<tr>
<td><?= $vd['id_producto'] ?? '' ?></td>
<td><?= htmlspecialchars($vd['marca'] . ' ' . $vd['modelo']) ?? '' ?></td>
<td>$ <?= number_format($vd['precio_unitario'], 2, '.', ',') ?></td>
<td><?= htmlspecialchars($vd['cantidad'] ?? '') ?></td>
<td><?= number_format($vd['descuento_pct'], 2, '.', ',') ?> %</td>
<td>$ <?= number_format($vd['subtotal'], 2, '.', ',') ?></td>
<td><?= htmlspecialchars($vd['id_pedido'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Tabla, búsqueda y filtro por columnas -->
<script src="js/ventas/ventaTableDetalle.js"></script>