Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 10 additions & 23 deletions pyx12/error_997.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,10 +395,6 @@ def visit_seg(self, err_seg: Any) -> None:
:param err_seg: Segment error handler
:type err_seg: L{error_handler.err_seg}
"""
# Legacy raw-X12 codes accepted by the AK304 channel today.
# PR 5 will drop this fallback once all producers emit pyx12 codes
# routed through pyx12.error_codes.ERROR_CODES.
valid_AK3_codes = ("1", "2", "3", "4", "5", "6", "7", "8")
seg_base = pyx12.segment.Segment("AK3", "~", "*", ":")
seg_base.append(err_seg.seg_id)
seg_base.append("%i" % err_seg.seg_count)
Expand All @@ -407,16 +403,17 @@ def visit_seg(self, err_seg: Any) -> None:
else:
seg_base.append("")
seg_str = seg_base.format("~", "*", ":")
# Every err_cde reaching this point is a pyx12 code from
# pyx12.error_codes.ERROR_CODES (PRs #175-#178 migrated all
# producers). None spec or None ak_code means the code does not
# surface in the 4010 AK3 channel (e.g. parser HL1/HL2/LX).
emitted: set[str] = set()
for err_cde in [x[0] for x in err_seg.errors]:
spec = pyx12.error_codes.ERROR_CODES.get(err_cde)
if spec is not None:
ak_code = spec.ak_code
elif err_cde in valid_AK3_codes:
ak_code = err_cde
else:
ak_code = None
if ak_code is None or ak_code in emitted:
if spec is None or spec.ak_code is None:
continue
ak_code = spec.ak_code
if ak_code in emitted:
continue
seg_data = pyx12.segment.Segment(seg_str, "~", "*", ":")
seg_data.set("AK304", ak_code)
Expand All @@ -432,10 +429,6 @@ def visit_ele(self, err_ele: Any) -> None:
:param err_ele: Segment error handler
:type err_ele: L{error_handler.err_ele}
"""
# Legacy raw-X12 codes accepted by the AK403 channel today.
# PR 5 will drop this fallback once all producers emit pyx12 codes
# routed through pyx12.error_codes.ERROR_CODES.
valid_AK4_codes = ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")
seg_base = pyx12.segment.Segment("AK4", "~", "*", ":")
if err_ele.subele_pos:
seg_base.append("%i:%i" % (err_ele.ele_pos, err_ele.subele_pos))
Expand All @@ -446,16 +439,10 @@ def visit_ele(self, err_ele: Any) -> None:
seg_str = seg_base.format("~", "*", ":")
for err_cde, err_str, bad_value in err_ele.errors:
spec = pyx12.error_codes.ERROR_CODES.get(err_cde)
if spec is not None:
ak_code = spec.ak_code
elif err_cde in valid_AK4_codes:
ak_code = err_cde
else:
ak_code = None
if ak_code is None:
if spec is None or spec.ak_code is None:
continue
seg_data = pyx12.segment.Segment(seg_str, "~", "*", ":")
seg_data.set("AK403", ak_code)
seg_data.set("AK403", spec.ak_code)
if bad_value:
seg_data.set("AK404", error_visitor.ascii_only(bad_value))
self._write(seg_data)
Expand Down
55 changes: 10 additions & 45 deletions pyx12/error_999.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,29 +325,23 @@ def visit_seg(self, err_seg: Any) -> None:
:param err_seg: Segment error handler
:type err_seg: L{error_handler.err_seg}
"""
# Legacy raw-X12 codes accepted by the IK304 channel today.
# PR 5 will drop this fallback once all producers emit pyx12 codes
# routed through pyx12.error_codes.ERROR_CODES.
valid_IK3_codes = ("1", "2", "3", "4", "5", "6", "7", "8", "I4", "I6", "I7", "I8", "I9")
seg_base = pyx12.segment.Segment("IK3", "~", "*", ":")
seg_base.set("01", err_seg.seg_id)
seg_base.set("02", "%i" % err_seg.seg_count)
if err_seg.ls_id:
seg_base.set("03", err_seg.ls_id)
seg_str = seg_base.format("~", "*", ":")
# Resolve each err_cde to its IK3-04 code via the pyx12.error_codes
# table. None means "drop" (e.g. parser HL1/HL2/LX). Falls through
# to legacy raw-X12 lookup for codes not in the table.
# Every err_cde reaching this point is a pyx12 code from
# pyx12.error_codes.ERROR_CODES (PRs #175-#178 migrated all
# producers). None spec or None ik_code means the code does not
# surface in the 5010 IK3 channel (e.g. parser HL1/HL2/LX).
emitted: set[str] = set()
for err_cde in [x[0] for x in err_seg.errors]:
spec = pyx12.error_codes.ERROR_CODES.get(err_cde)
if spec is not None:
ik_code = spec.ik_code
elif err_cde in valid_IK3_codes:
ik_code = err_cde
else:
ik_code = None
if ik_code is None or ik_code in emitted:
if spec is None or spec.ik_code is None:
continue
ik_code = spec.ik_code
if ik_code in emitted:
continue
seg_data = pyx12.segment.Segment(seg_str, "~", "*", ":")
seg_data.set("IK304", ik_code)
Expand All @@ -363,29 +357,6 @@ def visit_ele(self, err_ele: Any) -> None:
:param err_ele: Segment error handler
:type err_ele: L{error_handler.err_ele}
"""
# Legacy raw-X12 codes accepted by the IK403 channel today.
# PR 5 will drop this fallback once all producers emit pyx12 codes
# routed through pyx12.error_codes.ERROR_CODES.
valid_IK4_codes = (
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"12",
"13",
"I10",
"I11",
"I12",
"I13",
"I6",
"I9",
)
seg_base = pyx12.segment.Segment("IK4", "~", "*", ":")
seg_base.set("01-1", "%i" % (err_ele.ele_pos))
if err_ele.subele_pos:
Expand All @@ -397,16 +368,10 @@ def visit_ele(self, err_ele: Any) -> None:
seg_str = seg_base.format("~", "*", ":")
for err_cde, err_str, bad_value in err_ele.errors:
spec = pyx12.error_codes.ERROR_CODES.get(err_cde)
if spec is not None:
ik_code = spec.ik_code
elif err_cde in valid_IK4_codes:
ik_code = err_cde
else:
ik_code = None
if ik_code is None:
if spec is None or spec.ik_code is None:
continue
seg_data = pyx12.segment.Segment(seg_str, "~", "*", ":")
seg_data.set("IK403", ik_code)
seg_data.set("IK403", spec.ik_code)
if bad_value:
seg_data.set("IK404", pyx12.error_visitor.ascii_only(bad_value))
self.wr.Write(seg_data)
30 changes: 16 additions & 14 deletions pyx12/error_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@
"030",
"031",
)
seg_errors = ("1", "2", "3", "4", "5", "6", "7", "8")
ele_errors = ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")


