@cosyte/x12 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +369 -2
- package/KNOWN-LIMITATIONS.md +325 -8
- package/dist/index.cjs +131 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +277 -25
- package/dist/index.d.ts +277 -25
- package/dist/index.mjs +130 -27
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/KNOWN-LIMITATIONS.md
CHANGED
|
@@ -16,6 +16,238 @@ model.
|
|
|
16
16
|
|
|
17
17
|
## Data / decode boundaries
|
|
18
18
|
|
|
19
|
+
- **🩺 Which `ST-03` implementation-convention references resolve to an 837 variant CHANGED in this
|
|
20
|
+
release, and some already-published files therefore decode differently** (`X12-VARIANT-ICR-UNGROUNDED`).
|
|
21
|
+
Through `0.0.13` `get837Claims` recognised exactly three references: `005010X222A2`, `005010X223A3`
|
|
22
|
+
and `005010X224A2`. **That set contained none of the identifiers HIPAA adopts at 45 CFR 162.1102**
|
|
23
|
+
(`005010X222`, `005010X223` + `005010X223A1`, `005010X224` + `005010X224A1`), **and it was missing
|
|
24
|
+
`005010X222A1` and `005010X223A2`**, which CMS and state Medicaid companion guides require in ST-03
|
|
25
|
+
and GS-08 on production professional and institutional claims. A conformant 837P declaring
|
|
26
|
+
`005010X222A1` therefore resolved to no variant at all and fell through to the `SVx` scan, where a
|
|
27
|
+
single stray `SV2` anywhere in the body re-typed the whole submission, and
|
|
28
|
+
`X12_837_UNKNOWN_VARIANT` accused a document that was not non-conformant. The reader now recognises
|
|
29
|
+
each base guide and each of its published errata. Sources for every key are named beside the table
|
|
30
|
+
in `src/transactions/claim/get-837.ts` and in
|
|
31
|
+
`documentation/agent-notes/x12-variant-icr-ungrounded.md`.
|
|
32
|
+
- **🛑 What this changes for a consumer, and it is a behaviour change on already-published
|
|
33
|
+
decoding.** On a file whose `ST-03` is now recognised: `submission.variant` can differ from what
|
|
34
|
+
`0.0.13` read, where the first `SVx` in the body disagreed with the declaration; and
|
|
35
|
+
**`X12_837_AMBIGUOUS_VARIANT` and `X12_837_UNKNOWN_VARIANT` no longer fire at all**, because no
|
|
36
|
+
guess was made. A predicate written against either code goes quiet on such a file. **🩺 And a
|
|
37
|
+
service line whose `SVx` kind disagrees with the declaration is no longer DECODED, so a code
|
|
38
|
+
STARTS firing on a document that may have carried `warnings: []`**: under `005010X222A1` with a
|
|
39
|
+
body whose only service segment is an `SV2`, `0.0.13` read `variant` `"I"` and `charge` `7300`
|
|
40
|
+
with no warnings, and this release reads `variant` `"P"`, `charge` `undefined`, and
|
|
41
|
+
`X12_837_SERVICE_LINE_NOT_DECODED` at that line's `LX`. A mis-stamped envelope is an ordinary
|
|
42
|
+
vendor variant and this reader cannot tell one from a conformant document; the loss is warned
|
|
43
|
+
rather than silent. **Read all of this as ONE property and never as a closed list: where `ST-03`
|
|
44
|
+
is now recognised the document's own declaration decides the variant instead of its first
|
|
45
|
+
service segment, and everything downstream follows from that.** Re-check any routing driven off
|
|
46
|
+
`submission.variant` for 837s read on `0.0.13` or earlier.
|
|
47
|
+
- **🛑 The `SVx` fallback is NOT narrowed.** First-wins still takes the first service segment in the
|
|
48
|
+
body, orphans included, on every document that still reaches it. What changed is which documents
|
|
49
|
+
reach it.
|
|
50
|
+
- **It is a LIST of cited identifiers, never a pattern.** A reference outside the set, in a
|
|
51
|
+
different case, or carrying leading whitespace, still falls through exactly as before. Nothing
|
|
52
|
+
trims, lower-cases or prefix-matches `ST-03`, because no source says to.
|
|
53
|
+
- **The set is not claimed exhaustive and no count of it is published**, here or in any warning
|
|
54
|
+
message. Both variant messages named the three old keys literally and were wrong the moment the
|
|
55
|
+
table was corrected, so neither enumerates the set any more.
|
|
56
|
+
- **🩺 CLOSED, in the slice after this one: the emit side takes a caller override**
|
|
57
|
+
(`X12-837-EMIT-IDENTIFIER-FIXED`). `build837P` / `build837I` / `build837D` still DEFAULT to
|
|
58
|
+
`005010X222A2` / `005010X223A3` / `005010X224A2` in ST-03 and GS-08, and
|
|
59
|
+
**`Build837EnvelopeSpec.implementationConventionReference` now states another** - one value,
|
|
60
|
+
both elements. Two of the defaults are not what the companion guides above require, so a partner
|
|
61
|
+
asking for `005010X222A1` or `005010X223A2` used to reject what this builder emitted and there
|
|
62
|
+
was no way to comply. **The default itself was deliberately NOT re-stamped:** which published
|
|
63
|
+
guide identifier a partner accepts is a partner fact rather than a spec fact, and changing bytes
|
|
64
|
+
this library already emitted would break the partners it works with today.
|
|
65
|
+
- **What the override refuses of its own, all `X12_837_BUILD_INVALID_SPEC`, and none of it
|
|
66
|
+
echoes your value back:** an empty reference (a trailing empty element is not emitted, so it
|
|
67
|
+
would delete ST-03 and GS-08 rather than send them empty); one carrying an active delimiter or
|
|
68
|
+
the release character; and one this library's own reader resolves to a **different** 837
|
|
69
|
+
variant, which would emit a file declaring one variant and carrying another's service
|
|
70
|
+
segments. **No total is published**, because those sit on top of the element-type guard every
|
|
71
|
+
string slot in every builder already has, which refuses a non-string with the same code. Take
|
|
72
|
+
the list as what this field adds, never as a closed account of everything that can refuse.
|
|
73
|
+
- **🩺 It bounds the VALUE, not the length, and the two elements' maxima differ.** GS-08 is data
|
|
74
|
+
element 480 (`AN 1/12`) and ST-03 is element 1705 (`AN 1/35`), so "one value, both elements"
|
|
75
|
+
is true of the bytes and not of what each element may legally hold. Nothing here refuses an
|
|
76
|
+
over-length reference, in line with every other envelope field this library takes.
|
|
77
|
+
- **A reference outside the read table is emitted as given, deliberately.** The published-errata
|
|
78
|
+
set is not provably exhaustive, so refusing an unrecognised identifier would claim an
|
|
79
|
+
exhaustiveness nothing here supports. The honest cost, pinned as a test: this library's own
|
|
80
|
+
reader falls back to the `SVx` scan on such a file, exactly as it does for any unrecognised
|
|
81
|
+
ST-03.
|
|
82
|
+
|
|
83
|
+
- **🩺 A `?` immediately before the element separator inside an envelope segment now frames as ONE
|
|
84
|
+
element, and that is a SYMMETRIC behaviour change on already-published decoding**
|
|
85
|
+
(`X12-ENVELOPE-SPLITTER-NOT-RELEASE-AWARE`).
|
|
86
|
+
Through `0.0.14` the envelope segments' element splitter was a plain `String.prototype.split`, so a
|
|
87
|
+
released element separator (`?*`) still ended the element and SHIFTED every element after it down a
|
|
88
|
+
slot. It was a property of the whole envelope segment and never of one element, so the element a
|
|
89
|
+
reader went looking for came back out of its neighbour's slot. Measured then, through `parseX12`:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
GS*HC*S*R*20260601*1200*1*X*005010?*X222A1~ ten elements, GS-08 read "005010?"
|
|
93
|
+
applicationSenderCode "SEND*ER" GS-08 read "X", the GS-07 agency code
|
|
94
|
+
groupControlNumber "1*2" GS-08 read "X"; warnings: []
|
|
95
|
+
transactionSetControlNumber "00*01" ST-03 read "01"; warnings: []
|
|
96
|
+
CLM*PT?*ACCT*150.00~ three elements, CLM-01 held "PT?*ACCT"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The body element was the control and was always correct. `splitElements` now honours the release
|
|
100
|
+
character exactly as `decodeSegment` and the segment-terminator scanner already did, so every row
|
|
101
|
+
above reads one element fewer and the value lands in its own slot.
|
|
102
|
+
- **🛑 READ THE CHANGE AS SYMMETRIC. IT IS NOT ONLY A CORRECTION.** A `?` before the separator has
|
|
103
|
+
two readings and 005010 does not transmit which the sender meant. Where the sender **escaped** a
|
|
104
|
+
delimiter, the release before this one framed it wrongly and this one frames it correctly: a
|
|
105
|
+
correction, the rows above. Where the sender sent a **literal `?`** as the element's last byte,
|
|
106
|
+
the release before this one framed it correctly and this one merges the element with its
|
|
107
|
+
successor, so **the segment loses its LAST element**: a regression.
|
|
108
|
+
`GS*HC*SUB1*RCV?*20260601*1200*000000123*X*005010X222A1~` read nine entries through `0.0.14` and
|
|
109
|
+
reads **eight** here, with GS-06 answering `"X"` and GS-08 gone.
|
|
110
|
+
- **🛑 What this changes for a consumer.** One input class decodes differently: an envelope segment
|
|
111
|
+
(`GS`, `GE`, `ST`, `SE`, `IEA`, `TA1`) carrying a `?` immediately before the element separator.
|
|
112
|
+
Every other release sequence in an envelope element (`??`, `?:`, `?^`, `?~`, `?A`) framed
|
|
113
|
+
identically before and after and still does. **No warning code is added, and
|
|
114
|
+
`X12_CONTROL_NUMBER_MISMATCH` moves in BOTH DIRECTIONS.** Where the old shift displaced a control
|
|
115
|
+
number it STOPS firing, so a consumer that rejects on that code will now **accept** such a
|
|
116
|
+
document; where a literal `?` newly displaces one it STARTS firing, so that same consumer will
|
|
117
|
+
now **reject** a document `0.0.14` accepted. A genuine mismatch still raises it.
|
|
118
|
+
- **🩺 The regression direction reaches an 837's variant and its money, by one route.** `ST-03` is
|
|
119
|
+
what decides the 837 variant, so an `ST-02` ending in a literal `?` destroys `ST-03` and the
|
|
120
|
+
document falls back to the `SVx` scan. An 837 declaring `005010X222A1` whose only service segment
|
|
121
|
+
is an `SV2` read `variant` `"P"`, `charge` `undefined` and `X12_837_SERVICE_LINE_NOT_DECODED`
|
|
122
|
+
through `0.0.14`, and here reads `variant` `"I"` with `charge` `150.00` and that warning silent.
|
|
123
|
+
**A warned non-decode becomes a decoded amount.** If you receive envelope elements that may end
|
|
124
|
+
in a literal `?`, re-check any routing driven off `submission.variant`.
|
|
125
|
+
- **Why it was taken anyway, and it is CONSISTENCY rather than a spec clause.** The two readings
|
|
126
|
+
are mutually exclusive and nothing in 005010 picks between them. `decodeSegment` has read BODY
|
|
127
|
+
elements the escape-wins way on **every released version** (`REF*EA*RCV?*NEXT` has always been
|
|
128
|
+
two elements). The envelope now obeys the one rule the rest of the package already obeyed, and
|
|
129
|
+
`buildInterchange` stops disagreeing with itself: at `0.0.14` it released GS-02 on emit and then
|
|
130
|
+
answered GS-08 as `"X"` from its own return value.
|
|
131
|
+
- **🩺 THE EXPOSURE IS NOT INBOUND BYTES ONLY.** Envelope slots routed through the builders'
|
|
132
|
+
release escaper are safe. **Not every emit slot is routed through it, and no total is published
|
|
133
|
+
here** - what follows is the routes measured to REACH the regression direction, not a closed
|
|
134
|
+
account of what bypasses the escaper.
|
|
135
|
+
|
|
136
|
+
**`buildInterchange` does not escape GS-04, GS-05 or GS-07** (`groupDate`, `groupTime`,
|
|
137
|
+
`responsibleAgencyCode`). A `groupDate` of `"2026060?"` emits
|
|
138
|
+
`GS*HC*SENDER*RECEIVER*2026060?*1200*1*X*005010X222A1`, which its own return value read as nine
|
|
139
|
+
elements with GS-08 intact through `0.0.14` and reads as **eight** here, GS-08 gone, plus
|
|
140
|
+
`X12_CONTROL_NUMBER_MISMATCH`.
|
|
141
|
+
|
|
142
|
+
- **An envelope element ending in a literal `?` is a dangling release character and is NOT warned.**
|
|
143
|
+
`X12_DANGLING_RELEASE_CHAR` fires only for an odd run of `?` at the very END of a segment, so a
|
|
144
|
+
mid-segment one reaches no check. That is true of body elements too and is unchanged here.
|
|
145
|
+
- **Values are still RAW, pre-`?`-unescape**, exactly as `X12Segment.elements` has always
|
|
146
|
+
documented: `gs.elements[2]` on the first row reads `"SEND?*ER"`, not `"SEND*ER"`. Read through
|
|
147
|
+
`getSegmentValue` if you want the logical value. `elements.join(separator)` therefore still
|
|
148
|
+
reproduces the segment byte for byte, which is what `serializeX12` relies on when it substitutes
|
|
149
|
+
a recomputed `SE-01` / `GE-01` / `IEA-01` into a control segment.
|
|
150
|
+
- **🩺 The ISA is deliberately exempt and stays positional.** ASC X12 .5 makes the ISA fixed-width,
|
|
151
|
+
which is what lets the delimiters be recovered from it before anything is parsed, so a `?` in an
|
|
152
|
+
ISA element is content and never an escape. `buildInterchange` states the same rule from the emit
|
|
153
|
+
side: it pads each ISA element and never escapes one.
|
|
154
|
+
- **A degenerate delimiter set whose element separator IS `?` is unchanged.** `?` cannot both
|
|
155
|
+
separate and escape, so the splitter falls back to the literal split, the same guard the
|
|
156
|
+
segment-terminator scanner already carried.
|
|
157
|
+
- **🩺 This does NOT make an UNESCAPED delimiter safe, and nothing here claims it does.** A bare
|
|
158
|
+
active delimiter in an envelope element still ends that element, because that is what a delimiter
|
|
159
|
+
is; only the sender escaping it is now honoured. `build837`'s
|
|
160
|
+
`implementationConventionReference` still REFUSES a value carrying an active delimiter or the
|
|
161
|
+
release character, and is deliberately not relaxed on the strength of this fix: a partner's
|
|
162
|
+
parser is not obliged to be release-aware either. `buildInterchange` applies no domain guard to
|
|
163
|
+
any envelope field and will still emit all of them, though it now reports back what it wrote -
|
|
164
|
+
at `0.0.14` it released GS-02 on emit and then answered GS-08 as `"X"` from its own return value.
|
|
165
|
+
|
|
166
|
+
- **🩺 `buildTA1` now RELEASES its five caller-supplied elements, so an Accept acknowledgment this
|
|
167
|
+
library emits no longer reads back as a Reject, and the bytes it writes for such a value CHANGED**
|
|
168
|
+
(`X12-TA1-EMIT-NOT-RELEASE-AWARE`). Through `0.0.14` `buildTA1` joined the five values with the
|
|
169
|
+
element separator and escaped none of them, so a value carrying an active delimiter took a slot of
|
|
170
|
+
its own and shifted every element after it down one. TA1-04 is the disposition and TA1-05 the note,
|
|
171
|
+
and `parseTA1` narrows an out-of-enum TA1-04 to `R`. Measured with `parseX12` + `parseTA1` over
|
|
172
|
+
`ISA … <what buildTA1 returned> … IEA`, `ackCode` `"A"` and `noteCode` `"000"` throughout:
|
|
173
|
+
|
|
174
|
+
| `interchangeControlNumber` | emitted at `0.0.14` | read `ackCode` | read TA1-01 | warnings |
|
|
175
|
+
| -------------------------- | --------------------------------- | -------------- | -------------------- | ------------------------ |
|
|
176
|
+
| `"000000001"` | `TA1*000000001*260601*1200*A*000` | `"A"` | `"000000001"` | none |
|
|
177
|
+
| `"00000001?"` | `TA1*00000001?*260601*1200*A*000` | `"R"` | `"00000001?*260601"` | none |
|
|
178
|
+
| `"0000*0001"` | `TA1*0000*0001*260601*1200*A*000` | `"R"` | `"0000"` | none |
|
|
179
|
+
| `"0000~0001"` | `TA1*0000~0001*260601*1200*A*000` | `"R"` | `"0000"` | `X12_UNEXPECTED_SEGMENT` |
|
|
180
|
+
|
|
181
|
+
**An Accept acknowledgment this library emitted read back as a Reject, on the element that
|
|
182
|
+
reassociates it, with nothing raised on any channel.** The `*` and `~` rows did that on every
|
|
183
|
+
released version; the `?` row is the one the envelope-splitter entry above opened. **🩺 The inverse
|
|
184
|
+
exists and is the less safe direction:** the read narrows an out-of-enum TA1-04 to `R`, so a
|
|
185
|
+
well-typed shift always lands on Reject, but `noteCode` is checked by the type system and by
|
|
186
|
+
nothing at run time, so a `noteCode` of literally `"A"` shifted onto TA1-04 and made a **Reject
|
|
187
|
+
read back as an Accept** - and a sender who reads an Accept does not resubmit. All four now read
|
|
188
|
+
back the disposition that was emitted.
|
|
189
|
+
- **The grounding is inside this package, not in a spec clause**, the same tiebreak the
|
|
190
|
+
envelope-splitter entry records. `buildTA1` emitted bytes that this package's own reader decoded
|
|
191
|
+
into a different disposition than the caller asked for, while every other builder already
|
|
192
|
+
released the same class of element through the same helper. Nobody here has read a clause that
|
|
193
|
+
settles it, and nothing above claims one.
|
|
194
|
+
- **🛑 It changes bytes this library already put on the wire, which is the cost.** A value
|
|
195
|
+
containing none of the four delimiters and no `?` is emitted byte-for-byte as before, and that
|
|
196
|
+
is every conformant TA1: TA1-01 echoes ISA-13, TA1-02 / TA1-03 echo ISA-09 / ISA-10, and
|
|
197
|
+
TA1-04 / TA1-05 are code list values. A value containing one is now released.
|
|
198
|
+
- **🛑 No warning code is added and no case moves onto a new code, but the consumer predicate
|
|
199
|
+
MOVES IN BOTH DIRECTIONS.** `parseTA1` of a `buildTA1` output now reports the disposition and
|
|
200
|
+
note the caller passed; before, it reported whatever element the shift left in TA1-04, which
|
|
201
|
+
could be the caller's, a coincidental in-enum value, or an out-of-enum one narrowed to `"R"`. So
|
|
202
|
+
`ackCode === "R"` **stops** firing where an Accept had been shifted onto it, and **starts**
|
|
203
|
+
firing where a Reject had been shifted off it: `interchangeTime: "12*A"` with `ackCode: "R"`
|
|
204
|
+
read `"A"` before and reads `"R"` now, with every field a valid member of its union.
|
|
205
|
+
`ackCode === "A"` moves the same two ways. What is one-directional is the safety, which is a
|
|
206
|
+
different statement: nothing now reports a disposition the caller did not ask for.
|
|
207
|
+
- **What releasing the REST of the set costs, and where it does not cost.** Only `*`, `~` and a
|
|
208
|
+
`?` immediately before the separator ever shifted the segment's own element framing. **`^` and
|
|
209
|
+
`:` moved the dot-path reader instead, and releasing them is a gain there:**
|
|
210
|
+
`getSegmentValue(ta1, "01")` answered `"0000"` through `0.0.14` for a control number of
|
|
211
|
+
`"0000^0001"`, silently truncating the reassociation key to the first repetition, and answers
|
|
212
|
+
`"0000^0001"` now; the composite read `"01-1"` answered `"0000"` for `"0000:0001"` and answers
|
|
213
|
+
the whole value now. **The measured pure cost is a mid-string `?`, and only on the surfaces
|
|
214
|
+
documented as raw**: `raw`, `elements` and `parseTA1`'s fields read `"0000??0001"` where they
|
|
215
|
+
read `"0000?0001"`, while every dot-path read unescapes and answered `"0000?0001"` on both.
|
|
216
|
+
No total is published: that is what was measured, not a closed account. (`getSegmentValue`
|
|
217
|
+
takes an `X12Segment` and `Ta1Segment` carries no `id`, so add one to read a TA1 through it.)
|
|
218
|
+
- **A caller who was hand-rolling the escape** (the remedy this file named while the defect was
|
|
219
|
+
open) regresses on both kinds of surface: `"00000001??"` in, `TA1*00000001????*…` out, and
|
|
220
|
+
`getSegmentValue` answering `"00000001??"` where it answered `"00000001?"`. The framing and the
|
|
221
|
+
disposition stay correct, but **drop the hand-rolled escape.**
|
|
222
|
+
- **An EMPTY control number is not refused and never was.** `escapeRelease` early-returns on `""`
|
|
223
|
+
and `buildTA1` carries no required-field guard, so `interchangeControlNumber: ""` emits
|
|
224
|
+
`TA1**260601*1200*A*000` with `warnings: []`, here and at every earlier release. Only a
|
|
225
|
+
NON-string refuses. Unchanged and tracked as its own item.
|
|
226
|
+
- **🩺 The READ half did not move. `parseTA1` still reads elements RAW, pre-`?`-unescape**, exactly
|
|
227
|
+
as `X12Segment.elements` has always documented, so a control number of `"00000001?"` now reads
|
|
228
|
+
back as `"00000001??"` rather than as `"00000001?*260601"`. The disposition is correct where it
|
|
229
|
+
was inverted; apply `unescapeRelease` if you need the value rather than the bytes. Unescaping on
|
|
230
|
+
the read side would move every TA1 a consumer already reads and is not done here.
|
|
231
|
+
- **The release is scoped to the delimiter set the CALLER states.** `BuildTA1Options` gained
|
|
232
|
+
`repetitionSeparator` / `componentSeparator` / `segmentTerminator` beside the existing
|
|
233
|
+
`elementSeparator`, the same four `build999` already takes, and they exist for escaping and
|
|
234
|
+
nothing else - `buildTA1` still emits no terminator. Escaping against a guessed set is a value
|
|
235
|
+
corruption rather than a safe default: `unescapeRelease` preserves `?X` verbatim for any `X`
|
|
236
|
+
outside the declared set, so a value released against the wrong delimiter comes back carrying a
|
|
237
|
+
stray `?`. **The defaults are the cosyte archetype and this function cannot verify them** - if
|
|
238
|
+
you embed a TA1 in an envelope that declares different separators, state them.
|
|
239
|
+
- **A non-string element now REFUSES** with `AckBuildError` / `X12_ACK_INVALID_SPEC`, and that is a
|
|
240
|
+
prerequisite rather than a bonus. Releasing a value means routing it through the escape helper,
|
|
241
|
+
and the bare `escapeRelease` underneath it returns its empty accumulator for a `number`, so
|
|
242
|
+
escaping without the type check would have replaced a shifted TA1-01 with a vanished one. A
|
|
243
|
+
numeric control number emitted `TA1*12345*…` at `0.0.14` (the number surviving onto `elements`,
|
|
244
|
+
inside a value typed `readonly string[]`) and an absent one emitted `TA1**250101*…`; both refuse.
|
|
245
|
+
**No existing refusal moves code:** `ackCode` `"A"` with a non-`000` note still reports
|
|
246
|
+
`X12_TA1_ACCEPT_WITH_NOTE` and still runs first.
|
|
247
|
+
- **What this does NOT close.** `buildTA1` still uses no segment joiner, so its refusal names the
|
|
248
|
+
builder and never the slot; `buildInterchange` still does not escape GS-04 / GS-05 / GS-07; and
|
|
249
|
+
an unescaped active delimiter is still not safe anywhere, because that is what a delimiter is.
|
|
250
|
+
|
|
19
251
|
- **🩺 BREAKING for `build277` callers: a 277 service line now REQUIRES `unitsOfService`, because
|
|
20
252
|
SVC-07 is a required element in `005010X212` and this library was not emitting it at all**
|
|
21
253
|
(`X12-277-SVC07-NOT-DECODED`). Through the release before this one `get277Status` read SVC-01
|
|
@@ -321,9 +553,9 @@ model.
|
|
|
321
553
|
confident `0` through `0.0.12`, and this code has nothing to do with either.
|
|
322
554
|
- **🩺 IT SAYS NOTHING ABOUT THE VARIANT, AND A FIRST DRAFT OF THIS BOUND CLAIMED IT DID.** Variant
|
|
323
555
|
resolution runs before the walk. A caller-supplied `type` option wins first; absent one, and
|
|
324
|
-
where `ST-03` names
|
|
556
|
+
where `ST-03` names no implementation convention this reader recognises, it **falls back to the
|
|
325
557
|
first `SVx` segment id anywhere in the transaction body - orphans included**. So a stray `SV2`
|
|
326
|
-
under an `ST-03`
|
|
558
|
+
under an unrecognised `ST-03` re-types the whole submission as Institutional, and every
|
|
327
559
|
conformant `SV1` line in it then reads `charge` `undefined`, `units` `undefined` (both a
|
|
328
560
|
fabricated `0` through `0.0.12`) and an `undefined` procedure
|
|
329
561
|
code - `undefined`, not `""`, which on such a line is the `revenueCode`. Passing
|
|
@@ -331,7 +563,88 @@ model.
|
|
|
331
563
|
identical at `0.0.10`, **not** introduced or changed by this code, and **not** narrowed here - excluding
|
|
332
564
|
orphans from the fallback would change how existing documents decode and is its own slice. It
|
|
333
565
|
is warned rather than silent (`X12_837_SERVICE_LINE_NOT_DECODED` at each `LX`), and
|
|
334
|
-
`submission.variant` is the field that tells you.
|
|
566
|
+
`submission.variant` is the field that tells you. **The resolution itself is now reported too
|
|
567
|
+
wherever it was contested**, which is the entry below; the fallback is still not narrowed.
|
|
568
|
+
|
|
569
|
+
- **🩺 An 837 variant the `SVx` fallback resolved in a body that names more than one variant raises
|
|
570
|
+
`X12_837_AMBIGUOUS_VARIANT`, anchored at the `ST`.** The fallback is unchanged and is deliberately
|
|
571
|
+
NOT narrowed: it still takes the **first** `SV1` / `SV2` / `SV3` in the body, orphans included, so on
|
|
572
|
+
every document that reaches it the variant resolved is byte-for-byte what it was through `0.0.13`.
|
|
573
|
+
**WHICH documents reach it changed in this release** - see the ST-03 entry at the top of this list. What this code
|
|
574
|
+
adds is that the resolution said so. Through `0.0.13` a stray `SV2` re-typed a whole Professional
|
|
575
|
+
submission Institutional and **only the line-level consequences were on any channel** - a consumer
|
|
576
|
+
routing on `submission.variant` saw a confident `"I"` with nothing to contradict it. Four bounds,
|
|
577
|
+
each a committed test:
|
|
578
|
+
- **It reports the RESOLUTION, never the document.** A caller-supplied `type` wins ahead of the
|
|
579
|
+
fallback, and so does an `ST-03` this reader turns into a variant; in
|
|
580
|
+
either case no guess was made and this code is not raised **however mixed the body is**. Never
|
|
581
|
+
restate it as "the file carries more than one kind of service segment".
|
|
582
|
+
- **🩺 Which service segment is the stray one is NOT decided, and nothing here should ever start
|
|
583
|
+
deciding it.** This reader cannot tell a stray service segment from a conformant one, and the
|
|
584
|
+
fallback takes the first whether or not a Loop 2400 was open at it, so an orphan decides the
|
|
585
|
+
variant like any other. Reporting the conflict is honest; picking a winner would be inventing.
|
|
586
|
+
- **It is ADDITIVE and nothing moved onto it.** `X12_837_SERVICE_LINE_NOT_DECODED`,
|
|
587
|
+
`X12_837_SERVICE_SEGMENT_WITHOUT_LX` and `X12_837_SERVICE_LINE_DROPPED` fire on exactly the
|
|
588
|
+
documents they fired on before **this code was added**, in the same positions, and no predicate
|
|
589
|
+
changes meaning because of this code. **That is a claim about this code and NOT about the
|
|
590
|
+
release:** the ST-03 entry at the top of this list changed which documents reach the fallback at
|
|
591
|
+
all, and a line whose `SVx` kind disagrees with a now-recognised `ST-03` stops decoding and
|
|
592
|
+
starts raising `X12_837_SERVICE_LINE_NOT_DECODED`. Read that entry beside this bound.
|
|
593
|
+
- **It can never travel with `X12_837_UNKNOWN_VARIANT`**, which is the other outcome of the same
|
|
594
|
+
resolution: a body with conflicting service segments has, by construction, at least one to fall
|
|
595
|
+
back on. It fires **once per transaction**, because there is one resolution per transaction.
|
|
596
|
+
- **What it does NOT close:** narrowing the fallback, which is deliberately left alone. It no
|
|
597
|
+
longer leaves a second `SVx` inside an already-open Loop 2400 unreported: that was its own
|
|
598
|
+
slice, and it is the entry below. **Read the earlier wording as withdrawn** - it said such a
|
|
599
|
+
segment is "read into nothing", which was true only of one whose kind does not match the
|
|
600
|
+
resolved variant; a DUPLICATE of the matching kind is read into the line and REPLACES what the
|
|
601
|
+
first one wrote.
|
|
602
|
+
|
|
603
|
+
- **🩺 A second `SV1` / `SV2` / `SV3` inside an ALREADY-OPEN Loop 2400 raises
|
|
604
|
+
`X12_837_SERVICE_SEGMENT_REPEATED`, anchored at the repeated service segment itself.** A service
|
|
605
|
+
line carries **one** service segment's worth of slots, and every decoder writes **all** of the
|
|
606
|
+
slots its kind writes, so the model holds what the **last** service segment matching the
|
|
607
|
+
submission's resolved variant wrote and nothing of any earlier one. **Through `0.0.13` that
|
|
608
|
+
happened in complete silence, and it is money and a procedure code:** under an `ST-03` of
|
|
609
|
+
`005010X222A2`, `SV1*HC:99213*8500*UN*4***1~` followed by `SV1*HC:99999*12*UN*1***1~` inside one
|
|
610
|
+
`LX` left ONE line reading `charge` `12` and `procedureCode` `99999`, with `warnings: []`. `8500`
|
|
611
|
+
became `12`, CPT `99213` became `99999`, and nothing was raised on any channel. Six bounds, each a
|
|
612
|
+
committed test:
|
|
613
|
+
- **🛑 The decode is NOT narrowed, and that is deliberate.** Last-wins is unchanged, element for
|
|
614
|
+
element, so which values a document decodes to are byte-for-byte what they were at `0.0.13`.
|
|
615
|
+
This reader cannot tell a stray service segment from a conformant one, so choosing the first
|
|
616
|
+
instead would be inventing; and changing which occurrence wins changes how **already-published
|
|
617
|
+
documents decode**, the same call made about the variant fallback in the entry above. **This
|
|
618
|
+
closes only the silence.**
|
|
619
|
+
- **🩺 The worst corner is a repeat whose own charge element is ABSENT.** It writes `undefined`
|
|
620
|
+
over the amount the first one stated, and `X12_837_SERVICE_LINE_NOT_DECODED` does **not** fire
|
|
621
|
+
on that line, because a service segment _did_ decode. This code is then the only thing on the
|
|
622
|
+
channel that says why the charge is empty.
|
|
623
|
+
- **It fires on a repeat of ANY kind, decoded or not.** A service segment whose kind does not
|
|
624
|
+
match the resolved variant is read into nothing and overwrites nothing - the loss there is that
|
|
625
|
+
what it carries reaches no part of the typed model - and it is reported the same way, whether it
|
|
626
|
+
arrives before or after the matching one. That case was silent at the segment through `0.0.13`
|
|
627
|
+
too, and the entry above recorded it as a deferred residual.
|
|
628
|
+
- **Once per repeat, and the count is scoped to the LINE, never latched.** Three service segments
|
|
629
|
+
in one Loop 2400 are two warnings at two positions; a first service segment under a later `LX`,
|
|
630
|
+
or in a later claim, is a first and never a repeat. A latching flag would report a conformant
|
|
631
|
+
second line.
|
|
632
|
+
- **It can never name the same segment as `X12_837_SERVICE_SEGMENT_WITHOUT_LX`**, which requires
|
|
633
|
+
that NO Loop 2400 be open where this one requires that one is. It can travel with
|
|
634
|
+
`X12_837_SERVICE_LINE_NOT_DECODED` (at the `LX`), with `X12_837_AMBIGUOUS_VARIANT` (at the
|
|
635
|
+
`ST`), and with `X12_UNPARSEABLE_DECIMAL` raised by the repeat's own amount bytes. **That last
|
|
636
|
+
pair sits at the SAME `position.segmentIndex`, separated only by the decimal code's own
|
|
637
|
+
`position.elementIndex`** - measured, and pinned by a committed test. Never read a co-occurring
|
|
638
|
+
pair here as two segments.
|
|
639
|
+
- **It is ADDITIVE and nothing moved onto it.** Every code this reader raised on a document of
|
|
640
|
+
this shape before **this code was added**, it still raises, at the same position, and no
|
|
641
|
+
predicate changes meaning because of this code. Read that as invariance and **not** as a list of
|
|
642
|
+
what else you will see, and **not** as a claim about the release: the ST-03 entry at the top of
|
|
643
|
+
this list changed which documents reach the variant fallback.
|
|
644
|
+
**The package's own documentation was a consumer that needed updating**: the cookbook's
|
|
645
|
+
"gate before you post a line amount" recipe named four codes, none of which fires on the
|
|
646
|
+
overwrite document, so it was blind to it - it now names this one too, and a committed test
|
|
647
|
+
pins that the four-code gate misses what the five-code gate catches.
|
|
335
648
|
|
|
336
649
|
- **🩺 An `N3` / `N4` / `PER` / `REF` discarded after a stray `LX` raises
|
|
337
650
|
`X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX`, anchored at the discarded segment itself.** This is
|
|
@@ -786,10 +1099,12 @@ N-char spec limit` refusal, one per emitting module, where the branch fires **be
|
|
|
786
1099
|
than reaching the message.
|
|
787
1100
|
|
|
788
1101
|
**Read the "through a builder's segment joiner" qualifier literally.** `buildTA1` does not use one,
|
|
789
|
-
and is therefore **not covered
|
|
790
|
-
|
|
791
|
-
(`TA1**250101*1200*A*000`)
|
|
792
|
-
|
|
1102
|
+
and is therefore **not covered by the segment guard**. Through `0.0.14` that meant nothing checked
|
|
1103
|
+
its five elements at all, and a numeric or `undefined` `interchangeControlNumber` was emitted
|
|
1104
|
+
silently (`TA1**250101*1200*A*000`); `X12-TA1-EMIT-NOT-RELEASE-AWARE` closed that by routing them
|
|
1105
|
+
through the escape helper, which type-checks first and refuses with `AckBuildError` /
|
|
1106
|
+
`X12_ACK_INVALID_SPEC`. What being outside the segment guard still costs is the **slot** in the
|
|
1107
|
+
refusal message: it names `buildTA1`, never `TA1-01`.
|
|
793
1108
|
|
|
794
1109
|
The monetary slots got their own guard on top of it: a slot typed `X12Decimal` refuses anything that
|
|
795
1110
|
is not one, rather than rendering `number.toString()` into the document.
|
|
@@ -801,7 +1116,9 @@ N-char spec limit` refusal, one per emitting module, where the branch fires **be
|
|
|
801
1116
|
`1?*BOGUS`); the fixed-width ISA slots do not go through it at all.
|
|
802
1117
|
2. **Whether an `X12Decimal` carries the SCALE you meant.** `fromString("0.3")` and
|
|
803
1118
|
`fromString("0.30")` are both accepted and both emit verbatim. That choice is yours.
|
|
804
|
-
3. **
|
|
1119
|
+
3. **The delimiter set `buildTA1` releases against**, per the paragraph above. It defaults to the
|
|
1120
|
+
cosyte archetype and the function cannot verify it; state the separators on `BuildTA1Options`
|
|
1121
|
+
if you embed a TA1 in an envelope that declares different ones.
|
|
805
1122
|
4. **`build835`'s balance-equation amounts, which refuse UNTYPED.** `build835` runs its balance
|
|
806
1123
|
guard before it builds the escape helper, and that guard calls `X12Decimal` methods on your
|
|
807
1124
|
value. So a raw `number` there throws a plain `TypeError` with **no `code`** - some saying the
|