Mejoras integrales: visitas, POS, WACRM, reportes, temas y módulos legacy

- Módulo Visitas completo (auto desde agenda, insumos con descargo de
  inventario, fotos antes/después y documentos, receta imprimible)
- Punto de Venta (catálogo + ticket sticky, cobro con cambio, pago con
  puntos monedero, ticket imprimible)
- WACRM: leads automáticos desde WhatsApp, asignación de conversaciones
  y leads a agentes, conversión lead→paciente, ficha del paciente en chat
- Pacientes: completitud de expediente, alertas clínicas, historial
  unificado con detalle, foto, WhatsApp, estado de cuenta, filtros
  rápidos (VIP/recientes/médico), documentos (expediente escaneado + galería)
- Agenda: vistas por médico y por hora, filtros rápidos (libres, primera
  vez, check-in, no-show), modal de acciones, bloqueos por médico,
  drag&drop para mover citas
- Reportes: 18 pestañas (diario, cortes, ingresos, inventario, adeudos,
  comisiones, pagos, devoluciones, top clientes, horas, paquetes,
  vendedores, concentrado, recomendaciones, KPIs) con exportación Excel
- Temas: nuevo tema Clásico (look legacy AdminLTE) con submenús tipo
  treeview, selector de tema; accesos rápidos personalizables con 3
  presentaciones; búsqueda global; notificaciones reales
- Configuración: secciones (clínica, usuarios con permisos por sección,
  recetas, catálogos de diagnósticos y procedimientos)
- Inventario: alertas de caducidad y sugerencia de compra, cron diario
  que descuenta artículos caducados, compras/bajas
- Consultas Médicas, página Expedientes, importadores delta
  (citas/visitas legacy idempotentes), depuración de duplicados
- Infra: tema Tailwind conectado (@config), gzip en nginx, secuencias
  Odoo corregidas (noupdate, company_id), rollback en validaciones
This commit is contained in:
2026-08-13 23:30:08 +00:00
parent a718592291
commit 01f6007e30
109 changed files with 19067 additions and 931 deletions

View File

@@ -5,7 +5,7 @@ import Layout from '../components/Layout';
import { Card, PageHeader, Button, toast } from '../components/ui';
import { odooApi } from '../services/odoo';
import type { Birthday } from '../services/odoo';
import { downloadCsv } from '../lib/utils';
import { exportToExcel } from '../lib/exporter';
type Period = 'today' | 'week' | 'month';
@@ -50,18 +50,35 @@ const Cumpleanos: FC = () => {
};
}, [period]);
const exportBirthdays = () => {
const rows: (string | number | boolean | null | undefined)[][] = [
['Nombre', 'Teléfono', 'Fecha nacimiento', 'Cumple el', 'Cumple años', 'Última visita', 'Total gastado', 'Adeudo', 'VIP'],
];
items.forEach((b) =>
rows.push([
b.name, b.phone, b.birth_date, b.occurs_on, b.turning_age, b.last_visit,
b.total_spent, b.amount_due, b.is_vip ? 'Sí' : 'No',
])
);
downloadCsv(`cumpleaneros-skeen-${period}-${new Date().toISOString().split('T')[0]}.csv`, rows);
toast.success(`CSV descargado (${items.length} cumpleañeros)`);
const exportBirthdays = async () => {
try {
await exportToExcel({
filename: `cumpleaneros-skeen-${period}-${new Date().toISOString().split('T')[0]}`,
sheetName: 'Cumpleañeros',
title: 'Cumpleañeros SKEEN',
subtitle: `Periodo: ${period} · ${items.length} pacientes`,
columns: [
{ header: 'Nombre', key: 'name' },
{ header: 'Teléfono', key: 'phone' },
{ header: 'Fecha nacimiento', key: 'birth_date', format: 'date' },
{ header: 'Cumple el', key: 'occurs_on', format: 'date' },
{ header: 'Cumple años', key: 'turning_age', format: 'number' },
{ header: 'Última visita', key: 'last_visit', format: 'date' },
{ header: 'Total gastado', key: 'total_spent', format: 'currency' },
{ header: 'Adeudo', key: 'amount_due', format: 'currency' },
{ header: 'VIP', key: 'vip' },
],
rows: items.map((b) => ({
name: b.name, phone: b.phone, birth_date: b.birth_date, occurs_on: b.occurs_on,
turning_age: b.turning_age, last_visit: b.last_visit, total_spent: b.total_spent,
amount_due: b.amount_due, vip: b.is_vip ? 'Sí' : 'No',
})),
});
toast.success(`Excel descargado (${items.length} cumpleañeros)`);
} catch (err) {
toast.error('Error al exportar');
console.error(err);
}
};
return (