Skip to content

Commit 46650c2

Browse files
hf-kkleinKonstantin
andauthored
feat: Support <Uebertragungsdatei> in AhbReader (adds flag is_on_uebertragungsdatei_level to AHB Segment model) (#132)
feat: Support `<Uebertragungsdatei>` in `AhbReader` add flag to `Segment` model Co-authored-by: Konstantin <[email protected]>
1 parent 2f17435 commit 46650c2

File tree

5 files changed

+41
-9
lines changed

5 files changed

+41
-9
lines changed

src/fundamend/models/anwendungshandbuch.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ class Segment(FundamendBaseModel):
8787
number: str #: e.g. '00002'
8888
ahb_status: str | None #: e.g. 'Muss'
8989
data_elements: tuple[DataElement | DataElementGroup, ...]
90+
is_on_uebertragungsdatei_level: bool = False
91+
"""
92+
true for e.g. S_UNA, S_UNB or S_UNZ which occur outside the regular AHB <M_FORMAT> structure.
93+
We decided against introducing another (mostly useless) layer around the actual body of the Anwendungsfall.
94+
This is only relevant (true) for MSCONS AHBs as of 2025-04-23 although the MIGs read like this _might_ also be true
95+
for REMADV and INVOIC. We all know, how (self-)consistent BDEW documents are, even if you pay for them.
96+
"""
9097

9198

9299
class SegmentGroup(FundamendBaseModel):

src/fundamend/reader/ahbreader.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def _to_data_element_group(element: ET.Element) -> DataElementGroup:
119119
)
120120

121121

122-
def _to_segment(element: ET.Element) -> Segment:
122+
def _to_segment(element: ET.Element, is_uebertragungsdatei_level: bool = False) -> Segment:
123123
assert _is_segment(element)
124124
data_elements: list[DataElement | DataElementGroup] = []
125125
for child in element:
@@ -138,6 +138,7 @@ def _to_segment(element: ET.Element) -> Segment:
138138
number=element.attrib["Number"],
139139
ahb_status=ahb_status,
140140
data_elements=tuple(data_elements),
141+
is_on_uebertragungsdatei_level=is_uebertragungsdatei_level,
141142
)
142143

143144

@@ -231,17 +232,22 @@ def get_anwendungsfaelle(self) -> list[Anwendungsfall]:
231232
result.append(self._read_anwendungsfall(element))
232233
return result
233234

234-
def _iter_segments_and_segment_groups(self, element: ET.Element) -> list[SegmentGroup | Segment]:
235+
def _iter_segments_and_segment_groups(
236+
self, element: ET.Element, is_uebertragungsdatei_level: bool = False
237+
) -> list[SegmentGroup | Segment]:
235238
"""recursive function that builds a list of all segments and segment groups"""
236239
result: list[Segment | SegmentGroup] = []
237-
should_go_deeper = _is_anwendungsfall(element) or _is_format(element) or _is_uebertragungsdatei(element)
240+
should_go_deeper = _is_anwendungsfall(element) or _is_format(element)
238241
if should_go_deeper:
239242
for sub_element in element:
240243
result.extend(self._iter_segments_and_segment_groups(sub_element))
241-
if _is_segment_group(element):
244+
if _is_uebertragungsdatei(element):
245+
for sub_element in element:
246+
result.extend(self._iter_segments_and_segment_groups(sub_element, is_uebertragungsdatei_level=True))
247+
elif _is_segment_group(element):
242248
result.append(_to_segment_group(element))
243249
elif _is_segment(element):
244-
result.append(_to_segment(element))
250+
result.append(_to_segment(element, is_uebertragungsdatei_level=is_uebertragungsdatei_level))
245251
return result
246252

247253
def _read_anwendungsfall(self, original_element: ET.Element) -> Anwendungsfall:

src/fundamend/sqlmodels/anwendungshandbuch.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ class Segment(SQLModel, table=True):
184184
name: str = Field(index=True) #: e.g. 'Beginn der Nachricht'
185185
number: str = Field(index=True) #: e.g. '00002'
186186
ahb_status: str | None #: e.g. 'Muss'
187+
is_on_uebertragungsdatei_level: bool
187188
data_elements: list[DataElement] = Relationship(back_populates="segment")
188189
data_element_groups: list[DataElementGroup] = Relationship(back_populates="segment")
189190
position: Optional[int] = Field(default=None, index=True)
@@ -202,6 +203,7 @@ def from_model(cls, model: PydanticSegment, position: Optional[int] = None) -> "
202203
number=model.number,
203204
ahb_status=model.ahb_status,
204205
position=position,
206+
is_on_uebertragungsdatei_level=model.is_on_uebertragungsdatei_level,
205207
)
206208
for _position, element in enumerate(model.data_elements):
207209
if isinstance(element, PydanticDataElement):
@@ -225,6 +227,7 @@ def to_model(self) -> PydanticSegment:
225227
key=lambda y: y.position or 0,
226228
)
227229
),
230+
is_on_uebertragungsdatei_level=self.is_on_uebertragungsdatei_level,
228231
)
229232

230233

unittests/__snapshots__/test_ahbreader.ambr

Lines changed: 3 additions & 3 deletions
Large diffs are not rendered by default.

unittests/test_ahb_serialisierung.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import pytest
55

6+
from fundamend.models.anwendungshandbuch import Segment as AhbSegment
67
from fundamend.models.messageimplementationguide import Segment as MigSegment
78
from fundamend.reader import AhbReader, MigReader
89

@@ -61,7 +62,22 @@ def test_deserializing_all_migs() -> None:
6162
_ = reader.read() # must not crash
6263

6364

64-
def test_uebertragungsdatei_flag_is_set() -> None:
65+
def test_uebertragungsdatei_level_flag_is_set_ahb() -> None:
66+
if not is_private_submodule_checked_out():
67+
pytest.skip("Skipping test because of missing private submodule")
68+
reader = AhbReader(data_path / "FV2504" / "MSCONS_AHB_3_1f_Fehlerkorrektur_20250320.xml")
69+
ahb13012 = [awf for awf in reader.read().anwendungsfaelle if awf.pruefidentifikator == "13012"][0]
70+
unb_segment = ahb13012.elements[0]
71+
assert isinstance(unb_segment, AhbSegment) and unb_segment.id == "UNB"
72+
unz_segment = ahb13012.elements[-1]
73+
assert isinstance(unz_segment, AhbSegment) and unz_segment.id == "UNZ"
74+
unh_segment = [s for s in ahb13012.elements if isinstance(s, AhbSegment) and s.id == "UNH"][0]
75+
assert unb_segment.is_on_uebertragungsdatei_level
76+
assert unz_segment.is_on_uebertragungsdatei_level
77+
assert not unh_segment.is_on_uebertragungsdatei_level
78+
79+
80+
def test_uebertragungsdatei_flag_is_set_mig() -> None:
6581
if not is_private_submodule_checked_out():
6682
pytest.skip("Skipping test because of missing private submodule")
6783
mig_file_path = data_path / "FV2504" / "UTILMD_MIG_Strom_S2_1_Fehlerkorrektur_20250320.xml"

0 commit comments

Comments
 (0)