Files
roa2web-service-auto/tasks/prd-bulk-upload-list-integration.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

21 KiB

PRD: Bulk Upload Integration in Receipts List

1. Introducere

Funcționalitatea actuală de bulk upload există într-o pagină separată (BulkUploadView.vue). Când un fișier dă eroare la procesare, utilizatorul nu îl mai regăsește - acesta "dispare" din UI. Această funcționalitate integrează bulk upload direct în lista principală de bonuri, permițând vizibilitatea completă a tuturor bonurilor: în curs de upload, în procesare, procesate cu succes și cu erori.

Problema de rezolvat: Bonurile cu erori din bulk upload se pierd și nu mai pot fi regăsite. Utilizatorul nu are vizibilitate asupra batch-ului după ce părăsește pagina de bulk upload.

Soluția: Integrare completă în lista de bonuri cu:

  • Row grouping vizual per batch
  • Coloană pentru batch ID și status procesare
  • Mesaje de eroare vizibile în listă
  • Drag & drop overlay pe toată pagina
  • Quick filter chips pentru statusuri de procesare

2. Obiective

Obiectiv Principal

  • Vizibilitate completă a tuturor bonurilor din bulk upload direct în lista principală

Obiective Secundare

  • Eliminarea paginii separate de bulk upload (consolidare UX)
  • Recuperarea bonurilor cu erori fără a le pierde
  • Persistență: bonurile rămân vizibile chiar și după refresh/revenire

Metrici de Succes

  • 0 bonuri "pierdute" după erori OCR
  • 100% tracking vizibil pentru toate batch-urile
  • Timp de onboarding pentru bulk upload < 30 secunde

3. User Stories

US-001: Drag Anywhere pentru Upload

Ca utilizator data-entry Vreau să pot trage fișiere oriunde pe pagina de bonuri Pentru că vreau un flow natural fără să caut zona specifică de upload

Acceptance Criteria:

  • Tragerea unui fișier oriunde pe pagina de bonuri activează un overlay fullscreen
  • Overlay-ul arată: icon upload + text "Eliberează pentru a încărca X fișiere"
  • La eliberare, fișierele se validează și se uploadează
  • Fișierele invalide se afișează în toast cu motivul
  • Overlay dispare dacă utilizatorul trage fișierele în afara paginii
  • npm run typecheck passes
  • Verify in browser that overlay appears on drag

Detalii tehnice:

  • Global event listeners pe window: dragenter, dragover, dragleave, drop
  • Cleanup listeners în onUnmounted pentru a evita memory leaks
  • e.preventDefault() pe toate evenimentele pentru a preveni deschiderea fișierelor în browser

US-002: Row Grouping per Batch în DataTable

Ca utilizator Vreau să văd bonurile din același batch grupate vizual Pentru că vreau să identific rapid care bonuri aparțin aceluiași upload

Acceptance Criteria:

  • Bonurile cu același batch_id apar grupate în tabel
  • Header de grup expandabil: "Batch B-001 • 12 Jan 2026 • 15 fișiere"
  • Click pe header expandează/colapsează grupul
  • Bonurile fără batch (create manual) apar în grupul "Alte bonuri"
  • Grupurile sunt sortate după timestamp (cel mai recent sus)
  • Grupul în procesare este automat expandat
  • npm run typecheck passes
  • Verify in browser that groups expand/collapse correctly

Detalii tehnice:

  • PrimeVue DataTable cu rowGroupMode="subheader" și groupRowsBy="batch_id"
  • Custom header slot pentru styling
  • State local pentru expanded groups

US-003: Coloană Status Batch în Tabel

Ca utilizator Vreau să văd statusul fiecărui bon din batch într-o coloană dedicată Pentru că vreau să știu rapid care bonuri au reușit și care au erori

Acceptance Criteria:

  • Coloană nouă "Procesare" după coloana "Status" existentă
  • Valori posibile:
    • pending - "În așteptare" (gri)
    • processing - "Se procesează..." + spinner (albastru)
    • completed - "✓ Procesat" (verde)
    • failed - "✗ Eroare" (roșu) cu expand pentru mesaj
  • Bonurile manuale (fără batch) arată "-" în această coloană
  • Click pe "✗ Eroare" deschide tooltip/popover cu mesajul complet
  • npm run typecheck passes
  • Verify in browser that status updates in real-time

