fix(6-mejoras): subcategoría+apartado libre+PDF+coacreditado+docs+dedup

1. Subcategoría en modal descarte (catálogo completo + select)
2. Apartado sin monto mínimo (eliminada validación de 0,000)
3. PDF solo con propuesta elegida (no las 4)
4. Co-acreditado: checkbox 'no tiene' valida sin campos obligatorios
5. Documentos apartado: previsualizar + descargar (endpoint + frontend)
6. Oportunidades: sin duplicados, orden Confirmar→Afluencia→Cotizar→Aparto(verde)→Descartar
This commit is contained in:
2026-09-12 07:09:15 +00:00
parent c8357fe582
commit 852182fabc
12 changed files with 261 additions and 112 deletions

View File

@@ -65,7 +65,6 @@ export default function CotizadorPage() {
apartado,
setApartado,
apartadoEfectivo,
errorApartado,
enganchePct,
setEnganchePct,
errorEnganche,
@@ -181,7 +180,7 @@ export default function CotizadorPage() {
const propuestaActiva = propuestas[Math.min(propuestaIndex, Math.max(propuestas.length - 1, 0))]
// Total pagado por el cliente con la propuesta elegida; null si no se puede conocer
const totalActivo = propuestaActiva ? totalPagado(propuestaActiva, apartadoEfectivo) : null
const puedeGenerarPdf = Boolean(unidad) && !errorApartado && !errorEnganche && !guardando
const puedeGenerarPdf = Boolean(unidad) && !errorEnganche && !guardando
// ---------- Preselección por defecto: la propuesta de menor total pagado ----------
// Al cambiar de unidad (o al elegir la primera) se preselecciona la propuesta
@@ -290,7 +289,8 @@ export default function CotizadorPage() {
acabados,
apartado: apartadoEfectivo,
precioLista,
propuestas,
// El PDF imprime SOLO la propuesta elegida por el vendedor
propuestas: propuestaActiva ? [propuestaActiva] : [],
cliente,
desarrollo: params,
...(folio ? { folio } : {}),
@@ -530,20 +530,15 @@ export default function CotizadorPage() {
<input
id="apartado"
type="number"
min={0}
step={1000}
value={apartado}
onChange={(e) => {
const v = Number(e.target.value)
setApartado(Number.isFinite(v) && v >= 0 ? v : 0)
}}
className={errorApartado ? campoInputError : campoInput}
className={campoInput}
/>
{errorApartado ? (
<p className="mt-1 text-xs text-red-700">{errorApartado}</p>
) : (
<p className="mt-1 text-xs text-[#64748B] dark:text-[#94A3B8]">{formatoMoneda(apartado)}</p>
)}
<p className="mt-1 text-xs text-[#64748B] dark:text-[#94A3B8]">{formatoMoneda(apartado)}</p>
</div>
<div>