Files
roa2web-service-auto/CLAUDE.md
Claude Agent bb72b690ab fix(ocr): Fix mobile file upload ERR_NETWORK on Android/iOS
Clone file to memory immediately after selection to avoid SnapshotState
invalidation in W3C File API. Android/iOS browsers invalidate File object
references after accessing properties (name, size, type).

See: https://issues.chromium.org/40703873

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 14:22:56 +00:00

19 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 ultrathin monolith architecture:

  1. Reports Module (backend/modules/reports/) - Read-only reports from Oracle (raportări)
  2. Data Entry Module (backend/modules/data_entry/) - Data input with approval workflow (introduceri date)
  3. Telegram Bot Module (backend/modules/telegram/) - Telegram bot integration

Main Branch: main (use for PRs) Working Directory: Repository root

Quick Reference: See README.md for complete setup, commands, deployment, and testing instructions.


📁 Module-Specific Instructions

Important

: When working on a specific module, read its documentation first!

Module Documentation Location Description
Data Entry docs/data-entry/DATA-ENTRY-MODULE.md Bonuri fiscale, chitanțe, workflow aprobare
Reports This file (below) Rapoarte Oracle read-only
Telegram Bot docs/telegram/README.md Bot Telegram

When to Use Which Instructions

Working on Data Entry (backend/modules/data_entry/ or src/modules/data-entry/): → FIRST read docs/data-entry/DATA-ENTRY-MODULE.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
  • Oracle nomenclatures integration

Working on Reports Module (backend/modules/reports/ or src/modules/reports/): → Use instructions from this file (below)

Working on shared components (shared/auth/, shared/database/, shared/frontend/): → These are used by ALL modules - be careful with changes! → shared/frontend/ contains: LoginView.vue, auth store factory, login styles


🏗️ Architecture

Important Architecture Decisions: See docs/ARCHITECTURE-DECISIONS.md for critical decisions about:

  • Why single worker (--workers 1) is required for Telegram bot
  • Ultrathin monolith vs microservices rationale
  • IIS sub-application deployment strategy
  • Performance characteristics and scaling considerations

Ultrathin Monolith Structure

.
├── backend/                   # Unified FastAPI backend (port 8000/8001)
│   ├── modules/              # Business logic modules
│   │   ├── reports/         # Reports module (Oracle read-only)
│   │   ├── data_entry/      # Data entry module (SQLite + workflow)
│   │   ├── telegram/        # Telegram bot module
│   │   └── data/            # Shared data (telegram_bot.db)
│   ├── config.py            # Centralized configuration
│   └── main.py              # FastAPI app entry point
│
├── src/                      # Unified Vue.js 3 frontend
│   ├── modules/             # Feature modules
│   │   ├── reports/         # Reports frontend
│   │   └── data-entry/      # Data entry frontend
│   ├── shared/              # Shared frontend components
│   │   ├── components/      # Reusable Vue components
│   │   └── stores/          # Pinia stores
│   ├── assets/              # Global CSS, images
│   ├── router/              # Vue Router
│   └── App.vue              # Root component
│
├── shared/                   # Shared backend components
│   ├── database/            # Oracle pool
│   ├── auth/                # JWT auth & middleware
│   └── frontend/            # Shared frontend assets
│       ├── components/      # LoginView.vue
│       ├── stores/          # Auth store factory
│       └── styles/          # Login CSS
│
├── docs/                     # Architecture & style guides
├── deployment/               # Production deployment scripts
└── ssh-tunnel/              # SSH tunnel for Oracle DB access

Starting Services

Quick Start (Unified backend + frontend):

./start-prod.sh               # Prod env: Backend :8001, Frontend :3000
./start-test.sh              # Test env: Backend :8001, Frontend :3000

Individual Service Control:

./start-frontend.sh restart   # Restart frontend only (~7s - fastest!)
./start-backend.sh            # Start unified backend :8000 or :8001
./status.sh                   # Show services status + health checks

Infrastructure:

./ssh-tunnel-prod.sh start    # Oracle DB tunnel (production: 10.0.20.36)
./ssh-tunnel-test.sh start    # Oracle TEST tunnel (LXC: 10.0.20.121)

Benefits:

  • Single backend process: All modules in one FastAPI app
  • Faster startup: No multi-service coordination overhead
  • Easier debugging: Single process to monitor
  • Shared resources: Connection pools, cache, and middleware

Key Architectural Decisions

Core Architecture:

  • Ultrathin Monolith: Single backend process with modular structure (backend/modules/)
  • Single Worker Mode: --workers 1 (required for Telegram bot - see ADR-002 in docs/ARCHITECTURE-DECISIONS.md)
  • Shared Database Pool: Singleton OraclePool in shared/database/oracle_pool.py (python-oracledb with connection pooling)
  • Centralized Auth: JWT-based auth in shared/auth/ with middleware auto-injecting request.state.user