CSS Design Tokens:

.processing-pending { background: var(--surface-hover); color: var(--text-color-secondary); }
.processing-active  { background: var(--blue-50); color: var(--blue-600); }
.processing-success { background: var(--green-50); color: var(--green-600); }
.processing-failed  { background: var(--red-50); color: var(--red-600); }

US-004: Mesaj Eroare Vizibil în Listă

Ca utilizator Vreau să văd mesajul de eroare direct în listă (prescurtat) Pentru că vreau să înțeleg problema fără să deschid detalii

Acceptance Criteria:

  • Pentru bonuri cu status=failed, se afișează primele 50 caractere din mesaj
  • Mesajul e trunchiat cu "..." dacă depășește 50 caractere
  • Hover/click arată mesajul complet într-un tooltip
  • Mesajul e afișat sub rândul principal (inline expand) sau în popover
  • npm run typecheck passes
  • Verify in browser that full error message is accessible

Exemple de mesaje:

  • "OCR failed: format nerecunoscut pentru bon"
  • "Duplicate: bon similar existent (ID: 123)"
  • "Validare: suma totală nu poate fi 0"

US-005: Quick Filter Chips pentru Statusuri Procesare

Ca utilizator Vreau filtre rapide pentru a vedea doar bonurile cu erori sau în procesare Pentru că vreau să mă concentrez pe ce necesită atenție

Acceptance Criteria:

  • Chips noi în rândul de statusuri existente:
    • "În procesare (3)" - bonuri cu processing_status='processing' sau 'pending'
    • "Cu erori (2)" - bonuri cu processing_status='failed'
  • Chips apar doar când există batch-uri active (count > 0)
  • Click pe chip filtrează lista
  • Chips sunt colorat conform statusului (albastru/roșu)
  • npm run typecheck passes
  • Verify in browser that filtering works correctly

US-006: Retry Individual și Retry All Failed

Ca utilizator Vreau să pot re-procesa bonurile cu erori Pentru că unele erori pot fi temporare sau vreau să încerc din nou

Acceptance Criteria:

  • Buton "Reîncercă" pe fiecare rând cu eroare
  • Buton "Reîncercă toate erorile" în header-ul grupului de batch
  • La retry, statusul revine la "pending" și se re-uploadează fișierul
  • Retry păstrează batch_id original
  • Dacă fișierul original nu mai există, se afișează eroare
  • npm run typecheck passes
  • Verify in browser that retry updates status correctly

Detalii tehnice:

  • Fișierele originale trebuie păstrate în storage până la cleanup (7 zile)
  • Retry apelează același endpoint de upload cu job_id existent

US-007: Reject Automat pentru Duplicate (File Hash)

Ca sistem Vreau să detectez și să reject fișierele duplicate la upload Pentru că utilizatorul nu trebuie să proceseze același bon de două ori

Acceptance Criteria:

  • La upload, se calculează SHA-256 hash al fișierului
  • Dacă hash-ul există deja în DB, upload-ul e respins
  • Mesaj: "Fișier duplicat - există deja ca bon #123"
  • Link direct către bonul existent
  • Verificarea se face înainte de a crea job-ul OCR
  • pytest tests pass
  • API returns correct error schema for duplicates

US-008: Auto-Cleanup Erori După 7 Zile

Ca sistem Vreau să șterg automat bonurile cu erori după 7 zile Pentru că vreau să păstrez lista curată fără intervenție manuală

Acceptance Criteria:

  • Background job zilnic verifică bonurile cu processing_status='failed'
  • Bonurile mai vechi de 7 zile se șterg automat
  • Fișierele atașate se șterg și ele
  • Notificare toast la login: "3 bonuri cu erori au fost șterse (expirate)"
  • Utilizatorul poate vedea/extinde TTL în setări (opțional, nice-to-have)
  • pytest tests pass

