## 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>
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
onUnmountedpentru 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_idapar 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"șigroupRowsBy="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 batchprocessing_status(enum: pending/processing/completed/failed, nullable)processing_error(text, nullable) - mesaj eroare completfile_hash(string, nullable) - SHA-256 pentru duplicate detectionprocessing_started_at(datetime, nullable)processing_completed_at(datetime, nullable)
- Index pe
batch_idpentru query-uri eficiente - Index pe
file_hashpentru 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_statusfuncționează - Filtrare pe
batch_idfuncționează - Sorting pe
processing_started_atfuncț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-uploadroute - Ș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
- [REQ-001] Drag & drop pe toată pagina de bonuri activează upload overlay
- [REQ-002] Bonurile din batch-uri sunt grupate vizual în tabel
- [REQ-003] Coloană dedicată pentru status procesare (pending/processing/success/failed)
- [REQ-004] Mesajele de eroare sunt vizibile direct în listă
- [REQ-005] Fișierele duplicate (același hash) sunt respinse automat
- [REQ-006] Retry disponibil per bon și per batch pentru erori
- [REQ-007] Bonurile cu erori se șterg automat după 7 zile
- [REQ-008] Starea procesării persistă la refresh
- [REQ-009] Bonurile în procesare sunt read-only (locked)
- [REQ-010] Procesarea folosește configurația existentă din .env pentru workers paraleli
- [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.jsexistent - Refolosește componente din
components/bulk/unde posibil - Extinde
receiptsStore.jscu 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:
- US-011: Backend - Extindere schema DB
- US-012: Backend - Endpoint list cu batch info
- US-007: Backend - Duplicate detection (file hash)
- US-001: Frontend - Drag Anywhere Overlay
- US-002: Frontend - Row Grouping per Batch
- US-003: Frontend - Processing Status Column
- US-004: Frontend - Error Message Display
- US-005: Frontend - Quick Filter Chips
- US-010: Frontend - Lock Row în Procesare
- US-006: Frontend - Retry Individual + All
- US-009: Frontend - Persistence at Refresh
- US-008: Backend - Auto-Cleanup Job
- 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)