diff --git a/frontend/src/pages/Medicos.tsx b/frontend/src/pages/Medicos.tsx
index 1c2ec21..1fd1903 100644
--- a/frontend/src/pages/Medicos.tsx
+++ b/frontend/src/pages/Medicos.tsx
@@ -13,11 +13,41 @@ const isMedicalTitle = (jobTitle?: string) => {
return medicalTerms.some((term) => normalized.includes(term));
};
-const fmtMoney = (n: number) =>
- (n || 0).toLocaleString('es-MX', { style: 'currency', currency: 'MXN', maximumFractionDigits: 0 });
+const fmtNum = (n: number | null | undefined, decimals = 2) =>
+ (n ?? 0).toLocaleString('es-MX', { maximumFractionDigits: decimals });
-const fmtUsd = (n: number | null | undefined) =>
- n == null ? '—' : n.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
+/** Badges de ventas 30d al estilo del sistema legacy:
+ * píldora verde "N ventas" + gris "N recomendaciones" y
+ * píldoras índigo apiladas: usd / m.n. / tarjeta m.n. */
+const StatBadges: FC<{ d: Doctor }> = ({ d }) => (
+
+
+
+ {d.ventas_30d ?? 0} ventas
+
+
+ {d.recomendaciones_30d ?? 0} recomendaciones
+
+
+
+
+ {d.total_30d_usd == null ? '—' : fmtNum(d.total_30d_usd)} usd
+
+
+ {fmtNum(d.total_30d ?? 0, 0)} m.n.
+
+
+ {fmtNum(d.card_30d ?? 0, 0)} tarjeta m.n.
+
+
+
+);
const Medicos: FC = () => {
const [employees, setEmployees] = useState([]);
@@ -95,10 +125,7 @@ const Medicos: FC = () => {
Teléfono |
Email |
% Comisión |
- Ventas 30d |
- Total 30d |
- Total USD |
- Pagado tarjeta |
+ Últimos 30 días |
@@ -144,10 +171,9 @@ const Medicos: FC = () => {
- {d.ventas_30d ?? 0} |
- {fmtMoney(d.total_30d ?? 0)} |
- {fmtUsd(d.total_30d_usd)} |
- {fmtMoney(d.card_30d ?? 0)} |
+
+
+ |
))}
@@ -169,10 +195,7 @@ const Medicos: FC = () => {
{ label: 'Teléfono', value: d.work_phone || '-' },
{ label: 'Email', value: d.work_email || '-' },
{ label: '% Comisión', value: `${d.commission_pct ?? 0}%` },
- { label: 'Ventas 30d', value: String(d.ventas_30d ?? 0) },
- { label: 'Total 30d', value: fmtMoney(d.total_30d ?? 0) },
- { label: 'Total USD', value: fmtUsd(d.total_30d_usd) },
- { label: 'Pagado tarjeta', value: fmtMoney(d.card_30d ?? 0) },
+ { label: 'Últimos 30 días', value: },
]}
/>
))}
diff --git a/frontend/src/services/odoo.ts b/frontend/src/services/odoo.ts
index 08fbf45..b143a52 100644
--- a/frontend/src/services/odoo.ts
+++ b/frontend/src/services/odoo.ts
@@ -250,6 +250,7 @@ export interface Doctor {
work_email: string;
commission_pct: number;
patient_count?: number;
+ recomendaciones_30d?: number;
ventas_30d?: number;
total_30d?: number;
total_30d_usd?: number | null;
diff --git a/odoo-addons/skeen_whatsapp/controllers/frontend.py b/odoo-addons/skeen_whatsapp/controllers/frontend.py
index d5e7105..2292e0e 100644
--- a/odoo-addons/skeen_whatsapp/controllers/frontend.py
+++ b/odoo-addons/skeen_whatsapp/controllers/frontend.py
@@ -1657,6 +1657,16 @@ class SkeenFrontendController(http.Controller):
# ---- Ventas de los últimos 30 días por médico ----
since = (date.today() - timedelta(days=30)).strftime('%Y-%m-%d')
+
+ # Recomendaciones: pacientes captados en 30d asignados al médico
+ # con "recomendado por" capturado (misma métrica visible del legacy)
+ recomendaciones = {}
+ for g in request.env['res.partner'].sudo().read_group(
+ [('is_patient', '=', True), ('primary_doctor_id', 'in', doctors.ids),
+ ('referred_by', 'not in', [False, '']), ('create_date', '>=', since)],
+ [], ['primary_doctor_id']):
+ recomendaciones[g['primary_doctor_id'][0]] = g['primary_doctor_id_count']
+
stats = {} # doctor_id -> {'ventas': set, 'total': float}
card_by_venta = {}
ventas = request.env['skeen.venta'].sudo().search([
@@ -1700,6 +1710,7 @@ class SkeenFrontendController(http.Controller):
'work_email': d.work_email or '',
'commission_pct': d.commission_pct or 0.0,
'patient_count': counts.get(d.id, 0),
+ 'recomendaciones_30d': recomendaciones.get(d.id, 0),
'ventas_30d': len(stats.get(d.id, {}).get('ventas', set())),
'total_30d': round(stats.get(d.id, {}).get('total', 0.0), 2),
'total_30d_usd': round(stats.get(d.id, {}).get('total', 0.0) / rate, 2) if rate else None,