## 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>
500 lines
21 KiB
Markdown
500 lines
21 KiB
Markdown
# 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:**
|
|
```css
|
|
.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ă
|
|
|
|
```sql
|
|
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
|
|
|
|
```javascript
|
|
// 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
|
|
```css
|
|
.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
|
|
```css
|
|
.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
|
|
|
|
- [x] Comportament la refresh → **Backend continuă procesarea, frontend reia polling automat la revenire**
|
|
- [x] Auto-cleanup după 7 zile → **Confirmat**
|
|
- [x] Retry individual + batch → **Confirmat ambele**
|
|
- [x] Row complet blocat în procesare → **Confirmat**
|
|
- [x] Grupare vizuală per batch → **Confirmat, row grouping**
|
|
- [x] Mesaj eroare prescurtat → **Confirmat, 50 caractere**
|
|
- [x] Drag anywhere → **Confirmat, overlay fullscreen**
|
|
- [x] Quick filter chips → **Confirmat**
|
|
- [x] Reject duplicate (hash) → **Confirmat**
|
|
- [x] Batch info permanent → **Confirmat, rămâne în DB**
|
|
- [x] 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)
|