US-009: Auto-Resume Polling la Refresh/Revenire

Ca utilizator Vreau ca procesarea să continue și să văd statusul actualizat automat când revin Pentru că nu vreau să pierd progresul sau să fac acțiuni manuale

Acceptance Criteria:

  • Procesarea OCR pe backend CONTINUĂ indiferent de starea browser-ului
  • La refresh/revenire, frontend detectează batch-uri incomplete și reia polling automat
  • Starea se stochează în localStorage: active_batch_ids
  • La completare, se curăță din localStorage
  • Dacă utilizatorul revine după ce procesarea s-a terminat, vede statusul final corect
  • npm run typecheck passes
  • Verify in browser that progress resumes after refresh

Detalii tehnice:

  • Backend worker pool procesează independent de frontend
  • La onMounted, verifică localStorage pentru batch-uri active
  • Query backend pentru status curent al fiecărui batch
  • Reia polling doar pentru batch-uri care încă nu sunt complete
  • Afișează toast: "Procesare în curs detectată, se actualizează..."

US-010: Lock Row în Procesare (Read-Only)

Ca utilizator Vreau ca bonurile în procesare să fie read-only Pentru că nu are sens să editez un bon care încă nu e complet

Acceptance Criteria:

  • Bonuri cu processing_status='pending' sau 'processing' au butoanele dezactivate
  • Visual: row are opacity 0.7 sau border-left albastru
  • Tooltip pe butoane: "Bonul se procesează, vă rugăm așteptați"
  • După completare, row-ul devine interactiv normal
  • npm run typecheck passes
  • Verify in browser that buttons are disabled during processing

US-011: Backend - Stocare Batch și Processing Status

Ca developer Vreau să extind schema Receipt pentru a stoca informații de batch Pentru că am nevoie de persistență pentru tracking

Acceptance Criteria:

  • Câmpuri noi în tabelul receipts:
    • batch_id (string, nullable) - UUID batch
    • processing_status (enum: pending/processing/completed/failed, nullable)
    • processing_error (text, nullable) - mesaj eroare complet
    • file_hash (string, nullable) - SHA-256 pentru duplicate detection
    • processing_started_at (datetime, nullable)
    • processing_completed_at (datetime, nullable)
  • Index pe batch_id pentru query-uri eficiente
  • Index pe file_hash pentru duplicate detection
  • Migration reversibilă
  • pytest tests pass
  • Alembic migration works

US-012: Backend - Endpoint List cu Batch Info

Ca developer Vreau să extind endpoint-ul GET /receipts pentru a include info de batch Pentru că frontend-ul are nevoie de toate datele într-un singur request

Acceptance Criteria:

  • Response include câmpurile noi pentru fiecare receipt
  • Filtrare pe processing_status funcționează
  • Filtrare pe batch_id funcționează
  • Sorting pe processing_started_at funcționează
  • Include count-uri pentru fiecare processing_status în response (pentru chips)
  • pytest tests pass

US-013: Eliminare Pagină Separată Bulk Upload

Ca developer Vreau să elimin pagina separată de bulk upload Pentru că funcționalitatea e acum integrată în lista principală

Acceptance Criteria:

  • Șterge /data-entry/bulk-upload route
  • Șterge BulkUploadView.vue
  • Redirect de la vechea rută la /data-entry (pentru bookmarks)
  • Actualizare meniu/navigație să nu mai arate link-ul separat
  • npm run typecheck passes
  • Verify in browser that old route redirects correctly

4. Cerințe Funcționale

  1. [REQ-001] Drag & drop pe toată pagina de bonuri activează upload overlay
  2. [REQ-002] Bonurile din batch-uri sunt grupate vizual în tabel
  3. [REQ-003] Coloană dedicată pentru status procesare (pending/processing/success/failed)
  4. [REQ-004] Mesajele de eroare sunt vizibile direct în listă
  5. [REQ-005] Fișierele duplicate (același hash) sunt respinse automat
  6. [REQ-006] Retry disponibil per bon și per batch pentru erori
  7. [REQ-007] Bonurile cu erori se șterg automat după 7 zile
  8. [REQ-008] Starea procesării persistă la refresh
  9. [REQ-009] Bonurile în procesare sunt read-only (locked)
  10. [REQ-010] Procesarea folosește configurația existentă din .env pentru workers paraleli
  11. [REQ-011] Procesarea backend continuă independent de browser; polling se reia automat la revenire

