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:
136
shared/routes/calendar.py
Normal file
136
shared/routes/calendar.py
Normal file
@@ -0,0 +1,136 @@
|
||||
"""
|
||||
Shared Calendar Router Factory for ROA2WEB Applications
|
||||
|
||||
Creates a FastAPI router for /api/calendar endpoints that can be used
|
||||
by both reports-app and data-entry-app.
|
||||
|
||||
Usage:
|
||||
from shared.routes.calendar import create_calendar_router
|
||||
|
||||
calendar_router = create_calendar_router(oracle_pool, cache_decorator=cached)
|
||||
app.include_router(calendar_router, prefix="/api/calendar")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional, Callable, List
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
|
||||
from auth.dependencies import get_current_user
|
||||
from auth.models import CurrentUser
|
||||
from models.calendar import CalendarPeriod, CalendarPeriodsResponse
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Romanian month names
|
||||
MONTH_NAMES_RO = [
|
||||
"Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie",
|
||||
"Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie"
|
||||
]
|
||||
|
||||
|
||||
def create_calendar_router(
|
||||
oracle_pool,
|
||||
cache_decorator: Optional[Callable] = None,
|
||||
tags: Optional[List[str]] = None
|
||||
) -> APIRouter:
|
||||
"""
|
||||
Factory function to create a calendar 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 calendar endpoints
|
||||
"""
|
||||
router = APIRouter(
|
||||
redirect_slashes=False,
|
||||
tags=tags or ["calendar"]
|
||||
)
|
||||
|
||||
# Helper to get schema for company
|
||||
async def _get_schema_for_company(company_id: int) -> Optional[str]:
|
||||
"""Get Oracle schema for company ID."""
|
||||
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
|
||||
|
||||
# Apply cache to schema lookup if decorator provided
|
||||
if cache_decorator:
|
||||
_get_schema_for_company = cache_decorator(
|
||||
cache_type='schema',
|
||||
key_params=['company_id']
|
||||
)(_get_schema_for_company)
|
||||
|
||||
# Helper to get periods - can be cached
|
||||
async def _get_available_periods(company_id: int) -> CalendarPeriodsResponse:
|
||||
"""Get available accounting periods for a company."""
|
||||
schema = await _get_schema_for_company(company_id)
|
||||
if not schema:
|
||||
logger.warning(f"Schema not found for company {company_id}")
|
||||
return CalendarPeriodsResponse(periods=[], current_period=None, total_count=0)
|
||||
|
||||
try:
|
||||
async with oracle_pool.get_connection() as connection:
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(f"""
|
||||
SELECT ANUL, LUNA
|
||||
FROM {schema}.CALENDAR
|
||||
ORDER BY ANUL DESC, LUNA DESC
|
||||
""")
|
||||
rows = cursor.fetchall()
|
||||
|
||||
periods = []
|
||||
for row in rows:
|
||||
an, luna = row[0], row[1]
|
||||
month_name = MONTH_NAMES_RO[luna - 1]
|
||||
periods.append(CalendarPeriod(
|
||||
an=an,
|
||||
luna=luna,
|
||||
display_name=f"{month_name} {an}"
|
||||
))
|
||||
|
||||
current_period = periods[0] if periods else None
|
||||
|
||||
logger.info(f"Loaded {len(periods)} periods for company {company_id}")
|
||||
|
||||
return CalendarPeriodsResponse(
|
||||
periods=periods,
|
||||
current_period=current_period,
|
||||
total_count=len(periods)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error fetching periods for company {company_id}: {e}")
|
||||
return CalendarPeriodsResponse(periods=[], current_period=None, total_count=0)
|
||||
|
||||
# Apply cache decorator if provided
|
||||
if cache_decorator:
|
||||
_get_available_periods = cache_decorator(
|
||||
cache_type='calendar_periods',
|
||||
key_params=['company_id']
|
||||
)(_get_available_periods)
|
||||
|
||||
@router.get("/periods", response_model=CalendarPeriodsResponse)
|
||||
async def get_calendar_periods(
|
||||
company: int = Query(..., description="Company ID"),
|
||||
current_user: CurrentUser = Depends(get_current_user)
|
||||
) -> CalendarPeriodsResponse:
|
||||
"""
|
||||
Get available accounting periods for a company.
|
||||
Returns periods ordered by year DESC, month DESC with Romanian month names.
|
||||
"""
|
||||
# Validate company access
|
||||
if str(company) not in current_user.companies:
|
||||
raise HTTPException(403, f"Nu aveți acces la firma {company}")
|
||||
|
||||
return await _get_available_periods(company)
|
||||
|
||||
return router
|
||||
Reference in New Issue
Block a user