@dataclass(slots=True, frozen=True)
Expand Down Expand Up @@ -84,12 +82,14 @@ class SegError(ErrorItem):
ls_id: str | None = None

def __post_init__(self) -> None:
# During the PR 2-4 producer migration, accept either legacy
# raw-X12 codes ("1"-"8") or new pyx12 codes from ERROR_CODES.
# PR 5 narrows this to ERROR_CODES only once every producer has
# migrated.
if self.err_cde not in seg_errors and self.err_cde not in ERROR_CODES:
raise EngineError('Invalid segment level error code "%s"' % (self.err_cde))
# PR 5: producers all emit pyx12 codes from ERROR_CODES. The
# legacy raw-X12 fallback that accepted "1"-"8" is gone; an
# unknown err_cde here is now a hard error.
if self.err_cde not in ERROR_CODES:
raise EngineError(
'Unknown pyx12 error code "%s" (not in pyx12.error_codes.ERROR_CODES)'
% (self.err_cde)
)


@dataclass(slots=True, frozen=True)
Expand All @@ -103,9 +103,11 @@ class EleError(ErrorItem):
map_node: Any = None

def __post_init__(self) -> None:
# During the PR 2-4 producer migration, accept either legacy
# raw-X12 codes ("1"-"10") or new pyx12 codes from ERROR_CODES.
# PR 5 narrows this to ERROR_CODES only once every producer has
# migrated.
if self.err_cde not in ele_errors and self.err_cde not in ERROR_CODES:
raise EngineError('Invalid element level error code "%s"' % (self.err_cde))
# PR 5: producers all emit pyx12 codes from ERROR_CODES. The
# legacy raw-X12 fallback that accepted "1"-"10" is gone; an
# unknown err_cde here is now a hard error.
if self.err_cde not in ERROR_CODES:
raise EngineError(
'Unknown pyx12 error code "%s" (not in pyx12.error_codes.ERROR_CODES)'
% (self.err_cde)
)
44 changes: 30 additions & 14 deletions pyx12/test/test_error_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,62 +35,78 @@ def test_invalid_code_empty(self):


