Compare commits
3 Commits
58f1e6fb82
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| dff64be964 | |||
| 7d48b4c838 | |||
| af8fa9bf9e |
@@ -7,7 +7,7 @@ Die App ermöglicht die Verwaltung von Patienten und Wunddokumentationen,
|
||||
unterstützt Fotos und digitale Unterschriften und erzeugt aus
|
||||
abgeschlossenen Dokumentationen PDF-Dateien.
|
||||
|
||||
> **Projektstatus:** beta1.1
|
||||
> **Projektstatus:** beta1.4
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
@@ -398,12 +398,12 @@ werden kann.
|
||||
|
||||
### Phase 6 -- Qualität und Stabilität
|
||||
|
||||
- [ ] Unit Tests
|
||||
- [ ] Room Tests
|
||||
- [ ] UI Tests
|
||||
- [ ] Fehlerbehandlung
|
||||
- [x] Unit Tests
|
||||
- [x] Room Tests
|
||||
- [x] UI Tests
|
||||
- [x] Fehlerbehandlung
|
||||
- [ ] Tablet-Optimierung
|
||||
- [ ] Datenschutz-Review
|
||||
- [x] Datenschutz-Review
|
||||
|
||||
### Phase 7 -- Multi-Device-Synchronisation
|
||||
|
||||
|
||||
@@ -28,11 +28,11 @@ import de.medapp.wunddoku.domain.model.InputSpec
|
||||
* Abschnitt 2.3b.
|
||||
*
|
||||
* [CATALOG_VERSION] wird beim Erstellen einer neuen Dokumentation als rein informatives
|
||||
* Metadatum in Documentation.catalogVersion übernommen. Auf 5 erhöht (Decubitus als einfache Checkbox, Umbau "Empfohlene Wundversorgung").
|
||||
* Metadatum in Documentation.catalogVersion übernommen. Auf 6 erhöht (Wundart/Wundgrund geteilt, Wundrandschutz neu geordnet, Granudacyn und Freitext entkoppelt).
|
||||
*/
|
||||
object DocumentationCatalog {
|
||||
|
||||
const val CATALOG_VERSION: Int = 5
|
||||
const val CATALOG_VERSION: Int = 6
|
||||
|
||||
/**
|
||||
* Label der automatisch an jeden Block angehängten Freitext-Option. Zentral hier definiert,
|
||||
@@ -65,30 +65,31 @@ object DocumentationCatalog {
|
||||
CheckItemDefinition(id = "wundart_ulcus_cruris_arteriosum", label = "Ulcus Cruris art.", order = 6),
|
||||
CheckItemDefinition(id = "wundart_ulcus_cruris_venosum_arteriosum", label = "Ulcus Cruris ven.-art.", order = 7),
|
||||
CheckItemDefinition(id = "wundart_diabetisches_fusssyndrom", label = "Diab. Fuß Syndrom", order = 8),
|
||||
CheckItemDefinition(id = "wundart_post_operative_wundheilungsstoerung", label = "Post-operative Wundheilungsstörung / Dehiszenz", order = 9),
|
||||
CheckItemDefinition(id = "wundart_schuerfwunde", label = "Schürfwunde", order = 10),
|
||||
CheckItemDefinition(id = "wundart_intertrigo", label = "Intertrigo / Wunde nach Intertrigo", order = 11),
|
||||
CheckItemDefinition(id = "wundart_iad", label = "IAD (Inkontinenz-assoziierte Dermatitis) / Wunde nach IAD", order = 12),
|
||||
CheckItemDefinition(id = "wundart_amputationswunde", label = "Amputationswunde", order = 13),
|
||||
CheckItemDefinition(id = "wundart_spalthautentnahmestelle", label = "Spalthautentnahmestelle", order = 14),
|
||||
CheckItemDefinition(id = "wundart_post_operative_wundheilungsstoerung", label = "Post-operative Wundheilungsstörung", order = 9),
|
||||
CheckItemDefinition(id = "wundart_dehiszenz", label = "Dehiszenz", order = 10),
|
||||
CheckItemDefinition(id = "wundart_schuerfwunde", label = "Schürfwunde", order = 11),
|
||||
CheckItemDefinition(id = "wundart_intertrigo", label = "Intertrigo / Wunde nach Intertrigo", order = 12),
|
||||
CheckItemDefinition(id = "wundart_iad", label = "IAD (Inkontinenz-assoziierte Dermatitis) / Wunde nach IAD", order = 13),
|
||||
CheckItemDefinition(id = "wundart_amputationswunde", label = "Amputationswunde", order = 14),
|
||||
CheckItemDefinition(id = "wundart_spalthautentnahmestelle", label = "Spalthautentnahmestelle", order = 15),
|
||||
CheckItemDefinition(
|
||||
id = "wundart_transplantatverlust",
|
||||
label = "Wunde nach Transplantatverlust",
|
||||
order = 15,
|
||||
order = 16,
|
||||
inputSpec = InputSpec(
|
||||
pdfTemplate = "Wunde nach Transplantatverlust, Spalthautdeckung ca. {0}%",
|
||||
fields = listOf(InputFieldDef.Number(label = "Spalthautdeckung ca. (%)", maxLength = 3)),
|
||||
),
|
||||
),
|
||||
CheckItemDefinition(id = "wundart_sekundaer_heilende_wunde", label = "Sekundär heilende Wunde", order = 16),
|
||||
CheckItemDefinition(id = "wundart_tumorwunde", label = "Tumorwunde", order = 17),
|
||||
CheckItemDefinition(id = "wundart_wunde_nach_abszess", label = "Wunde nach Abszess", order = 18),
|
||||
CheckItemDefinition(id = "wundart_verbrennung_verbruehung", label = "Verbrennung / Verbrühung", order = 19),
|
||||
CheckItemDefinition(id = "wundart_platzwunde", label = "Platzwunde", order = 20),
|
||||
CheckItemDefinition(id = "wundart_schnittwunde", label = "Schnittwunde", order = 21),
|
||||
CheckItemDefinition(id = "wundart_bisswunde", label = "Bisswunde", order = 22),
|
||||
CheckItemDefinition(id = "wundart_chemisch_veraetzung", label = "Chemisch hervorgerufene Wunde / Verätzung", order = 23),
|
||||
CheckItemDefinition(id = "wundart_strahlenschaden", label = "Strahlenschaden", order = 24),
|
||||
CheckItemDefinition(id = "wundart_sekundaer_heilende_wunde", label = "Sekundär heilende Wunde", order = 17),
|
||||
CheckItemDefinition(id = "wundart_tumorwunde", label = "Tumorwunde", order = 18),
|
||||
CheckItemDefinition(id = "wundart_wunde_nach_abszess", label = "Wunde nach Abszess", order = 19),
|
||||
CheckItemDefinition(id = "wundart_verbrennung_verbruehung", label = "Verbrennung / Verbrühung", order = 20),
|
||||
CheckItemDefinition(id = "wundart_platzwunde", label = "Platzwunde", order = 21),
|
||||
CheckItemDefinition(id = "wundart_schnittwunde", label = "Schnittwunde", order = 22),
|
||||
CheckItemDefinition(id = "wundart_bisswunde", label = "Bisswunde", order = 23),
|
||||
CheckItemDefinition(id = "wundart_chemisch_veraetzung", label = "Chemisch hervorgerufene Wunde / Verätzung", order = 24),
|
||||
CheckItemDefinition(id = "wundart_strahlenschaden", label = "Strahlenschaden", order = 25),
|
||||
),
|
||||
),
|
||||
BlockDefinition(
|
||||
@@ -165,24 +166,25 @@ object DocumentationCatalog {
|
||||
items = listOf(
|
||||
CheckItemDefinition(id = "wundgrund_wie_ausgestanzt", label = "Wie ausgestanzt", order = 1),
|
||||
CheckItemDefinition(id = "wundgrund_kraterfoermig", label = "Kraterförmig", order = 2),
|
||||
CheckItemDefinition(id = "wundgrund_unregelmaessig_landkartenartig", label = "Unregelmäßig (landkartenartig/amorph)", order = 3),
|
||||
CheckItemDefinition(id = "wundgrund_linear_spaltfoermig", label = "Linear/spaltförmig", order = 4),
|
||||
CheckItemDefinition(id = "wundgrund_rund", label = "Rund", order = 5),
|
||||
CheckItemDefinition(id = "wundgrund_oval_elliptisch", label = "Oval/elliptisch", order = 6),
|
||||
CheckItemDefinition(id = "wundgrund_quadrangulaer", label = "Quadrangulär", order = 7),
|
||||
CheckItemDefinition(id = "wundgrund_eben", label = "Eben", order = 8),
|
||||
CheckItemDefinition(id = "wundgrund_auf_niveau", label = "Auf Niveau", order = 9),
|
||||
CheckItemDefinition(id = "wundgrund_ueber_niveau", label = "Über Niveau", order = 10),
|
||||
CheckItemDefinition(id = "wundgrund_glaenzend", label = "Glänzend", order = 11),
|
||||
CheckItemDefinition(id = "wundgrund_grob", label = "Grob", order = 12),
|
||||
CheckItemDefinition(id = "wundgrund_fein", label = "Fein", order = 13),
|
||||
CheckItemDefinition(id = "wundgrund_glatt", label = "Glatt", order = 14),
|
||||
CheckItemDefinition(id = "wundgrund_rot", label = "Rot", order = 15),
|
||||
CheckItemDefinition(id = "wundgrund_blass", label = "Blass", order = 16),
|
||||
CheckItemDefinition(id = "wundgrund_knochen_sehnen", label = "Knochen/Sehnen", order = 17),
|
||||
CheckItemDefinition(id = "wundgrund_haemorrhagie", label = "Hämorrhagie", order = 18),
|
||||
CheckItemDefinition(id = "wundgrund_nicht_kaum_einsehbar", label = "Nicht/kaum einsehbar", order = 19),
|
||||
CheckItemDefinition(id = "wundgrund_erscheinung_erscheinung", label = "Erscheinung", order = 20, isFreeText = true),
|
||||
CheckItemDefinition(id = "wundgrund_unregelmaessig", label = "Unregelmäßig", order = 3),
|
||||
CheckItemDefinition(id = "wundgrund_landkartenartig_amorph", label = "Landkartenartig/amorph", order = 4),
|
||||
CheckItemDefinition(id = "wundgrund_linear_spaltfoermig", label = "Linear/spaltförmig", order = 5),
|
||||
CheckItemDefinition(id = "wundgrund_rund", label = "Rund", order = 6),
|
||||
CheckItemDefinition(id = "wundgrund_oval_elliptisch", label = "Oval/elliptisch", order = 7),
|
||||
CheckItemDefinition(id = "wundgrund_quadrangulaer", label = "Quadrangulär", order = 8),
|
||||
CheckItemDefinition(id = "wundgrund_eben", label = "Eben", order = 9),
|
||||
CheckItemDefinition(id = "wundgrund_auf_niveau", label = "Auf Niveau", order = 10),
|
||||
CheckItemDefinition(id = "wundgrund_ueber_niveau", label = "Über Niveau", order = 11),
|
||||
CheckItemDefinition(id = "wundgrund_glaenzend", label = "Glänzend", order = 12),
|
||||
CheckItemDefinition(id = "wundgrund_grob", label = "Grob", order = 13),
|
||||
CheckItemDefinition(id = "wundgrund_fein", label = "Fein", order = 14),
|
||||
CheckItemDefinition(id = "wundgrund_glatt", label = "Glatt", order = 15),
|
||||
CheckItemDefinition(id = "wundgrund_rot", label = "Rot", order = 16),
|
||||
CheckItemDefinition(id = "wundgrund_blass", label = "Blass", order = 17),
|
||||
CheckItemDefinition(id = "wundgrund_knochen_sehnen", label = "Knochen/Sehnen", order = 18),
|
||||
CheckItemDefinition(id = "wundgrund_haemorrhagie", label = "Hämorrhagie", order = 19),
|
||||
CheckItemDefinition(id = "wundgrund_nicht_kaum_einsehbar", label = "Nicht/kaum einsehbar", order = 20),
|
||||
CheckItemDefinition(id = "wundgrund_erscheinung_erscheinung", label = "Erscheinung", order = 21, isFreeText = true),
|
||||
),
|
||||
),
|
||||
BlockDefinition(
|
||||
@@ -498,61 +500,61 @@ object DocumentationCatalog {
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_sterile_kompresse", label = "Sterile Kompresse", order = 2),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_nacl", label = "NaCl 0,9%", order = 3),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_octenisept", label = "Octenisept", order = 4),
|
||||
// Granudacyn: Checkbox + direkt folgendes Freitextfeld, das nur bei angehakter
|
||||
// Checkbox aktiv ist (= vorhandenes isFreeText-Verhalten, s. FreeTextCheckItemRow).
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_granudacyn", label = "Granudacyn", order = 5, isFreeText = true),
|
||||
// Granudacyn: einfache Checkbox. Der Freitext ist ein eigener, separat
|
||||
// anzuhakender Punkt direkt darunter (Bezeichnung "Freitext" wird in PDF/JPG
|
||||
// und, sobald angehakt, auch in der App nicht angezeigt).
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_granudacyn", label = "Granudacyn", order = 5),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_granudacyn_freitext", label = FREE_TEXT_LABEL, order = 6, isFreeText = true),
|
||||
CheckItemDefinition(
|
||||
id = "empfohlene_wundversorgung_getraenkte_kompresse",
|
||||
label = "Getränkte Kompresse",
|
||||
order = 6,
|
||||
order = 7,
|
||||
inputSpec = InputSpec(
|
||||
pdfTemplate = "Getränkte Kompresse für ca. {0} Min. auflegen",
|
||||
fields = listOf(InputFieldDef.Choice(label = "Minuten", options = MINUTE_1_TO_15_OPTIONS)),
|
||||
),
|
||||
),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_tupfend", label = "Tupfend", order = 7),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_wischend", label = "Wischend", order = 8),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_nachspuelen_nacl", label = "Nachspülen mit NaCl 0,9%", order = 9),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_umgebung_trocken_tupfen", label = "Umgebung trocken tupfen", order = 10),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_primaer_abdecken_austamponieren", label = "Primär Wunde abdecken/austamponieren", order = 11),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_ueber_wundrand_hinaus", label = "Über Wundrand hinaus", order = 12),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_nicht_ueber_wundrand_mit", label = "Nicht über Wundrand mit", order = 13),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_nicht_ueber_wundrand_mit_freitext", label = FREE_TEXT_LABEL, order = 14, isFreeText = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_hinweis_wundrandschutz", label = "Wundrandschutz:", order = 15, isStaticHint = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_wundrandschutz_ja", label = "Ja", order = 16),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_wundrandschutz_nein", label = "Nein", order = 17),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_wundrandschutz_freitext", label = FREE_TEXT_LABEL, order = 18, isFreeText = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_tupfend", label = "Tupfend", order = 8),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_wischend", label = "Wischend", order = 9),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_nachspuelen_nacl", label = "Nachspülen mit NaCl 0,9%", order = 10),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_umgebung_trocken_tupfen", label = "Umgebung trocken tupfen", order = 11),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_primaer_abdecken_austamponieren", label = "Primär Wunde abdecken/austamponieren", order = 12),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_ueber_wundrand_hinaus", label = "Über Wundrand hinaus", order = 13),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_nicht_ueber_wundrand_mit", label = "Nicht über Wundrand mit", order = 14),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_nicht_ueber_wundrand_mit_freitext", label = FREE_TEXT_LABEL, order = 15, isFreeText = true),
|
||||
// Wundrandschutz: EINE Checkbox mit eigenem Freitextfeld (kein Ja/Nein mehr).
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_wundrandschutz", label = "Wundrandschutz", order = 16, isFreeText = true),
|
||||
// "Wie oft pro Woche": eigene Checkbox; die Auswahl (1-7x) ist erst nach dem
|
||||
// Anhaken aktiv (InputSpec.requiresOwnCheckbox).
|
||||
CheckItemDefinition(
|
||||
id = "empfohlene_wundversorgung_wundrandschutz_tage_pro_woche",
|
||||
label = "Wie oft pro Woche",
|
||||
order = 19,
|
||||
order = 17,
|
||||
inputSpec = InputSpec(
|
||||
pdfTemplate = "{0}x pro Woche",
|
||||
fields = listOf(InputFieldDef.Choice(label = "Tage pro Woche", options = WEEKDAY_COUNT_OPTIONS)),
|
||||
requiresOwnCheckbox = true,
|
||||
),
|
||||
),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_hinweis_sekundaere_wundauflage", label = "Sekundäre Wundauflage:", order = 20, isStaticHint = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_sekundaer_sterile_kompresse", label = "Sterile Kompresse", order = 21),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_saugkompresse", label = "Saugkompresse", order = 22),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_sonstige_wundauflage", label = "Sonstiges (Wundauflage)", order = 23, isFreeText = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_hinweis_fixieren_mit", label = "Fixieren mit:", order = 24, isStaticHint = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_mullbinde", label = "Mullbinde", order = 25),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_klebevlies", label = "Klebevlies", order = 26),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_folienverband", label = "Folienverband", order = 27),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_schlauchverband", label = "Schlauchverband", order = 28),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_kohaesive_fixierbinde", label = "Kohäsive Fixierbinde", order = 29),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_kohaesive_fixierbinde_freitext", label = FREE_TEXT_LABEL, order = 30, isFreeText = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_kompressionsverband_system", label = "Kompressionsverband / System", order = 31),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_kompressionsverband_system_freitext", label = FREE_TEXT_LABEL, order = 32, isFreeText = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_wechselintervall", label = "Wechselintervall", order = 33, isFreeText = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_hinweis_bedarf", label = "+ bei Bedarf", order = 34, isStaticHint = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_hinweis_sekundaere_wundauflage", label = "Sekundäre Wundauflage:", order = 18, isStaticHint = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_sekundaer_sterile_kompresse", label = "Sterile Kompresse", order = 19),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_saugkompresse", label = "Saugkompresse", order = 20),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_sonstige_wundauflage", label = "Sonstiges (Wundauflage)", order = 21, isFreeText = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_hinweis_fixieren_mit", label = "Fixieren mit:", order = 22, isStaticHint = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_mullbinde", label = "Mullbinde", order = 23),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_klebevlies", label = "Klebevlies", order = 24),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_folienverband", label = "Folienverband", order = 25),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_schlauchverband", label = "Schlauchverband", order = 26),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_kohaesive_fixierbinde", label = "Kohäsive Fixierbinde", order = 27),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_kohaesive_fixierbinde_freitext", label = FREE_TEXT_LABEL, order = 28, isFreeText = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_kompressionsverband_system", label = "Kompressionsverband / System", order = 29),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_kompressionsverband_system_freitext", label = FREE_TEXT_LABEL, order = 30, isFreeText = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_wechselintervall", label = "Wechselintervall", order = 31, isFreeText = true),
|
||||
CheckItemDefinition(id = "empfohlene_wundversorgung_hinweis_bedarf", label = "+ bei Bedarf", order = 32, isStaticHint = true),
|
||||
CheckItemDefinition(
|
||||
id = "empfohlene_wundversorgung_hinweis_abklaerung",
|
||||
label = "Generell: bei akuter Verschlechterung sofort ärztliche Abklärung",
|
||||
order = 35,
|
||||
order = 33,
|
||||
isStaticHint = true,
|
||||
),
|
||||
),
|
||||
@@ -571,9 +573,8 @@ object DocumentationCatalog {
|
||||
CheckItemDefinition(id = "ziel_abheilung", label = "Abheilung", order = 7),
|
||||
CheckItemDefinition(id = "ziel_geruchsreduzierung", label = "Geruchsreduzierung", order = 8),
|
||||
CheckItemDefinition(id = "ziel_einfache_durchfuehrung", label = "Einfache Durchführung", order = 9),
|
||||
CheckItemDefinition(id = "ziel_wundrandschutz", label = "Wundrandschutz", order = 10),
|
||||
CheckItemDefinition(id = "ziel_aufbruch_vorhandenes_material", label = "Aufbruch des vorhandenen Materials", order = 11),
|
||||
CheckItemDefinition(id = "ziel_selbstversorgung_foerdern", label = "Selbstversorgung der Wunde durch Patient/Angehörige unterstützen/fördern", order = 12),
|
||||
CheckItemDefinition(id = "ziel_aufbruch_vorhandenes_material", label = "Aufbruch des vorhandenen Materials", order = 10),
|
||||
CheckItemDefinition(id = "ziel_selbstversorgung_foerdern", label = "Selbstversorgung der Wunde durch Patient/Angehörige unterstützen/fördern", order = 11),
|
||||
),
|
||||
),
|
||||
BlockDefinition(
|
||||
|
||||
@@ -2,10 +2,13 @@ package de.medapp.wunddoku.pdf
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.pdf.PdfDocument
|
||||
import android.text.Layout
|
||||
import android.text.StaticLayout
|
||||
import android.text.TextPaint
|
||||
import de.medapp.wunddoku.domain.error.FailureCategory
|
||||
import de.medapp.wunddoku.domain.model.InputSpecCodec
|
||||
@@ -66,6 +69,8 @@ class AndroidPdfDocumentExporter(
|
||||
private const val ITEM_TEXT_SIZE = 11f
|
||||
private const val FREE_TEXT_TEXT_SIZE = 10.5f
|
||||
private const val FREE_TEXT_INDENT = 20f
|
||||
private const val RUNNING_HEADER_TEXT_SIZE = 9.5f
|
||||
private const val RUNNING_HEADER_SPACING_AFTER = 10f
|
||||
|
||||
// PDF-003: Signaturbereich. Fachlich handschriftliche Unterschriften, keine
|
||||
// kryptographische PDF-Signatur (s. Aufgabenstellung Abschnitt 5).
|
||||
@@ -160,6 +165,9 @@ class AndroidPdfDocumentExporter(
|
||||
marginRight = MARGIN,
|
||||
marginTop = MARGIN,
|
||||
marginBottom = MARGIN,
|
||||
pageHeaderDrawer = PageHeaderDrawer { canvas, pageNumber, left, top, width ->
|
||||
drawRunningHeader(canvas, pageNumber, left, top, width, model, paints)
|
||||
},
|
||||
)
|
||||
|
||||
drawHeader(cursor, model, paints)
|
||||
@@ -243,6 +251,32 @@ class AndroidPdfDocumentExporter(
|
||||
textSize = FREE_TEXT_TEXT_SIZE
|
||||
color = Color.DKGRAY
|
||||
}
|
||||
// Zwischenüberschriften innerhalb eines Blocks: kursiv + unterstrichen.
|
||||
val itemSubheading = TextPaint().apply {
|
||||
isAntiAlias = true
|
||||
textSize = ITEM_TEXT_SIZE
|
||||
typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC)
|
||||
isUnderlineText = true
|
||||
color = Color.BLACK
|
||||
}
|
||||
// Wichtiger Hinweis: fett + unterstrichen.
|
||||
val itemWarning = TextPaint().apply {
|
||||
isAntiAlias = true
|
||||
textSize = ITEM_TEXT_SIZE
|
||||
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
|
||||
isUnderlineText = true
|
||||
color = Color.BLACK
|
||||
}
|
||||
// Seitenkopf ab Seite 2 (Patient + Erstelldatum).
|
||||
val runningHeader = TextPaint().apply {
|
||||
isAntiAlias = true
|
||||
textSize = RUNNING_HEADER_TEXT_SIZE
|
||||
color = Color.DKGRAY
|
||||
}
|
||||
val runningHeaderLine = Paint().apply {
|
||||
color = Color.LTGRAY
|
||||
strokeWidth = 1f
|
||||
}
|
||||
|
||||
// PDF-003: Signaturbereich.
|
||||
val signatureLabel = TextPaint().apply {
|
||||
@@ -286,6 +320,37 @@ class AndroidPdfDocumentExporter(
|
||||
cursor.drawDivider(spacingAfter = 12f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Seitenkopf für alle Seiten AB Seite 2 (Änderungswunsch: auf jeder Seite müssen Name des
|
||||
* Patienten und Erstellungsdatum stehen): Seite 1 trägt bereits den großen Dokumentkopf
|
||||
* ([drawHeader]). Gilt auch für Unterschriften- und Fotoseiten. Verwendet ausschließlich
|
||||
* Snapshot-Daten aus dem [PdfExportModel]. Liefert die belegte Höhe zurück.
|
||||
*/
|
||||
private fun drawRunningHeader(
|
||||
canvas: Canvas,
|
||||
pageNumber: Int,
|
||||
left: Float,
|
||||
top: Float,
|
||||
width: Float,
|
||||
model: PdfExportModel,
|
||||
paints: ExportPaints,
|
||||
): Float {
|
||||
if (pageNumber <= 1) return 0f
|
||||
val text = "Patient: ${model.patientFullName}, geb. ${model.patientBirthDate.format(HEADER_DATE_FORMATTER)}" +
|
||||
" · ${model.patientDisplayAddress} · Erstellt am: ${model.createdAt.format(HEADER_DATE_FORMATTER)}"
|
||||
val layout = StaticLayout.Builder.obtain(text, 0, text.length, paints.runningHeader, width.toInt().coerceAtLeast(1))
|
||||
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
|
||||
.setIncludePad(false)
|
||||
.build()
|
||||
canvas.save()
|
||||
canvas.translate(left, top)
|
||||
layout.draw(canvas)
|
||||
canvas.restore()
|
||||
val lineY = top + layout.height + 4f
|
||||
canvas.drawLine(left, lineY, left + width, lineY, paints.runningHeaderLine)
|
||||
return layout.height + 4f + 1f + RUNNING_HEADER_SPACING_AFTER
|
||||
}
|
||||
|
||||
/** Überschriften erscheinen im PDF immer mit abschließendem Doppelpunkt (kein doppelter
|
||||
* Doppelpunkt, falls der Titel bereits mit einem endet). */
|
||||
private fun headingText(title: String): String {
|
||||
@@ -294,6 +359,8 @@ class AndroidPdfDocumentExporter(
|
||||
}
|
||||
|
||||
private fun drawBlock(cursor: PdfPageCursor, block: PdfExportBlock, paints: ExportPaints) {
|
||||
// Änderungswunsch: "Empfohlene Wundversorgung" beginnt immer auf einer eigenen Seite.
|
||||
if (block.startsOnNewPage) cursor.startNewPageUnlessEmpty()
|
||||
cursor.drawText(headingText(block.title), paints.blockTitle, spacingAfter = 6f)
|
||||
|
||||
for (item in block.items) {
|
||||
@@ -330,14 +397,37 @@ class AndroidPdfDocumentExporter(
|
||||
return
|
||||
}
|
||||
|
||||
// Generische "Freitext"-Einträge (Änderungswunsch): Bezeichnung wird NICHT gedruckt, nur
|
||||
// der eingegebene Text (eingerückt unter dem vorhergehenden Eintrag). Ein angehakter,
|
||||
// aber leerer Freitext erzeugt keine Ausgabe.
|
||||
if (!item.showLabel) {
|
||||
if (item.freeTextValue.isNotBlank()) {
|
||||
cursor.drawText(
|
||||
text = item.freeTextValue,
|
||||
paint = paints.item,
|
||||
indent = FREE_TEXT_INDENT,
|
||||
spacingAfter = 6f,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Die Freitext-Option bekommt einen abschließenden Doppelpunkt, sofern tatsächlich Text
|
||||
// folgt (z. B. "Granudacyn" ohne Text bleibt ohne hängenden Doppelpunkt), normale
|
||||
// Optionen nie.
|
||||
val labelText = if (item.isFreeText && item.freeTextValue.isNotBlank()) "${item.label}:" else item.label
|
||||
val baseLabel = if (item.isFreeText && item.freeTextValue.isNotBlank()) "${item.label}:" else item.label
|
||||
|
||||
// Änderungswunsch: Zwischenüberschriften kursiv + unterstrichen, wichtiger Hinweis fett +
|
||||
// unterstrichen mit Ausrufezeichen davor und dahinter.
|
||||
val (labelText, labelPaint) = when (item.style) {
|
||||
PdfItemStyle.SUBHEADING -> baseLabel to paints.itemSubheading
|
||||
PdfItemStyle.WARNING -> "! $baseLabel !" to paints.itemWarning
|
||||
PdfItemStyle.NORMAL -> baseLabel to paints.item
|
||||
}
|
||||
|
||||
cursor.drawText(
|
||||
text = labelText,
|
||||
paint = paints.item,
|
||||
paint = labelPaint,
|
||||
indent = ITEM_INDENT,
|
||||
spacingAfter = 3f,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package de.medapp.wunddoku.pdf
|
||||
|
||||
import de.medapp.wunddoku.domain.catalog.DocumentationCatalog
|
||||
import de.medapp.wunddoku.domain.model.CheckItem
|
||||
import de.medapp.wunddoku.domain.model.Documentation
|
||||
import de.medapp.wunddoku.domain.model.DocumentationBlock
|
||||
import de.medapp.wunddoku.domain.usecase.BlockEditedPolicy
|
||||
import java.time.ZoneId
|
||||
|
||||
@@ -27,22 +30,24 @@ object DocumentationToPdfExportModelMapper {
|
||||
.sortedBy { it.order }
|
||||
.map { block ->
|
||||
PdfExportBlock(
|
||||
definitionId = block.definitionId,
|
||||
title = block.title,
|
||||
// Änderungswunsch: nur angehakte Optionen erscheinen im PDF - nicht
|
||||
// angehakte Checkboxen werden komplett weggelassen (nicht nur unausgefüllt
|
||||
// dargestellt). Ein leeres Ergebnis ist hier ausgeschlossen: BlockEditedPolicy
|
||||
startsOnNewPage = block.definitionId in BLOCKS_ON_OWN_PAGE,
|
||||
// Nur angehakte Optionen erscheinen im PDF. Hinweistexte (isStaticHint) sind
|
||||
// technisch immer angehakt und werden gruppenabhängig gefiltert, s.
|
||||
// [exportedItems]. Ein leeres Ergebnis ist ausgeschlossen: BlockEditedPolicy
|
||||
// (Filter oben) lässt nur Blöcke mit mindestens einer angehakten Option durch.
|
||||
items = block.items
|
||||
.filter { it.checked }
|
||||
.sortedBy { it.order }
|
||||
.map { item ->
|
||||
PdfExportItem(
|
||||
label = item.label,
|
||||
isFreeText = item.isFreeText,
|
||||
freeTextValue = item.freeTextValue,
|
||||
inputSpec = item.inputSpec,
|
||||
)
|
||||
},
|
||||
items = exportedItems(block).map { item ->
|
||||
PdfExportItem(
|
||||
label = item.label,
|
||||
isFreeText = item.isFreeText,
|
||||
freeTextValue = item.freeTextValue,
|
||||
inputSpec = item.inputSpec,
|
||||
// Generische "Freitext"-Bezeichnung nie drucken, nur den Text.
|
||||
showLabel = !(item.isFreeText && item.label == DocumentationCatalog.FREE_TEXT_LABEL),
|
||||
style = styleFor(item),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -80,4 +85,67 @@ object DocumentationToPdfExportModelMapper {
|
||||
photos = photos,
|
||||
)
|
||||
}
|
||||
|
||||
/** Blöcke, die im PDF/JPG immer auf einer neuen Seite beginnen. */
|
||||
private val BLOCKS_ON_OWN_PAGE: Set<String> = setOf("empfohlene_wundversorgung")
|
||||
|
||||
/** Zwischenüberschriften (kursiv + unterstrichen), anhand der stabilen Definitions-IDs. */
|
||||
private val SUBHEADING_DEFINITION_IDS: Set<String> = setOf(
|
||||
"empfohlene_wundversorgung_hinweis_reinigen_mit",
|
||||
"empfohlene_wundversorgung_primaer_abdecken_austamponieren",
|
||||
"empfohlene_wundversorgung_wundrandschutz",
|
||||
"empfohlene_wundversorgung_hinweis_sekundaere_wundauflage",
|
||||
"empfohlene_wundversorgung_hinweis_fixieren_mit",
|
||||
"empfohlene_wundversorgung_wechselintervall",
|
||||
)
|
||||
|
||||
/** Wichtige Hinweise (fett + unterstrichen, mit Ausrufezeichen). */
|
||||
private val WARNING_DEFINITION_IDS: Set<String> = setOf(
|
||||
"empfohlene_wundversorgung_hinweis_abklaerung",
|
||||
)
|
||||
|
||||
private fun styleFor(item: CheckItem): PdfItemStyle = when (item.definitionId) {
|
||||
in WARNING_DEFINITION_IDS -> PdfItemStyle.WARNING
|
||||
in SUBHEADING_DEFINITION_IDS -> PdfItemStyle.SUBHEADING
|
||||
else -> PdfItemStyle.NORMAL
|
||||
}
|
||||
|
||||
/**
|
||||
* Bestimmt die zu exportierenden Einträge eines Blocks (sortiert nach order):
|
||||
* - normale Einträge: nur wenn angehakt
|
||||
* - Hinweistexte (isStaticHint, technisch immer angehakt): Ein Hinweis, dem im Block
|
||||
* eigene Einträge folgen (bis zum nächsten Hinweis), ist eine Gruppenüberschrift und
|
||||
* wird nur gedruckt, wenn mindestens einer dieser Einträge angehakt ist (z. B. "Zu
|
||||
* versorgendes Areal" bei Größe). Hinweise ohne folgende Einträge ("+ bei Bedarf",
|
||||
* "Generell: ...") bleiben immer erhalten. Der automatisch angehängte generische
|
||||
* Freitext eines Blocks gehört zu keiner Gruppe. Trägt der erste angehakte Eintrag der
|
||||
* Gruppe dieselbe Bezeichnung wie der Hinweis, entfällt der Hinweis (keine Doppelung).
|
||||
*
|
||||
* Ausschließlich Snapshot-Daten, kein Zugriff auf den aktuellen Katalog.
|
||||
*/
|
||||
private fun exportedItems(block: DocumentationBlock): List<CheckItem> {
|
||||
val sorted = block.items.sortedBy { it.order }
|
||||
val genericFreeTextId = "${block.definitionId}_freetext"
|
||||
val result = mutableListOf<CheckItem>()
|
||||
for ((index, item) in sorted.withIndex()) {
|
||||
if (!item.checked) continue
|
||||
if (!item.isStaticHint) {
|
||||
result.add(item)
|
||||
continue
|
||||
}
|
||||
val groupItems = sorted
|
||||
.drop(index + 1)
|
||||
.takeWhile { !it.isStaticHint }
|
||||
.filter { it.definitionId != genericFreeTextId }
|
||||
val keep = when {
|
||||
groupItems.isEmpty() -> true
|
||||
else -> {
|
||||
val firstChecked = groupItems.firstOrNull { it.checked }
|
||||
firstChecked != null && !firstChecked.label.trim().equals(item.label.trim(), ignoreCase = true)
|
||||
}
|
||||
}
|
||||
if (keep) result.add(item)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,12 @@ data class PdfExportModel(
|
||||
)
|
||||
|
||||
data class PdfExportBlock(
|
||||
/** Stabile Definitions-ID des Blocks (z. B. "empfohlene_wundversorgung"). */
|
||||
val definitionId: String = "",
|
||||
val title: String,
|
||||
/** `true`: der Block beginnt im PDF/JPG zwingend auf einer neuen Seite (Änderungswunsch:
|
||||
* "Empfohlene Wundversorgung" auf eigener Seite). */
|
||||
val startsOnNewPage: Boolean = false,
|
||||
/** Bereits gefiltert (NUR angehakte Optionen, s. Mapper - Änderungswunsch) und sortiert
|
||||
* (itemOrder). Nicht angehakte Optionen erscheinen im PDF überhaupt nicht mehr. */
|
||||
val items: List<PdfExportItem>,
|
||||
@@ -45,8 +50,23 @@ data class PdfExportItem(
|
||||
val isFreeText: Boolean,
|
||||
val freeTextValue: String,
|
||||
val inputSpec: String = "",
|
||||
/** `false`: die Bezeichnung wird nicht gedruckt, nur der eingegebene Text (generische
|
||||
* "Freitext"-Einträge, Änderungswunsch: Schriftzug "Freitext" im PDF/JPG nicht anzeigen). */
|
||||
val showLabel: Boolean = true,
|
||||
val style: PdfItemStyle = PdfItemStyle.NORMAL,
|
||||
)
|
||||
|
||||
/** Darstellung der Bezeichnung eines Eintrags im PDF (rein layoutbezogen). */
|
||||
enum class PdfItemStyle {
|
||||
NORMAL,
|
||||
|
||||
/** Zwischenüberschrift innerhalb eines Blocks: kursiv und unterstrichen. */
|
||||
SUBHEADING,
|
||||
|
||||
/** Wichtiger Hinweis: fett, unterstrichen, mit Ausrufezeichen davor und dahinter. */
|
||||
WARNING,
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimale, für den Fotoanhang benötigte Referenz. Enthält bewusst nur [fileName] -
|
||||
* `documentationId` steht bereits am [PdfExportModel] selbst zur Verfügung (nötig, um über
|
||||
|
||||
@@ -10,6 +10,15 @@ import android.text.Layout
|
||||
import android.text.StaticLayout
|
||||
import android.text.TextPaint
|
||||
|
||||
/**
|
||||
* Zeichnet den Seitenkopf einer (neuen) Seite und liefert die dafür belegte Höhe zurück
|
||||
* (0f, wenn auf dieser Seite kein Seitenkopf gezeichnet wird, z. B. Seite 1 mit dem großen
|
||||
* Dokumentkopf). [left]/[top] sind die obere linke Ecke des Inhaltsbereichs, [width] dessen Breite.
|
||||
*/
|
||||
internal fun interface PageHeaderDrawer {
|
||||
fun draw(canvas: Canvas, pageNumber: Int, left: Float, top: Float, width: Float): Float
|
||||
}
|
||||
|
||||
/**
|
||||
* Verwaltet Seiten, Zeichenfläche und die aktuelle vertikale Schreibposition ("Cursor") beim
|
||||
* Erzeugen eines [PdfDocument]. Kapselt die komplette Seitenumbruch-Logik an einer Stelle, damit
|
||||
@@ -33,8 +42,13 @@ internal class PdfPageCursor(
|
||||
private val marginRight: Float,
|
||||
private val marginTop: Float,
|
||||
private val marginBottom: Float,
|
||||
/** Optionaler Seitenkopf, der auf JEDER neu begonnenen Seite gezeichnet wird (Änderungswunsch:
|
||||
* Patientenname und Erstellungsdatum auf jeder Seite). */
|
||||
private val pageHeaderDrawer: PageHeaderDrawer? = null,
|
||||
) {
|
||||
private var pageNumber = 0
|
||||
/** Y-Position, ab der auf der aktuellen Seite Inhalt beginnt (nach einem evtl. Seitenkopf). */
|
||||
private var pageContentTop: Float = marginTop
|
||||
private var currentPage: PdfDocument.Page? = null
|
||||
private var canvas: Canvas? = null
|
||||
private var cursorY: Float = marginTop
|
||||
@@ -74,7 +88,15 @@ internal class PdfPageCursor(
|
||||
val page = pdfDocument.startPage(info)
|
||||
currentPage = page
|
||||
canvas = page.canvas
|
||||
cursorY = marginTop
|
||||
val headerHeight = pageHeaderDrawer?.draw(
|
||||
canvas = page.canvas,
|
||||
pageNumber = pageNumber,
|
||||
left = marginLeft,
|
||||
top = marginTop,
|
||||
width = pageWidth - marginLeft - marginRight,
|
||||
) ?: 0f
|
||||
pageContentTop = marginTop + headerHeight
|
||||
cursorY = pageContentTop
|
||||
}
|
||||
|
||||
private fun finishCurrentPageIfOpen() {
|
||||
@@ -98,6 +120,15 @@ internal class PdfPageCursor(
|
||||
startNewPage()
|
||||
}
|
||||
|
||||
/**
|
||||
* Beginnt eine neue Seite, außer die aktuelle Seite ist noch völlig leer (Cursor steht am
|
||||
* Inhaltsanfang) - so entstehen keine Leerseiten, wenn ein Block, der immer auf einer
|
||||
* eigenen Seite beginnt, ohnehin ganz oben stünde.
|
||||
*/
|
||||
fun startNewPageUnlessEmpty() {
|
||||
if (currentPage == null || cursorY > pageContentTop) startNewPage()
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeichnet [bitmap] proportional eingepasst (kein Zuschneiden, keine Verzerrung) horizontal
|
||||
* über die volle Inhaltsbreite und vertikal zentriert im aktuell verbleibenden Platz der
|
||||
|
||||
+9
-2
@@ -85,6 +85,7 @@ import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.medapp.wunddoku.R
|
||||
import de.medapp.wunddoku.domain.catalog.DocumentationCatalog
|
||||
import de.medapp.wunddoku.domain.model.CheckItem
|
||||
import de.medapp.wunddoku.domain.model.InputFieldDef
|
||||
import de.medapp.wunddoku.domain.model.InputSpecCodec
|
||||
@@ -907,19 +908,25 @@ private fun FreeTextCheckItemRow(
|
||||
onCheckedChanged: (checkItemStateId: String, checked: Boolean) -> Unit,
|
||||
onFreeTextChanged: (checkItemStateId: String, text: String) -> Unit,
|
||||
) {
|
||||
// Änderungswunsch: Ist ein generischer "Freitext"-Eintrag angehakt, wird der Schriftzug
|
||||
// "Freitext" nicht mehr angezeigt (nur Häkchenkästchen + Eingabefeld). Fachlich benannte
|
||||
// Freitext-Einträge (z. B. "Wundrandschutz", "Wechselintervall") behalten ihre Bezeichnung.
|
||||
val hideName = item.checked && item.label == DocumentationCatalog.FREE_TEXT_LABEL
|
||||
val fieldLabel: (@Composable () -> Unit)? =
|
||||
if (hideName) null else ({ Text(stringResource(R.string.documentation_edit_free_text_label)) })
|
||||
Column {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Checkbox(
|
||||
checked = item.checked,
|
||||
onCheckedChange = { checked -> onCheckedChanged(item.checkItemStateId, checked) },
|
||||
)
|
||||
Text(item.label)
|
||||
if (!hideName) Text(item.label)
|
||||
}
|
||||
OutlinedTextField(
|
||||
value = item.freeTextValue,
|
||||
onValueChange = { text -> onFreeTextChanged(item.checkItemStateId, text) },
|
||||
enabled = item.checked,
|
||||
label = { Text(stringResource(R.string.documentation_edit_free_text_label)) },
|
||||
label = fieldLabel,
|
||||
modifier = Modifier.fillMaxWidth().padding(start = 40.dp),
|
||||
minLines = 2,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package de.medapp.wunddoku.domain.catalog
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Strukturprüfungen für den statischen Katalog sowie die fachlichen Änderungswünsche
|
||||
* (Wundart/Wundgrund geteilt, Wundrandschutz, Granudacyn).
|
||||
*/
|
||||
class DocumentationCatalogTest {
|
||||
|
||||
private fun items(blockId: String) = DocumentationCatalog.findBlock(blockId)!!.items
|
||||
|
||||
@Test
|
||||
fun alle_checkitem_ids_sind_katalogweit_eindeutig() {
|
||||
val ids = DocumentationCatalog.blocks.flatMap { b -> b.items.map { it.id } }
|
||||
assertEquals(ids.size, ids.toSet().size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun order_ist_je_block_eindeutig_und_aufsteigend() {
|
||||
for (block in DocumentationCatalog.blocks) {
|
||||
val orders = block.items.map { it.order }
|
||||
assertEquals("Block ${block.id}", orders.sorted(), orders)
|
||||
assertEquals("Block ${block.id}", orders.size, orders.toSet().size)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wundart_postoperativ_und_dehiszenz_sind_getrennte_punkte() {
|
||||
val wundart = items("wundart")
|
||||
assertEquals("Post-operative Wundheilungsstörung", wundart.single { it.id == "wundart_post_operative_wundheilungsstoerung" }.label)
|
||||
assertEquals("Dehiszenz", wundart.single { it.id == "wundart_dehiszenz" }.label)
|
||||
assertTrue(wundart.none { it.label.contains("/ Dehiszenz") })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wundgrund_unregelmaessig_und_landkartenartig_sind_getrennte_punkte() {
|
||||
val wundgrund = items("wundgrund_erscheinung")
|
||||
assertEquals("Unregelmäßig", wundgrund.single { it.id == "wundgrund_unregelmaessig" }.label)
|
||||
assertEquals("Landkartenartig/amorph", wundgrund.single { it.id == "wundgrund_landkartenartig_amorph" }.label)
|
||||
assertNull(wundgrund.firstOrNull { it.id == "wundgrund_unregelmaessig_landkartenartig" })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wundrandschutz_ist_aus_ziel_entfernt() {
|
||||
assertTrue(items("ziel").none { it.label.contains("Wundrandschutz") })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wundrandschutz_ist_eine_einzelne_freitext_box_ohne_ja_nein() {
|
||||
val ev = items("empfohlene_wundversorgung")
|
||||
val wrs = ev.single { it.id == "empfohlene_wundversorgung_wundrandschutz" }
|
||||
assertEquals("Wundrandschutz", wrs.label)
|
||||
assertTrue(wrs.isFreeText)
|
||||
assertNull(ev.firstOrNull { it.id == "empfohlene_wundversorgung_hinweis_wundrandschutz" })
|
||||
assertNull(ev.firstOrNull { it.id == "empfohlene_wundversorgung_wundrandschutz_ja" })
|
||||
assertNull(ev.firstOrNull { it.id == "empfohlene_wundversorgung_wundrandschutz_nein" })
|
||||
// "Wie oft pro Woche" bleibt als eigene Checkbox erhalten.
|
||||
assertNotNull(ev.firstOrNull { it.id == "empfohlene_wundversorgung_wundrandschutz_tage_pro_woche" })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun granudacyn_ist_einfache_checkbox_und_freitext_ein_eigener_punkt_direkt_darunter() {
|
||||
val ev = items("empfohlene_wundversorgung")
|
||||
val granudacyn = ev.single { it.id == "empfohlene_wundversorgung_granudacyn" }
|
||||
val freitext = ev.single { it.id == "empfohlene_wundversorgung_granudacyn_freitext" }
|
||||
|
||||
assertFalse(granudacyn.isFreeText)
|
||||
assertTrue(freitext.isFreeText)
|
||||
assertEquals(DocumentationCatalog.FREE_TEXT_LABEL, freitext.label)
|
||||
assertEquals(granudacyn.order + 1, freitext.order)
|
||||
}
|
||||
}
|
||||
+147
-1
@@ -172,10 +172,156 @@ class DocumentationToPdfExportModelMapperTest {
|
||||
|
||||
val items = DocumentationToPdfExportModelMapper.map(doc).blocks.single().items
|
||||
|
||||
assertEquals(PdfExportItem("Freitext", isFreeText = true, freeTextValue = "Rötung", inputSpec = ""), items[0])
|
||||
// Generischer "Freitext": Bezeichnung wird nicht gedruckt (showLabel = false).
|
||||
assertEquals(
|
||||
PdfExportItem("Freitext", isFreeText = true, freeTextValue = "Rötung", inputSpec = "", showLabel = false),
|
||||
items[0],
|
||||
)
|
||||
assertEquals(PdfExportItem("Maße", isFreeText = false, freeTextValue = "3|4", inputSpec = "SPEC"), items[1])
|
||||
}
|
||||
|
||||
// --- Änderungswünsche: Freitext, Hinweisgruppen, Stile, eigene Seite ------------------
|
||||
|
||||
@Test
|
||||
fun benannter_freitext_behaelt_seine_bezeichnung() {
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block("a", 1, items = listOf(
|
||||
item("w", 1, checked = true, label = "Wundrandschutz", isFreeText = true, freeTextValue = "Cavilon"),
|
||||
)),
|
||||
)
|
||||
)
|
||||
|
||||
assertTrue(DocumentationToPdfExportModelMapper.map(doc).blocks.single().items.single().showLabel)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun gruppenhinweis_ohne_angehakten_eintrag_der_gruppe_entfaellt() {
|
||||
// Größe: "Zu versorgendes Areal" darf nicht erscheinen, wenn nur die Maße ausgefüllt sind.
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block("groesse", 1, items = listOf(
|
||||
item("h_masse", 1, checked = true, label = "Maße", isStaticHint = true),
|
||||
item("masse", 2, checked = true, label = "Maße (Länge / Breite / Tiefe)", freeTextValue = "3|4|1", inputSpec = "SPEC"),
|
||||
item("h_areal", 3, checked = true, label = "Zu versorgendes Areal", isStaticHint = true),
|
||||
item("areal", 4, checked = false, label = "Zu versorgendes Areal"),
|
||||
item("areal_ft", 5, checked = false, label = "Freitext für zu versorgendes Areal", isFreeText = true),
|
||||
// generischer Freitext des Blocks gehört zu keiner Gruppe
|
||||
item("groesse_freetext", 6, checked = false, label = "Freitext", isFreeText = true),
|
||||
)),
|
||||
)
|
||||
)
|
||||
|
||||
val labels = DocumentationToPdfExportModelMapper.map(doc).blocks.single().items.map { it.label }
|
||||
|
||||
assertEquals(listOf("Maße", "Maße (Länge / Breite / Tiefe)"), labels)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun generischer_freitext_laesst_gruppenhinweis_nicht_erscheinen() {
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block("groesse", 1, items = listOf(
|
||||
item("masse", 1, checked = true, label = "Maße", freeTextValue = "3|4|1", inputSpec = "SPEC"),
|
||||
item("h_areal", 2, checked = true, label = "Zu versorgendes Areal", isStaticHint = true),
|
||||
item("areal", 3, checked = false, label = "Areal"),
|
||||
item("groesse_freetext", 4, checked = true, label = "Freitext", isFreeText = true, freeTextValue = "x"),
|
||||
)),
|
||||
)
|
||||
)
|
||||
|
||||
val labels = DocumentationToPdfExportModelMapper.map(doc).blocks.single().items.map { it.label }
|
||||
|
||||
assertEquals(listOf("Maße", "Freitext"), labels)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun gruppenhinweis_mit_angehaktem_eintrag_bleibt_erhalten() {
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block("a", 1, items = listOf(
|
||||
item("h", 1, checked = true, label = "Fixieren mit:", isStaticHint = true),
|
||||
item("m", 2, checked = true, label = "Mullbinde"),
|
||||
item("k", 3, checked = false, label = "Klebevlies"),
|
||||
)),
|
||||
)
|
||||
)
|
||||
|
||||
val labels = DocumentationToPdfExportModelMapper.map(doc).blocks.single().items.map { it.label }
|
||||
|
||||
assertEquals(listOf("Fixieren mit:", "Mullbinde"), labels)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun gruppenhinweis_mit_gleichnamigem_angehaktem_eintrag_wird_nicht_doppelt_gedruckt() {
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block("a", 1, items = listOf(
|
||||
item("h", 1, checked = true, label = "Zu versorgendes Areal", isStaticHint = true),
|
||||
item("areal", 2, checked = true, label = "Zu versorgendes Areal"),
|
||||
)),
|
||||
)
|
||||
)
|
||||
|
||||
val labels = DocumentationToPdfExportModelMapper.map(doc).blocks.single().items.map { it.label }
|
||||
|
||||
assertEquals(listOf("Zu versorgendes Areal"), labels)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun abschliessende_hinweise_ohne_folgende_eintraege_bleiben_immer_erhalten() {
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block("empfohlene_wundversorgung", 1, items = listOf(
|
||||
item("x", 1, checked = true, label = "Mullbinde"),
|
||||
item("empfohlene_wundversorgung_hinweis_bedarf", 2, checked = true, label = "+ bei Bedarf", isStaticHint = true),
|
||||
item("empfohlene_wundversorgung_hinweis_abklaerung", 3, checked = true, label = "Generell: ...", isStaticHint = true),
|
||||
item("empfohlene_wundversorgung_freetext", 4, checked = false, label = "Freitext", isFreeText = true),
|
||||
)),
|
||||
)
|
||||
)
|
||||
|
||||
val labels = DocumentationToPdfExportModelMapper.map(doc).blocks.single().items.map { it.label }
|
||||
|
||||
assertEquals(listOf("Mullbinde", "+ bei Bedarf", "Generell: ..."), labels)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun stile_werden_anhand_der_definitions_id_vergeben() {
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block("empfohlene_wundversorgung", 1, items = listOf(
|
||||
item("empfohlene_wundversorgung_hinweis_reinigen_mit", 1, checked = true, label = "Reinigen mit:", isStaticHint = true),
|
||||
item("empfohlene_wundversorgung_nacl", 2, checked = true, label = "NaCl 0,9%"),
|
||||
item("empfohlene_wundversorgung_primaer_abdecken_austamponieren", 3, checked = true, label = "Primär Wunde abdecken/austamponieren"),
|
||||
item("empfohlene_wundversorgung_hinweis_abklaerung", 4, checked = true, label = "Generell: ...", isStaticHint = true),
|
||||
)),
|
||||
)
|
||||
)
|
||||
|
||||
val styles = DocumentationToPdfExportModelMapper.map(doc).blocks.single().items.map { it.style }
|
||||
|
||||
assertEquals(
|
||||
listOf(PdfItemStyle.SUBHEADING, PdfItemStyle.NORMAL, PdfItemStyle.SUBHEADING, PdfItemStyle.WARNING),
|
||||
styles,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun empfohlene_wundversorgung_beginnt_auf_eigener_seite_andere_bloecke_nicht() {
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block("weitere_ueberlegung", 1, items = listOf(item("a", 1, checked = true))),
|
||||
block("empfohlene_wundversorgung", 2, items = listOf(item("b", 1, checked = true))),
|
||||
block("ziel", 3, items = listOf(item("c", 1, checked = true))),
|
||||
)
|
||||
)
|
||||
|
||||
val blocks = DocumentationToPdfExportModelMapper.map(doc).blocks
|
||||
|
||||
assertEquals(listOf(false, true, false), blocks.map { it.startsOnNewPage })
|
||||
}
|
||||
|
||||
// --- Sortierung ----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user