feat: Add shared components, refactor stores, improve data-entry workflow

Shared Components:
- Add CompanySelector.vue and PeriodSelector.vue components
- Add AppHeader.vue and SlideMenu.vue layout components
- Add shared stores factories (companies.js, accountingPeriod.js)
- Add shared routes factories (companies.py, calendar.py)
- Add shared models (company.py, calendar.py)
- Add shared layout styles (header.css, navigation.css)

Data Entry App:
- Update CLAUDE.md with prod/test server documentation
- Improve nomenclature sync service with better error handling
- Update receipts router and CRUD operations
- Add company/period stores using shared factories
- Update App.vue layout with shared components
- Fix OCRUploadZone file handling

Reports App:
- Refactor stores to use shared factories
- Update App.vue to use shared layout components

Infrastructure:
- Replace start-data-entry.sh with separate dev/test scripts
- Add .claude/rules for authentication, backend patterns, etc.
- Add implementation plan for OCR receipt improvements
- Clean up old documentation files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-15 15:00:45 +02:00
parent c5fde510a8
commit 1a6e9b17d2
47 changed files with 4079 additions and 2595 deletions

View File

@@ -1,346 +0,0 @@
# Plan: Implementare Auth SSO + Nomenclatoare Sync
> **Plan Handover Document** - Salvat pentru continuare în altă sesiune
> **Data**: 2025-12-13 | **Branch**: `feature/data-entry-receipts`
## Obiectiv
Integrare autentificare SSO și sincronizare nomenclatoare Oracle în data-entry-app conform `IMPLEMENTATION_PLAN_AUTH_UNITAR.md`.
---
## Instrucțiuni Implementare
### Metodologie
1. **Execută fazele în paralel** unde e posibil (Faza 1+2 pot rula simultan, Faza 3+4 pot rula simultan)
2. **Folosește agenți Task** pentru viteza - lansează agenți în paralel pentru task-uri independente
3. **Testează după fiecare fază** - nu trece la următoarea fără validare
4. **Urmărește progresul** în acest fișier - marchează task-urile completate cu ✅
### Comenzi de Start
```bash
# Asigură-te că SSH tunnel rulează (pentru Oracle)
./ssh_tunnel.sh start
# Backend reports (pentru auth API - port 8001)
cd reports-app/backend && uvicorn app.main:app --reload --port 8001
# Backend data-entry (port 8003)
cd data-entry-app/backend && uvicorn app.main:app --reload --port 8003
# Frontend data-entry (port 3010)
cd data-entry-app/frontend && npm run dev
```
### Progres Implementare
- [x] **FAZA 1**: Auth Backend - ✅ 6/6 task-uri COMPLETE
- [x] **FAZA 2**: Auth Frontend - ✅ 6/6 task-uri COMPLETE
- [x] **FAZA 3**: Nomenclatoare Sync - ✅ 6/6 task-uri COMPLETE
- [x] **FAZA 4**: OCR + Supplier Search - ✅ 2/2 task-uri COMPLETE
> **Status**: ✅ **IMPLEMENTARE COMPLETĂ** - 2025-12-13
---
## Stare Curentă (IMPLEMENTAT)
### Backend Data-Entry ✅
- ✅ Models: Receipt, ReceiptAttachment, AccountingEntry - complete
- ✅ CRUD operations - complete
- ✅ API Routers: receipts.py, ocr.py, **nomenclature.py**
- ✅ Services: receipt_service, ocr_service, **sync_service**
- ✅ Workflow: DRAFT → PENDING → APPROVED/REJECTED
-**Auth**: Integrare shared/auth (middleware + CurrentUser)
-**Nomenclatoare**: SQLite sync (SyncedSupplier, LocalSupplier, SyncedCashRegister)
-`sys.path.insert` pentru shared/ în main.py
### Frontend Data-Entry ✅
- ✅ Views: List, Create, Detail, Approval, **LoginView**
- ✅ Components: OCR components + **Create Supplier Dialog**
- ✅ Store: receiptsStore.js + **auth.js**
- ✅ Router: routes + **auth guards + /login**
-**Auth Store**: `src/stores/auth.js` - creat
-**Login View**: `src/views/LoginView.vue` - creat
-**Router Guards**: beforeEach cu requiresAuth
-**API Service**: `src/services/api.js` - creat cu interceptors
### Shared Auth (disponibil pentru integrare)
-`shared/auth/routes.py` - `create_auth_router()` (linia 39-430)
-`shared/auth/middleware.py` - `AuthenticationMiddleware`
-`shared/auth/dependencies.py` - `get_current_user`
-`shared/auth/models.py` - `CurrentUser`, `TokenResponse`
### Referință Reports-App (pentru copiere)
- `reports-app/frontend/src/stores/auth.js` - 119 linii
- `reports-app/frontend/src/services/api.js` - 141 linii
- `reports-app/frontend/src/views/LoginView.vue` - 367 linii
- `reports-app/frontend/src/router/index.js` - auth guard la liniile 96-114
---
## Faze Implementare
### FAZA 1: Auth Backend (6 task-uri)
#### Task 1.1: Adaugă AuthenticationMiddleware în main.py
**Fișier**: `data-entry-app/backend/app/main.py`
**Acțiune**: După CORS middleware (linia 75), adaugă:
```python
from auth.middleware import AuthenticationMiddleware
app.add_middleware(
AuthenticationMiddleware,
excluded_paths=["/docs", "/redoc", "/openapi.json", "/health", "/", "/api/auth/login", "/api/auth/refresh"]
)
```
#### Task 1.2: Adaugă Auth Router în main.py
**Fișier**: `data-entry-app/backend/app/main.py`
**Acțiune**: După include_router pentru ocr (linia 98), adaugă:
```python
from auth.routes import create_auth_router
auth_router = create_auth_router()
app.include_router(auth_router, prefix="/api/auth", tags=["auth"])
```
#### Task 1.3: Înlocuiește get_current_user în receipts.py
**Fișier**: `data-entry-app/backend/app/routers/receipts.py`
**Acțiune**: Șterge liniile 38-59 și înlocuiește cu:
```python
from auth.dependencies import get_current_user
from auth.models import CurrentUser
```
Apoi actualizează type hints: `current_user: str``current_user: CurrentUser`
Și accesează `current_user.username` în loc de `current_user`
#### Task 1.4: Înlocuiește get_current_user în ocr.py
**Fișier**: `data-entry-app/backend/app/routers/ocr.py`
**Acțiune**: Similar cu receipts.py, adaugă importurile auth și folosește `CurrentUser`
#### Task 1.5: Actualizează type hints în toate endpoint-urile
Actualizează toate funcțiile care folosesc `current_user: str` să folosească `current_user: CurrentUser`
#### Task 1.6: Testare backend auth
```bash
cd data-entry-app/backend
uvicorn app.main:app --reload --port 8003
# Test: curl http://localhost:8003/api/receipts/ → 401 Unauthorized
```
---
### FAZA 2: Auth Frontend (6 task-uri)
#### Task 2.1: Crează API service
**Fișier NOU**: `data-entry-app/frontend/src/services/api.js`
**Acțiune**: Copiază din `reports-app/frontend/src/services/api.js` cu modificări:
- Schimbă BASE_URL pentru a funcționa cu proxy-ul
- Modifică refresh token URL
#### Task 2.2: Crează Auth Store
**Fișier NOU**: `data-entry-app/frontend/src/stores/auth.js`
**Acțiune**: Copiază din `reports-app/frontend/src/stores/auth.js`
- Modifică import apiService din `../services/api`
#### Task 2.3: Crează LoginView
**Fișier NOU**: `data-entry-app/frontend/src/views/LoginView.vue`
**Acțiune**: Copiază din `reports-app/frontend/src/views/LoginView.vue`
- Schimbă titlul: "ROA Reports" → "Data Entry"
- Schimbă subtitle: "Rapoarte ERP" → "Introducere Bonuri Fiscale"
- Schimbă redirect după login: "/dashboard" → "/"
#### Task 2.4: Actualizează Router cu auth guards
**Fișier**: `data-entry-app/frontend/src/router/index.js`
**Acțiune**: Adaugă auth guard similar cu reports-app (liniile 96-114)
```javascript
import { useAuthStore } from '@/stores/auth'
// Adaugă rută login
// Adaugă meta: { requiresAuth: true } la rutele protejate
// Adaugă beforeEach guard
```
#### Task 2.5: Actualizează vite.config.js pentru auth proxy
**Fișier**: `data-entry-app/frontend/vite.config.js`
**Acțiune**: Adaugă proxy pentru auth:
```javascript
'/api/auth': {
target: 'http://localhost:8001',
changeOrigin: true,
}
```
#### Task 2.6: Testare frontend auth
```bash
cd data-entry-app/frontend
npm run dev
# Test: Accesează http://localhost:3010 → Redirect la /login
# Login cu credențiale Oracle → Redirect la /
```
---
### FAZA 3: Nomenclatoare Oracle→SQLite (6 task-uri)
#### Task 3.1: Crează modele SQLModel
**Fișier NOU**: `data-entry-app/backend/app/db/models/nomenclature.py`
- `SyncedSupplier` - furnizori sincronizați din Oracle
- `LocalSupplier` - furnizori creați local (din OCR)
- `SyncedCashRegister` - case/bănci sincronizate
#### Task 3.2: Crează Alembic migration
```bash
cd data-entry-app/backend
alembic revision --autogenerate -m "add nomenclature tables"
alembic upgrade head
```
#### Task 3.3: Crează Sync Service
**Fișier NOU**: `data-entry-app/backend/app/services/sync_service.py`
- `sync_suppliers(company_id, schema)` - sync furnizori Oracle→SQLite
- `sync_cash_registers(company_id, schema)` - sync case/bănci
- `get_schema_for_company(company_id)` - lookup schema
#### Task 3.4: Crează Nomenclature Router
**Fișier NOU**: `data-entry-app/backend/app/routers/nomenclature.py`
- `GET /suppliers/search` - căutare furnizor (SQLite + Oracle live)
- `POST /suppliers/local` - creare furnizor local
- `POST /sync/suppliers` - trigger manual sync
#### Task 3.5: Înregistrează router în main.py
```python
from app.routers import nomenclature
app.include_router(nomenclature.router, prefix="/api/nomenclature", tags=["nomenclature"])
```
#### Task 3.6: Actualizare nomenclature_service.py existent
Înlocuiește mock data cu query-uri din tabelele SQLite sincronizate
---
### FAZA 4: Integrare OCR + Supplier Search (2 task-uri)
#### Task 4.1: Actualizare ReceiptCreateView.vue
**Fișier**: `data-entry-app/frontend/src/views/receipts/ReceiptCreateView.vue`
**Acțiune**: După OCR result, caută automat furnizor după CUI:
```javascript
async function handleOCRResult(ocrData) {
if (ocrData.cui) {
const result = await receiptsStore.searchSupplier(ocrData.cui);
if (result.found) {
form.partner_id = result.supplier.id;
form.partner_name = result.supplier.name;
} else {
showCreateSupplierDialog(ocrData);
}
}
}
```
#### Task 4.2: Adaugă supplier search în receiptsStore.js
**Fișier**: `data-entry-app/frontend/src/stores/receiptsStore.js`
**Acțiune**: Adaugă action `searchSupplier(fiscalCode)` și `createLocalSupplier(data)`
---
## Sumar Fișiere
### De Modificat
| Fișier | Faza |
|--------|------|
| `data-entry-app/backend/app/main.py` | 1, 3 |
| `data-entry-app/backend/app/routers/receipts.py` | 1 |
| `data-entry-app/backend/app/routers/ocr.py` | 1 |
| `data-entry-app/frontend/src/router/index.js` | 2 |
| `data-entry-app/frontend/vite.config.js` | 2 |
| `data-entry-app/frontend/src/views/receipts/ReceiptCreateView.vue` | 4 |
| `data-entry-app/frontend/src/stores/receiptsStore.js` | 4 |
### De Creat (NOU)
| Fișier | Faza |
|--------|------|
| `data-entry-app/frontend/src/services/api.js` | 2 |
| `data-entry-app/frontend/src/stores/auth.js` | 2 |
| `data-entry-app/frontend/src/views/LoginView.vue` | 2 |
| `data-entry-app/backend/app/db/models/nomenclature.py` | 3 |
| `data-entry-app/backend/app/services/sync_service.py` | 3 |
| `data-entry-app/backend/app/routers/nomenclature.py` | 3 |
| `migrations/versions/xxx_nomenclature.py` | 3 |
---
## Ordine Execuție
**Faza 1 + 2 (Auth)****Faza 3 + 4 (Nomenclatoare)**
Fazele 1-2 sunt blocante pentru funcționalitatea completă, dar Faza 3-4 poate fi amânată dacă e nevoie (nomenclatoarele rămân mock data temporar).
---
## Strategie Execuție cu Agenți
### Agenți Paraleli Recomandați
**Round 1 - Auth (Backend + Frontend simultan):**
```
Agent A: Faza 1 - Task 1.1-1.5 (Backend auth)
Agent B: Faza 2 - Task 2.1-2.3 (Frontend auth files)
```
După Round 1, testare manuală auth flow.
**Round 2 - Finalizare Auth + Start Nomenclatoare:**
```
Agent A: Faza 2 - Task 2.4-2.5 (Router guards, vite config)
Agent B: Faza 3 - Task 3.1-3.2 (Modele SQLModel + migration)
```
**Round 3 - Nomenclatoare + Integration:**
```
Agent A: Faza 3 - Task 3.3-3.6 (Sync service + router)
Agent B: Faza 4 - Task 4.1-4.2 (Frontend OCR supplier)
```
### Validare După Fiecare Fază
**După Faza 1:**
```bash
curl http://localhost:8003/api/receipts/
# Expected: 401 Unauthorized
```
**După Faza 2:**
```bash
# Browser: http://localhost:3010
# Expected: Redirect to /login
# Login cu credențiale Oracle → Redirect la /
```
**După Faza 3:**
```bash
curl http://localhost:8003/api/nomenclature/suppliers/search?fiscal_code=RO12345678
# Expected: Search result sau sugestie creare local
```
**După Faza 4:**
```
# Browser: Crează bon nou → Upload poză → OCR
# Expected: Furnizor găsit automat sau dialog creare
```
---
## Context pentru Sesiune Următoare
### Fișiere Cheie de Citit
1. Acest plan: `/home/marius/.claude/plans/unified-orbiting-sonnet.md`
2. CLAUDE.md principal: `/mnt/e/proiecte/roa2web/CLAUDE.md`
3. CLAUDE.md data-entry: `/mnt/e/proiecte/roa2web/data-entry-app/CLAUDE.md`
### Comenzi Quick Start
```bash
cd /mnt/e/proiecte/roa2web
git status # Verifică branch feature/data-entry-receipts
./ssh_tunnel.sh start # SSH tunnel pentru Oracle
```
### Dependențe Servicii
- **reports-backend:8001** - NECESAR pentru auth API (login, refresh)
- **data-entry-backend:8003** - Backend principal
- **Oracle DB** - Via SSH tunnel, necesar pentru auth + nomenclatoare

