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

407 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PRD: Bulk Receipt Upload & Auto-Processing
## 1. Introducere
Sistemul actual permite upload-ul și procesarea unui singur bon la un moment dat, cu intervenție manuală la editare/salvare. Această funcționalitate adaugă o pagină separată pentru upload multiple bonuri (PDF/PNG/JPG) care sunt procesate automat prin OCR și salvate direct în baza de date, fără intervenție manuală.
**Context tehnic:** Există deja infrastructura de job queue (SQLite) și worker pool pentru procesare paralelă OCR. Această funcționalitate va extinde sistemul existent.
## 2. Obiective
### Obiectiv Principal
- Permiterea upload-ului bulk de bonuri (10-50 fișiere) cu procesare automată end-to-end
### Obiective Secundare
- Reducerea timpului de introducere date cu 90%+ pentru batch-uri mari
- Vizibilitate în timp real asupra progresului procesării
- Separare clară între flow-ul manual (editare) și automat (bulk)
### Metrici de Succes
- Timp mediu per bon < 10 secunde (vs. 2-3 minute manual)
- Rata de succes OCR > 80% (bonuri procesate fără erori)
- Upload batch 50 bonuri în < 10 minute
## 3. User Stories
### US-001: Upload Multiple Fișiere
**Ca** utilizator data-entry
**Vreau** selectez/trag multiple fișiere (PDF/PNG/JPG) într-o zonă de upload
**Pentru că** vreau procesez un lot întreg de bonuri dintr-o dată
**Acceptance Criteria:**
- [ ] Drag & drop zone acceptă multiple fișiere simultan
- [ ] Click pe zonă deschide file picker cu multi-select activat
- [ ] Fișierele acceptate: PDF, PNG, JPG (max 10MB/fișier)
- [ ] Fișierele invalide sunt ignorate cu mesaj de avertizare
- [ ] Lista fișierelor selectate apare sub zona de upload
- [ ] Buton "Șterge" per fișier pentru eliminare din batch
- [ ] npm run typecheck passes
- [ ] Verify in browser that files appear in list after selection
- [ ] **CSS:** Drop zone folosește `var(--surface-card)` background, `var(--surface-border)` border
- [ ] **CSS:** Drop zone hover/active folosește `var(--blue-50)` background
- [ ] **CSS:** Spacing între elemente folosește tokens (`--space-md`, `--space-lg`)
- [ ] **CSS:** Testează în dark mode - drop zone vizibilă și contrastantă
### US-002: Vizualizare Batch Înainte de Submit
**Ca** utilizator
**Vreau** văd lista fișierelor selectate cu preview
**Pentru că** vreau verific am selectat fișierele corecte înainte de procesare
**Acceptance Criteria:**
- [ ] Lista arată: thumbnail (pentru imagini), nume fișier, mărime
- [ ] Pentru PDF-uri se arată icon generic PDF
- [ ] Counter total: "X fișiere selectate (Y MB)"
- [ ] Buton "Golește lista" pentru resetare completă
- [ ] Buton "Adaugă fișiere" pentru a adăuga la selecție existentă
- [ ] npm run typecheck passes
- [ ] Verify in browser that thumbnails render correctly
- [ ] **CSS:** Lista folosește pattern `.card` din `cards.css`
- [ ] **CSS:** Thumbnail cu `border-radius: var(--radius-md)`
- [ ] **CSS:** File size text cu `color: var(--color-text-secondary)`, `font-size: var(--text-sm)`
- [ ] **CSS:** Butoane folosesc clasele `.btn .btn-secondary` și `.btn .btn-primary`
- [ ] **CSS:** Testează în dark mode - thumbnails și text lizibile
### US-003: Submit Batch pentru Procesare
**Ca** utilizator
**Vreau** trimit toate fișierele pentru procesare cu un singur click
**Pentru că** vreau declanșez procesarea automată a întregului lot
**Acceptance Criteria:**
- [ ] Buton "Procesează X bonuri" submit-ează batch-ul
- [ ] La submit, toate fișierele se uploadează și se creează câte un OCR job per fișier
- [ ] După submit, UI-ul trece în modul "progres" (nu mai permite adăugare fișiere)
- [ ] Dacă un upload individual eșuează (network error), se reîncearcă automat (max 3 retry)
- [ ] npm run typecheck passes
- [ ] Verify in browser that submit disables file addition
### US-004: Progres Real-Time per Fișier
**Ca** utilizator
**Vreau** văd progresul fiecărui fișier în timp real
**Pentru că** vreau știu câte bonuri s-au procesat și câte mai sunt în așteptare
**Acceptance Criteria:**
- [ ] Fiecare fișier din listă arată status: "În așteptare" / "Se procesează..." / "Completat" / "Eroare"
- [ ] Status vizual diferențiat: badge/icon color-coded (gri/albastru/verde/roșu)
- [ ] La completare se arată confidence score overall (ex: "87% confidence")
- [ ] Progress bar global: "15/50 procesate"
- [ ] Timpul estimat rămas bazat pe average processing time
- [ ] npm run typecheck passes
- [ ] Verify in browser that status updates in real-time without page refresh
- [ ] **CSS:** Status badges folosesc culorile din tabel (vezi secțiunea 6):
- Pending: `background: var(--surface-hover)`, `color: var(--color-text-secondary)`
- Processing: `background: var(--blue-50)`, `color: var(--blue-600)` + spinner
- Success: `background: var(--green-50)`, `color: var(--green-600)`
- Error: `background: var(--red-50)`, `color: var(--red-600)`
- [ ] **CSS:** Progress bar folosește PrimeVue ProgressBar (stilizat global)
- [ ] **CSS:** Confidence score cu `font-family: var(--font-mono)` pentru numere
- [ ] **CSS:** Testează în dark mode - badges vizibile și contrastate
### US-005: Salvare Automată a Bonurilor Procesate
**Ca** sistem
**Vreau** salvez automat bonurile procesate cu succes în baza de date
**Pentru că** utilizatorul dorește procesare 100% automată fără intervenție
**Acceptance Criteria:**
- [ ] După OCR completat cu succes, se creează automat un receipt în DB
- [ ] Receipt-ul primește status "DRAFT" (poate fi editat ulterior dacă e nevoie)
- [ ] Se atașează automat fișierul original la receipt
- [ ] Se salvează toate câmpurile extrase: vendor, CUI, dată, sumă, TVA
- [ ] Se generează automat accounting entries (ca la flow-ul manual)
- [ ] npm run typecheck passes
- [ ] Verify that receipts appear in receipt list after bulk processing
### US-006: Gestionare Erori OCR
**Ca** utilizator
**Vreau** ca bonurile cu erori OCR fie marcate pentru review manual
**Pentru că** vreau procesez restul batch-ului fără blocaj, dar nu pierd bonurile problematice
**Acceptance Criteria:**
- [ ] La eroare OCR, fișierul primește status "Eroare" cu mesaj explicativ
- [ ] Bonurile cu erori rămân în lista vizibilă, nu sunt șterse
- [ ] Buton "Deschide în editor" pentru fiecare bon cu eroare (redirect la pagina de editare manuală)
- [ ] Procesarea celorlalte bonuri continuă independent
- [ ] La final se arată sumar: "45 procesate cu succes, 5 cu erori"
- [ ] npm run typecheck passes
- [ ] Verify in browser that error files show retry/edit options
- [ ] **CSS:** Error row cu `background: var(--red-50)`, `border-left: 3px solid var(--red-500)`
- [ ] **CSS:** Error message cu `color: var(--red-600)`, `font-size: var(--text-sm)`
- [ ] **CSS:** Action buttons în error row folosesc `.btn .btn-sm` pattern
- [ ] **CSS:** Testează în dark mode - erori vizibile dar nu agresive
### US-007: Rezumat Final Batch
**Ca** utilizator
**Vreau** văd un rezumat la finalul procesării batch-ului
**Pentru că** vreau știu câte bonuri s-au salvat și ce acțiuni mai am de făcut
**Acceptance Criteria:**
- [ ] Modal/panel de rezumat cu statistici: procesate OK, erori, total sumă
- [ ] Link direct la lista de receipts filtrat pe batch-ul curent (by date/user)
- [ ] Opțiune "Încarcă alt batch" pentru a începe de la zero
- [ ] Opțiune "Vezi bonurile cu erori" pentru review rapid
- [ ] npm run typecheck passes
- [ ] Verify in browser that summary modal shows correct counts
- [ ] **CSS:** Modal folosește PrimeVue Dialog (stilizat global) sau pattern `.card`
- [ ] **CSS:** Statistici success cu `color: var(--green-600)`, errors cu `color: var(--red-600)`
- [ ] **CSS:** Total sumă cu `font-size: var(--text-2xl)`, `font-weight: var(--font-bold)`, `font-family: var(--font-mono)`
- [ ] **CSS:** Spacing consistent: `padding: var(--space-lg)`, `gap: var(--space-md)`
- [ ] **CSS:** Testează în dark mode - modal și conținut lizibile
### US-008: Backend - Endpoint Batch Upload
**Ca** developer
**Vreau** un endpoint optimizat pentru upload multiple fișiere
**Pentru că** upload-ul secvențial ar fi prea lent pentru 50+ fișiere
**Acceptance Criteria:**
- [ ] POST `/api/data-entry/bulk/upload` acceptă multipart cu multiple fișiere
- [ ] Returnează lista de job_id-uri pentru tracking
- [ ] Validare: max 100 fișiere per batch, max 10MB per fișier
- [ ] Jobs se creează atomic (toate sau niciunul)
- [ ] Returnează și un batch_id pentru grouping
- [ ] pytest tests pass
- [ ] API returns correct response schema
### US-009: Backend - Auto-Save Receipt din OCR Result
**Ca** developer
**Vreau** un service care creează automat receipt-uri din rezultatele OCR
**Pentru că** flow-ul bulk trebuie fie end-to-end fără intervenție UI
**Acceptance Criteria:**
- [ ] `ReceiptAutoCreateService.create_from_ocr_result(job_id, ocr_result, user)`
- [ ] Mapare completă OCR fields Receipt fields
- [ ] Creare attachment cu fișierul original
- [ ] Generare accounting entries via existing logic
- [ ] Validare minimă: suma > 0, dată validă
- [ ] Return receipt_id sau error message
- [ ] pytest tests pass
### US-010: Backend - Batch Status Endpoint
**Ca** developer
**Vreau** un endpoint pentru status-ul întregului batch
**Pentru că** frontend-ul trebuie să poll-eze eficient pentru toate fișierele
**Acceptance Criteria:**
- [ ] GET `/api/data-entry/bulk/batches/{batch_id}/status`
- [ ] Returnează status agregat: pending_count, processing_count, completed_count, failed_count
- [ ] Include lista de job_id + status pentru fiecare fișier
- [ ] Include receipt_id pentru jobs completate cu succes
- [ ] Suportă long-polling (wait param) pentru eficiență
- [ ] pytest tests pass
## 4. Cerințe Funcționale
1. [REQ-001] Sistemul trebuie să accepte upload simultan de până la 100 fișiere
2. [REQ-002] Fiecare fișier trebuie să fie max 10MB
3. [REQ-003] Formatele acceptate: PDF, PNG, JPG, JPEG
4. [REQ-004] Procesarea trebuie să fie paralelă (max N workers din config)
5. [REQ-005] Bonurile procesate cu succes se salvează automat cu status DRAFT
6. [REQ-006] Bonurile cu erori rămân disponibile pentru retry/editare manuală
7. [REQ-007] Fișierele originale se atașează automat la receipt-uri
8. [REQ-008] Se generează automat accounting entries pentru fiecare receipt
9. [REQ-009] Batch-urile trebuie să fie tracked per user (nu se văd batch-urile altora)
10. [REQ-010] Job files se curăță automat după 24h (cleanup existing)
## 5. Non-Goals (Ce NU facem)
- **NU** facem aprobare automată (bonurile rămân DRAFT, nu APPROVED)
- **NU** facem machine learning pentru îmbunătățirea OCR-ului
- **NU** facem procesare pe server extern (totul rămâne local)
- **NU** facem notificări (email/push) la finalizare batch
- **NU** facem preview/editare în bulk page - pentru asta există pagina individuală
- **NU** facem undo/rollback batch (bonurile create pot fi șterse individual)
- **NU** facem scheduling (procesare imediată, nu amânată)
- **NU** facem duplicate detection (poate fi adăugat ulterior)
## 6. Considerații Tehnice
### Stack/Tehnologii
- **Frontend:** Vue 3 Composition API, PrimeVue (FileUpload, ProgressBar, DataTable)
- **Backend:** FastAPI, SQLite (job queue existent), SQLModel (receipts)
- **State:** Pinia store pentru batch progress tracking
### Patterns de Urmat
- Folosește `OCRJobQueue` existent pentru job management
- Extinde `job_worker.py` pentru auto-save la completare
- Folosește pattern-ul de polling din `OCRUploadZone.vue` existent
### ⚠️ REGULI CSS OBLIGATORII
**CITEȘTE ÎNTÂI:** `docs/ONBOARDING_CSS.md` și `docs/DESIGN_TOKENS.md`
#### Golden Rules
```
✅ Folosește DOAR design tokens - NICIODATĂ valori hardcodate
✅ Verifică CSS_PATTERNS.md înainte de a scrie CSS nou
✅ Testează în AMBELE teme (light + dark mode)
❌ NICIODATĂ :deep() în componente (PrimeVue → vendor/)
❌ NICIODATĂ duplicate CSS (write once, use everywhere)
```
#### Design Tokens Obligatorii
| Categorie | ❌ GREȘIT | ✅ CORECT |
|-----------|-----------|-----------|
| Spacing | `padding: 8px` | `padding: var(--space-sm)` |
| Spacing | `margin: 16px` | `margin: var(--space-md)` |
| Spacing | `gap: 24px` | `gap: var(--space-lg)` |
| Font size | `font-size: 14px` | `font-size: var(--text-sm)` |
| Font weight | `font-weight: 500` | `font-weight: var(--font-medium)` |
| Font weight | `font-weight: 600` | `font-weight: var(--font-semibold)` |
| Colors | `color: #111827` | `color: var(--color-text)` |
| Colors | `color: #6b7280` | `color: var(--color-text-secondary)` |
| Colors | `background: #ffffff` | `background: var(--surface-card)` |
| Colors | `background: #f0fdf4` | `background: var(--green-50)` |
| Colors | `border: #e5e7eb` | `border-color: var(--surface-border)` |
| Radius | `border-radius: 8px` | `border-radius: var(--radius-md)` |
| Shadow | `box-shadow: 0 4px 6px...` | `box-shadow: var(--shadow-md)` |
| Transition | `transition: 0.2s` | `transition: var(--transition-fast)` |
#### Spacing Scale Reference
| Token | Value | Use Case |
|-------|-------|----------|
| `--space-xs` | 4px | Icon gaps, badges |
| `--space-sm` | 8px | Between related items |
| `--space-md` | 16px | Component padding |
| `--space-lg` | 24px | Section padding, cards |
| `--space-xl` | 32px | Page margins |
#### Status Colors (pentru progres/erori)
| Status | Background | Text/Icon |
|--------|------------|-----------|
| Pending | `var(--surface-hover)` | `var(--color-text-secondary)` |
| Processing | `var(--blue-50)` | `var(--blue-600)` |
| Success | `var(--green-50)` | `var(--green-600)` |
| Error | `var(--red-50)` | `var(--red-600)` |
| Warning | `var(--yellow-50)` | `var(--yellow-600)` |
#### Dark Mode - OBLIGATORIU
- Folosește `--surface-*` tokens pentru backgrounds (auto-switch în dark mode)
- Testează cu theme toggle din header (auto → light → dark)
- NU folosi culori hardcodate care nu se schimbă în dark mode
#### Patterns Existente de Folosit
| Pattern | File | Use Case |
|---------|------|----------|
| `.card` | `cards.css` | Container principal |
| `.btn`, `.btn-primary` | `buttons.css` | Butoane |
| `.form-group`, `.form-label` | `forms.css` | Formulare |
| `.spinner` | `spinners.css` | Loading states |
| `.trend`, `.trend-up` | `trends.css` | Indicators |
| Utility classes | `utilities/` | `gap-md`, `text-center`, etc. |
#### PrimeVue Components
- FileUpload, ProgressBar, DataTable, Tag, Badge - toate sunt stilizate global
- NU adăuga `:deep()` în componente
- Modificări PrimeVue → `src/assets/css/vendor/primevue-overrides.css`
### Dependențe
- Job Queue existent: `backend/modules/data_entry/services/ocr/job_queue.py`
- Worker Pool existent: `backend/modules/data_entry/services/ocr/ocr_worker_pool.py`
- Receipt CRUD: `backend/modules/data_entry/db/crud/receipt.py`
- Attachment CRUD: `backend/modules/data_entry/db/crud/attachment.py`
### Riscuri Tehnice
- **Memory pressure:** Upload simultan de 100 fișiere × 10MB = 1GB potențial
- Mitigare: Upload secvențial intern, buffer 5 fișiere max în memorie
- **Queue overflow:** 100 jobs noi pot încetini procesarea existentă
- Mitigare: Worker pool deja limitează concurența
- **Browser crash:** Tab închis pierde tracking progress
- Mitigare: Jobs persistă în DB, refresh poate recupera status
## 7. Considerații UI/UX
### Layout
1. **Header:** Titlu "Upload Bulk Bonuri" + link înapoi la lista principală
2. **Drop Zone:** Mare, centrată, cu icon și text instructiv
3. **File List:** Tabel/listă sub drop zone cu progres per fișier
4. **Actions Bar:** Butoane "Procesează", "Golește lista" - sticky la bottom
### Layout CSS Structure
```
.bulk-upload-page
├── .page-header (pattern existent)
│ └── h1 + breadcrumb
├── .card (drop zone container)
│ └── .upload-zone (dashed border, centered)
├── .card (file list container)
│ └── DataTable sau custom list
└── .form-actions (sticky footer cu butoane)
```
### Stări UI cu CSS
| Stare | Background | Border | Elements |
|-------|------------|--------|----------|
| **Empty** | `var(--surface-card)` | `2px dashed var(--surface-border)` | Icon mare + text instructiv |
| **Drag Over** | `var(--blue-50)` | `2px dashed var(--blue-500)` | Border evidențiat |
| **Files Selected** | `var(--surface-card)` | `1px solid var(--surface-border)` | Lista + action buttons |
| **Processing** | `var(--surface-card)` | - | Spinner global + per-file status |
| **Complete** | `var(--green-50)` subtle | - | Summary card |
| **Has Errors** | - | - | Error items highlighted |
### Status Badge Styles
```css
/* Folosește PrimeVue Tag sau custom badges */
.status-pending { background: var(--surface-hover); color: var(--color-text-secondary); }
.status-processing { background: var(--blue-50); color: var(--blue-600); }
.status-success { background: var(--green-50); color: var(--green-600); }
.status-error { background: var(--red-50); color: var(--red-600); }
```
### Accesibilitate
- Keyboard navigation pentru file list
- Screen reader announcements la status changes
- Focus management la modal rezumat
- WCAG contrast ratios respectate (toate token-urile sunt compliant)
## 8. Success Metrics
- **Upload Success Rate:** > 99% (fișierele ajung în queue)
- **OCR Success Rate:** > 80% (bonuri procesate fără erori)
- **Average Processing Time:** < 8 secunde/bon
- **User Satisfaction:** Reducere timp introducere date cu 90%
## 9. Open Questions
- [ ] Limita de 100 fișiere este suficientă sau trebuie mărită?
- [ ] Dorim afișăm preview al datelor extrase înainte de save (ar contrazice "100% automat")?
- [ ] Ce facem cu bonurile duplicate detectate ulterior (același număr bon + dată)?
- [ ] Trebuie un "dry run" mode care procesează dar nu salvează?
---
## 10. Dependențe între User Stories
```
US-001 (Upload Files)
US-002 (Preview List) ← independent
US-003 (Submit Batch) → US-008 (Backend Upload Endpoint)
US-004 (Progress) ← US-010 (Backend Status Endpoint)
US-005 (Auto-Save) ← US-009 (Backend Auto-Create Service)
US-006 (Error Handling)
US-007 (Summary)
```
**Ordine recomandată de implementare:**
1. US-008: Backend - Batch Upload Endpoint
2. US-010: Backend - Batch Status Endpoint
3. US-009: Backend - Auto-Save Service
4. US-001: Frontend - Upload Zone
5. US-002: Frontend - File Preview
6. US-003: Frontend - Submit Batch
7. US-004: Frontend - Progress Tracking
8. US-005: Integration - Auto-Save Flow
9. US-006: Frontend - Error Handling
10. US-007: Frontend - Summary Modal
---
**Last Updated:** 2026-01-09
**Author:** Claude Code
**Status:** Draft - Pending Review