Files
roa2web-service-auto/tasks/prd-bulk-actions-sse-refresh.md
Claude Agent 7b3541403f feat(data-entry): Bulk Receipt Upload cu Mobile UX Android Nativ
## Funcționalități Principale

### Bulk Upload & Processing
- Drag & drop pentru upload bonuri multiple oriunde pe pagină
- Batch processing cu job queue și worker pool
- Real-time updates via SSE (Server-Sent Events) cu fallback polling
- Duplicate detection via SHA-256 file hash
- Auto-retry pentru job-uri failed
- Cancel individual jobs sau batch complet

### Mobile UX - Android Native Style
- Top bar fixă cu hamburger, titlu centrat, acțiuni (search/filter)
- Bottom navigation cu 4 tab-uri (Bonuri, Upload, Rapoarte, Setări)
- FAB (Floating Action Button) cu hide/show on scroll
- Filter chips orizontal scrollabile
- Selecție multiplă prin long-press (500ms)
- Select All + Bulk Delete cu confirmare
- Layout Android pentru Create/Edit/View bon (Gmail compose style)

### Bug Fixes
- Refresh individual via SSE în loc de refresh total pagină
- Bonurile cu eroare OCR rămân vizibile pentru editare manuală
- Afișare nume fișier original pentru toate bonurile
- Upload stabil pe mobil (fix race condition File API)
- Păstrare ordine bonuri la refresh (nu se reordonează)

### Backend
- SSE endpoint pentru status updates real-time
- Bulk delete endpoint cu partial success
- Auto-cleanup bonuri failed după 7 zile
- Batch model cu tracking complet

### Testing
- E2E tests cu Playwright
- Unit tests pentru bulk upload, auto-create, cleanup

## Commits Squashed: 43 user stories (US-001 → US-043)
## Branch: ralph/bulk-receipt-upload
## Timp dezvoltare: ~3 zile (Ralph autonomous)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 08:33:17 +00:00

301 lines
11 KiB
Markdown