View File

@@ -1,484 +0,0 @@
# Plan: Sincronizare Nomenclatoare Oracle + Auth SSO + OCR Furnizori
## Obiective
1. **Sincronizare nomenclatoare din Oracle în SQLite** (furnizori, casa/banca)
2. **Auth pentru data-entry-app** cu SSO (frontend-uri separate pe path)
3. **OCR: căutare furnizor după CUI** + creare locală dacă nu există
4. **Deploy Windows IIS** cu path routing
---
## Arhitectura Aleasă
```
roa2web.romfast.ro (IIS + ARR)
├── /reports/ → reports-app/frontend/
├── /data/ → data-entry-app/frontend/
├── /api/reports/* → reports-backend:8001
├── /api/data/* → data-entry-backend:8003
└── /api/auth/* → reports-backend (auth provider)
```
**URL-uri compacte:**
- `roa2web.romfast.ro/reports/` - Rapoarte
- `roa2web.romfast.ro/data/` - Introducere date (bonuri fiscale)
- `roa2web.romfast.ro/api/reports/` - API rapoarte
- `roa2web.romfast.ro/api/data/` - API introducere date
**SSO**: Același domeniu = localStorage partajat = token JWT valid pentru ambele
---
## Faza 1: Auth pentru Data-Entry-App
### 1.1 Backend - Integrare shared/auth/
**Fișiere de modificat:**
- `data-entry-app/backend/app/main.py`
- `data-entry-app/backend/app/routers/receipts.py`
- `data-entry-app/backend/app/core/config.py`
**Acțiuni:**
```python
# main.py - Adăugare middleware
import sys
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent.parent / "shared"))
from auth.middleware import AuthenticationMiddleware
from auth.dependencies import get_current_user
app.add_middleware(
AuthenticationMiddleware,
excluded_paths=["/docs", "/redoc", "/openapi.json", "/health", "/"]
)
```
```python
# receipts.py - Înlocuire placeholder
from auth.dependencies import get_current_user
from auth.models import CurrentUser
@router.get("/")
async def list_receipts(
current_user: CurrentUser = Depends(get_current_user)
):
# folosește current_user.username
```
### 1.2 Frontend - Auth Store + Login Page
**Fișiere de creat/copiat din reports-app:**
- `data-entry-app/frontend/src/stores/auth.js` (copiat)
- `data-entry-app/frontend/src/views/LoginView.vue` (copiat)
- `data-entry-app/frontend/src/router/index.js` (adăugat guard)
- `data-entry-app/frontend/src/services/api.js` (axios interceptor)
**Decizie SSO:**
- Frontend data-entry folosește `/api/auth/login` de pe reports-backend
- Sau: redirect la `/login` (reports-app) care setează token în localStorage
- Token valid pentru ambele (același JWT_SECRET_KEY)
---
## Faza 2: Sincronizare Nomenclatoare Oracle → SQLite
### 2.1 Noi Modele SQLModel
**Fișier:** `data-entry-app/backend/app/db/models/nomenclature.py`
```python
class SyncedSupplier(SQLModel, table=True):
"""Furnizori sincronizați din Oracle"""
__tablename__ = "synced_suppliers"
id: int = Field(primary_key=True) # ID din Oracle (ID_PART)
company_id: int = Field(index=True)
name: str = Field(max_length=200) # DEN_PART
fiscal_code: Optional[str] = Field(max_length=20, index=True) # COD_FISCAL
address: Optional[str] = Field(max_length=500)
synced_at: datetime = Field(default_factory=datetime.utcnow)
class LocalSupplier(SQLModel, table=True):
"""Furnizori creați local din OCR (neexistenți în Oracle)"""
__tablename__ = "local_suppliers"
id: Optional[int] = Field(default=None, primary_key=True)
company_id: int = Field(index=True)
name: str = Field(max_length=200)
fiscal_code: str = Field(max_length=20, unique=True, index=True)
address: Optional[str] = Field(max_length=500)
created_by: str = Field(max_length=100)
created_at: datetime = Field(default_factory=datetime.utcnow)
oracle_synced: bool = Field(default=False) # True când e creat în Oracle
class SyncedCashRegister(SQLModel, table=True):
"""Case/Bănci sincronizate din Oracle"""
__tablename__ = "synced_cash_registers"
id: int = Field(primary_key=True) # ID din Oracle
company_id: int = Field(index=True)
name: str = Field(max_length=100)
account_code: str = Field(max_length=20) # 5311, 5121 etc.
register_type: str = Field(max_length=20) # CASA sau BANCA
synced_at: datetime = Field(default_factory=datetime.utcnow)
```
### 2.2 Alembic Migration
**Fișier:** `data-entry-app/backend/migrations/versions/xxx_add_nomenclature_tables.py`
### 2.3 Sync Service
**Fișier:** `data-entry-app/backend/app/services/sync_service.py`
```python
class NomenclatureSyncService:
"""Sincronizare nomenclatoare din Oracle în SQLite"""
@staticmethod
async def sync_suppliers(company_id: int, schema: str) -> int:
"""Sincronizează furnizori pentru o companie"""
async with oracle_pool.get_connection() as conn:
cursor = conn.cursor()
cursor.execute(f"""
SELECT ID_PART, DEN_PART, COD_FISCAL, ADRESA
FROM {schema}.NOM_PARTENERI
WHERE TIP_PART IN ('F', 'A') -- Furnizori sau Ambele
""")
# Upsert în SQLite
@staticmethod
async def sync_cash_registers(company_id: int, schema: str) -> int:
"""Sincronizează case și bănci"""
# Similar pentru NOM_CASE și NOM_BANCI
@staticmethod
async def get_schema_for_company(company_id: int) -> str:
"""Obține schema Oracle pentru o companie"""
# Folosește cache din shared sau query V_NOM_FIRME
```
### 2.4 Strategia de Sync Hibrid
1. **La startup app**: Sync automat (background task)
2. **Periodic**: Task programat la 4h
3. **On-demand**: Căutare live în Oracle când CUI nu există local
**Fișier:** `data-entry-app/backend/app/main.py`
```python
@app.on_event("startup")
async def startup_sync():
# Background sync pentru company-urile active
asyncio.create_task(sync_nomenclatures_background())
```
---
## Faza 3: OCR + Căutare Furnizor după CUI
### 3.1 Flow Căutare Furnizor
```
OCR extrage CUI
Căutare în SyncedSupplier (SQLite)
↓ (nu găsit)
Căutare în LocalSupplier (SQLite)
↓ (nu găsit)
Căutare LIVE în Oracle (NOM_PARTENERI)
↓ (nu găsit)
Creare LocalSupplier cu date OCR
Utilizator poate edita înainte de submit
```
### 3.2 Endpoint Căutare Furnizor
**Fișier:** `data-entry-app/backend/app/routers/nomenclature.py`
```python
@router.get("/suppliers/search")
async def search_supplier(
company_id: int,
fiscal_code: Optional[str] = None,
name: Optional[str] = None,
current_user: CurrentUser = Depends(get_current_user)
) -> SupplierSearchResult:
"""
Caută furnizor:
1. În SQLite (synced + local)
2. Live în Oracle dacă nu găsit
3. Returnează sugestie creare dacă nu există
"""
@router.post("/suppliers/local")
async def create_local_supplier(
supplier: LocalSupplierCreate,
current_user: CurrentUser = Depends(get_current_user)
) -> LocalSupplier:
"""Crează furnizor local din date OCR"""
```
### 3.3 Modificare OCR Flow în Frontend
**Fișier:** `data-entry-app/frontend/src/views/ReceiptCreateView.vue`
```javascript
// După OCR, caută automat furnizor
async function handleOCRResult(ocrData) {
if (ocrData.cui) {
const result = await api.get('/api/data-entry/suppliers/search', {
params: { company_id: selectedCompany.id, fiscal_code: ocrData.cui }
});
if (result.found) {
form.partner_id = result.supplier.id;
form.partner_name = result.supplier.name;
} else {
// Afișează opțiune creare locală
showCreateSupplierDialog(ocrData);
}
}
}
```
---
## Faza 4: Deploy Windows IIS
### 4.1 Serviciu Windows pentru data-entry-backend
**Fișier:** `deployment/windows/scripts/Install-DataEntry.ps1`
Similar cu Install-ROA2WEB.ps1 dar:
- ServiceName: `ROA2WEB-DataEntry`
- Port: 8003
- BackendPath: `C:\inetpub\wwwroot\roa2web\data-entry-app\backend`
- FrontendPath: `C:\inetpub\wwwroot\roa2web\data-entry-app\frontend`
**Actualizare Install-ROA2WEB.ps1** pentru structura unitară:
- BackendPath: `C:\inetpub\wwwroot\roa2web\reports-app\backend`
- FrontendPath: `C:\inetpub\wwwroot\roa2web\reports-app\frontend`
### 4.2 Actualizare web.config
**Fișier:** `deployment/windows/config/web.config`
Reguli URL compacte (`/reports/`, `/data/`, `/api/reports/`, `/api/data/`):
```xml
<!-- API Auth (comun) -->
<rule name="Auth API" stopProcessing="true">
<match url="^api/auth/(.*)" />
<action type="Rewrite" url="http://localhost:8001/api/auth/{R:1}" />
</rule>
<!-- API Data Entry -->
<rule name="Data Entry API" stopProcessing="true">
<match url="^api/data/(.*)" />
<action type="Rewrite" url="http://localhost:8003/api/{R:1}" />
</rule>
<!-- API Reports -->
<rule name="Reports API" stopProcessing="true">
<match url="^api/reports/(.*)" />
<action type="Rewrite" url="http://localhost:8001/api/{R:1}" />
</rule>
<!-- Frontend Data Entry SPA (/data/) -->
<rule name="Data Entry SPA" stopProcessing="true">
<match url="^data($|/.*)" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Rewrite" url="/data/index.html" />
</rule>
<!-- Frontend Reports SPA (/reports/) -->
<rule name="Reports SPA" stopProcessing="true">
<match url="^reports($|/.*)" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Rewrite" url="/reports/index.html" />
</rule>
<!-- Root redirect la /reports/ -->
<rule name="Root Redirect" stopProcessing="true">
<match url="^$" />
<action type="Redirect" url="/reports/" redirectType="Found" />
</rule>
```
**IIS Virtual Directories (pentru URL-uri compacte):**
```powershell
# /reports/ → reports-app/frontend/
New-WebVirtualDirectory -Site "Default Web Site" -Name "reports" `
-PhysicalPath "C:\inetpub\wwwroot\roa2web\reports-app\frontend"
# /data/ → data-entry-app/frontend/
New-WebVirtualDirectory -Site "Default Web Site" -Name "data" `
-PhysicalPath "C:\inetpub\wwwroot\roa2web\data-entry-app\frontend"
```
### 4.3 Structura Foldere (UNITARĂ - identică dev/prod)
**În development (git repo):**
```
roa2web/
├── reports-app/
│ ├── backend/ # FastAPI port 8001
│ ├── frontend/ # Vue.js port 3000
│ └── telegram-bot/ # Bot Telegram
├── data-entry-app/
│ ├── backend/ # FastAPI port 8003
│ └── frontend/ # Vue.js port 3010
└── shared/ # Cod partajat (auth, database)
```
**În producție (Windows IIS) - IDENTIC:**
```
C:\inetpub\wwwroot\roa2web\
├── reports-app/
│ ├── backend/ # Serviciu Windows port 8001
│ └── frontend/ # Servit de IIS pe /
├── data-entry-app/
│ ├── backend/ # Serviciu Windows port 8003
│ └── frontend/ # Servit de IIS pe /data-entry/
├── telegram-bot/ # Serviciu Windows port 8002
└── shared/ # Cod partajat
```
**Avantaje structură unitară:**
- Deploy simplu: `xcopy /E /Y source\reports-app dest\reports-app`
- Path-uri identice în cod (no surprises)
- Un singur script de deploy pentru ambele medii
---
## Faza 5: Configurare Dev (identic cu prod)
### 5.1 Vite Config pentru URL-uri Compacte
**Fișier:** `data-entry-app/frontend/vite.config.js`
```javascript
export default defineConfig({
base: '/data/', // URL compact în producție
server: {
proxy: {
'/api/auth': 'http://localhost:8001',
'/api/data': 'http://localhost:8003'
}
}
})
```
**Fișier:** `reports-app/frontend/vite.config.js` (ACTUALIZAT)
```javascript
export default defineConfig({
base: '/reports/', // URL compact în producție (era '/')
server: {
proxy: {
'/api/auth': 'http://localhost:8001',
'/api/reports': 'http://localhost:8001'
}
}
})
```
**IMPORTANT:** Actualizare API calls în frontend:
- Reports: `/api/reports/companies`, `/api/reports/invoices`, etc.
- Data Entry: `/api/data/receipts`, `/api/data/suppliers`, etc.
- Auth (comun): `/api/auth/login`, `/api/auth/refresh`
### 5.2 Script Start Unificat
**Fișier:** `start-all.sh` (nou)
```bash
#!/bin/bash
# Pornește toate serviciile pentru dev
# SSH tunnel
./ssh_tunnel.sh start
# Reports backend
cd reports-app/backend && uvicorn app.main:app --port 8001 &
# Data entry backend
cd data-entry-app/backend && uvicorn app.main:app --port 8003 &
# Reports frontend
cd reports-app/frontend && npm run dev -- --port 3000 &
# Data entry frontend
cd data-entry-app/frontend && npm run dev -- --port 3010 &
wait
```
---
## Ordine Implementare
| # | Task | Efort | Dependențe |
|---|------|-------|------------|
| 1 | Modele SQLModel nomenclatoare | 30 min | - |
| 2 | Alembic migration | 15 min | #1 |
| 3 | Sync service (Oracle → SQLite) | 2h | #2 |
| 4 | Auth middleware în data-entry-backend | 1h | - |
| 5 | Auth store + login în data-entry-frontend | 1h | #4 |
| 6 | Endpoint căutare furnizor | 1h | #3 |
| 7 | Frontend OCR + furnizor flow | 1.5h | #6 |
| 8 | web.config IIS actualizat | 30 min | - |
| 9 | Script deploy data-entry Windows | 1h | #8 |
| 10 | Testare end-to-end | 1h | all |
**Total estimat: ~10h**
---
## Fișiere Critice de Modificat/Creat
### Backend data-entry-app:
- `app/main.py` - middleware auth + startup sync
- `app/db/models/nomenclature.py` - noi modele (CREARE)
- `app/services/sync_service.py` - sync Oracle (CREARE)
- `app/services/nomenclature_service.py` - refactorizare
- `app/routers/nomenclature.py` - endpoint-uri noi (CREARE)
- `app/routers/receipts.py` - auth dependencies
- `migrations/versions/xxx_nomenclature.py` - migrare (CREARE)
### Frontend data-entry-app:
- `src/stores/auth.js` - copiat din reports-app
- `src/views/LoginView.vue` - copiat + adaptat
- `src/router/index.js` - auth guard
- `src/services/api.js` - axios config
- `src/views/ReceiptCreateView.vue` - OCR + supplier flow
### Deploy (structură unitară):
- `deployment/windows/config/web.config` - reguli noi + actualizate
- `deployment/windows/scripts/Install-ROA2WEB.ps1` - ACTUALIZAT pentru structura unitară
- `deployment/windows/scripts/Install-DataEntry.ps1` - NOU
- `deployment/windows/scripts/Build-ROA2WEB.ps1` - ACTUALIZAT pentru ambele apps
- `deployment/windows/docs/WINDOWS_DEPLOYMENT.md` - ACTUALIZAT cu noua structură
### Shared:
- Nu necesită modificări (refolosim exact ce există)
---
## Întrebări Rezolvate
| Întrebare | Răspuns |
|-----------|---------|
| Furnizor nou din OCR? | Creare automată în SQLite (LocalSupplier) |
| Sync strategy? | Hibrid: startup + periodic 4h + on-demand |
| Auth sharing? | Frontend-uri separate pe path, același token JWT (SSO via localStorage) |
| Deployment? | IIS path routing, servicii Windows separate |
| Structura directoare? | **UNITARĂ** - grupat pe app (`{app}/backend`, `{app}/frontend`) identic dev/prod |
| SSO cum funcționează? | Același domeniu IIS → localStorage partajat → token valid pentru ambele API-uri |
| URL-uri? | **COMPACTE**: `/reports/`, `/data/`, `/api/reports/`, `/api/data/` |
| Root (/)? | Redirect automat la `/reports/` |