5. Non-Goals (Ce NU facem)

  • NU implementăm WebSocket pentru status updates (rămânem pe long-polling existent)
  • NU adăugăm suport pentru editare inline în listă
  • NU implementăm preview imagine în row (poate fi adăugat ulterior)
  • NU facem grouping recursiv (batch în batch)
  • NU implementăm undo pentru retry
  • NU adăugăm notificări push/email la completare batch
  • NU facem drag & drop pentru reordonare (doar upload)

6. Considerații Tehnice

Schema DB Extinsă

ALTER TABLE receipts ADD COLUMN batch_id VARCHAR(36);
ALTER TABLE receipts ADD COLUMN processing_status VARCHAR(20)
  CHECK (processing_status IN ('pending', 'processing', 'completed', 'failed'));
ALTER TABLE receipts ADD COLUMN processing_error TEXT;
ALTER TABLE receipts ADD COLUMN file_hash VARCHAR(64);
ALTER TABLE receipts ADD COLUMN processing_started_at TIMESTAMP;
ALTER TABLE receipts ADD COLUMN processing_completed_at TIMESTAMP;

CREATE INDEX idx_receipts_batch_id ON receipts(batch_id);
CREATE INDEX idx_receipts_file_hash ON receipts(file_hash);
CREATE INDEX idx_receipts_processing_status ON receipts(processing_status);

Component Architecture

ReceiptsListView.vue (enhanced)
├── DragDropOverlay.vue (new - fullscreen overlay)
├── BatchGroupHeader.vue (new - expandable group header)
├── ProcessingStatusCell.vue (new - status + error display)
├── QuickFilterChips.vue (enhanced - add processing filters)
└── DataTable (PrimeVue with row grouping)

Store Changes

// receiptsStore.js - extended
state: {
  // ... existing
  processingStats: {
    pending_count: 0,
    processing_count: 0,
    failed_count: 0
  },
  activeBatchIds: [] // for localStorage persistence
}

// batchProgressStore.js - reuse existing
// Just connect to receiptsStore for updates

⚠️ REGULI CSS OBLIGATORII

CITEȘTE ÎNTÂI: docs/ONBOARDING_CSS.md și docs/DESIGN_TOKENS.md

Processing Status Colors

Status Background Text/Icon Border
Pending var(--surface-hover) var(--text-color-secondary) -
Processing var(--blue-50) var(--blue-600) var(--blue-500)
Success var(--green-50) var(--green-600) -
Failed var(--red-50) var(--red-600) var(--red-500)

Drag Overlay

.drag-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.7);
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
}

.drag-overlay-content {
  background: var(--surface-card);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  text-align: center;
  border: 3px dashed var(--primary-500);
}

Row Grouping Header

.batch-group-header {
  background: var(--surface-ground);
  padding: var(--space-sm) var(--space-md);
  font-weight: var(--font-semibold);
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  cursor: pointer;
}

.batch-group-header:hover {
  background: var(--surface-hover);
}

Dependențe

  • Refolosește batchProgressStore.js existent
  • Refolosește componente din components/bulk/ unde posibil
  • Extinde receiptsStore.js cu câmpuri noi

Riscuri Tehnice

  • Row grouping performance: Pentru liste mari (>500 bonuri), grouping poate fi lent
    • Mitigare: Virtualizare sau paginare strictă
  • LocalStorage limits: Dacă sunt multe batch-uri active
    • Mitigare: Stochează doar ID-uri, nu date complete
  • Race condition la retry: Dacă utilizatorul face retry în timp ce procesarea originală se termină
    • Mitigare: Check status înainte de retry, abort dacă deja completat

7. Considerații UI/UX

