Skip to content

Commit c1051ac

Browse files
authored
Merge pull request #71 from Llorente29/fix/recepcion-lista-recibido
fix(supply): recibido visible arriba + metrica de fiabilidad correcta
2 parents 67d5677 + 4b1ef6c commit c1051ac

2 files changed

Lines changed: 80 additions & 30 deletions

File tree

src/modules/supply/pages/GoodsReceiptsPage.tsx

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -184,17 +184,27 @@ export default function GoodsReceiptsPage() {
184184
// los locales" no se muestra, mezclar locales no dice nada útil de UNO). Usa
185185
// los ids ya cargados arriba (receipts, ya viene ordenado por receipt_date
186186
// desc) — no repite la consulta de recepciones, solo lee sus líneas.
187+
//
188+
// ENCARGO CODE (14/08) fix/recepcion-lista-recibido, §3 — candidatos =
189+
// 'recibido' o 'confirmado' (antes solo 'confirmado': mezclaba el histórico
190+
// entero del flujo clásico con las recepciones del asistente).
191+
// getReceiptCorrectionStreak filtra a las del asistente y aplica el tope.
192+
// 200 en bruto es margen de sobra para llegar a 60 del asistente sin barrer
193+
// toda la tabla.
187194
const [streak, setStreak] = useState<CorrectionStreak | null>(null)
188195
useEffect(() => {
189196
let cancelled = false
190-
const confirmedIds = resolvedLocationId
191-
? receipts.filter(r => r.status === 'confirmado').slice(0, 60).map(r => r.id)
197+
const candidates = resolvedLocationId
198+
? receipts
199+
.filter(r => r.status === 'confirmado' || r.status === 'recibido')
200+
.slice(0, 200)
201+
.map(r => ({ id: r.id, status: r.status }))
192202
: []
193-
// Sin local elegido o sin recepciones confirmadas: resuelve a null igual,
194-
// pero SIEMPRE por la vía async (nunca setState síncrono en el cuerpo del
195-
// efecto — mismo criterio que el resto del fetching de esta página).
196-
const fetchPromise = confirmedIds.length > 0
197-
? getReceiptCorrectionStreak(confirmedIds)
203+
// Sin local elegido o sin candidatos: resuelve a null igual, pero SIEMPRE
204+
// por la vía async (nunca setState síncrono en el cuerpo del efecto —
205+
// mismo criterio que el resto del fetching de esta página).
206+
const fetchPromise = candidates.length > 0
207+
? getReceiptCorrectionStreak(candidates)
198208
: Promise.resolve(null)
199209
fetchPromise
200210
.then(s => { if (!cancelled) setStreak(s) })
@@ -240,17 +250,27 @@ export default function GoodsReceiptsPage() {
240250
const doc = (r.supplierDocNumber ?? '').toLowerCase()
241251
return code.includes(q) || sup.includes(q) || doc.includes(q)
242252
})
243-
// Lo accionable primero: BORRADORES arriba (pendientes de confirmar), luego el
244-
// resto; dentro de cada grupo, por fecha de recepción descendente (lo reciente
245-
// antes). Así la oficina ve de un vistazo lo que tiene que revisar.
246-
const rank = (s: string) => (s === 'borrador' ? 0 : s === 'confirmado' ? 1 : 2)
253+
// Lo accionable primero: BORRADORES y RECIBIDO arriba (esperan acción —
254+
// confirmar o revisar), luego CONFIRMADO/ANULADO (histórico); dentro de
255+
// cada grupo, por fecha de recepción descendente (lo reciente antes). Así
256+
// la oficina ve de un vistazo lo que tiene que revisar.
257+
//
258+
// ENCARGO CODE (14/08) fix/recepcion-lista-recibido, §2 — 'recibido' caía
259+
// en el mismo rango que 'confirmado'/'anulado' (rango 2): con 99
260+
// confirmadas por delante, una recepción 'recibido' quedaba enterrada al
261+
// fondo de la lista — "no aparece en pantalla" aunque la fila existiera.
262+
const rank = (s: string) => (s === 'borrador' ? 0 : s === 'recibido' ? 1 : s === 'confirmado' ? 2 : 3)
247263
return [...base].sort((a, b) => {
248264
const dr = rank(a.status) - rank(b.status)
249265
if (dr !== 0) return dr
250266
return (b.receiptDate ?? '').localeCompare(a.receiptDate ?? '')
251267
})
252268
}, [receipts, search, supplierNameById])
253269

