# 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** să selectez/trag multiple fișiere (PDF/PNG/JPG) într-o zonă de upload **Pentru că** vreau să 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** să văd lista fișierelor selectate cu preview **Pentru că** vreau să verific că 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** să trimit toate fișierele pentru procesare cu un singur click **Pentru că** vreau să 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** să văd progresul fiecărui fișier în timp real **Pentru că** vreau să ș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** să 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 să fie marcate pentru review manual **Pentru că** vreau să procesez restul batch-ului fără blocaj, dar să 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** să văd un rezumat la finalul procesării batch-ului **Pentru că** vreau să ș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 să 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 să 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