feat: Enhance currency handling and validation across scenarios

- Updated form template to prefill currency input with default value and added help text for clarity.
- Modified integration tests to assert more descriptive error messages for invalid currency codes.
- Introduced new tests for currency normalization and validation in various scenarios, including imports and exports.
- Added comprehensive tests for pricing calculations, ensuring defaults are respected and overrides function correctly.
- Implemented unit tests for pricing settings repository, ensuring CRUD operations and default settings are handled properly.
- Enhanced scenario pricing evaluation tests to validate currency handling and metadata defaults.
- Added simulation tests to ensure Monte Carlo runs are accurate and handle various distribution scenarios.
This commit is contained in:
2025-11-11 18:29:59 +01:00
parent 032e6d2681
commit 795a9f99f4
50 changed files with 5110 additions and 81 deletions

View File

@@ -121,9 +121,32 @@ async def export_projects(
) -> Response:
project_repo = _ensure_repository(
getattr(uow, "projects", None), "Project")
start = time.perf_counter()
try:
start = time.perf_counter()
projects = project_repo.filtered_for_export(request.filters)
except ValueError as exc:
_record_export_audit(
uow=uow,
dataset="projects",
status="failure",
export_format=request.format,
row_count=0,
filename=None,
)
logger.warning(
"export.validation_failed",
extra={
"event": "export",
"dataset": "projects",
"status": "validation_failed",
"format": request.format.value,
"error": str(exc),
},
)
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
detail=str(exc),
) from exc
except Exception as exc:
_record_export_audit(
uow=uow,
@@ -145,7 +168,6 @@ async def export_projects(
raise exc
filename = f"projects-{_timestamp_suffix()}"
start = time.perf_counter()
if request.format == ExportFormat.CSV:
stream = stream_projects_to_csv(projects)
@@ -226,10 +248,33 @@ async def export_scenarios(
) -> Response:
scenario_repo = _ensure_repository(
getattr(uow, "scenarios", None), "Scenario")
start = time.perf_counter()
try:
start = time.perf_counter()
scenarios = scenario_repo.filtered_for_export(
request.filters, include_project=True)
except ValueError as exc:
_record_export_audit(
uow=uow,
dataset="scenarios",
status="failure",
export_format=request.format,
row_count=0,
filename=None,
)
logger.warning(
"export.validation_failed",
extra={
"event": "export",
"dataset": "scenarios",
"status": "validation_failed",
"format": request.format.value,
"error": str(exc),
},
)
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
detail=str(exc),
) from exc
except Exception as exc:
_record_export_audit(
uow=uow,
@@ -251,7 +296,6 @@ async def export_scenarios(
raise exc
filename = f"scenarios-{_timestamp_suffix()}"
start = time.perf_counter()
if request.format == ExportFormat.CSV:
stream = stream_scenarios_to_csv(scenarios)