Files
roa2web-service-auto/shared/routes/companies.py
Marius Mutu 1a6e9b17d2 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>
2025-12-15 15:00:45 +02:00

176 lines
6.0 KiB
Python

"""
Shared Companies Router Factory for ROA2WEB Applications
Creates a FastAPI router for /api/companies endpoints that can be used
by both reports-app and data-entry-app.
Usage:
from shared.routes.companies import create_companies_router
companies_router = create_companies_router(oracle_pool, cache_decorator=cached)
app.include_router(companies_router, prefix="/api/companies")
"""
import logging
from typing import Optional, Callable, List
from fastapi import APIRouter, Depends, HTTPException, Request
from auth.dependencies import get_current_user
from auth.models import CurrentUser
from models.company import Company, CompanyListResponse
logger = logging.getLogger(__name__)
def create_companies_router(
oracle_pool,
cache_decorator: Optional[Callable] = None,
tags: Optional[List[str]] = None
) -> APIRouter:
"""
Factory function to create a companies router.
Args:
oracle_pool: The Oracle connection pool instance
cache_decorator: Optional caching decorator (e.g., @cached)
tags: OpenAPI tags for the router
Returns:
Configured FastAPI router for company endpoints
"""
router = APIRouter(
redirect_slashes=False,
tags=tags or ["companies"]
)
# Helper function to get companies - can be cached
async def _get_user_companies_data(username: str) -> List[Company]:
"""
Get list of companies for a user from Oracle.
"""
companies = []
async with oracle_pool.get_connection() as connection:
with connection.cursor() as cursor:
try:
# Get user ID
cursor.execute("""
SELECT ID_UTIL, UTILIZATOR
FROM UTILIZATORI
WHERE UPPER(UTILIZATOR) = :username
""", {'username': username.upper()})
user_row = cursor.fetchone()
if not user_row:
logger.warning(f"User {username} not found in UTILIZATORI")
return []
user_id = user_row[0]
# Get companies for user (program 2 = data entry/reports)
cursor.execute("""
SELECT A.ID_FIRMA, A.FIRMA, A.SCHEMA, A.COD_FISCAL
FROM V_NOM_FIRME A
WHERE A.ID_FIRMA IN (
SELECT ID_FIRMA
FROM VDEF_UTIL_FIRME
WHERE ID_PROGRAM = 2 AND ID_UTIL = :user_id
)
ORDER BY A.FIRMA
""", {'user_id': user_id})
for row in cursor.fetchall():
companies.append(Company(
id_firma=row[0],
name=row[1],
schema_name=row[2],
fiscal_code=row[3],
is_active=True
))
logger.info(f"Found {len(companies)} companies for user {username}")
except Exception as e:
logger.error(f"Error fetching companies: {e}")
return companies
# Apply cache decorator if provided
if cache_decorator:
_get_user_companies_data = cache_decorator(
cache_type='companies',
key_params=['username']
)(_get_user_companies_data)
@router.get("", response_model=CompanyListResponse)
@router.get("/", response_model=CompanyListResponse)
async def get_user_companies(
request: Request,
current_user: CurrentUser = Depends(get_current_user)
):
"""Get list of companies the user has access to."""
try:
companies = await _get_user_companies_data(current_user.username)
return CompanyListResponse(
companies=companies,
total_count=len(companies)
)
except Exception as e:
logger.error(f"Error in get_user_companies: {e}")
raise HTTPException(500, f"Eroare la obținerea listei de firme: {str(e)}")
@router.get("/{company_id}", response_model=Company)
async def get_company_details(
company_id: str,
current_user: CurrentUser = Depends(get_current_user)
):
"""Get details of a specific company."""
# Validate access
if company_id not in current_user.companies:
raise HTTPException(403, f"Nu aveți acces la firma {company_id}")
try:
async with oracle_pool.get_connection() as connection:
with connection.cursor() as cursor:
cursor.execute("""
SELECT ID_FIRMA, FIRMA, SCHEMA, COD_FISCAL
FROM V_NOM_FIRME
WHERE ID_FIRMA = :company_id
""", {'company_id': int(company_id)})
row = cursor.fetchone()
if not row:
raise HTTPException(404, f"Firma {company_id} nu a fost găsită")
return Company(
id_firma=row[0],
name=row[1],
schema_name=row[2],
fiscal_code=row[3] or "",
is_active=True
)
except HTTPException:
raise
except Exception as e:
raise HTTPException(500, f"Eroare la obținerea detaliilor firmei: {str(e)}")
@router.get("/{company_id}/validate")
async def validate_company_access(
company_id: str,
current_user: CurrentUser = Depends(get_current_user)
):
"""Validate if user has access to a company."""
has_access = company_id in current_user.companies
return {
"company_id": company_id,
"has_access": has_access,
"user": current_user.username,
"message": "Acces validat" if has_access else "Acces refuzat"
}
return router