class TestSegError(unittest.TestCase):
# PR 5 (#TBD) tightens SegError validation to require pyx12 codes
# from pyx12.error_codes.ERROR_CODES. Raw X12 codes like "1" / "8"
# are no longer accepted.
def test_valid_code(self):
e = SegError("1", "segment error")
self.assertEqual(e.err_cde, "1")
e = SegError("SEG_1_segment_not_found", "segment error")
self.assertEqual(e.err_cde, "SEG_1_segment_not_found")
self.assertEqual(e.err_str, "segment error")

def test_valid_code_8(self):
e = SegError("8", "seg error")
self.assertEqual(e.err_cde, "8")
e = SegError("SEG_8_trailing_terminators", "seg error")
self.assertEqual(e.err_cde, "SEG_8_trailing_terminators")

def test_err_val_default_none(self):
e = SegError("1", "seg error")
e = SegError("SEG_1_segment_not_found", "seg error")
self.assertIsNone(e.err_val)

def test_err_val_set(self):
e = SegError("2", "seg error", err_val="BAD*SEG")
e = SegError("SEG_2_segment_not_used", "seg error", err_val="BAD*SEG")
self.assertEqual(e.err_val, "BAD*SEG")

def test_invalid_code_raises(self):
with self.assertRaises(EngineError):
SegError("99", "bad code")

def test_invalid_code_legacy_raw_X12_rejected(self):
# Raw X12 codes were accepted pre-PR-5. Now rejected.
with self.assertRaises(EngineError):
SegError("1", "bad code")

def test_invalid_code_zero(self):
with self.assertRaises(EngineError):
SegError("0", "bad code")


class TestEleError(unittest.TestCase):
# PR 5 (#TBD) tightens EleError validation to require pyx12 codes
# from pyx12.error_codes.ERROR_CODES. Raw X12 codes like "1" / "10"
# are no longer accepted.
def test_valid_code(self):
e = EleError("1", "element error")
self.assertEqual(e.err_cde, "1")
e = EleError("ELE_1_mandatory_missing", "element error")
self.assertEqual(e.err_cde, "ELE_1_mandatory_missing")
self.assertEqual(e.err_str, "element error")

def test_valid_code_10(self):
e = EleError("10", "element error")
self.assertEqual(e.err_cde, "10")
e = EleError("ELE_10_not_used", "element error")
self.assertEqual(e.err_cde, "ELE_10_not_used")

def test_refdes_default_none(self):
e = EleError("1", "element error")
e = EleError("ELE_1_mandatory_missing", "element error")
self.assertIsNone(e.refdes)

def test_refdes_set(self):
e = EleError("1", "element error", refdes="03")
e = EleError("ELE_1_mandatory_missing", "element error", refdes="03")
self.assertEqual(e.refdes, "03")

def test_err_val_default_none(self):
e = EleError("1", "element error")
e = EleError("ELE_1_mandatory_missing", "element error")
self.assertIsNone(e.err_val)

def test_err_val_set(self):
e = EleError("2", "element error", err_val="BADVAL")
e = EleError("ELE_4_too_short", "element error", err_val="BADVAL")
self.assertEqual(e.err_val, "BADVAL")

def test_invalid_code_raises(self):
with self.assertRaises(EngineError):
EleError("11", "bad code")

def test_invalid_code_legacy_raw_X12_rejected(self):
# Raw X12 codes were accepted pre-PR-5. Now rejected.
with self.assertRaises(EngineError):
EleError("1", "bad code")

def test_invalid_code_zero(self):
with self.assertRaises(EngineError):
EleError("0", "bad code")
Expand Down