@cosyte/x12 0.0.12 → 0.0.14
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 +398 -2
- package/KNOWN-LIMITATIONS.md +313 -30
- package/README.md +2 -2
- package/dist/index.cjs +258 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +437 -51
- package/dist/index.d.ts +437 -51
- package/dist/index.mjs +253 -75
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,227 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
12
|
+
- **🩺 `X12_837_SERVICE_SEGMENT_REPEATED`, the 33rd Tier-2 warning code, plus the public factory
|
|
13
|
+
`serviceSegmentRepeated(position)`** (`X12-837-SV1-OVERWRITE`). A **second** `SV1` / `SV2` / `SV3`
|
|
14
|
+
arriving inside an **already-open** Loop 2400 now says so, at that repeated segment. Through
|
|
15
|
+
`0.0.13` it said nothing at all.
|
|
16
|
+
|
|
17
|
+
**🩺 What that cost, and it is money and a procedure code.** A service line carries **one** service
|
|
18
|
+
segment's worth of slots, and every decoder writes **all** of the slots its kind writes. Under an
|
|
19
|
+
`ST-03` of `005010X222A2`, `SV1*HC:99213*8500*UN*4***1~` followed by `SV1*HC:99999*12*UN*1***1~`
|
|
20
|
+
inside one `LX` left ONE line reading `charge` **`12`** and `procedureCode` **`99999`**, with
|
|
21
|
+
`warnings: []`. `8500` became `12`, CPT `99213` became `99999`, and nothing was raised on any
|
|
22
|
+
channel. **The worst corner is a repeat whose own charge element is ABSENT:** it writes `undefined`
|
|
23
|
+
over the amount the first one stated, and `X12_837_SERVICE_LINE_NOT_DECODED` does **not** fire
|
|
24
|
+
there, because a service segment did decode.
|
|
25
|
+
|
|
26
|
+
**🛑 THIS CLOSES ONLY THE SILENCE, AND THE RESTRAINT IS THE POINT.** The decode is **not** narrowed:
|
|
27
|
+
last-wins is unchanged, element for element, so which values a document decodes to are byte-for-byte
|
|
28
|
+
what they were at `0.0.13`. This reader cannot tell a stray service segment from a conformant one,
|
|
29
|
+
so choosing the first would be inventing; and changing which occurrence wins changes how
|
|
30
|
+
**already-published documents decode**. That is the same call made for the `SVx` variant fallback.
|
|
31
|
+
|
|
32
|
+
**It fires on a repeat of ANY kind, decoded or not.** One whose kind does not match the resolved
|
|
33
|
+
variant is read into nothing and overwrites nothing - what it carries reaches no part of the typed
|
|
34
|
+
model - and it is reported the same way, before or after the matching one.
|
|
35
|
+
|
|
36
|
+
**Anchored at the repeated service segment**, with **no `elementIndex`**: what is reported is a
|
|
37
|
+
second occurrence of the segment, not a defect in an element of it. **Once per repeat**, and the
|
|
38
|
+
count is scoped to the LINE and never latched, so a first service segment under a later `LX` is a
|
|
39
|
+
first. It can never name the same segment as `X12_837_SERVICE_SEGMENT_WITHOUT_LX`, which requires
|
|
40
|
+
that no Loop 2400 be open.
|
|
41
|
+
|
|
42
|
+
**It is additive and nothing moved onto it**, pinned by committed tests asserting the whole warning
|
|
43
|
+
channel with the new code filtered out. **No consumer predicate written against any existing code
|
|
44
|
+
changes meaning.** Read that as invariance, not as a list of what else you will see.
|
|
45
|
+
|
|
46
|
+
**🛑 The package's own documentation was a consumer, and it was blind.** The cookbook's "gate before
|
|
47
|
+
you post a line amount" recipe named four codes and **none** of them fires on the overwrite
|
|
48
|
+
document, so a consumer following it posted `12` for a line the sender also sent as `8500`. The
|
|
49
|
+
cookbook, the troubleshooting table and `KNOWN-LIMITATIONS.md` now name this code beside the other
|
|
50
|
+
four, and a committed test pins that the four-code gate misses what the five-code gate catches.
|
|
51
|
+
**The recipe was not the only page:** `spec-notes-money` named `X12_837_SERVICE_LINE_NOT_DECODED`
|
|
52
|
+
as "the known instance" of an 837 charge reading `undefined` from a slot no reader read, and the
|
|
53
|
+
repeat corner is a second instance by a different route on which that code does not fire. It now
|
|
54
|
+
says so.
|
|
55
|
+
|
|
56
|
+
- **🩺 `X12_837_AMBIGUOUS_VARIANT`, the 32nd Tier-2 warning code, plus the public factory
|
|
57
|
+
`ambiguous837Variant(position)`** (`X12-837-RESIDUALS`). An 837 whose variant was decided by the
|
|
58
|
+
`SVx` fallback, in a transaction body that carries service segments for **more than one variant**,
|
|
59
|
+
now says so at the `ST`. Through `0.0.13` it said nothing at all about that resolution.
|
|
60
|
+
|
|
61
|
+
**What that cost.** Variant resolution runs before the walk as
|
|
62
|
+
`explicitType ?? variantFromIcr ?? variantFromSegment`. Absent a caller `type` option, and where
|
|
63
|
+
`ST-03` names no implementation convention this reader recognises, the reader falls back to the
|
|
64
|
+
**first** `SV1` / `SV2` / `SV3` in the body, orphans included. One stray `SV2` ahead of a conformant
|
|
65
|
+
Professional claim therefore re-types the whole submission Institutional: `submission.variant` reads
|
|
66
|
+
`"I"`, and a consumer routing on that field sends a Professional claim down an Institutional path.
|
|
67
|
+
The line-level consequences were reported; **the submission-level typing that produced them was on
|
|
68
|
+
no channel**, so `submission.variant` carried a confident value with nothing to contradict it.
|
|
69
|
+
|
|
70
|
+
**🛑 THIS CLOSES ONLY THE SILENCE, AND THE RESTRAINT IS THE POINT.** The fallback is **not**
|
|
71
|
+
narrowed and first-wins is unchanged, so on every document that reaches the fallback the variant
|
|
72
|
+
resolved and the lines decoded are byte-for-byte what they were at `0.0.13`, and this code is added
|
|
73
|
+
beside whatever the walk already raised. (`X12-VARIANT-ICR-UNGROUNDED`, in this same release,
|
|
74
|
+
changed WHICH documents reach the fallback; read its entry for what decodes differently.) Excluding orphans from
|
|
75
|
+
the fallback would change how already-published documents decode and is its own slice.
|
|
76
|
+
|
|
77
|
+
**🩺 Which service segment is the stray one is NOT decided.** This reader cannot tell a stray
|
|
78
|
+
service segment from a conformant one, and the fallback takes the first whether or not a Loop 2400
|
|
79
|
+
was open at it. Reporting the conflict is honest; picking a winner would be inventing. Re-read with
|
|
80
|
+
`get837Claims(delimiters, tx, { type })` to decode against a variant you trust.
|
|
81
|
+
|
|
82
|
+
**It reports the RESOLUTION, never the document.** A caller `type`, or an `ST-03` naming a known
|
|
83
|
+
convention, means no guess was made and this code is **not** raised however mixed the body is.
|
|
84
|
+
|
|
85
|
+
**Anchored at the `ST`** (`tx.segments[0]`, which carries `ST-03`), with **no `elementIndex`**: the
|
|
86
|
+
conflict is a property of the body rather than of an element, and one route into it is an `ST-03`
|
|
87
|
+
that is absent altogether. Raised **once per transaction**, because there is one resolution per
|
|
88
|
+
transaction, and it can never travel with `X12_837_UNKNOWN_VARIANT`, which is the other outcome of
|
|
89
|
+
that same resolution.
|
|
90
|
+
|
|
91
|
+
**🩺 It is additive, and nothing moved onto it.** `X12_837_SERVICE_LINE_NOT_DECODED`,
|
|
92
|
+
`X12_837_SERVICE_SEGMENT_WITHOUT_LX` and `X12_837_SERVICE_LINE_DROPPED` fire on exactly the
|
|
93
|
+
documents they fired on before **this code was added**, in the same positions, pinned by committed
|
|
94
|
+
tests that assert the whole warning channel with the new code filtered out. **No consumer predicate
|
|
95
|
+
changes meaning because of this code.** (`X12-VARIANT-ICR-UNGROUNDED`, in this same release, DID
|
|
96
|
+
change which documents reach the `SVx` fallback; read its entry above.) Read that as **invariance and not as a list of what else you will
|
|
97
|
+
see** on a contested document: it does not promise that any particular loss on one is reported at
|
|
98
|
+
all, and one that is not was not reported before this code existed either. A stray `LX` that opened
|
|
99
|
+
no line, for one, already suppressed `X12_837_SERVICE_SEGMENT_WITHOUT_LX` for the service segments
|
|
100
|
+
inside it, and still does.
|
|
101
|
+
|
|
102
|
+
- **🩺 `X12_STATED_AMOUNT_DISCARDED`, the 31st Tier-2 warning code, plus the public factory
|
|
103
|
+
`statedAmountDiscarded(position)`** (`X12-STATED-AMOUNT-DISCARDED`). The entry below reports a row
|
|
104
|
+
whose amount this library could not read. This one is the opposite case: **the reader discarded
|
|
105
|
+
the row for a reason that is not about the amount at all**, so the amount was never the problem
|
|
106
|
+
and, on one of the two routes, was never even looked at. The bytes stay verbatim on
|
|
107
|
+
`tx.segments[…].raw`; decode them yourself before posting. Through `0.0.12` both routes were
|
|
108
|
+
silent on every channel.
|
|
109
|
+
|
|
110
|
+
**The two routes, enumerated.** An **820 `RMR`** under an open remittance loop whose `RMR-01` and
|
|
111
|
+
`RMR-02` are **both empty** while `RMR-04` or `RMR-05` is populated: `decodeRmr` refuses the open
|
|
112
|
+
item on identity **before** either amount element is read, so `RMR****150.00*150.00~` gave
|
|
113
|
+
`openItems: []` and `warnings: []`, taking a stated payment, a stated amount due and `RMR-03`'s
|
|
114
|
+
payment action code together. And an **837 `AMT`** arriving while a Loop 2430 line adjudication
|
|
115
|
+
(`SVD`) is open, whose `AMT-02` decoded: the v1 adjudication model carries no amount row, and
|
|
116
|
+
attaching one to this submission's own service line would put another payer's figure on it, so
|
|
117
|
+
`AMT*EAF*75.00~` (Remaining Patient Liability) was skipped in silence.
|
|
118
|
+
|
|
119
|
+
**🩺 That second route is where the channel read BACKWARDS, and squaring it is the point.** Under
|
|
120
|
+
an open `SVD`, `AMT*EAF~` raised `X12_AMOUNT_ROW_DROPPED` and `AMT*EAF*75.00~` raised nothing: the
|
|
121
|
+
report was present exactly where **less** was lost. Both report now, and which code arrives is
|
|
122
|
+
what says which loss it was.
|
|
123
|
+
|
|
124
|
+
**It carries no `position.elementIndex`.** On the `RMR` route the loss spans `RMR-04` and
|
|
125
|
+
`RMR-05` and no single element names it; on the `AMT` route the element is fixed by the segment.
|
|
126
|
+
|
|
127
|
+
**🩺 It is additive, and nothing moved onto it.** `X12_AMOUNT_ROW_DROPPED` and
|
|
128
|
+
`X12_UNPARSEABLE_DECIMAL` fire on exactly the documents they fired on before, pinned by committed
|
|
129
|
+
tests. **The two amount-row codes are disjoint and can never name the same segment**, because this
|
|
130
|
+
one requires an amount element the sender populated and the other requires one that decoded no
|
|
131
|
+
value, so the code you get is the discriminant. **Gate on both.** `KNOWN-LIMITATIONS.md`, the
|
|
132
|
+
money spec-note and the troubleshooting table were corrected with the code.
|
|
133
|
+
|
|
134
|
+
**Bounds, stated as properties of the READ.** What is reported is a segment that populated its
|
|
135
|
+
amount element and arrived **while the loop that would carry its row was open**. One reaching a
|
|
136
|
+
reader with no such loop open is a different loss and is **still silent**: the 834's `AMT` with no
|
|
137
|
+
`HD` open, the 820's `ADX` with no remittance open, and the 835's and the 837's `AMT` before any
|
|
138
|
+
claim or service line. A bare `RMR~` states nothing and is silent, as is an identity-less `RMR`
|
|
139
|
+
carrying only a payment action code. And this code says **nothing** about whether the amount would
|
|
140
|
+
have decoded: the `RMR` route refuses the row before attempting the decode, so it is raised on
|
|
141
|
+
`RMR****1,234.56~` exactly as on `RMR****150.00*150.00~`, with **no `X12_UNPARSEABLE_DECIMAL`
|
|
142
|
+
beside it in either case**, on this release and on every earlier one. **Do not read an
|
|
143
|
+
unaccompanied instance as evidence the bytes are postable.** Only the `AMT` route guarantees a
|
|
144
|
+
decodable amount, because there `AMT-02` decoded before the row was skipped.
|
|
145
|
+
|
|
146
|
+
- **🩺 `X12_AMOUNT_ROW_DROPPED`, the 30th Tier-2 warning code, plus the public factory
|
|
147
|
+
`amountRowDropped(position)`** (`X12-AMT-ADX-ABSENT-AMOUNT`). An `AMT` or `ADX` is not a slot on a
|
|
148
|
+
bigger record: each one **is** a record, carrying an amount plus the thing the amount is about. So
|
|
149
|
+
when the amount element (`AMT-02`, `ADX-01`) decodes no value there is no row to build, and
|
|
150
|
+
`AMT-01`'s qualifier or `ADX-02`'s adjustment reason code is dropped with it. Through `0.0.12`
|
|
151
|
+
that happened with **no diagnostic on any channel**: `AMT*B6~` gave `claim.amounts: []` and
|
|
152
|
+
`warnings: []`, which reads exactly like a document that never carried the segment. It is now
|
|
153
|
+
reported, at the `AMT` / `ADX` itself.
|
|
154
|
+
|
|
155
|
+
Raised by four surfaces: the 835's `AMT`, the 837's `AMT`, the 834's coverage `AMT` and the 820's
|
|
156
|
+
`ADX`. The 834's lands on that **member's** own `warnings`, the same per-member scoping the
|
|
157
|
+
decimal sink beside it already used, because a roster-level report would say a premium was lost
|
|
158
|
+
without saying whose. **On the 835 and the 837 the `AMT` attaches to the open SERVICE LINE first
|
|
159
|
+
and to the claim only when there is none, so the row lost may be a line-level one** - an unchanged
|
|
160
|
+
`claim.amounts` is not evidence the warning is stale.
|
|
161
|
+
|
|
162
|
+
**It carries no `position.elementIndex`**, deliberately. One of its two routes is an absent
|
|
163
|
+
element, and an absent element has no index to name; the segment fixes which element was being
|
|
164
|
+
read anyway.
|
|
165
|
+
|
|
166
|
+
**🩺 It is additive, and no case moved onto it.** An amount that is present and does not decode
|
|
167
|
+
still raises `X12_UNPARSEABLE_DECIMAL` at its own `elementIndex`, now **alongside** this code
|
|
168
|
+
rather than instead of it, so a gate you already wrote against that code fires on exactly the
|
|
169
|
+
documents it fired on before. Whether one accompanies this code at the same `position.segmentIndex`
|
|
170
|
+
is what separates the absent route from the unparseable one, since this code is raised for both
|
|
171
|
+
and discriminates neither. What a one-code gate never caught, on any release, is the
|
|
172
|
+
absent-amount row: **gate on both.** `KNOWN-LIMITATIONS.md`, the money spec-note and the
|
|
173
|
+
troubleshooting table were corrected with the code, and a committed test pins both halves - that
|
|
174
|
+
the one-code gate misses the absent-amount document, and that it still fires where it always did.
|
|
175
|
+
|
|
176
|
+
**Bounds, stated because the wider reading is the tempting one, and stated as properties of the
|
|
177
|
+
READ rather than of the walker's control flow.** What is reported is a row whose amount was read
|
|
178
|
+
and decoded no value. A segment discarded **before** its amount is read is not on this channel
|
|
179
|
+
(the 834's `AMT` with no `HD` open, the 820's `ADX` with no remittance open), and neither is one
|
|
180
|
+
whose amount decoded and then found nothing open to attach the row to. **Do not shorten that to
|
|
181
|
+
"nothing open means silent"** - the 835 and the 837 decode first, so an `AMT` with an absent
|
|
182
|
+
amount and no claim open does raise this code. An 820 `RMR` is not on this channel either, and
|
|
183
|
+
**not** because its row survives: `decodeRmr` drops on open-item identity (`RMR-01` and `RMR-02`
|
|
184
|
+
both empty) **before** `RMR-04` is read, so an `RMR` stating an open item and no amount keeps its
|
|
185
|
+
row with `amountPaid` left `undefined`, while one stating an amount and **no** open item is
|
|
186
|
+
dropped whole. That second case, and the 837's Loop 2430 `AMT` which an open `SVD` discards
|
|
187
|
+
outright, are separate losses: nothing failed to decode in either, so neither is this code's
|
|
188
|
+
shape, and `X12_STATED_AMOUNT_DISCARDED` above is what reports them.
|
|
189
|
+
|
|
190
|
+
- **🩺 `X12_835_BALANCE_NOT_EVALUABLE`, the 29th Tier-2 warning code, plus the public factory
|
|
191
|
+
`balanceNotEvaluable(position, invariant)`** (`X12-837-SV-UNDEFINED-DECIMAL`). Raised where a term
|
|
192
|
+
of one of the three TR3 005010X221A1 §1.10.2 balance equations is `undefined` on the model, so the
|
|
193
|
+
equation has nothing to compare on one side. `invariant` is the same library-owned
|
|
194
|
+
`BALANCE_INVARIANTS` discriminant `X12_835_REMIT_BALANCE_MISMATCH` takes, and the message names the
|
|
195
|
+
equation and carries no amount, like every other warning in this registry.
|
|
196
|
+
|
|
197
|
+
**It is a different code from the mismatch on purpose.** The mismatch asserts a computed inequality
|
|
198
|
+
between amounts the sender supplied; this one asserts only that the comparison could not be made.
|
|
199
|
+
Through `0.0.12` an undecoded term collapsed to `X12Decimal.ZERO` and the equation then reported a
|
|
200
|
+
mismatch between the payer's own amounts and a zero this library invented, which is the reading
|
|
201
|
+
that made an absent `CLP-03` indistinguishable from a claim submitted at zero.
|
|
202
|
+
|
|
203
|
+
**An EMPTY list of adjustments is NOT an absent term** and does not raise it: a claim carrying no
|
|
204
|
+
`CAS` really did state no adjustments, so its sum is `X12Decimal.ZERO`. Only a term this library
|
|
205
|
+
decoded no value from stops the equation. `build835` reaches the same verdict as
|
|
206
|
+
`X12_835_BUILD_INVALID_SPEC` rather than as its balance-mismatch code, and is unreachable there
|
|
207
|
+
from TypeScript because every balance term on `Build835Spec` is a required `X12Decimal`.
|
|
208
|
+
|
|
209
|
+
**🩺 GATE ON BOTH CODES.** A posting gate written against `X12_835_REMIT_BALANCE_MISMATCH` alone
|
|
210
|
+
**stops firing** on these documents when you upgrade, because through `0.0.12` the undecoded term
|
|
211
|
+
collapsed to zero and raised the mismatch. This library warns either way; your gate has to look
|
|
212
|
+
for both. The recipes in `docs-content/quickstart.md` and `docs-content/cookbook.md` and the
|
|
213
|
+
triage table in `docs-content/troubleshooting.md` were corrected with the code, and a committed
|
|
214
|
+
test pins that the one-code gate misses a document the two-code gate catches.
|
|
215
|
+
|
|
216
|
+
- **🩺 `X12_837_PAY_TO_ADDRESS_REPEATED`, the 28th Tier-2 warning code, plus the public factory
|
|
217
|
+
`payToAddressRepeated(position)`** (`X12-PAY-TO-FUSION`). Raised at the **second and each
|
|
218
|
+
subsequent `NM1*87` within one Loop 2000A**, where the TR3s allow Loop 2010AB at most once.
|
|
219
|
+
`position.segmentIndex` names the repeated `NM1*87` itself and carries no `elementIndex`: what is
|
|
220
|
+
reported is a second occurrence of the segment, not a defect in any element of it. The counter
|
|
221
|
+
resets at the Loop 2000A `HL`, beside the pay-to slot it guards, so a first `NM1*87` under a later
|
|
222
|
+
billing provider is a first and not a repeat.
|
|
223
|
+
|
|
224
|
+
The model has **one** pay-to address slot, so it cannot carry two, and this code is the only thing
|
|
225
|
+
that says the document named two. It reports that the **document** repeated the loop; it does not
|
|
226
|
+
report that anything was mis-read, and it says nothing about the subjects of the other 837 codes.
|
|
227
|
+
An `NM1*87` arriving while a `CLM` is open never reaches the pay-to route at all, so it neither
|
|
228
|
+
warns nor arms the warning. **Where it lands instead is not stated as one destination, because two
|
|
229
|
+
were measured:** with a Loop 2400 open it joins that line's `serviceLine.providers` (TR3 Loop
|
|
230
|
+
2420), and with a claim but no line open it joins `claim.providers` (Loop 2310). Both are
|
|
231
|
+
pre-existing and identical at `0.0.12`.
|
|
232
|
+
|
|
12
233
|
- **🩺 `X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX`, the 27th Tier-2 warning code, plus the public
|
|
13
234
|
factory `entitySegmentDiscardedAfterLx(position)`** (`X12-DISCARD-AFTER-STRAY-LX`). Raised for
|
|
14
235
|
each 837 `N3` / `N4` / `PER` / `REF` that reached **no party at all** because an earlier `LX` with
|
|
@@ -139,6 +360,130 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
139
360
|
|
|
140
361
|
### Changed
|
|
141
362
|
|
|
363
|
+
- **🩺 `get837Claims` now recognises every published `ST-03` implementation-convention reference for
|
|
364
|
+
the three 837 guides, and some already-published files therefore decode differently**
|
|
365
|
+
(`X12-VARIANT-ICR-UNGROUNDED`). This is a grounding unit: the table `VARIANT_BY_ICR` had three keys
|
|
366
|
+
and they were grounded against nothing.
|
|
367
|
+
|
|
368
|
+
**🩺 What that cost, measured at `668afea`, which is `main` at published `0.0.13`.** The table held exactly
|
|
369
|
+
`005010X222A2`, `005010X223A3` and `005010X224A2`. **It contained none of the identifiers HIPAA
|
|
370
|
+
adopts at 45 CFR 162.1102** (`005010X222`; `005010X223` with its `005010X223A1` Type 1 errata;
|
|
371
|
+
`005010X224` with `005010X224A1`), **and it was missing `005010X222A1` and `005010X223A2`**, which
|
|
372
|
+
CMS and state Medicaid companion guides require in ST-03 and GS-08 on production professional and
|
|
373
|
+
institutional claims. So a conformant, HIPAA-mandated 837P declaring `005010X222A1` resolved to no
|
|
374
|
+
variant at all, fell through to the `SVx` scan, and one stray `SV2` anywhere in the body re-typed
|
|
375
|
+
the whole submission Institutional. **The `SVx` fallback was the NORMAL path on production
|
|
376
|
+
professional and institutional traffic rather than the exception**, and `X12_837_UNKNOWN_VARIANT`
|
|
377
|
+
on such a file was a fabricated non-conformance claim about a document that was not
|
|
378
|
+
non-conformant. The sources for every key are named beside the table and in
|
|
379
|
+
`documentation/agent-notes/x12-variant-icr-ungrounded.md`; the three later published errata guides
|
|
380
|
+
are the weakest leg and say so in place.
|
|
381
|
+
|
|
382
|
+
**🛑 This IS a behaviour change on already-published decoding, and it is disclosed rather than
|
|
383
|
+
buried.** On a file whose `ST-03` is now recognised: `submission.variant` can differ from what
|
|
384
|
+
`0.0.13` read, wherever the first `SVx` in the body disagreed with the declaration; and
|
|
385
|
+
**`X12_837_AMBIGUOUS_VARIANT` and `X12_837_UNKNOWN_VARIANT` no longer fire on it at all**, because
|
|
386
|
+
no guess was made. **A predicate written against either code goes quiet on such a file.**
|
|
387
|
+
|
|
388
|
+
**🩺 And a service line whose `SVx` kind disagrees with the declaration is no longer DECODED, so a
|
|
389
|
+
code STARTS firing on a document that may have carried `warnings: []`.** Under an `ST-03` of
|
|
390
|
+
`005010X222A1` with a body whose only service segment is an `SV2`, `0.0.13` read `variant` `"I"`,
|
|
391
|
+
`charge` `7300`, `units` `2` and `warnings: []`; this release reads `variant` `"P"`, `charge` and
|
|
392
|
+
`units` `undefined` with the rest of the service segment undecoded, and
|
|
393
|
+
`X12_837_SERVICE_LINE_NOT_DECODED` at that line's `LX`. **Read only the decimal slots as
|
|
394
|
+
`undefined`:** an undecoded line SEEDS its identity fields, so `procedureCode` is `""` on a P or D
|
|
395
|
+
line and `revenueCode` is `""` on an I one. A predicate of `procedureCode === undefined` does NOT
|
|
396
|
+
detect this.
|
|
397
|
+
A mis-stamped envelope is an ordinary vendor variant and this reader can no more tell one from a
|
|
398
|
+
conformant document than it can tell a stray `SVx` from a conformant one, so the loss is **warned
|
|
399
|
+
rather than silent**, and the cookbook's post-a-line-amount gate already names that code first.
|
|
400
|
+
|
|
401
|
+
**🛑 Read all of that as ONE property and never as a closed list of consequences:** where `ST-03`
|
|
402
|
+
is now recognised, **the document's own declaration decides the variant instead of its first
|
|
403
|
+
service segment**, and everything downstream follows from that single substitution. A first draft
|
|
404
|
+
of this entry published a census of three and a refuter measured it false by finding a fourth.
|
|
405
|
+
|
|
406
|
+
That is
|
|
407
|
+
the hazard a widening onto a new code carries, taken here in the opposite direction from the two
|
|
408
|
+
slices before it: they refused to narrow a fallback because the reader had no evidence beyond the
|
|
409
|
+
segments, and here the reader had the evidence in ST-03 and was ignoring it. Re-check any routing
|
|
410
|
+
driven off `submission.variant` for 837s you read on `0.0.13` or earlier.
|
|
411
|
+
|
|
412
|
+
**🛑 The `SVx` fallback is NOT narrowed.** First-wins still takes the first `SV1` / `SV2` / `SV3` in
|
|
413
|
+
the body, orphans included, on every document that still reaches it, and precedence is unchanged:
|
|
414
|
+
a caller `type` still wins ahead of ST-03, and ST-03 ahead of the segments. What changed is which
|
|
415
|
+
documents reach the fallback.
|
|
416
|
+
|
|
417
|
+
**It is a LIST of cited identifiers, never a pattern.** A reference outside the set, in a different
|
|
418
|
+
case, or carrying leading whitespace still falls through exactly as before. The set is **not
|
|
419
|
+
claimed exhaustive and no count of it is published**: both variant-resolution messages named the
|
|
420
|
+
three old keys literally, so both were wrong the moment the table was corrected, and neither
|
|
421
|
+
enumerates the set any more. A committed tripwire reds if any registry message quotes a TR3
|
|
422
|
+
identifier again.
|
|
423
|
+
|
|
424
|
+
**🩺 OPEN, and deliberately not fixed here: the EMIT side still stamps the old three.** `build837P`
|
|
425
|
+
/ `build837I` / `build837D` write `005010X222A2` / `005010X223A3` / `005010X224A2` and a caller
|
|
426
|
+
cannot override them, so **a partner that requires `005010X222A1` or `005010X223A2` will reject an
|
|
427
|
+
837 this builder emits.** Which published guide identifier a partner accepts is a partner fact
|
|
428
|
+
rather than a spec fact, and changing bytes this library already emitted would break the partners
|
|
429
|
+
it works with today. `KNOWN-LIMITATIONS.md` carries it as an open residual.
|
|
430
|
+
|
|
431
|
+
- **🩺 BREAKING (read model): every monetary, percent and quantity slot the readers used to fill with
|
|
432
|
+
a fabricated `X12Decimal.ZERO` is now `X12Decimal | undefined`** (`X12-837-SV-UNDEFINED-DECIMAL`).
|
|
433
|
+
Through `0.0.12` an **absent** `SV1-02` - a monetary field on an 837 claim service line - read back
|
|
434
|
+
as `X12Decimal.ZERO`, so a consumer could not tell "the sender stated zero" from "the sender stated
|
|
435
|
+
nothing" and this library presented the second as the first. Fourteen model slots carried that
|
|
436
|
+
fabrication: `X12Claim.totalCharge` (CLM-02), a service line's `charge` and `units` (SV1-02 /
|
|
437
|
+
SV2-03 / SV3-02 and SV1-04 / SV2-05 / SV3-06), `X12LineAdjudication.amountPaid` (SVD-02),
|
|
438
|
+
`X12RemitClaim.totalChargeAmount` / `totalPaymentAmount` / `patientResponsibilityAmount` (CLP-03 /
|
|
439
|
+
04 / 05), `X12RemitServiceLine.chargeAmount` / `paymentAmount` (SVC-02 / SVC-03),
|
|
440
|
+
`X12RemitPaymentHeader.totalActualPayment` and `X12PremiumPaymentHeader.totalPremiumAmount`
|
|
441
|
+
(BPR-02), `X12PremiumOpenItem.amountPaid` (RMR-04), `X12RemitAdjustment.amount` (a `CAS` triple,
|
|
442
|
+
read by both the 835 and the 837) and `X12RemitProviderAdjustment.amount` (a `PLB` pair).
|
|
443
|
+
|
|
444
|
+
**`undefined` means "this library decoded no value from that element", NOT "the element was
|
|
445
|
+
absent."** A present element holding bytes that do not decode lands there too, and
|
|
446
|
+
`X12_UNPARSEABLE_DECIMAL` at that `position.elementIndex` is the only thing that separates them.
|
|
447
|
+
Read it in exactly that direction. A **stated** zero is untouched: it still decodes, still reads
|
|
448
|
+
`0`, and keeps its lexical form, so `0.00` is still `0.00`.
|
|
449
|
+
|
|
450
|
+
**Migrating:** `x.toString()` becomes `x?.toString()` at every one of those slots, and each site is
|
|
451
|
+
a decision rather than a rewrite - an `undefined` amount is not zero, and a `?? 0` reinstates the
|
|
452
|
+
defect. If you posted cash off a slot that read `0` on `0.0.12` or earlier without also gating on
|
|
453
|
+
`.warnings`, re-read those files.
|
|
454
|
+
|
|
455
|
+
Two things this did **not** do. `elementDecimalOrZero` is a public export and is unchanged: it
|
|
456
|
+
still substitutes `X12Decimal.ZERO`, because that is its documented behaviour and a consumer
|
|
457
|
+
walking segments itself may still want it. What changed is that no reader in this library calls it
|
|
458
|
+
any more. And the 837's `X12_837_SERVICE_LINE_NOT_DECODED` is still the only thing that says WHY a
|
|
459
|
+
line's `charge` and `units` are empty: `undefined` alone does not separate "no `SVx` was decoded
|
|
460
|
+
onto this line" from "an `SVx` was decoded and carried no charge element".
|
|
461
|
+
|
|
462
|
+
- **🩺 BREAKING (emit side): `Build837ServiceLineSpec.units` is now required, and `build837P` /
|
|
463
|
+
`build837I` / `build837D` refuse a service line without it** (`X12-837-SV-UNDEFINED-DECIMAL`).
|
|
464
|
+
Through `0.0.12` an omitted `units` was emitted as the literal `0` into SV1-04 / SV2-05 / SV3-06,
|
|
465
|
+
so the builder stated a service unit count no caller supplied - the read-side fabrication above,
|
|
466
|
+
running the other way. Measured on `0.0.12`: a Professional line with no `units` emitted
|
|
467
|
+
`SV1*HC:99213*8500*UN*0*11**1~`. The refusal is `X12_837_BUILD_INVALID_SPEC`, naming the structural
|
|
468
|
+
locator (`billing[0].subscriber[0].claim[0]`) and no caller value.
|
|
469
|
+
|
|
470
|
+
**Refusing rather than emitting an empty element is the stance, and it is not new here:**
|
|
471
|
+
`build277` already refuses a service line without `SVC-07` on the same grounds. The parser is
|
|
472
|
+
liberal and the serializer conservative, and a count is not something a serializer may leave for
|
|
473
|
+
the receiver to guess at. A caller that supplies `units` is unaffected in every respect, including
|
|
474
|
+
one that supplies a zero, which is emitted because the caller did state it.
|
|
475
|
+
|
|
476
|
+
- **`build835` refuses a JS caller's `undefined` balance term as an invalid spec rather than throwing
|
|
477
|
+
an untyped `TypeError`** (`X12-837-SV-UNDEFINED-DECIMAL`). Unreachable from TypeScript: every term
|
|
478
|
+
of the three §1.10.2 invariants is a required `X12Decimal` on `Build835Spec`. Through `0.0.12` a JS
|
|
479
|
+
caller passing `undefined` reached `undefined.add` inside the balance guard. It is now
|
|
480
|
+
`X12_835_BUILD_INVALID_SPEC` with the registry text, and deliberately **not**
|
|
481
|
+
`X12_835_BUILD_BALANCE_MISMATCH`: nothing was measured out of balance, a required amount is simply
|
|
482
|
+
missing. Passing a raw `number` into a balance term still throws **an** untyped `TypeError` from
|
|
483
|
+
the same guard, so the documented dichotomy did not move; on some slots the message is now
|
|
484
|
+
`X12Decimal`'s own tampering text where it used to be `.add is not a function`, which the pinned
|
|
485
|
+
regex already admitted. Still disclosed in `KNOWN-LIMITATIONS.md`.
|
|
486
|
+
|
|
142
487
|
- **`KNOWN-LIMITATIONS.md` now ships in the published tarball** (`X12-837-LOOP-RESIDUALS`). Two
|
|
143
488
|
shipped warning messages, `X12_837_SERVICE_LINE_DROPPED` and
|
|
144
489
|
`X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX`, name that document ("see KNOWN-LIMITATIONS.md", each
|
|
@@ -556,6 +901,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
556
901
|
|
|
557
902
|
### Fixed
|
|
558
903
|
|
|
904
|
+
- **🩺 An `AMT` / `ADX` row whose amount decodes nothing is no longer dropped in silence**
|
|
905
|
+
(`X12-AMT-ADX-ABSENT-AMOUNT`). Reproduced on the base tree at `9db104b` across all four readers,
|
|
906
|
+
and the report it now raises is the `X12_AMOUNT_ROW_DROPPED` entry under **Added** above. The
|
|
907
|
+
filing that named this defect stated the bound precisely and it is repeated here because the wider
|
|
908
|
+
form is false: an **absent** amount element was silent on every channel, while a **present** one
|
|
909
|
+
holding undecodable bytes was already reported by `X12_UNPARSEABLE_DECIMAL` at its own
|
|
910
|
+
`elementIndex`. Both now also raise the new code; nothing moved off the old one.
|
|
911
|
+
|
|
912
|
+
- **A cookbook sentence said an undecoded 837 service line ships `charge` and `units` as `0`.** It
|
|
913
|
+
reads `undefined` on the same release that made the rest of that page true, so the page contradicted
|
|
914
|
+
the money spec-note it links to. Corrected in passing while sweeping that paragraph for the new
|
|
915
|
+
code. This is a documentation correction only; no behaviour changed with it.
|
|
916
|
+
|
|
917
|
+
- **🩺 A repeated `NM1*87` in one 837 Loop 2000A no longer fuses two pay-to addresses into one the
|
|
918
|
+
sender never sent** (`X12-PAY-TO-FUSION`). Reproduced at `0.0.12`: `payToAddress` is a bare
|
|
919
|
+
accumulator with no entity object to own it, cleared only at the next Loop 2000A `HL`. Every other
|
|
920
|
+
party gets a fresh object at its `NM1`, so a trailing `N3` / `N4` found `address === undefined` and
|
|
921
|
+
its write replaced; the two pay-to arms had nothing replaced under them and wrote onto whatever the
|
|
922
|
+
previous `NM1*87` left, `withLines` appending and `mergeAddress` falling back. Two `NM1*87`s each
|
|
923
|
+
with an `N3` and an `N4` read back **a street line from each of two addresses** plus a
|
|
924
|
+
`countryCode` taken off the **first** `N4`, on an address whose own `N4` names no country, with
|
|
925
|
+
`warnings: []`. Re-emitted through `build837P` that became a single Loop 2010AB stating a payment
|
|
926
|
+
destination no sender had stated.
|
|
927
|
+
|
|
928
|
+
**The fix is the missing object identity, not a clear.** Each `NM1*87` now opens a fresh
|
|
929
|
+
accumulator that the address arms read and write, so values from two occurrences can never meet.
|
|
930
|
+
|
|
931
|
+
**🛑 Two earlier remedies were refuted, both for one reason, and it is why the emit side was in
|
|
932
|
+
scope from the start: on this slot an emptied value is not a neutral absence, because the emit side
|
|
933
|
+
reads it.** `build837P/I/D` gates Loop 2010AB on `payToAddress !== undefined` and `emitAddress`
|
|
934
|
+
writes `N3` only for non-empty `lines` and `N4` only for a defined field. Clearing the accumulator
|
|
935
|
+
at the `NM1*87` therefore erased an address a repeat carrying no `N3` / `N4` did state, re-emitting
|
|
936
|
+
as **no pay-to loop at all**; a flag consumed by the first write after it did the same on a
|
|
937
|
+
valueless `N3` or `N4`, re-emitting as a **bare `NM1*87`**. Both are positive statements about
|
|
938
|
+
where a payment goes that no sender made. So the rule is stated in the emit side's own terms: the
|
|
939
|
+
current occurrence takes the slot **only when it states an address the emit would write at least
|
|
940
|
+
one segment for**, and an occurrence that states none leaves a stated one alone. The predicate is a
|
|
941
|
+
single shared module (`src/transactions/claim/address-segments.ts`) that `emitAddress` also asks,
|
|
942
|
+
so the reader and the writer cannot drift apart. This is the discipline the 835 already uses, where
|
|
943
|
+
the emit guard reuses the read side's own balance validators.
|
|
944
|
+
|
|
945
|
+
**A document with at most one `NM1*87` per Loop 2000A is byte-for-byte unaffected**, warning
|
|
946
|
+
channel included, and that is pinned both ways.
|
|
947
|
+
|
|
948
|
+
**🩺 The cost, stated rather than argued away: a repeat that states only part of an address now
|
|
949
|
+
re-emits only that part.** A second `NM1*87` followed by an `N4` and no `N3` reads back that `N4`
|
|
950
|
+
with `lines: []` and re-emits a Loop 2010AB with no `N3`. Keeping the earlier occurrence's street
|
|
951
|
+
lines there **is** the fusion: one sender's street under another sender's city. The loop is still
|
|
952
|
+
present, the warning is on the channel, and what is gone is the fabricated street.
|
|
953
|
+
`KNOWN-LIMITATIONS.md` records it.
|
|
954
|
+
|
|
559
955
|
- **🩺 `build278Request` / `build278Response` refuse a review whose HL-03 level code is outside `EV`
|
|
560
956
|
and `SS`, instead of emitting a review its own reader cannot decode**
|
|
561
957
|
(`REFUSAL-MESSAGE-PHI-ECHO`). `Build278ReviewSpec.levelCode` is the **one** caller-supplied HL-03 in
|
|
@@ -633,8 +1029,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
633
1029
|
are both the line charge, so reading a service segment into a line the walker never opened
|
|
634
1030
|
mis-reads money. Refusing to read is the safe half; doing it silently was the defect. **This says
|
|
635
1031
|
nothing about how the variant resolved**, and `KNOWN-LIMITATIONS.md` now discloses why: a caller's
|
|
636
|
-
`type` option wins first, and absent one, where `ST-03` names
|
|
637
|
-
|
|
1032
|
+
`type` option wins first, and absent one, where `ST-03` names no implementation convention this
|
|
1033
|
+
reader recognises, the reader falls back to the first `SVx` in the transaction body,
|
|
638
1034
|
orphans included, so a stray `SV2` re-types the whole submission. That is pre-existing behaviour, measured identical at `0.0.10`, and is deliberately
|
|
639
1035
|
not narrowed here.
|
|
640
1036
|
|