Consolidate Reports and Data Entry apps into a single Vue.js SPA with: Architecture: - Module-based structure with lazy-loaded routes (@reports, @data-entry) - Error boundaries per module to prevent cascade failures - Dual API proxy in Vite for microservices (reports:8001, data-entry:8003) - Pinia store factories for shared auth, company, and period stores - Vite path aliases for clear module boundaries (@shared, @reports, @data-entry) Service Management: - Granular service control scripts (backend-reports.sh, backend-data-entry.sh, bot.sh, frontend.sh) - 87% faster frontend restart: 7s vs 53s full restart - 38% faster full startup: 33s vs 53s via parallel backend initialization - Enhanced start-dev.sh with proper service timeouts (OCR: 30s, Vite: 15s, Bot: 10s) - status.sh for comprehensive health checks Features: - Auto-select first company on login with period auto-load - Hamburger menu with feature toggle support - JWT token auto-injection via axios interceptors - Unified header with company/period selectors - IIS web.config for production deployment with multi-API routing UX Improvements: - Vue watchers for reactive company/period loading - Lazy store initialization with graceful error handling - Period persistence per user+company in localStorage - Feature flags for optional modules Deployment: - Single IIS site serves unified frontend with API proxy rules - Maintains separate backend processes for microservices - Windows line ending fixes (.env CRLF → LF conversion) Stats: 112 files changed, 38,342 insertions(+), 2,342 deletions(-) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
16 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
🚀 Project Overview
ROA2WEB - Modern ERP Application with two main modules:
- Reports App (
reports-app/) - Read-only reports from Oracle (raportări) - Data Entry App (
data-entry-app/) - Data input with approval workflow (introduceri date)
Main Branch: main (use for PRs)
Working Directory: Repository root
Quick Reference: See README.md for complete setup, commands, deployment, and testing instructions.
📁 Application-Specific Instructions
Important
: When working on a specific application, ALWAYS read its dedicated CLAUDE.md first!
| Application | CLAUDE.md Location | Description |
|---|---|---|
| Data Entry | data-entry-app/CLAUDE.md |
Bonuri fiscale, chitanțe, workflow aprobare |
| Reports | This file (below) | Rapoarte Oracle read-only |
| Telegram Bot | reports-app/telegram-bot/README.md |
Bot Telegram |
When to Use Which Instructions
Working on data-entry-app/:
→ FIRST read data-entry-app/CLAUDE.md for:
- SQLModel + Alembic patterns (NOT Oracle)
- SQLite database (NOT Oracle pool)
- Workflow states (DRAFT → PENDING → APPROVED)
- Receipt/Attachment/AccountingEntry models
- Expense types and auto-generation logic
Working on reports-app/ or shared/:
→ Use instructions from this file (below)
Working on shared components (shared/auth/, shared/database/, shared/frontend/):
→ These are used by BOTH apps - be careful with changes!
→ shared/frontend/ contains: LoginView.vue, auth store factory, login styles
🏗️ Architecture
Microservices Structure
.
├── shared/ # Shared components (DB pool, auth, frontend)
│ ├── database/ # Oracle pool (used by both apps)
│ ├── auth/ # JWT auth (used by both apps)
│ └── frontend/ # Shared Vue components, stores, styles
│ ├── components/ # LoginView.vue
│ ├── stores/ # auth.js (Pinia store factory)
│ └── styles/ # login.css
│
├── reports-app/ # READ-ONLY reports from Oracle
│ ├── backend/ # FastAPI API (port 8001)
│ ├── frontend/ # Vue.js 3 UI (port 3000-3005)
│ └── telegram-bot/ # Telegram bot (port 8002 internal)
│
├── data-entry-app/ # DATA INPUT with approval workflow
│ ├── backend/ # FastAPI API (port 8003) - SQLite + SQLModel
│ ├── frontend/ # Vue.js 3 UI (port 3010)
│ └── CLAUDE.md # ⚠️ READ THIS for data-entry work!
│
├── docs/ # Architecture & style guides
├── deployment/ # Production deployment scripts
└── ssh-tunnel/ # SSH tunnel for Oracle DB access
Starting Services
Quick Start (All services with parallel backend startup):
./start-dev.sh # Dev: Backend :8001, :8003, Bot :8002, Frontend :3000 (~11s)
./start-test.sh # Test: Same ports (~33s - Oracle pool init takes longer)
Individual Service Control (for quick development iterations):
./frontend.sh restart # Restart frontend only (~7s - fastest!)
./backend-reports.sh start # Start Reports backend :8001
./backend-data-entry.sh stop # Stop Data Entry backend :8003
./bot.sh status # Check Telegram bot :8002 status
./status.sh # Show all services status + health checks
Infrastructure:
./ssh_tunnel.sh start # Oracle DB tunnel (production: 10.0.20.36)
./ssh-tunnel-test.sh start # Oracle TEST tunnel (LXC: 10.0.20.121)
Benefits:
- 87% faster frontend restart: 7s vs 53s full restart
- 38% faster full startup: 33s vs 53s (test) via parallel backend init
- Granular control: Restart individual services without affecting others
Key Architectural Decisions
- Shared Database Pool: Singleton
OraclePoolinshared/database/oracle_pool.py(python-oracledb with connection pooling) - Centralized Auth: JWT-based auth in
shared/auth/with middleware auto-injectingrequest.state.user - Two-Tier Cache System: Hybrid L1 (Memory) + L2 (SQLite) cache in
backend/app/cache/- MANDATORY for all new endpoints - SSH Tunnel: Required for Oracle DB connections (development/Linux) - see Database Setup
- FastAPI Structure: Services in
backend/app/services/with@cacheddecorator, routers inbackend/app/routers/, models inbackend/app/models/orschemas/ - Telegram Bot: Standalone SQLite database for bot data, communicates with backend via HTTP API
🗄️ Database Setup
Schema: CONTAFIN_ORACLE (authentication and user management)
Connection: SSH tunnel required (Oracle on remote network)
SSH Tunnel (Development/Linux)
./ssh_tunnel.sh start # localhost:1526 → remote:1521
./ssh_tunnel.sh status # Check tunnel
./ssh_tunnel.sh stop # Stop tunnel
IMPORTANT: Always ensure SSH tunnel is running before starting backend services.
Environment Variables (reports-app/backend/.env)
# Oracle Database (through SSH tunnel)
ORACLE_USER=CONTAFIN_ORACLE
ORACLE_PASSWORD=your_password
ORACLE_HOST=localhost
ORACLE_PORT=1526
ORACLE_SID=ROA
# JWT Authentication
JWT_SECRET_KEY=your_secret_key
JWT_ALGORITHM=HS256
JWT_EXPIRE_MINUTES=30
# Telegram Bot Integration
TELEGRAM_BOT_INTERNAL_API=http://localhost:8002
Windows Production: Direct Oracle connection, no SSH tunnel required. Ensure TELEGRAM_BOT_INTERNAL_API is set for auth code management.
🔑 Authentication Flow
- Login:
POST /api/auth/login→ callspack_drepturi.verificautilizator(username, password) - Token: JWT includes
username,user_id,companies[],permissions[],exp,iat,type - Middleware:
AuthenticationMiddlewareinshared/auth/middleware.pyvalidates tokens, injects user - Protected Routes: All routes except
excluded_pathsrequire valid JWT
Key Files:
shared/auth/middleware.py- FastAPI middleware with rate limiting (5 req/5 min)shared/auth/jwt_handler.py- Token creation/validationreports-app/backend/app/main.py- Auth router inline definition
📝 Common Development Tasks
Adding a New API Endpoint
IMPORTANT: Always use the cache system for database queries to improve performance.
- Create service in
reports-app/backend/app/services/your_service.py(NOT in router!) - Define Pydantic schemas in
app/schemas/orapp/models/ - Add caching using
@cacheddecorator in service methods - Create router in
reports-app/backend/app/routers/your_router.py(calls service) - Register router in
app/main.py:app.include_router(your_router, prefix="/api/your_prefix")
Service Example with Caching (RECOMMENDED):
# app/services/your_service.py
from app.cache.decorators import cached
from database.oracle_pool import oracle_pool
class YourService:
@staticmethod
@cached(cache_type='schema', key_params=['company_id'])
async def _get_schema(company_id: int) -> str:
"""Get schema for company (CACHED 24h)"""
async with oracle_pool.get_connection() as connection:
with connection.cursor() as cursor:
cursor.execute("""
SELECT schema FROM CONTAFIN_ORACLE.v_nom_firme
WHERE id_firma = :company_id
""", {'company_id': company_id})
result = cursor.fetchone()
return result[0] if result else None
@staticmethod
@cached(cache_type='your_data', key_params=['filter_params', 'username'])
async def get_your_data(filter_params: YourFilter, username: str) -> YourResponse:
"""
Get your data from Oracle (CACHED 10 min)
Cache automatically:
- Generates unique key from filter_params + username
- Stores in L1 (memory) + L2 (SQLite)
- Returns cached data on subsequent calls
- Tracks performance metrics
"""
schema = await YourService._get_schema(filter_params.company_id)
async with oracle_pool.get_connection() as connection:
with connection.cursor() as cursor:
cursor.execute(f"""
SELECT * FROM {schema}.your_table
WHERE your_condition = :param
""", {'param': filter_params.param})
rows = cursor.fetchall()
# Process results...
return YourResponse(data=processed_data)
Router Example (calls service):
# app/routers/your_router.py
from app.services.your_service import YourService
@router.get("/", response_model=YourResponse)
async def get_your_data(
filter_params: YourFilter = Depends(),
current_user: CurrentUser = Depends(get_current_user)
):
"""Get your data - delegated to service with caching"""
return await YourService.get_your_data(filter_params, current_user.username)
Cache Configuration (add to app/cache/config.py if new cache type):
# Add TTL for your cache type
ttl_your_data: int = int(os.getenv('CACHE_TTL_YOUR_DATA', '600')) # 10 min default
# Add to get_ttl_for_type() method:
'your_data': self.ttl_your_data,
Cache Best Practices:
- ✅ Use
@cacheddecorator for ALL database queries - ✅ Place logic in services (NOT routers)
- ✅ Cache schema lookups separately (long TTL: 24h)
- ✅ Choose appropriate TTL (frequently changing data: 5-10 min, static data: 30 min - 24h)
- ✅ Include
usernameinkey_paramsfor user-specific data - ✅ Include filter parameters in
key_paramsfor query variations - ❌ Don't query Oracle directly in routers (use services with caching)
- ❌ Don't skip caching for performance-critical endpoints
Adding a New Frontend Page/Component
IMPORTANT: Follow the established CSS architecture and design system.
Before writing ANY CSS: Read docs/ONBOARDING_CSS.md (5-minute quick start) → See "Documentation Index" below for complete guide list.
Golden Rules:
- ✅ Use global patterns first (
.roa-card,.roa-metric,.roa-badge-*) - checkCSS_PATTERNS.md - ✅ Use design tokens (
var(--color-primary)) not hardcoded values (#2563eb) - ✅ Use shared CSS from
src/assets/css/- NEVER create new CSS when shared classes exist - ✅ For inline stats/totals use
.summary-stats-inline,.stat-item,.stat-label,.stat-valuefromstats.css - ❌ Never use
:deep()in components (usesrc/assets/css/vendor/for PrimeVue overrides) - ❌ Never duplicate CSS (write once, use everywhere)
- ❌ Never add new scoped CSS for patterns that already exist in shared CSS files
Tables - Unified Column Structure & Filter Buttons:
- ✅ ALWAYS use separate columns for related data (Debit | Credit, not Debit+Credit stacked)
- ✅ Use PrimeVue DataTable with one value per
<Column>component - ✅ Add filter/action buttons (clear, export Excel, export PDF, refresh) on separate row below filters
- ✅ PrimeVue Button components with icon + label (not icon-only!)
- ✅ Export ALL data from backend (page_size: 999999), not just current page
- ❌ Never group multiple values vertically in a single column
- ❌ Never use HTML
<button>for filter actions (use PrimeVue<Button>) - ❌ Never put action buttons on same row as filters (separate row!)
- ❌ Never export only current page (must fetch all data for export)
- 📖 See:
CSS_PATTERNS.md→ Table Patterns → Unified Table Column Structure & Filter/Action Buttons
Adding a New Telegram Bot Command
IMPORTANT: Follow established command patterns and formatting.
Before coding: Read reports-app/telegram-bot/TELEGRAM_COMMANDS.md for command patterns → See "Documentation Index" for complete guides.
Standard Pattern (add handler in app/bot/handlers.py):
async def your_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
telegram_id = update.effective_user.id
# 1. Check authentication (use helpers.py)
user = await get_user_by_telegram_id(telegram_id)
if not user:
await update.message.reply_text("⚠️ Account not linked. Use /start with code.")
return
# 2. Check active company (use session.py)
active_company = await get_active_company(telegram_id)
if not active_company:
await update.message.reply_text("⚠️ Select company first: /selectcompany")
return
# 3. Call backend API (use api/client.py)
# 4. Format response (use formatters.py)
# 5. Add tests + update MANUAL_TESTING_CHECKLIST.md
Adding Shared Functionality
- Place in
shared/{database|auth|utils}/ - Import using
sys.path.append()pattern (seebackend/app/main.py:21) - Add tests in
shared/tests/
Frontend API Integration
// Store pattern (src/stores/yourStore.js)
import axios from 'axios';
const api = axios.create({
baseURL: 'http://localhost:8001/api',
headers: { 'Authorization': `Bearer ${token}` }
});
const response = await api.get('/endpoint');
🔒 Security
- Git Hooks: Security scanning in
security/install_hooks.sh - Environment Files: Never commit
.env(use.env.exampleas template) - SSH Keys: In
ssh-tunnel/secrets/(gitignored) - JWT Secrets: Generate strong secrets for production
- Rate Limiting: Built into auth middleware (5 requests per 5 minutes)
📚 Documentation Index
Quick Start & Commands
→ README.md - Project overview, setup, development commands, testing, deployment
Data Entry App (Bonuri Fiscale)
data-entry-app/CLAUDE.md- ⚠️ READ FIRST when working on data-entrydata-entry-app/README.md- Quick start guidedocs/data-entry/REQUIREMENTS.md- Functional requirementsdocs/data-entry/ARCHITECTURE.md- Technical architecture (SQLModel, workflow)
Architecture & Planning
docs/ARCHITECTURE_SCHEMA.md- Architecture diagrams, cache system, and schemasdocs/MICROSERVICES_GUIDE.md- Microservices architecture detailsDEVELOPMENT_BLUEPRINT.md- Detailed development plan
Frontend Development
docs/ONBOARDING_CSS.md- CSS system quick start (START HERE for new components)docs/CSS_PATTERNS.md- Complete CSS patterns library (cards, forms, buttons, etc.)docs/DESIGN_TOKENS.md- Design tokens (colors, spacing, typography)docs/STYLING_GUIDELINES.md- CSS best practices and conventionsdocs/COMPONENT_STYLING.md- Component-specific styling guidedocs/FORM_TEMPLATE.md- Standardized form templatereports-app/frontend/README.md- Frontend architecture and setupreports-app/frontend/tests/README.md- Playwright E2E testing guide
Backend Development
reports-app/backend/README.md- Backend architecture and API details- API endpoints documented in
README.md(Authentication, Companies, Dashboard, Invoices, Treasury, Telegram)
Telegram Bot Development
reports-app/telegram-bot/README.md- Complete bot architecture and development guide (START HERE)reports-app/telegram-bot/TELEGRAM_COMMANDS.md- Command reference and patternstests/MANUAL_TESTING_CHECKLIST.md- Manual testing procedures
Deployment
DEPLOYMENT_GUIDE.md- Production deployment (Linux/Docker & Windows/IIS)deployment/windows/README.md- Windows deployment quick startdeployment/windows/docs/WINDOWS_DEPLOYMENT.md- Complete Windows guidedeployment/windows/docs/TELEGRAM_BOT_TROUBLESHOOTING.md- Bot troubleshooting
Troubleshooting
→ README.md - Common issues (SSH tunnel, backend, frontend, database)
→ Backend: Check .env, SSH tunnel status, port 8001 availability
→ Frontend: Clear node_modules, check Node.js version (16+), Vite auto-ports
→ Database: Verify SSH tunnel, Oracle listener, credentials, test /health
🔧 Tech Stack
Backend: FastAPI, python-oracledb, JWT (PyJWT), Pydantic, pytest Frontend: Vue.js 3 (Composition API), PrimeVue, Pinia, Vite, Axios, Chart.js, Playwright Telegram Bot: python-telegram-bot, SQLite + aiosqlite, httpx, FastAPI (internal), pytest Infrastructure: Oracle Database, SSH Tunnel, Nginx (Linux prod), Docker (Linux prod), IIS + NSSM (Windows prod)
For detailed information on any topic, always check the Documentation Index above first.