Module-Specific:

  • Module-Based Cache: Each module can have its own cache strategy (Reports uses L1+L2 hybrid cache)
  • Telegram Bot: Integrated module with SQLite database (backend/modules/data/telegram_bot.db)
  • FastAPI Structure: Modules in backend/modules/*/, each with services, routers, models/schemas

Infrastructure:

  • SSH Tunnel: Required for Oracle DB connections (development/Linux) - see Database Setup
  • IIS Sub-Application: Deployed at /roa2web path, not root (production)

For detailed rationale and trade-offs, see docs/ARCHITECTURE-DECISIONS.md


🗄️ Database Setup

Schema: CONTAFIN_ORACLE (authentication and user management) Connection: SSH tunnel required (Oracle on remote network)

SSH Tunnel (Development/Linux)

./ssh-tunnel-prod.sh start     # localhost:1526 → remote:1521
./ssh-tunnel-prod.sh status    # Check tunnel
./ssh-tunnel-prod.sh stop      # Stop tunnel

IMPORTANT: Always ensure SSH tunnel is running before starting backend services.

Environment Variables (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

# Module Configuration
MODULE_REPORTS_ENABLED=true
MODULE_DATA_ENTRY_ENABLED=true
MODULE_TELEGRAM_ENABLED=true

Windows Production: Direct Oracle connection, no SSH tunnel required. Environment Files: Multiple .env files available (.env.dev, .env.test, .env.prod) - see backend/ENV-SETUP.md for details.


🔑 Authentication Flow

  1. Login: POST /api/auth/login → calls pack_drepturi.verificautilizator(username, password)
  2. Token: JWT includes username, user_id, companies[], permissions[], exp, iat, type
  3. Middleware: AuthenticationMiddleware in shared/auth/middleware.py validates tokens, injects user
  4. Protected Routes: All routes except excluded_paths require valid JWT

Key Files:

  • shared/auth/middleware.py - FastAPI middleware with rate limiting (5 req/5 min)
  • shared/auth/jwt_handler.py - Token creation/validation
  • backend/main.py - Main FastAPI app with auth router registration

📝 Common Development Tasks

Adding a New API Endpoint

IMPORTANT: Always use the cache system for database queries to improve performance.

  1. Create service in backend/modules/reports/services/your_service.py (NOT in router!)
  2. Define Pydantic schemas in modules/*/schemas/ or modules/*/models/
  3. Add caching using @cached decorator in service methods
  4. Create router in backend/modules/reports/routers/your_router.py (calls service)
  5. 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 @cached decorator 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 username in key_params for user-specific data
  • Include filter parameters in key_params for 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-*) - check CSS_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-value from stats.css
  • Never use :deep() in components (use src/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 docs/telegram/TELEGRAM_BUTTON_INTERFACE_PLAN.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

  1. Place in shared/{database|auth|utils}/
  2. Import using sys.path.append() pattern (see backend/app/main.py:21)
  3. 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.example as 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)

  • docs/data-entry/CLAUDE.md - ⚠️ READ FIRST when working on data-entry
  • docs/data-entry/README.md - Quick start guide
  • docs/data-entry/REQUIREMENTS.md - Functional requirements
  • docs/data-entry/ARCHITECTURE.md - Technical architecture (SQLModel, workflow)

Architecture & Planning

  • DEVELOPMENT_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 conventions
  • docs/COMPONENT_STYLING.md - Component-specific styling guide
  • docs/FORM_TEMPLATE.md - Standardized form template
  • Frontend module documentation in src/modules/ - Frontend architecture and setup
  • Playwright E2E testing (see tests/ directory) - Playwright E2E testing guide

Backend Development

  • Backend module documentation in backend/modules/ - Backend architecture and API details
  • API endpoints documented in README.md (Authentication, Companies, Dashboard, Invoices, Treasury, Telegram)

Telegram Bot Development

  • docs/telegram/README.md - Complete bot architecture and development guide (START HERE)
  • docs/telegram/TELEGRAM_BUTTON_INTERFACE_PLAN.md - Command reference and patterns
  • tests/MANUAL_TESTING_CHECKLIST.md - Manual testing procedures

Deployment

🚀 Quick Deploy from Claude-Agent LXC:

# Din orice director ROA2WEB (main, worktree, branch):
./deployment/linux/deploy.sh          # Full deploy (frontend + backend)
./deployment/linux/deploy.sh frontend # Frontend only
./deployment/linux/deploy.sh backend  # Backend only
./deployment/linux/deploy.sh test     # Test SSH connection

→ Server auto-deploys within 5 minutes (scheduled task)

  • deployment/linux/README.md - ⚠️ DEPLOY FROM LXC - Linux to Windows deployment guide
  • deployment/windows/README.md - Windows deployment quick start
  • deployment/windows/docs/WINDOWS_DEPLOYMENT.md - Complete Windows guide
  • deployment/windows/docs/TWO-TIER-IIS-DEPLOYMENT.md - ⚠️ PRODUCTION ARCHITECTURE - 2-tier IIS setup with public gateway
  • deployment/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

Known Issues & Fixes

Mobile File Upload ERR_NETWORK (Android/iOS)

Problem: File uploads (OCR, attachments) fail with ERR_NETWORK (status 0) on mobile browsers, but work on desktop.

Cause: Android/iOS browsers invalidate File object references after accessing properties (file.name, file.size, file.type) due to SnapshotState in the W3C File API. See Chromium Bug #40703873.

Solution: Clone the file into memory immediately after selection, before accessing any properties:

const handleFile = async (file) => {
  // Validations first...

  // FIX: Clone file to memory to avoid SnapshotState invalidation
  const arrayBuffer = await file.arrayBuffer()
  const clonedFile = new File([arrayBuffer], file.name, {
    type: file.type,
    lastModified: file.lastModified
  })
  selectedFile.value = clonedFile
}

Applied in: src/modules/data-entry/components/ocr/OCRUploadZone.vue


🔧 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.