# PRD: Bulk Actions & SSE Real-time Status Updates
## Branch de lucru
> **IMPORTANT**: Toate implementările din acest PRD trebuie făcute în branch-ul existent:
> `ralph/bulk-receipt-upload`
---
## 1. Introducere
Pagina de listă bonuri fiscale (`ReceiptsListView.vue`) are deja funcționalitate de selecție multiplă (select all și individual), dar lipsesc acțiunile bulk (în special ștergerea). De asemenea, actualizarea statusului bonurilor la bulk import se face prin polling periodic care reîncarcă toată lista, ceea ce creează o experiență vizuală neplăcută (flicker, pierdere scroll, etc.).
Acest PRD adresează:
1. **Bulk Delete** - posibilitatea de a șterge mai multe bonuri odată
2. **SSE Real-time Updates** - actualizări incrementale ale statusului în loc de refresh total
---
## 2. Obiective
### Obiectiv Principal
- Permiterea ștergerii în masă a bonurilor selectate cu UX fluid
### Obiective Secundare
- Eliminarea refresh-ului total al paginii la actualizarea statusului bonurilor
- Actualizări real-time via SSE pentru starea bonurilor în procesare
- Experiență vizuală smooth fără flicker
### Metrici de Succes
- Ștergerea a 10+ bonuri să dureze <2s
- Nicio reîncărcare completă a listei la update status
- SSE reducă request-urile de polling cu 80%+
---
## 3. User Stories
### US-001: Buton Șterge în Bulk Actions Bar
**Ca** utilizator
**Vreau** văd un buton "Șterge" când am selecții
**Pentru că** vreau pot șterge mai multe bonuri odată
**Acceptance Criteria:**
- [ ] Butonul "Șterge" apare în bulk actions bar când `selectedReceipts.length > 0`
- [ ] Butonul e vizibil lângă "Validează selectate" și "Deselectează" în header inline
- [ ] Butonul are icon `pi-trash` și severity `danger`
- [ ] Bonurile în procesare (pending/processing) NU sunt selectabile (deja implementat - verifică funcționează)
- [ ] npm run typecheck passes
- [ ] Verify in browser: butonul apare doar când există selecții
---
### US-002: Dialog Confirmare Ștergere Bulk
**Ca** utilizator
**Vreau** o confirmare simplă înainte de ștergere
**Pentru că** vreau evit ștergeri accidentale
**Acceptance Criteria:**
- [ ] La click pe "Șterge", apare dialog cu mesaj: "Ești sigur vrei ștergi {N} bonuri?"
- [ ] Dialog-ul are 2 butoane: "Anulează" (secondary) și "Șterge" (danger)
- [ ] Dialog-ul folosește componenta PrimeVue `ConfirmDialog` sau `Dialog`
- [ ] npm run typecheck passes
- [ ] Verify in browser: dialogul apare cu numărul corect de bonuri
---
### US-003: Backend Endpoint Bulk Delete
**Ca** frontend
**Vreau** pot trimite o listă de ID-uri pentru ștergere
**Pentru că** e mai eficient decât request-uri individuale
**Acceptance Criteria:**
- [ ] Endpoint `DELETE /api/data-entry/receipts/bulk` acceptă body: `{ "ids": [1, 2, 3] }`
- [ ] Validează fiecare bon e în status DRAFT și creat de user-ul curent
- [ ] Returnează partial success: `{ "deleted": [1, 2], "failed": [{"id": 3, "error": "..."}] }`
- [ ] Șterge atașamentele și înregistrările contabile asociate
- [ ] Bonurile în procesare nu pot fi șterse - returnează eroare specifică
- [ ] npm run typecheck passes (pentru Python: ruff check)
---
### US-004: Frontend Bulk Delete cu Partial Success
**Ca** utilizator
**Vreau** văd rezultatul ștergerii
**Pentru că** vreau știu dacă toate bonurile au fost șterse
**Acceptance Criteria:**
- [ ] După ștergere, toast arată rezultatul: "X bonuri șterse" sau "X din Y șterse, Z au eșuat"
- [ ] Bonurile șterse dispar instant din listă (fără animație per specificații)
- [ ] Lista se actualizează local (nu re-fetch complet dacă nu e necesar)
- [ ] Stats se actualizează după ștergere
- [ ] Selecția se golește după ștergere
- [ ] npm run typecheck passes
- [ ] Verify in browser: bonurile dispar instant, toast corect
---
### US-005: Navigare la Pagina Anterioară când Lista Devine Goală
**Ca** utilizator
**Vreau** fiu redirecționat automat la pagina anterioară
**Pentru că** nu vreau văd o pagină goală
**Acceptance Criteria:**
- [ ] După bulk delete, dacă pagina curentă devine goală și există pagini anterioare, navighează la pagina anterioară
- [ ] Dacă eram pe pagina 1 și devine goală, afișează empty state
- [ ] npm run typecheck passes
- [ ] Verify in browser: navigare automată funcționează
---
### US-006: Backend SSE Endpoint pentru Status Updates
**Ca** frontend
**Vreau** primesc notificări real-time despre schimbări de status
**Pentru că** vreau actualizez UI-ul fără polling
**Acceptance Criteria:**
- [ ] Endpoint `GET /api/data-entry/receipts/sse/status` returnează Server-Sent Events
- [ ] Conexiunea e persistent și trimite evenimente la schimbări de status
- [ ] Format eveniment: `data: {"receipt_id": 123, "status": "completed", "processing_status": "completed"}`
- [ ] Suport pentru filtrare pe batch_id (query param)
- [ ] Include timeout handling și reconnect hints
- [ ] ruff check passes
---
### US-007: Frontend SSE Client pentru Status Updates
**Ca** frontend
**Vreau** conectez la SSE și actualizez rândurile individual
**Pentru că** vreau actualizări smooth fără reload
**Acceptance Criteria:**
- [ ] Creează serviciu SSE în `src/modules/data-entry/services/sseService.js`
- [ ] Conectare automată când există bonuri în procesare în listă
- [ ] La primire eveniment, actualizează doar rândul afectat (nu toată lista)
- [ ] Deconectare automată când nu mai sunt bonuri în procesare
- [ ] Retry logic cu exponential backoff la disconnect
- [ ] npm run typecheck passes
- [ ] Verify in browser: statusul se actualizează în timp real fără flicker
---
### US-008: Înlocuire Polling cu SSE
**Ca** frontend
**Vreau** folosesc SSE în loc de polling interval
**Pentru că** e mai eficient și mai smooth
**Acceptance Criteria:**
- [ ] Elimină `setInterval` pentru auto-refresh când SSE e activ
- [ ] Fallback la polling dacă SSE nu e disponibil (WebSocket blocked, etc.)
- [ ] Când toate bonurile din batch sunt procesate, închide conexiunea SSE
- [ ] Logging pentru debug (console.log la conectare/deconectare/evenimente)
- [ ] npm run typecheck passes
- [ ] Verify in browser: consola arată evenimente SSE, nu polling requests
---
### US-009: Update Row Individual fără Re-render Lista
**Ca** frontend
**Vreau** actualizez un singur rând fără re-renderez toată lista
**Pentru că** vreau performanță și stabilitate vizuală
**Acceptance Criteria:**
- [ ] Creează metodă `updateReceiptInPlace(receiptId, updates)` în store
- [ ] Metoda modifică doar obiectul specific din array, nu înlocuiește array-ul
- [ ] Vue reactivity detectează schimbarea și actualizează doar rândul afectat
- [ ] Stats se actualizează separat (poate necesita re-fetch stats)
- [ ] npm run typecheck passes
- [ ] Verify in browser: DevTools Performance - doar un rând se re-renderează
---
### US-010: Graceful Degradation la SSE Failure
**Ca** utilizator
**Vreau** ca aplicația funcționeze și fără SSE
**Pentru că** pot avea probleme de rețea sau proxy
**Acceptance Criteria:**
- [ ] Dacă SSE fail la connect, fallback la polling clasic (interval 5s)
- [ ] Mesaj discret în consolă (nu toast pentru user)
- [ ] Retry SSE periodic (la 30s) pentru a vedea dacă funcționează din nou
- [ ] npm run typecheck passes
- [ ] Verify in browser: deconectează SSE, verifică polling-ul preia
---
## 4. Cerințe Funcționale
1. [REQ-001] Butonul "Șterge" trebuie fie roșu (severity danger) cu icon trash
2. [REQ-002] Bonurile cu `processing_status` în `pending` sau `processing` nu pot fi șterse
3. [REQ-003] Ștergerea bulk trebuie șteargă și atașamentele și înregistrările contabile asociate
4. [REQ-004] SSE trebuie trimită evenimente pentru: status change, processing_status change
5. [REQ-005] Frontend trebuie mențină conexiunea SSE doar când există bonuri în procesare
6. [REQ-006] La ștergerea ultimului bon de pe pagină, navighează automat la pagina anterioară
---
## 5. Non-Goals (Ce NU facem)
- Bulk edit (modificare date pe mai multe bonuri) - poate fi un alt PRD
- Animații fade/slide la ștergere - utilizatorul a cerut instant remove
- Undo pentru ștergere - nu e în scope
- WebSocket în loc de SSE - SSE e mai simplu și suficient
- Notificări push/toast pentru statusuri - doar actualizare în tabel
---
## 6. Considerații Tehnice
### Stack/Tehnologii
- **Backend**: FastAPI cu SSE support (StreamingResponse)
- **Frontend**: Vue 3 Composition API, Pinia store
- **SSE**: Native EventSource API (bun suport browser)
### Patterns de Urmat
- Service pattern în backend (nu logică în router)
- Store actions în frontend pentru mutații
- `@cached` decorator DOAR pentru reads, nu pentru deletes
### Dependențe
- PrimeVue ConfirmDialog sau Dialog pentru confirmare
- FastAPI `StreamingResponse` pentru SSE
- Deja există: bulk actions bar, selection logic, checkbox disable pentru processing
### Riscuri Tehnice
- SSE poate fi blocat de proxy/firewall - mitigat cu fallback la polling
- Concurrency la delete bulk - poate conflicta cu procesare - mitigat cu check status
### Structură Fișiere Noi
```
backend/modules/data_entry/
├── routers/
│ └── receipts.py # Adaugă DELETE /bulk și GET /sse/status
├── services/
│ └── sse_service.py # NOU: SSE broadcaster
└── schemas/
└── receipt.py # Adaugă BulkDeleteRequest, BulkDeleteResponse
src/modules/data-entry/
├── services/
│ └── sseService.js # NOU: SSE client
├── stores/
│ └── receiptsStore.js # Adaugă updateReceiptInPlace
└── views/receipts/
└── ReceiptsListView.vue # Adaugă buton Șterge, integrare SSE
```
---
## 7. Considerații UI/UX
### Layout
- Butonul Șterge: în bulk actions bar, header inline, lângă celelalte butoane
- Dialog confirmare: centered modal, compact
### Stări
- **Loading delete**: butonul Șterge arată spinner
- **Success**: toast verde "X bonuri șterse"
- **Partial fail**: toast warning "X din Y șterse, Z au eșuat"
- **SSE connected**: indicator vizual opțional (poate în devtools)
### Accesibilitate
- Dialog accesibil (focus trap, escape to close)
- Buton cu label clear
---
## 8. Success Metrics
- Bulk delete 10 bonuri: < 2 secunde end-to-end
- Zero full page refreshes la status updates
- SSE latency: < 500ms de la schimbare status la UI update
- Fallback polling activat în < 5s dacă SSE eșuează
---
## 9. Open Questions
- [x] Confirmare: simplă cu count
- [x] Bonuri în procesare: exclude automat
- [x] Polling: SSE
- [x] Error handling: partial success
- [x] Bara acțiuni: header inline
- [x] Animații: instant remove
- [x] Pagină goală: du la pagina anterioară
---
## 10. Ordine Implementare Recomandată
1. **US-003** - Backend bulk delete (fundație)
2. **US-001** - Buton Șterge în UI
3. **US-002** - Dialog confirmare
4. **US-004** - Frontend bulk delete cu toast
5. **US-005** - Navigare pagină anterioară
6. **US-009** - Update row in place (pregătire pentru SSE)
7. **US-006** - Backend SSE endpoint
8. **US-007** - Frontend SSE client
9. **US-008** - Înlocuire polling cu SSE
10. **US-010** - Graceful degradation