POS: selector Doctor que atendio para asignar la venta completa a un doctor (comisiones)

This commit is contained in:
2026-08-31 06:00:04 +00:00
parent fea4918494
commit bf36fb5586

View File

@@ -15,7 +15,7 @@ import {
Skeleton,
toast,
} from '../components/ui';
import { odooApi, type Service, type Patient, type InventoryItem, type PosCheckoutResult } from '../services/odoo';
import { odooApi, type Service, type Patient, type InventoryItem, type PosCheckoutResult, type Doctor } from '../services/odoo';
const fmtMoney = (n: number) =>
(n || 0).toLocaleString('es-MX', { style: 'currency', currency: 'MXN', maximumFractionDigits: 2 });
@@ -71,6 +71,11 @@ const Pos: FC = () => {
const [cobrando, setCobrando] = useState(false);
const [tcRate, setTcRate] = useState<number | null>(null);
// Doctor que atendió/recetó: se aplica como prescribed_by_id a todas las
// líneas al cobrar (alimenta "Mis ventas" y comisiones). Opcional.
const [doctors, setDoctors] = useState<Doctor[]>([]);
const [doctorId, setDoctorId] = useState('');
// Resultado
const [resultado, setResultado] = useState<PosCheckoutResult | null>(null);
@@ -123,6 +128,13 @@ const Pos: FC = () => {
.catch(() => {});
}, []);
// Doctores activos para el selector "Doctor que atendió"
useEffect(() => {
odooApi.getDoctors()
.then((res) => { if (res.status === 'success') setDoctors(res.doctors); })
.catch(() => {});
}, []);
// Tipo de cambio USD/MXN: al montar y refresco cada 5 min
useEffect(() => {
const fetchTc = () => {
@@ -256,6 +268,8 @@ const Pos: FC = () => {
...(l.item_id ? { item_id: l.item_id } : {}),
quantity: l.qty,
price_unit: l.price,
// Asignación a nivel venta: el doctor seleccionado aplica a todas las líneas
...(doctorId ? { prescribed_by_id: parseInt(doctorId, 10) } : {}),
})),
discount: discountNum,
payment_method: metodo,
@@ -280,12 +294,29 @@ const Pos: FC = () => {
}
};
// Selector "Doctor que atendió": se comparte entre el ticket y el modal de cobro
const doctorBlock = (
<Select
label="Doctor que atendió"
options={[
{ value: '', label: 'Sin asignar' },
...doctors.map((d) => ({
value: String(d.id),
label: (d.staff_type ?? 'doctor') === 'cosmetologa' ? `${d.name} (Cosmetóloga)` : d.name,
})),
]}
value={doctorId}
onChange={(e) => setDoctorId(e.target.value)}
/>
);
const nuevaVenta = () => {
setResultado(null);
setLines([]);
setDiscount('');
setPaciente(null);
setPacienteQuery('');
setDoctorId('');
};
// ---- Bloques del ticket (desktop sticky y modal móvil) ----
@@ -435,6 +466,9 @@ const Pos: FC = () => {
</span>
</span>
</div>
<div className="border border-theme-border rounded-xl p-2.5 bg-theme-bg">
{doctorBlock}
</div>
<Button
className="w-full mt-1 !py-3 text-base"
onClick={abrirCobro}
@@ -633,6 +667,12 @@ const Pos: FC = () => {
}
>
<div className="space-y-4">
<div className="border border-theme-border rounded-xl p-3 bg-theme-bg">
{doctorBlock}
<p className="text-xs text-theme-muted mt-1">
Se asigna la venta completa a este doctor para sus comisiones.
</p>
</div>
<Select label="Método de pago" options={METODO_OPTIONS} value={metodo} onChange={(e) => setMetodo(e.target.value)} />
{metodo === 'cash' && !conPuntos && (
<>