feat(orion): extend report layout settings to version 1.1

This commit is contained in:
Schubert Ferenc 2026-07-12 14:56:15 +02:00
parent 12e1761a5b
commit 613ffdc8b7
591 changed files with 3601 additions and 999 deletions

View file

@ -25,11 +25,13 @@ from app.api.v1.auth import login as auth_login
from app.api.v1 import domain as domain_api
from app.schemas.auth import LoginRequest
from app.schemas.domain import QuickStartCreateRequest, UserCreate, UserUpdate, ValidationCreate
from app.schemas.domain import OrionReportSettingsUpdate
from app.modules.orion.service import OrionReportService
from app.modules.orion.assets import SCHUBAMED_LOGO_PATH, schubamed_logo_uri
from app.modules.orion.result import validation_result_box, validation_result_presentation
from app.modules.orion.template_service import REPORT_SECTIONS, ReportTemplateService
from app.modules.helios.service import HeliosImportService
from app.services.report_settings import OrionReportSettingsService
def session() -> Session:
@ -785,6 +787,120 @@ def test_quickstart_auto_selects_single_customer_masterdata():
assert created.device_id == device.id
def test_quickstart_requires_location_when_customer_has_multiple_locations():
db = session()
customer, location, device = seed(db)
db.add(Location(customer_id=customer.id, name="Zweiter Standort"))
admin = User(
email="multi-location-admin@schubamed.de",
first_name="Quickstart",
last_name="Admin",
role=UserRole.ADMIN.value,
password_hash="hash",
is_active=True,
must_change_password=False,
)
db.add(admin)
db.flush()
with pytest.raises(ValueError, match="Bitte Standort auswählen"):
QuickStartService(db).create_validation(
QuickStartCreateRequest(
customer_id=customer.id,
device_id=device.id,
validation_type="Erstvalidierung",
),
examiner=admin,
)
created = QuickStartService(db).create_validation(
QuickStartCreateRequest(
customer_id=customer.id,
location_id=location.id,
device_id=device.id,
validation_type="Erstvalidierung",
),
examiner=admin,
)
assert created.location_id == location.id
def test_quickstart_rejects_foreign_location_and_contact_ids():
db = session()
customer, location, device = seed(db)
other_customer = Customer(customer_type=CustomerType.practice, name="Andere Praxis")
db.add(other_customer)
db.flush()
other_location = Location(customer_id=other_customer.id, name="Fremder Standort")
other_contact = Contact(customer_id=other_customer.id, full_name="Fremder Kontakt")
admin = User(
email="foreign-masterdata-admin@schubamed.de",
first_name="Quickstart",
last_name="Admin",
role=UserRole.ADMIN.value,
password_hash="hash",
is_active=True,
must_change_password=False,
)
db.add_all([other_location, other_contact, admin])
db.flush()
with pytest.raises(ValueError, match="Standort gehört nicht"):
QuickStartService(db).create_validation(
QuickStartCreateRequest(
customer_id=customer.id,
location_id=other_location.id,
device_id=device.id,
validation_type="Erstvalidierung",
),
examiner=admin,
)
with pytest.raises(ValueError, match="Ansprechpartner gehört nicht"):
QuickStartService(db).create_validation(
QuickStartCreateRequest(
customer_id=customer.id,
location_id=location.id,
contact_id=other_contact.id,
device_id=device.id,
validation_type="Erstvalidierung",
),
examiner=admin,
)
def test_quickstart_allows_multiple_contacts_without_required_selection():
db = session()
customer, location, device = seed(db)
db.add_all([
Contact(customer_id=customer.id, full_name="Kontakt A"),
Contact(customer_id=customer.id, full_name="Kontakt B"),
])
admin = User(
email="optional-contact-admin@schubamed.de",
first_name="Quickstart",
last_name="Admin",
role=UserRole.ADMIN.value,
password_hash="hash",
is_active=True,
must_change_password=False,
)
db.add(admin)
db.flush()
created = QuickStartService(db).create_validation(
QuickStartCreateRequest(
customer_id=customer.id,
location_id=location.id,
device_id=device.id,
validation_type="Erstvalidierung",
),
examiner=admin,
)
assert created.contact_id is None
def test_quickstart_device_last_validation_returns_latest_completed_validation():
db = session()
customer, location, device = seed(db)
@ -1257,3 +1373,194 @@ def test_confirmed_measurements_appear_in_orion_report(tmp_path):
assert "leak_rate" in html
assert "0,2" in html
def seed_admin(db: Session) -> User:
user = User(
email="admin@schubamed.de",
first_name="Admin",
last_name="User",
role=UserRole.ADMIN.value,
password_hash="hash",
is_active=True,
must_change_password=False,
)
db.add(user)
db.flush()
return user
def report_settings_payload(**overrides) -> OrionReportSettingsUpdate:
values = {
"layout_profile": "STANDARD",
"show_cover_result_text": True,
"signature_mode": "TECHNICAL_ONLY",
"image_size": "MEDIUM",
"page_break_before_main_chapters": True,
"page_margin": "STANDARD",
"section_spacing": "STANDARD",
"table_layout": "STANDARD",
"table_font_size": "STANDARD",
"image_position": "SIDE_BY_SIDE",
"max_images_per_page": 2,
"show_image_captions": True,
"show_header": True,
"show_footer": True,
"logo_size": "MEDIUM",
"compact_cover": False,
}
values.update(overrides)
return OrionReportSettingsUpdate(**values)
def test_report_settings_defaults_and_update_persist():
db = session()
admin = seed_admin(db)
settings = OrionReportSettingsService(db).get_global()
assert settings.scope == "GLOBAL"
assert settings.layout_profile == "STANDARD"
assert settings.show_cover_result_text is True
assert settings.signature_mode == "TECHNICAL_ONLY"
assert settings.image_size == "MEDIUM"
assert settings.page_break_before_main_chapters is True
assert settings.page_margin == "STANDARD"
assert settings.section_spacing == "STANDARD"
assert settings.table_layout == "STANDARD"
assert settings.table_font_size == "STANDARD"
assert settings.image_position == "SIDE_BY_SIDE"
assert settings.max_images_per_page == 2
assert settings.show_image_captions is True
assert settings.show_header is True
assert settings.show_footer is True
assert settings.logo_size == "MEDIUM"
assert settings.compact_cover is False
updated = OrionReportSettingsService(db).update_global(
report_settings_payload(
layout_profile="COMPACT",
show_cover_result_text=False,
signature_mode="TECHNICAL_AND_CLIENT",
image_size="LARGE",
page_break_before_main_chapters=False,
page_margin="WIDE",
section_spacing="COMPACT",
table_layout="COMPACT",
table_font_size="SMALL",
image_position="STACKED",
max_images_per_page=4,
show_image_captions=False,
show_header=False,
show_footer=False,
logo_size="LARGE",
compact_cover=True,
),
admin,
)
db.commit()
assert updated.layout_profile == "COMPACT"
assert updated.show_cover_result_text is False
assert updated.signature_mode == "TECHNICAL_AND_CLIENT"
assert updated.image_size == "LARGE"
assert updated.page_break_before_main_chapters is False
assert updated.page_margin == "WIDE"
assert updated.section_spacing == "COMPACT"
assert updated.table_layout == "COMPACT"
assert updated.table_font_size == "SMALL"
assert updated.image_position == "STACKED"
assert updated.max_images_per_page == 4
assert updated.show_image_captions is False
assert updated.show_header is False
assert updated.show_footer is False
assert updated.logo_size == "LARGE"
assert updated.compact_cover is True
assert updated.updated_by_user_id == admin.id
def test_report_settings_invalid_enum_is_rejected():
with pytest.raises(Exception):
report_settings_payload(
layout_profile="WILD",
)
def test_orion_settings_preview_pdf_contains_pdf_bytes(tmp_path):
db = session()
seed_admin(db)
pdf = OrionReportService(db, tmp_path).render_settings_preview_pdf(
report_settings_payload(
layout_profile="COMPACT",
show_cover_result_text=True,
signature_mode="TECHNICAL_AND_CLIENT",
image_size="SMALL",
page_break_before_main_chapters=False,
page_margin="NARROW",
section_spacing="COMPACT",
table_layout="COMPACT",
table_font_size="SMALL",
image_position="STACKED",
max_images_per_page=1,
show_image_captions=False,
show_header=False,
show_footer=False,
logo_size="SMALL",
compact_cover=True,
)
)
assert pdf.startswith(b"%PDF")
assert len(pdf) > 1024
def test_orion_report_settings_affect_html_without_removing_summary_result(tmp_path):
db = session()
customer, location, device = seed(db)
validation = valid_validation(customer, location, device)
validation.result = "BESTANDEN"
db.add(validation)
admin = seed_admin(db)
OrionReportSettingsService(db).update_global(
report_settings_payload(
layout_profile="COMPACT",
show_cover_result_text=False,
signature_mode="TECHNICAL_AND_CLIENT",
image_size="SMALL",
page_break_before_main_chapters=False,
page_margin="NARROW",
section_spacing="COMPACT",
table_layout="COMPACT",
table_font_size="SMALL",
image_position="STACKED",
max_images_per_page=1,
show_image_captions=False,
show_header=False,
show_footer=False,
logo_size="SMALL",
compact_cover=True,
),
admin,
)
db.commit()
html = OrionReportService(db, tmp_path).render_html(validation.id)
assert "layout-compact" in html
assert "page-margin-narrow" in html
assert "section-spacing-compact" in html
assert "table-layout-compact" in html
assert "table-font-small" in html
assert "image-size-small" in html
assert "image-position-stacked" in html
assert "image-page-max-1" in html
assert "image-captions-off" in html
assert "logo-size-small" in html
assert "cover-compact" in html
assert "main-chapter-flow" in html
assert "Ergebnis der Validierung" not in html.split('<section class="report-content">')[0]
assert "Unterschrift Auftraggeber" in html
assert '<header class="report-header"' not in html
assert '<footer class="report-footer"' not in html
assert html.count("result-box") >= 1