Layout Update

ReceiptsListView.vue
├── PageHeader
├── QuickFilterChips (enhanced: + Processing | Erori)
├── FiltersRow (existing)
├── DragDropOverlay (fullscreen, hidden until drag)
└── DataTable
    ├── BatchGroupHeader (expandable)
    │   └── Receipt rows (with new Processing column)
    ├── BatchGroupHeader
    │   └── Receipt rows
    └── "Alte bonuri" group (receipts fără batch)

Stări UI

Stare Visual
Idle Pagină normală, fără overlay
Dragging Overlay fullscreen cu drop zone
Uploading Toast progress + batch nou apare în listă
Processing Rows cu spinner, read-only
Complete Rows normale, toast success
Has Errors Rows roșii cu mesaj, buton retry

Mobile Considerations

  • Drag & drop nu funcționează pe mobile - oferă buton "Upload" explicit
  • Row grouping se transformă în cards grupate
  • Error messages în accordion expandabil

8. Success Metrics

  • Zero bonuri pierdute: 100% vizibilitate pentru toate fișierele uploadate
  • Retry success rate: > 50% dintre erorile retry-uite să reușească
  • Auto-cleanup: < 100 bonuri cu erori la orice moment (TTL 7 zile)
  • User satisfaction: Reducere support tickets pentru "nu găsesc bonul"

9. Open Questions

  • Comportament la refresh → Backend continuă procesarea, frontend reia polling automat la revenire
  • Auto-cleanup după 7 zile → Confirmat
  • Retry individual + batch → Confirmat ambele
  • Row complet blocat în procesare → Confirmat
  • Grupare vizuală per batch → Confirmat, row grouping
  • Mesaj eroare prescurtat → Confirmat, 50 caractere
  • Drag anywhere → Confirmat, overlay fullscreen
  • Quick filter chips → Confirmat
  • Reject duplicate (hash) → Confirmat
  • Batch info permanent → Confirmat, rămâne în DB
  • Workers paraleli → Configurabil din .env, existent

10. Dependențe între User Stories

US-011 (DB Schema) ─────────────────────────────────────────┐
        ↓                                                    │
US-012 (API List + Batch Info) ─────────────────────────────┤
        ↓                                                    │
US-007 (Duplicate Detection) ←───────────────────────────────┤
        ↓                                                    │
US-001 (Drag Anywhere) ─────────────────────────────────────┤
        ↓                                                    │
US-002 (Row Grouping) ←──────────────────────────────────────┤
        ↓                                                    │
US-003 (Processing Status Column) ←──────────────────────────┤
        ↓                                                    │
US-004 (Error Message Display) ←─────────────────────────────┤
        ↓                                                    │
US-005 (Quick Filter Chips)                                  │
        ↓                                                    │
US-010 (Lock Row Processing) ←───────────────────────────────┤
        ↓                                                    │
US-006 (Retry Individual + All)                              │
        ↓                                                    │
US-009 (Persistence at Refresh)                              │
        ↓                                                    │
US-008 (Auto-Cleanup 7 Days)                                 │
        ↓                                                    │
US-013 (Remove Old Bulk Page)                                │

Ordine recomandată de implementare:

  1. US-011: Backend - Extindere schema DB
  2. US-012: Backend - Endpoint list cu batch info
  3. US-007: Backend - Duplicate detection (file hash)
  4. US-001: Frontend - Drag Anywhere Overlay
  5. US-002: Frontend - Row Grouping per Batch
  6. US-003: Frontend - Processing Status Column
  7. US-004: Frontend - Error Message Display
  8. US-005: Frontend - Quick Filter Chips
  9. US-010: Frontend - Lock Row în Procesare
  10. US-006: Frontend - Retry Individual + All
  11. US-009: Frontend - Persistence at Refresh
  12. US-008: Backend - Auto-Cleanup Job
  13. US-013: Cleanup - Remove Old Bulk Page

Last Updated: 2026-01-11 Author: Claude Code Status: Draft - Pending Review Predecessor: prd-bulk-receipt-upload.md (implementat)