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:
@@ -18,7 +18,7 @@ import {
|
||||
badgeForSaleState,
|
||||
} from '../components/ui';
|
||||
import { odooApi, type Sale, type SaleLine, type Service, type Patient, type Doctor } from '../services/odoo';
|
||||
import { downloadCsv } from '../lib/utils';
|
||||
import { exportToExcel } from '../lib/exporter';
|
||||
|
||||
interface LineForm {
|
||||
service_id: string;
|
||||
@@ -246,19 +246,40 @@ const Ventas: FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const exportSales = () => {
|
||||
const rows: (string | number)[][] = [
|
||||
['Referencia', 'Paciente', 'Fecha', 'Subtotal', 'Descuento', 'Impuesto', 'Total', 'Pagado', 'Por cobrar', 'Estado', 'Devuelta', 'Monto devuelto'],
|
||||
];
|
||||
sales.forEach((s) =>
|
||||
rows.push([
|
||||
s.name, s.patient, s.date, s.subtotal, s.discount, s.tax, s.total,
|
||||
s.amount_paid, s.amount_due, s.state, s.refunded ? 'Sí' : 'No', s.refund_amount || 0,
|
||||
])
|
||||
);
|
||||
rows.push(['', '', 'Totales', '', '', '', sales.reduce((a, s) => a + s.total, 0), sales.reduce((a, s) => a + s.amount_paid, 0), sales.reduce((a, s) => a + s.amount_due, 0), '', '', sales.reduce((a, s) => a + (s.refund_amount || 0), 0)]);
|
||||
downloadCsv(`ventas-skeen-${new Date().toISOString().split('T')[0]}.csv`, rows);
|
||||
toast.success(`CSV descargado (${sales.length} ventas de la vista actual)`);
|
||||
const exportSales = async () => {
|
||||
try {
|
||||
await exportToExcel({
|
||||
filename: `ventas-skeen-${new Date().toISOString().split('T')[0]}`,
|
||||
sheetName: 'Ventas',
|
||||
title: 'Ventas SKEEN',
|
||||
subtitle: `${sales.length} ventas de la vista actual`,
|
||||
columns: [
|
||||
{ header: 'Referencia', key: 'name' },
|
||||
{ header: 'Paciente', key: 'patient' },
|
||||
{ header: 'Fecha', key: 'date', format: 'date' },
|
||||
{ header: 'Subtotal', key: 'subtotal', format: 'currency' },
|
||||
{ header: 'Descuento', key: 'discount', format: 'currency' },
|
||||
{ header: 'Impuesto', key: 'tax', format: 'currency' },
|
||||
{ header: 'Total', key: 'total', format: 'currency' },
|
||||
{ header: 'Pagado', key: 'amount_paid', format: 'currency' },
|
||||
{ header: 'Por cobrar', key: 'amount_due', format: 'currency' },
|
||||
{ header: 'Estado', key: 'state' },
|
||||
{ header: 'Devuelta', key: 'devuelta' },
|
||||
{ header: 'Monto devuelto', key: 'refund_amount', format: 'currency' },
|
||||
],
|
||||
rows: sales.map((s) => ({ ...s, devuelta: s.refunded ? 'Sí' : 'No', refund_amount: s.refund_amount || 0 })),
|
||||
totals: {
|
||||
name: '', total: sales.reduce((a, s) => a + s.total, 0),
|
||||
amount_paid: sales.reduce((a, s) => a + s.amount_paid, 0),
|
||||
amount_due: sales.reduce((a, s) => a + s.amount_due, 0),
|
||||
refund_amount: sales.reduce((a, s) => a + (s.refund_amount || 0), 0),
|
||||
},
|
||||
});
|
||||
toast.success(`Excel descargado (${sales.length} ventas de la vista actual)`);
|
||||
} catch (err) {
|
||||
toast.error('Error al exportar');
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
const patientOptions = [
|
||||
|
||||
Reference in New Issue
Block a user