POS: selector Doctor que atendio para asignar la venta completa a un doctor (comisiones)
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
|||||||
Skeleton,
|
Skeleton,
|
||||||
toast,
|
toast,
|
||||||
} from '../components/ui';
|
} 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) =>
|
const fmtMoney = (n: number) =>
|
||||||
(n || 0).toLocaleString('es-MX', { style: 'currency', currency: 'MXN', maximumFractionDigits: 2 });
|
(n || 0).toLocaleString('es-MX', { style: 'currency', currency: 'MXN', maximumFractionDigits: 2 });
|
||||||
@@ -71,6 +71,11 @@ const Pos: FC = () => {
|
|||||||
const [cobrando, setCobrando] = useState(false);
|
const [cobrando, setCobrando] = useState(false);
|
||||||
const [tcRate, setTcRate] = useState<number | null>(null);
|
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
|
// Resultado
|
||||||
const [resultado, setResultado] = useState<PosCheckoutResult | null>(null);
|
const [resultado, setResultado] = useState<PosCheckoutResult | null>(null);
|
||||||
|
|
||||||
@@ -123,6 +128,13 @@ const Pos: FC = () => {
|
|||||||
.catch(() => {});
|
.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
|
// Tipo de cambio USD/MXN: al montar y refresco cada 5 min
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchTc = () => {
|
const fetchTc = () => {
|
||||||
@@ -256,6 +268,8 @@ const Pos: FC = () => {
|
|||||||
...(l.item_id ? { item_id: l.item_id } : {}),
|
...(l.item_id ? { item_id: l.item_id } : {}),
|
||||||
quantity: l.qty,
|
quantity: l.qty,
|
||||||
price_unit: l.price,
|
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,
|
discount: discountNum,
|
||||||
payment_method: metodo,
|
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 = () => {
|
const nuevaVenta = () => {
|
||||||
setResultado(null);
|
setResultado(null);
|
||||||
setLines([]);
|
setLines([]);
|
||||||
setDiscount('');
|
setDiscount('');
|
||||||
setPaciente(null);
|
setPaciente(null);
|
||||||
setPacienteQuery('');
|
setPacienteQuery('');
|
||||||
|
setDoctorId('');
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- Bloques del ticket (desktop sticky y modal móvil) ----
|
// ---- Bloques del ticket (desktop sticky y modal móvil) ----
|
||||||
@@ -435,6 +466,9 @@ const Pos: FC = () => {
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="border border-theme-border rounded-xl p-2.5 bg-theme-bg">
|
||||||
|
{doctorBlock}
|
||||||
|
</div>
|
||||||
<Button
|
<Button
|
||||||
className="w-full mt-1 !py-3 text-base"
|
className="w-full mt-1 !py-3 text-base"
|
||||||
onClick={abrirCobro}
|
onClick={abrirCobro}
|
||||||
@@ -633,6 +667,12 @@ const Pos: FC = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="space-y-4">
|
<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)} />
|
<Select label="Método de pago" options={METODO_OPTIONS} value={metodo} onChange={(e) => setMetodo(e.target.value)} />
|
||||||
{metodo === 'cash' && !conPuntos && (
|
{metodo === 'cash' && !conPuntos && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user