diff --git a/src/web/src/features/puntos-de-venta/components/SecuenciasPanel.tsx b/src/web/src/features/puntos-de-venta/components/SecuenciasPanel.tsx
new file mode 100644
index 0000000..9dfa2b0
--- /dev/null
+++ b/src/web/src/features/puntos-de-venta/components/SecuenciasPanel.tsx
@@ -0,0 +1,96 @@
+import { toast } from 'sonner'
+import { Button } from '@/components/ui/button'
+import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
+import { TipoComprobante } from '../types'
+import { useReservarNumero, useProximoNumero } from '../hooks/useReservarNumero'
+
+const TIPOS: Array<{ value: TipoComprobante; label: string }> = [
+ { value: TipoComprobante.FacturaA, label: 'Factura A' },
+ { value: TipoComprobante.FacturaB, label: 'Factura B' },
+ { value: TipoComprobante.FacturaC, label: 'Factura C' },
+ { value: TipoComprobante.NotaCreditoA, label: 'Nota Crédito A' },
+ { value: TipoComprobante.NotaCreditoB, label: 'Nota Crédito B' },
+ { value: TipoComprobante.NotaCreditoC, label: 'Nota Crédito C' },
+]
+
+interface Props {
+ puntoDeVentaId: number
+ disabled: boolean
+}
+
+export function SecuenciasPanel({ puntoDeVentaId, disabled }: Props) {
+ return (
+
+
+
Reserva de números de comprobante
+
+ Cada reserva incrementa el correlativo y devuelve el número asignado.
+
+
+
+
+
+ Tipo
+ Próximo número
+ Acción
+
+
+
+ {TIPOS.map((tipo) => (
+
+ ))}
+
+
+
+ )
+}
+
+interface RowProps {
+ puntoDeVentaId: number
+ tipoValue: TipoComprobante
+ tipoLabel: string
+ disabled: boolean
+}
+
+function SecuenciaRow({ puntoDeVentaId, tipoValue, tipoLabel, disabled }: RowProps) {
+ const proximo = useProximoNumero(puntoDeVentaId, tipoValue)
+ const reservar = useReservarNumero(puntoDeVentaId)
+
+ const handleReservar = () => {
+ reservar.mutate(tipoValue, {
+ onSuccess: (data) => {
+ toast.success(`${tipoLabel}: número ${data.numeroReservado} reservado`)
+ },
+ onError: (err: unknown) => {
+ const apiError = err as { response?: { data?: { error?: string } } }
+ const code = apiError.response?.data?.error ?? 'error'
+ toast.error(`No se pudo reservar: ${code}`)
+ },
+ })
+ }
+
+ return (
+
+ {tipoLabel}
+
+ {proximo.isLoading ? '…' : proximo.data?.proximoNumero ?? '—'}
+
+
+
+
+
+ )
+}
diff --git a/src/web/src/features/puntos-de-venta/pages/PuntoDeVentaDetailPage.tsx b/src/web/src/features/puntos-de-venta/pages/PuntoDeVentaDetailPage.tsx
index bf7e68c..9d2f95a 100644
--- a/src/web/src/features/puntos-de-venta/pages/PuntoDeVentaDetailPage.tsx
+++ b/src/web/src/features/puntos-de-venta/pages/PuntoDeVentaDetailPage.tsx
@@ -7,6 +7,7 @@ import { useMedio } from '../../medios/hooks/useMedio'
import { DeactivatePuntoDeVentaModal } from '../components/DeactivatePuntoDeVentaModal'
import { MedioInactivoBanner } from '../components/MedioInactivoBanner'
import { PdvInactivoBanner } from '../components/PdvInactivoBanner'
+import { SecuenciasPanel } from '../components/SecuenciasPanel'
function formatDate(iso: string | null): string {
if (!iso) return '—'
@@ -103,6 +104,13 @@ export function PuntoDeVentaDetailPage() {
/>
+
+
+
+
)
}