270+
// ENCARGO CODE (14/08) fix/recepcion-lista-recibido, §2.2 — contador visible:
271+
// es lo único que espera acción de la oficina (lo confirmado es histórico).
272+
const recibidoCount = useMemo(() => receipts.filter(r => r.status === 'recibido').length, [receipts])
273+
254274
// Revisar y confirmar un BORRADOR: lee la recepción + líneas + la foto del
255275
// albarán y abre el form EN SITIO (isDraft). La oficina ve lo que se contó,
256276
// ajusta lo que falte y confirma la MISMA recepción (no crea otra ni anula).
@@ -591,6 +611,18 @@ export default function GoodsReceiptsPage() {
591611
</div>
592612
</div>
593613

614+
{/* ENCARGO CODE (14/08) fix/recepcion-lista-recibido, §2.2 — "la oficina
615+
no puede confirmar lo que no ve": destacado arriba, es lo único que
616+
espera acción de oficina (lo confirmado es histórico). */}
617+
{!loading && !error && recibidoCount > 0 && (
618+
<div className="p-3 rounded-md border border-accent/30 bg-accent-bg flex items-center gap-2">
619+
<Eye size={16} className="text-accent shrink-0" />
620+
<p className="text-sm font-medium text-text-primary">
621+
{recibidoCount} recepción{recibidoCount === 1 ? '' : 'es'} {recibidoCount === 1 ? 'espera' : 'esperan'} tu revisión
622+
</p>
623+
</div>
624+
)}
625+
594626
{/* ENCARGO CODE (13/08) fix/recepcion-p2-oficina, §5 — solo lectura + una
595627
propuesta, nada automático. Sin botón que escriba receipt_approval=
596628
'directo': ese valor no lo consume ninguna pantalla todavía (modo

src/modules/supply/services/goodsReceiptService.ts

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -648,23 +648,34 @@ export async function getReceiptDocTotal(aiSessionId: string): Promise<number |
648648
export const CORRECTION_STREAK_GOAL = 30
649649

650650
export interface CorrectionStreak {
651-
correctedCount: number // de las últimas `totalCount` recepciones confirmadas
651+
correctedCount: number // de las últimas `totalCount` recepciones DEL ASISTENTE
652652
totalCount: number
653-
streak: number // seguidas sin corrección, contando desde la más reciente
653+
streak: number // seguidas sin corrección, contando desde la más reciente (nunca > streakGoal)
654654
streakGoal: number
655655
metGoal: boolean // streak >= streakGoal
656656
}
657657

658658
/**
659-
* Racha de recepciones confirmadas SIN que la oficina tuviera que corregir una
660-
* cantidad (ENCARGO CODE 13/08 fix/recepcion-p2-oficina, §5). Recibe los ids de
661-
* recepciones CONFIRMADAS ya cargados por el llamador (lista de Recepciones,
662-
* ya filtrada por local), del más reciente al más antiguo — no vuelve a
663-
* consultar goods_receipt, solo lee sus líneas. "Corregida" = alguna línea SIN
664-
* ⚑ (flagged_for_office=false) con discrepancy_reason puesto por el picker de
665-
* oficina (isOfficeQtyReason) — NO cualquier discrepancy_reason (el dropdown
666-
* del panel de repaso usa el mismo campo para otra cosa: por qué difiere de lo
667-
* pedido/del albarán).
659+
* Racha de recepciones DEL ASISTENTE (no el histórico entero) SIN que la
660+
* oficina tuviera que corregir una cantidad (ENCARGO CODE 13/08
661+
* fix/recepcion-p2-oficina, §5; recorte de alcance ENCARGO CODE 14/08
662+
* fix/recepcion-lista-recibido, §3). Recibe los candidatos ya cargados por el
663+
* llamador (recepciones 'recibido' o 'confirmado', ya filtradas por local),
664+
* del más reciente al más antiguo.
665+
*
666+
* "Del asistente" = status='recibido' (el flujo clásico nunca pasa por ahí),
667+
* o alguna línea con la ⚑ (flagged_for_office) — columna que solo escribe el
668+
* asistente, ninguna recepción del flujo clásico puede tenerla en true. No
669+
* hay columna que sobreviva la transición recibido→confirmado (añadirla es
670+
* migración, fuera de este encargo si hace falta) así que una recepción del
671+
* asistente con TODAS sus líneas sin marcar deja de detectarse en cuanto se
672+
* confirma — error hacia el lado seguro: cuenta de menos, nunca de más, así
673+
* que nunca dispara antes de tiempo la propuesta de confirmación directa.
674+
*
675+
* "Corregida" = alguna línea SIN ⚑ (flagged_for_office=false) con
676+
* discrepancy_reason puesto por el picker de oficina (isOfficeQtyReason) — NO
677+
* cualquier discrepancy_reason (el dropdown del panel de repaso usa el mismo
678+
* campo para otra cosa: por qué difiere de lo pedido/del albarán).
668679
*
669680
* ENCARGO CODE (13/08) feat/recepcion-v2-asistente, Tramo C — matiz del
670681
* asistente: una línea que cocina marcó ⚑ ("que lo mire la oficina") y que
@@ -673,33 +684,40 @@ export interface CorrectionStreak {
673684
* MARCAR (cocina dio por buena una cantidad que resultó ser otra).
674685
*/
675686
export async function getReceiptCorrectionStreak(
676-
confirmedReceiptIdsRecentFirst: string[],
687+
candidatesRecentFirst: { id: string; status: GoodsReceiptStatus }[],
677688
): Promise<CorrectionStreak> {
678689
requireSupabase()
679-
if (confirmedReceiptIdsRecentFirst.length === 0) {
690+
if (candidatesRecentFirst.length === 0) {
680691
return { correctedCount: 0, totalCount: 0, streak: 0, streakGoal: CORRECTION_STREAK_GOAL, metGoal: false }
681692
}
682693
const { data, error } = await from('goods_receipt_line')
683694
.select('goods_receipt_id, discrepancy_reason, flagged_for_office')
684-
.in('goods_receipt_id', confirmedReceiptIdsRecentFirst)
695+
.in('goods_receipt_id', candidatesRecentFirst.map(r => r.id))
685696
if (error) throw new Error(`Error calculando la racha de correcciones: ${error.message}`)
697+
const lines = (data as Row[]) ?? []
698+
699+
const flaggedIds = new Set(lines.filter(l => l.flagged_for_office).map(l => l.goods_receipt_id as string))
700+
const assistantRecentFirst = candidatesRecentFirst
701+
.filter(r => r.status === 'recibido' || flaggedIds.has(r.id))
702+
.slice(0, 60)
686703

687704
const correctedIds = new Set<string>()
688-
for (const row of (data as Row[]) ?? []) {
705+
for (const row of lines) {
689706
if (isOfficeQtyReason(row.discrepancy_reason as string | null) && !row.flagged_for_office) {
690707
correctedIds.add(row.goods_receipt_id as string)
691708
}
692709
}
693710

694711
let streak = 0
695-
for (const id of confirmedReceiptIdsRecentFirst) {
696-
if (correctedIds.has(id)) break
712+
for (const r of assistantRecentFirst) {
713+
if (correctedIds.has(r.id)) break
697714
streak++
715+
if (streak >= CORRECTION_STREAK_GOAL) break
698716
}
699717

700718
return {
701-
correctedCount: correctedIds.size,
702-
totalCount: confirmedReceiptIdsRecentFirst.length,
719+
correctedCount: assistantRecentFirst.filter(r => correctedIds.has(r.id)).length,
720+
totalCount: assistantRecentFirst.length,
703721
streak,
704722
streakGoal: CORRECTION_STREAK_GOAL,
705723
metGoal: streak >= CORRECTION_STREAK_GOAL,

0 commit comments

Comments
 (0)