@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/dist/index.cjs
CHANGED
|
@@ -138,6 +138,7 @@ var WARNING_CODES = {
|
|
|
138
138
|
X12_UNKNOWN_HI_QUALIFIER: "X12_UNKNOWN_HI_QUALIFIER",
|
|
139
139
|
X12_MISSING_REQUIRED_LOOP: "X12_MISSING_REQUIRED_LOOP",
|
|
140
140
|
X12_837_UNKNOWN_VARIANT: "X12_837_UNKNOWN_VARIANT",
|
|
141
|
+
X12_837_AMBIGUOUS_VARIANT: "X12_837_AMBIGUOUS_VARIANT",
|
|
141
142
|
X12_UNKNOWN_CLAIM_STATUS_CATEGORY: "X12_UNKNOWN_CLAIM_STATUS_CATEGORY",
|
|
142
143
|
X12_UNKNOWN_CLAIM_STATUS: "X12_UNKNOWN_CLAIM_STATUS",
|
|
143
144
|
X12_834_UNKNOWN_MAINTENANCE_TYPE: "X12_834_UNKNOWN_MAINTENANCE_TYPE",
|
|
@@ -145,7 +146,12 @@ var WARNING_CODES = {
|
|
|
145
146
|
X12_837_SERVICE_LINE_NOT_DECODED: "X12_837_SERVICE_LINE_NOT_DECODED",
|
|
146
147
|
X12_837_SERVICE_LINE_DROPPED: "X12_837_SERVICE_LINE_DROPPED",
|
|
147
148
|
X12_837_SERVICE_SEGMENT_WITHOUT_LX: "X12_837_SERVICE_SEGMENT_WITHOUT_LX",
|
|
148
|
-
|
|
149
|
+
X12_837_SERVICE_SEGMENT_REPEATED: "X12_837_SERVICE_SEGMENT_REPEATED",
|
|
150
|
+
X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX: "X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX",
|
|
151
|
+
X12_837_PAY_TO_ADDRESS_REPEATED: "X12_837_PAY_TO_ADDRESS_REPEATED",
|
|
152
|
+
X12_835_BALANCE_NOT_EVALUABLE: "X12_835_BALANCE_NOT_EVALUABLE",
|
|
153
|
+
X12_AMOUNT_ROW_DROPPED: "X12_AMOUNT_ROW_DROPPED",
|
|
154
|
+
X12_STATED_AMOUNT_DISCARDED: "X12_STATED_AMOUNT_DISCARDED"
|
|
149
155
|
};
|
|
150
156
|
var CONTROL_NUMBER_PAIRS = {
|
|
151
157
|
/** ISA-13 against IEA-02. */
|
|
@@ -206,6 +212,9 @@ var WARNING_MESSAGES = {
|
|
|
206
212
|
X12_835_REMIT_BALANCE_MISMATCH_CLAIM: "835 balance invariant violated [CLP-04 + \u03A3(claim CAS + line CAS) == CLP-03]: the claim does not balance. Every amount is preserved verbatim on the model as an X12Decimal and is NEVER silently rebalanced.",
|
|
207
213
|
X12_835_REMIT_BALANCE_MISMATCH_SERVICE_LINE: "835 balance invariant violated [SVC-03 + \u03A3(line CAS) == SVC-02]: the service line does not balance. Every amount is preserved verbatim on the model as an X12Decimal and is NEVER silently rebalanced.",
|
|
208
214
|
X12_835_REMIT_BALANCE_MISMATCH_REMIT_TOTAL: "835 balance invariant violated [\u03A3(CLP-04) - \u03A3(PLB amounts) == BPR-02]: the remittance total does not balance. Every amount is preserved verbatim on the model as an X12Decimal and is NEVER silently rebalanced.",
|
|
215
|
+
X12_835_BALANCE_NOT_EVALUABLE_CLAIM: "835 balance invariant [CLP-04 + \u03A3(claim CAS + line CAS) == CLP-03] could not be evaluated: at least one term of it is `undefined` on the model, meaning this library decoded no value from that element. The equation is NOT reported as violated and NOT reported as satisfied, because an absent term has no value to compare - substituting 0 for it would be this library asserting a total the sender never sent. Which term is missing is read off the model, never from this message. Compare `X12_835_REMIT_BALANCE_MISMATCH`, which is raised only where every term decoded and the equation is genuinely out of balance.",
|
|
216
|
+
X12_835_BALANCE_NOT_EVALUABLE_SERVICE_LINE: "835 balance invariant [SVC-03 + \u03A3(line CAS) == SVC-02] could not be evaluated: at least one term of it is `undefined` on the model, meaning this library decoded no value from that element. The equation is NOT reported as violated and NOT reported as satisfied, because an absent term has no value to compare - substituting 0 for it would be this library asserting a total the sender never sent. Which term is missing is read off the model, never from this message. Compare `X12_835_REMIT_BALANCE_MISMATCH`, which is raised only where every term decoded and the equation is genuinely out of balance.",
|
|
217
|
+
X12_835_BALANCE_NOT_EVALUABLE_REMIT_TOTAL: "835 balance invariant [\u03A3(CLP-04) - \u03A3(PLB amounts) == BPR-02] could not be evaluated: at least one term of it is `undefined` on the model, meaning this library decoded no value from that element. The equation is NOT reported as violated and NOT reported as satisfied, because an absent term has no value to compare - substituting 0 for it would be this library asserting a total the sender never sent. Which term is missing is read off the model, never from this message. Compare `X12_835_REMIT_BALANCE_MISMATCH`, which is raised only where every term decoded and the equation is genuinely out of balance.",
|
|
209
218
|
X12_UNKNOWN_CARC: "Unknown CARC: the claim adjustment reason code is outside the bundled snapshot. The verbatim code is preserved on the adjustment; only its description is unavailable.",
|
|
210
219
|
X12_UNKNOWN_RARC: "Unknown RARC: the remittance advice remark code is outside the bundled snapshot. The verbatim code is preserved on the remark; only its description is unavailable.",
|
|
211
220
|
X12_HL_PARENT_MISMATCH: "HL-02 declares a parent id that no earlier HL in this transaction set emitted as its HL-01. The declared pointer is preserved verbatim and the hierarchy is NEVER silently re-numbered.",
|
|
@@ -215,15 +224,20 @@ var WARNING_MESSAGES = {
|
|
|
215
224
|
X12_MISSING_REQUIRED_LOOP_2000B: 'Missing required loop "2000B" (Subscriber HL): no Subscriber HL precedes the CLM.',
|
|
216
225
|
X12_MISSING_REQUIRED_LOOP_2010BA: 'Missing required loop "2010BA" (Subscriber Name): no Subscriber Name follows the Subscriber HL.',
|
|
217
226
|
X12_MISSING_REQUIRED_LOOP_2010BB: 'Missing required loop "2010BB" (Payer Name): no Payer Name follows the Subscriber HL.',
|
|
218
|
-
X12_837_UNKNOWN_VARIANT:
|
|
227
|
+
X12_837_UNKNOWN_VARIANT: "837 variant could not be resolved: ST-03's implementation convention reference is absent, empty, or not an 837 Technical Report Type 3 identifier this reader recognises, and no SVx service-line segment was present to fall back on. The set of references it recognises covers the professional, institutional and dental 837 guides HIPAA adopts at 45 CFR 162.1102 together with their published errata; it is NOT claimed to be exhaustive, and this message deliberately does not enumerate it, because a list here goes stale the moment a guide is added. A reference outside that set is not asserted to be invalid - all this code reports is that this reader could not turn it into a variant and had nothing to fall back on. The verbatim reference is preserved on the model; read it there rather than inferring what the sender meant.",
|
|
228
|
+
X12_837_AMBIGUOUS_VARIANT: "837 variant resolved from a service segment while the transaction body carries service segments of more than one variant: no caller `type` option was supplied and ST-03's implementation convention reference is absent, empty, or not an 837 Technical Report Type 3 identifier this reader recognises, so this reader fell back to the FIRST SV1 / SV2 / SV3 in the body, and a later one names a different variant. `submission.variant` is therefore a guess between contradictory evidence, and every claim and service line in the submission was read against it. Which service segment is the stray one is NOT decided here and is not derivable from the TR3s: this reader cannot tell a stray service segment from a conformant one, and the fallback takes the first regardless of whether any Loop 2400 was open at it, so an orphan segment decides the variant like any other. Read the bound literally, as a property of the RESOLUTION rather than of the document: this reports the fallback's own ambiguity, so a caller-supplied `type`, or an ST-03 this reader does turn into a variant, means no guess was made and this code is NOT raised however mixed the body is. Whatever else this reader raised on a document that reaches this code, it still raises, at the same position, and this one is added beside them. It is NOT a list of what else you will see, and it does not promise that any particular loss on such a document is reported at all. Re-read with the `type` option to decode the document against a variant you trust. The verbatim segments are preserved on the transaction set; read them there before acting on the submission's type.",
|
|
219
229
|
X12_UNKNOWN_CLAIM_STATUS_CATEGORY: "Unknown claim status category (CSCC): the STC composite's first component is outside the bundled snapshot. The verbatim code is preserved on the status; only its description is unavailable.",
|
|
220
230
|
X12_UNKNOWN_CLAIM_STATUS: "Unknown claim status code (CSC): the STC composite's second component is outside the bundled snapshot. The verbatim code is preserved on the status; only its description is unavailable.",
|
|
221
231
|
X12_834_UNKNOWN_MAINTENANCE_TYPE: "Unknown 834 maintenance type: the INS-03/HD-01 code is outside the bundled snapshot. The verbatim code is preserved on the enrollment and the action is NEVER inferred.",
|
|
222
232
|
X12_UNPARSEABLE_DECIMAL: "Unparseable decimal: the element at `position.elementIndex` held bytes this library could not decode as a decimal, so NO value was decoded from it. Whatever occupies that slot on the model, including 0 and undefined, is a stand-in and is NOT a value the sender supplied. The verbatim bytes are preserved on the segment; read them there before acting on the amount, quantity or percent.",
|
|
223
|
-
X12_837_SERVICE_LINE_NOT_DECODED: "837 service line with no decoded service segment: the Loop 2400 line opened at `position.segmentIndex` is followed by no SV1 / SV2 / SV3 matching the variant this submission resolved to, so NOTHING carried by the service segment was read. The line's `charge` and `units`
|
|
233
|
+
X12_837_SERVICE_LINE_NOT_DECODED: "837 service line with no decoded service segment: the Loop 2400 line opened at `position.segmentIndex` is followed by no SV1 / SV2 / SV3 matching the variant this submission resolved to, so NOTHING carried by the service segment was read. The line's `charge` and `units` are `undefined`, which is what this library puts on a decimal slot it decoded no value into; its procedure code, modifiers, unit of measure and place of service are equally undecoded. Two common causes: the line carries no SVx at all, or it carries one for a different 837 variant than ST-03 (or the caller's `type` option) named. Which side is wrong is NOT decided here, because a caller-supplied `type` can disagree with a perfectly conformant document. The verbatim segments are preserved on the transaction set; read them there before acting on the charge or the quantity.",
|
|
224
234
|
X12_837_SERVICE_LINE_DROPPED: "837 service line dropped from the typed model: the LX at `position.segmentIndex` opened no Loop 2400, so no line appears on any claim's `serviceLines` for it and the SV1 / SV2 / SV3 that followed - its charge, units, procedure code and modifiers - was read into nothing. Compare `X12_837_SERVICE_LINE_NOT_DECODED`, where the line IS on the model and only its service segment went unread. Two causes: no Loop 2300 (CLM) is open at this LX, so there is no claim to attach a line to; or the submission's variant is not one of P / I / D, so no variant-specific line shape could be built. Read `submission.variant` and `submission.claims` to tell them apart; do NOT expect `X12_837_UNKNOWN_VARIANT` alongside this code, because a caller-supplied `type` outside P / I / D reaches the second cause without it. Nothing is fabricated to stand in for the missing line and no claim is synthesized. What becomes of a DTP / AMT / NTE / REF that follows the dropped LX depends on the route and this message does not say; see KNOWN-LIMITATIONS.md. The verbatim segments are preserved on the transaction set; read them there before concluding the claim had no service lines.",
|
|
225
235
|
X12_837_SERVICE_SEGMENT_WITHOUT_LX: "837 service segment with no Loop 2400 to read it into: no service line was open at the SV1 / SV2 / SV3 at `position.segmentIndex`, so NOTHING it carries - its charge, units, procedure code, modifiers, unit of measure and place of service - was read. Read that literally: an LX may well appear earlier in the transaction, and what this reports is that none of them had opened a Loop 2400 still current at this segment. No line appears on any claim's `serviceLines` for it and nothing is fabricated to stand in. Compare the two codes anchored at an LX: `X12_837_SERVICE_LINE_DROPPED`, where an LX IS present and opened no line, and `X12_837_SERVICE_LINE_NOT_DECODED`, where the line is on the model and only its service segment went unread. Neither of those can report the SAME service segment as this code, because both are raised at an LX and this one only where no Loop 2400 is open; a document with several claims can still carry all three. This says NOTHING about how the submission's variant resolved: absent a caller-supplied `type` option, and where ST-03 names no known implementation convention, the reader falls back to the first SV1 / SV2 / SV3 in the transaction, and a segment reported here is eligible for that fallback like any other, so a stray one can decide the variant every line is read against. Read `submission.variant`. The verbatim segments are preserved on the transaction set; read them there before concluding the claim had no service lines.",
|
|
226
|
-
|
|
236
|
+
X12_837_SERVICE_SEGMENT_REPEATED: "837 service segment repeated inside one Loop 2400: the SV1 / SV2 / SV3 at `position.segmentIndex` is not the first service segment to arrive in the service line the LX before it opened. This reader's line carries ONE service segment's worth of slots, so it cannot hold both, and this code is the only thing that tells you the document sent more than one. It asserts nothing about what usage the TR3s give the segment; what it reports is that this reader has one set of those slots per line. What the line carries is what the LAST service segment MATCHING the submission's resolved variant wrote onto it, and only that. Such a segment writes every slot its kind writes - the charge, the units and the procedure code among them - so a matching one arriving second leaves NOTHING an earlier matching one wrote, including where the later one's charge or units element is ABSENT: there the line is left with undefined, which is what this library puts on a decimal slot it decoded no value into, over an amount the earlier one stated. A service segment of a kind that does NOT match the resolved variant is read into nothing at all - it overwrites no slot, and what it carries reaches no part of the typed model. Which of them the sender meant is NOT decided here and is not derivable from the TR3s: this reader cannot tell a stray service segment from a conformant one, and picking a winner would be inventing. Nothing is fabricated to stand in, no second line is synthesized - an LX is what opens a line and there is none here - and no earlier value is kept in a second slot. Read the bound literally, as a property of the READ: this reports a second service segment arriving while a Loop 2400 IS open, whether or not it decoded. It is disjoint from `X12_837_SERVICE_SEGMENT_WITHOUT_LX`, which requires that NO Loop 2400 be open where this one requires that one is, so the two can never name the same segment; read that as disjointness and NOT as a promise that a service segment outside an open Loop 2400 is always named by that code, because a service segment FOLLOWING an LX that opened no line at all is named by neither of them, the loss having already been reported at that LX by `X12_837_SERVICE_LINE_DROPPED`. `X12_837_SERVICE_LINE_NOT_DECODED`, anchored at the LX, reports that no service segment matching the variant decoded onto the line at all, so a document can carry both codes on different segments. It fires once per repeat, so three service segments in one Loop 2400 are two warnings, and the count resets with the line: a first service segment under a later LX is a first and never a repeat. It says nothing about how the submission's variant resolved; where the fallback decided that and the body names more than one, `X12_837_AMBIGUOUS_VARIANT` reports it separately. The verbatim segments are preserved on the transaction set; read them there before acting on the charge, the quantity or the procedure code.",
|
|
237
|
+
X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX: "837 entity segment read into nothing after a dropped LX: the N3 / N4 / PER / REF at `position.segmentIndex` arrived while no entity loop was open, because an earlier LX in this transaction opened no Loop 2400 (no CLM was open at it) and closed the entity loop that was current there. NOTHING this segment carries reached the model: no party's `address`, `contacts` or `references` was written from it, and no party, claim or line was synthesized to hold it. This is the code that names THAT loss; `X12_837_SERVICE_LINE_DROPPED` is raised at the LX itself and names the SERVICE LINE's loss, never an entity segment, so the two report different things about the same stretch of the document. Read the bound literally, because this code does NOT report every unattached entity segment: it reports one discarded after such an LX and only while nothing since has opened a new loop, so an N3 / N4 / PER / REF that reaches no party by any other route is still silent, and one arriving after a later NM1 is outside this code's scope, whether or not this reader surfaces that segment kind on that party. It reports that the segment reached NO party; it does not claim it would have reached one had the LX been absent, because this reader does not surface every one of these segment kinds on every party (a PER on a patient or a pay-to address, for one). Which party a segment following a stray LX belongs to is not derivable from the TR3s in either direction, so it is discarded rather than attributed: see KNOWN-LIMITATIONS.md. The verbatim segments are preserved on the transaction set; read them there before concluding a party had no address, no secondary identifier or no contact.",
|
|
238
|
+
X12_837_PAY_TO_ADDRESS_REPEATED: "837 pay-to address named more than once in one Loop 2000A: the NM1*87 at `position.segmentIndex` is not the first in this Loop 2000A, and the TR3s allow Loop 2010AB at most once there. The model has ONE pay-to address slot, so it cannot carry both, and this code is the only thing that tells you the document named more than one. The reader NEVER merges them: an N3 or N4 after this segment can no longer add a street line to, or fill a blank in, the address an earlier NM1*87 named, which is what this library did through 0.0.12 - it returned a fused address no sender sent, silently. What the slot carries instead is the LAST occurrence that stated an address of its own; an occurrence that states none - one carrying no N3 or N4 at all, or only a valueless N3 or N4 whose elements are empty - does NOT replace one that did, so nothing an earlier occurrence stated is blanked either. Where no occurrence states an address, the slot holds what the FIRST N3 or N4 to arrive under any of them produced, which may be an address with no elements at all, and which is not necessarily the first occurrence's: an occurrence that carries no N3 or N4 writes nothing, so a later one's valueless N3 is the first write and takes a slot still empty. Read that as the corner it is, and never restate it as the first OCCURRENCE winning. Which occurrence the sender meant is NOT decided here and is not derivable from the TR3s, and the losing occurrence's address is NOT on the model in any form. Read the bound literally: this reports repetition within one Loop 2000A, which is where the pay-to route lives, and an NM1*87 arriving while a CLM is open never reaches that route. The verbatim segments are preserved on the transaction set; read them there before acting on where a payment is to be sent.",
|
|
239
|
+
X12_AMOUNT_ROW_DROPPED: "Amount row dropped from the typed model: the AMT or ADX at `position.segmentIndex` carried no decodable amount (AMT-02, ADX-01), so NO row was built for it and the rest of the segment went with it - its qualifier or adjustment reason code, and any reference qualifier and id. Nothing is fabricated to stand in: a row is not built around a zero this library did not read, which is why the loss is reported rather than papered over. Two routes reach it and this code does not say which: the amount element was ABSENT, or it was present and held bytes that do not decode as a decimal. On this segment only the second route also raises `X12_UNPARSEABLE_DECIMAL`, carrying the failing element in its own `position.elementIndex`, so whether one is present at this `position.segmentIndex` is what separates them - and that code is unchanged by this one, which is raised alongside it rather than in place of it. Read the bound literally: this reports a row whose AMOUNT was read and decoded no value, and it is NOT a general report that an amount segment reached no model. A segment a reader discards before reading its amount is not on this channel, and neither is one whose amount decoded and then found no claim, service line, coverage or remittance open to attach the row to. An 820 RMR is not on it either, for its own reason: that row is dropped on open-item IDENTITY, RMR-01 and RMR-02 both empty, before the amount is read at all, so an RMR that states an open item and no amount keeps its row with amountPaid undefined while one that states an amount and no open item is dropped whole. That second case is a separate loss, and so is an 837 AMT that decoded while a Loop 2430 adjudication was open; `X12_STATED_AMOUNT_DISCARDED` reports both, and this code reports neither. The two can never name the same segment, because this one requires an amount element that decoded no value and that one requires the opposite. The verbatim segments are preserved on the transaction set; read them there before concluding the document stated no such amount.",
|
|
240
|
+
X12_STATED_AMOUNT_DISCARDED: "Stated amount discarded: the RMR or AMT at `position.segmentIndex` populated its amount element and this reader built NO row for it, for a reason that is not a failure to decode that amount. What the sender wrote reaches no part of the typed model, so an empty list of open items or amounts is not evidence the sender stated none. Read that as the only claim made here: this code does NOT assert the amount is decodable, and on the RMR route below it is raised without the bytes ever being DECODED, so they may be unreadable, blank-but-present, or a lone component separator. Two routes reach it and this code does not say which. First, an 820 RMR whose RMR-01 and RMR-02 are BOTH empty while a remittance loop is open: the open item is refused on identity before RMR-04 or RMR-05 is read at all, so a stated payment amount, a stated amount due and the payment action code beside them go together. Second, an 837 AMT arriving while a Loop 2430 line adjudication is open: AMT-02 decoded, and the v1 adjudication model carries no amount row to put it on, so the row is skipped. Compare `X12_AMOUNT_ROW_DROPPED`, which reports the other situation on the same segments: there the amount element decoded no value, so there was no row to build at all. The two can never name the same segment. Read the bound literally, as a property of the READ: this reports a segment whose amount element the sender populated, arriving while the loop that would carry its row was open. It does NOT report an AMT or ADX that reaches a reader with no such loop open, which stays silent and is recorded in KNOWN-LIMITATIONS.md. And on the RMR route it says nothing about whether that amount WOULD have decoded, because the row is refused before the decode is attempted, so no `X12_UNPARSEABLE_DECIMAL` accompanies it even where the bytes are unreadable. Nothing is fabricated to stand in. The verbatim segments are preserved on the transaction set; read them there before concluding the document stated no such amount."
|
|
227
241
|
};
|
|
228
242
|
var ALL_WARNING_MESSAGES = new Set(
|
|
229
243
|
Object.values(WARNING_MESSAGES)
|
|
@@ -245,6 +259,11 @@ var BALANCE_INVARIANT_MESSAGES = {
|
|
|
245
259
|
[BALANCE_INVARIANTS.SERVICE_LINE]: WARNING_MESSAGES.X12_835_REMIT_BALANCE_MISMATCH_SERVICE_LINE,
|
|
246
260
|
[BALANCE_INVARIANTS.REMIT_TOTAL]: WARNING_MESSAGES.X12_835_REMIT_BALANCE_MISMATCH_REMIT_TOTAL
|
|
247
261
|
};
|
|
262
|
+
var BALANCE_NOT_EVALUABLE_MESSAGES = {
|
|
263
|
+
[BALANCE_INVARIANTS.CLAIM]: WARNING_MESSAGES.X12_835_BALANCE_NOT_EVALUABLE_CLAIM,
|
|
264
|
+
[BALANCE_INVARIANTS.SERVICE_LINE]: WARNING_MESSAGES.X12_835_BALANCE_NOT_EVALUABLE_SERVICE_LINE,
|
|
265
|
+
[BALANCE_INVARIANTS.REMIT_TOTAL]: WARNING_MESSAGES.X12_835_BALANCE_NOT_EVALUABLE_REMIT_TOTAL
|
|
266
|
+
};
|
|
248
267
|
var REQUIRED_LOOP_MESSAGES = {
|
|
249
268
|
[REQUIRED_LOOPS.BILLING_PROVIDER_2000A]: WARNING_MESSAGES.X12_MISSING_REQUIRED_LOOP_2000A,
|
|
250
269
|
[REQUIRED_LOOPS.SUBSCRIBER_2000B]: WARNING_MESSAGES.X12_MISSING_REQUIRED_LOOP_2000B,
|
|
@@ -335,6 +354,13 @@ function remitBalanceMismatch(position, invariant) {
|
|
|
335
354
|
position
|
|
336
355
|
};
|
|
337
356
|
}
|
|
357
|
+
function balanceNotEvaluable(position, invariant) {
|
|
358
|
+
return {
|
|
359
|
+
code: WARNING_CODES.X12_835_BALANCE_NOT_EVALUABLE,
|
|
360
|
+
message: BALANCE_NOT_EVALUABLE_MESSAGES[invariant],
|
|
361
|
+
position
|
|
362
|
+
};
|
|
363
|
+
}
|
|
338
364
|
function unknownCarc(position) {
|
|
339
365
|
return {
|
|
340
366
|
code: WARNING_CODES.X12_UNKNOWN_CARC,
|
|
@@ -384,6 +410,13 @@ function unknown837Variant(position) {
|
|
|
384
410
|
position
|
|
385
411
|
};
|
|
386
412
|
}
|
|
413
|
+
function ambiguous837Variant(position) {
|
|
414
|
+
return {
|
|
415
|
+
code: WARNING_CODES.X12_837_AMBIGUOUS_VARIANT,
|
|
416
|
+
message: WARNING_MESSAGES.X12_837_AMBIGUOUS_VARIANT,
|
|
417
|
+
position
|
|
418
|
+
};
|
|
419
|
+
}
|
|
387
420
|
function serviceLineNotDecoded(position) {
|
|
388
421
|
return {
|
|
389
422
|
code: WARNING_CODES.X12_837_SERVICE_LINE_NOT_DECODED,
|
|
@@ -405,6 +438,13 @@ function serviceSegmentWithoutLx(position) {
|
|
|
405
438
|
position
|
|
406
439
|
};
|
|
407
440
|
}
|
|
441
|
+
function serviceSegmentRepeated(position) {
|
|
442
|
+
return {
|
|
443
|
+
code: WARNING_CODES.X12_837_SERVICE_SEGMENT_REPEATED,
|
|
444
|
+
message: WARNING_MESSAGES.X12_837_SERVICE_SEGMENT_REPEATED,
|
|
445
|
+
position
|
|
446
|
+
};
|
|
447
|
+
}
|
|
408
448
|
function entitySegmentDiscardedAfterLx(position) {
|
|
409
449
|
return {
|
|
410
450
|
code: WARNING_CODES.X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX,
|
|
@@ -412,6 +452,13 @@ function entitySegmentDiscardedAfterLx(position) {
|
|
|
412
452
|
position
|
|
413
453
|
};
|
|
414
454
|
}
|
|
455
|
+
function payToAddressRepeated(position) {
|
|
456
|
+
return {
|
|
457
|
+
code: WARNING_CODES.X12_837_PAY_TO_ADDRESS_REPEATED,
|
|
458
|
+
message: WARNING_MESSAGES.X12_837_PAY_TO_ADDRESS_REPEATED,
|
|
459
|
+
position
|
|
460
|
+
};
|
|
461
|
+
}
|
|
415
462
|
function unknownClaimStatusCategory(position) {
|
|
416
463
|
return {
|
|
417
464
|
code: WARNING_CODES.X12_UNKNOWN_CLAIM_STATUS_CATEGORY,
|
|
@@ -440,6 +487,20 @@ function unparseableDecimal(position) {
|
|
|
440
487
|
position
|
|
441
488
|
};
|
|
442
489
|
}
|
|
490
|
+
function amountRowDropped(position) {
|
|
491
|
+
return {
|
|
492
|
+
code: WARNING_CODES.X12_AMOUNT_ROW_DROPPED,
|
|
493
|
+
message: WARNING_MESSAGES.X12_AMOUNT_ROW_DROPPED,
|
|
494
|
+
position
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
function statedAmountDiscarded(position) {
|
|
498
|
+
return {
|
|
499
|
+
code: WARNING_CODES.X12_STATED_AMOUNT_DISCARDED,
|
|
500
|
+
message: WARNING_MESSAGES.X12_STATED_AMOUNT_DISCARDED,
|
|
501
|
+
position
|
|
502
|
+
};
|
|
503
|
+
}
|
|
443
504
|
|
|
444
505
|
// src/parser/release.ts
|
|
445
506
|
var RELEASE_CHAR = "?";
|
|
@@ -1900,11 +1961,13 @@ function isProcedureQualifier(qualifier) {
|
|
|
1900
1961
|
// src/transactions/remit/balance.ts
|
|
1901
1962
|
function checkClaimBalance(claim, position) {
|
|
1902
1963
|
const claimCasSum = sumAmounts(claim.adjustments);
|
|
1903
|
-
const lineCasSum =
|
|
1904
|
-
(
|
|
1905
|
-
X12Decimal.ZERO
|
|
1964
|
+
const lineCasSum = sumOptional(
|
|
1965
|
+
claim.serviceLines.map((sl) => sumAmounts(sl.adjustments))
|
|
1906
1966
|
);
|
|
1907
|
-
const computed = claim.totalPaymentAmount
|
|
1967
|
+
const computed = sumOptional([claim.totalPaymentAmount, claimCasSum, lineCasSum]);
|
|
1968
|
+
if (computed === void 0 || claim.totalChargeAmount === void 0) {
|
|
1969
|
+
return balanceNotEvaluable(position, BALANCE_INVARIANTS.CLAIM);
|
|
1970
|
+
}
|
|
1908
1971
|
if (computed.equals(claim.totalChargeAmount)) return void 0;
|
|
1909
1972
|
return remitBalanceMismatch(position, BALANCE_INVARIANTS.CLAIM);
|
|
1910
1973
|
}
|
|
@@ -1914,35 +1977,37 @@ function checkServiceLineBalance(claim, position) {
|
|
|
1914
1977
|
const line = claim.serviceLines[i];
|
|
1915
1978
|
if (line === void 0) continue;
|
|
1916
1979
|
const lineCasSum = sumAmounts(line.adjustments);
|
|
1917
|
-
const
|
|
1980
|
+
const linePosition = { ...position, segmentIndex: position.segmentIndex + i + 1 };
|
|
1981
|
+
const computed = sumOptional([line.paymentAmount, lineCasSum]);
|
|
1982
|
+
if (computed === void 0 || line.chargeAmount === void 0) {
|
|
1983
|
+
out.push(balanceNotEvaluable(linePosition, BALANCE_INVARIANTS.SERVICE_LINE));
|
|
1984
|
+
continue;
|
|
1985
|
+
}
|
|
1918
1986
|
if (computed.equals(line.chargeAmount)) continue;
|
|
1919
|
-
out.push(
|
|
1920
|
-
remitBalanceMismatch(
|
|
1921
|
-
{ ...position, segmentIndex: position.segmentIndex + i + 1 },
|
|
1922
|
-
BALANCE_INVARIANTS.SERVICE_LINE
|
|
1923
|
-
)
|
|
1924
|
-
);
|
|
1987
|
+
out.push(remitBalanceMismatch(linePosition, BALANCE_INVARIANTS.SERVICE_LINE));
|
|
1925
1988
|
}
|
|
1926
1989
|
return out;
|
|
1927
1990
|
}
|
|
1928
1991
|
function checkRemitTotalBalance(bpr02, claims, providerAdjustments, position) {
|
|
1929
|
-
const claimSum = claims.
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
(acc, p) => acc.add(p.amount),
|
|
1935
|
-
X12Decimal.ZERO
|
|
1936
|
-
);
|
|
1992
|
+
const claimSum = sumOptional(claims.map((c) => c.totalPaymentAmount));
|
|
1993
|
+
const plbSum = sumOptional(providerAdjustments.map((p) => p.amount));
|
|
1994
|
+
if (bpr02 === void 0 || claimSum === void 0 || plbSum === void 0) {
|
|
1995
|
+
return balanceNotEvaluable(position, BALANCE_INVARIANTS.REMIT_TOTAL);
|
|
1996
|
+
}
|
|
1937
1997
|
const computed = claimSum.subtract(plbSum);
|
|
1938
1998
|
if (computed.equals(bpr02)) return void 0;
|
|
1939
1999
|
return remitBalanceMismatch(position, BALANCE_INVARIANTS.REMIT_TOTAL);
|
|
1940
2000
|
}
|
|
1941
2001
|
function sumAmounts(adjustments) {
|
|
1942
|
-
return adjustments.
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
2002
|
+
return sumOptional(adjustments.map((a) => a.amount));
|
|
2003
|
+
}
|
|
2004
|
+
function sumOptional(terms) {
|
|
2005
|
+
let acc = X12Decimal.ZERO;
|
|
2006
|
+
for (const term of terms) {
|
|
2007
|
+
if (term === void 0) return void 0;
|
|
2008
|
+
acc = acc.add(term);
|
|
2009
|
+
}
|
|
2010
|
+
return acc;
|
|
1946
2011
|
}
|
|
1947
2012
|
|
|
1948
2013
|
// src/transactions/remit/get-835.ts
|
|
@@ -2108,7 +2173,10 @@ function get835(delimiters, tx) {
|
|
|
2108
2173
|
}
|
|
2109
2174
|
case "AMT": {
|
|
2110
2175
|
const amount = decodeAmt(seg, delimiters, sink);
|
|
2111
|
-
if (amount === void 0)
|
|
2176
|
+
if (amount === void 0) {
|
|
2177
|
+
warnings.push(amountRowDropped(position));
|
|
2178
|
+
break;
|
|
2179
|
+
}
|
|
2112
2180
|
if (currentServiceLine !== void 0) currentServiceLine.amounts.push(amount);
|
|
2113
2181
|
else if (currentClaim !== void 0) currentClaim.amounts.push(amount);
|
|
2114
2182
|
break;
|
|
@@ -2165,7 +2233,7 @@ function get835(delimiters, tx) {
|
|
|
2165
2233
|
function decodeBpr(seg, delimiters, sink) {
|
|
2166
2234
|
return Object.freeze({
|
|
2167
2235
|
transactionHandlingCode: elementValue(seg, 1, delimiters),
|
|
2168
|
-
totalActualPayment:
|
|
2236
|
+
totalActualPayment: elementDecimal(seg, 2, delimiters, sink),
|
|
2169
2237
|
creditDebitFlag: elementValue(seg, 3, delimiters),
|
|
2170
2238
|
method: elementValue(seg, 4, delimiters),
|
|
2171
2239
|
paymentFormatCode: elementOptional(seg, 5, delimiters),
|
|
@@ -2278,7 +2346,7 @@ function decodeCasAdjustments(seg, delimiters, warnings, position) {
|
|
|
2278
2346
|
groupCode,
|
|
2279
2347
|
reasonCode: code,
|
|
2280
2348
|
reasonDescription: entry?.description,
|
|
2281
|
-
amount
|
|
2349
|
+
amount,
|
|
2282
2350
|
quantity
|
|
2283
2351
|
})
|
|
2284
2352
|
);
|
|
@@ -2322,9 +2390,9 @@ function openClaim(seg, delimiters, clpSegmentIndex, sink) {
|
|
|
2322
2390
|
patientControlNumber: elementValue(seg, 1, delimiters),
|
|
2323
2391
|
claimStatusCode,
|
|
2324
2392
|
claimStatusDescription,
|
|
2325
|
-
totalChargeAmount:
|
|
2326
|
-
totalPaymentAmount:
|
|
2327
|
-
patientResponsibilityAmount:
|
|
2393
|
+
totalChargeAmount: elementDecimal(seg, 3, delimiters, sink),
|
|
2394
|
+
totalPaymentAmount: elementDecimal(seg, 4, delimiters, sink),
|
|
2395
|
+
patientResponsibilityAmount: elementDecimal(seg, 5, delimiters, sink),
|
|
2328
2396
|
claimFilingIndicatorCode: elementOptional(seg, 6, delimiters),
|
|
2329
2397
|
payerClaimControlNumber: elementOptional(seg, 7, delimiters),
|
|
2330
2398
|
facilityTypeCode,
|
|
@@ -2360,8 +2428,8 @@ function openServiceLine(seg, delimiters, sink) {
|
|
|
2360
2428
|
productServiceIdQualifier,
|
|
2361
2429
|
productServiceId,
|
|
2362
2430
|
modifiers,
|
|
2363
|
-
chargeAmount:
|
|
2364
|
-
paymentAmount:
|
|
2431
|
+
chargeAmount: elementDecimal(seg, 2, delimiters, sink),
|
|
2432
|
+
paymentAmount: elementDecimal(seg, 3, delimiters, sink),
|
|
2365
2433
|
revenueCode,
|
|
2366
2434
|
paidUnitsOfService,
|
|
2367
2435
|
originalUnitsOfService,
|
|
@@ -2406,7 +2474,7 @@ function decodePlb(seg, delimiters, sink) {
|
|
|
2406
2474
|
fiscalPeriodDate,
|
|
2407
2475
|
reasonCode: reasonCode ?? "",
|
|
2408
2476
|
subCode,
|
|
2409
|
-
amount
|
|
2477
|
+
amount
|
|
2410
2478
|
})
|
|
2411
2479
|
);
|
|
2412
2480
|
}
|
|
@@ -2421,7 +2489,7 @@ var EMPTY_ADDRESS = Object.freeze({
|
|
|
2421
2489
|
});
|
|
2422
2490
|
var EMPTY_HEADER = Object.freeze({
|
|
2423
2491
|
transactionHandlingCode: "",
|
|
2424
|
-
totalActualPayment:
|
|
2492
|
+
totalActualPayment: void 0,
|
|
2425
2493
|
creditDebitFlag: "",
|
|
2426
2494
|
method: "",
|
|
2427
2495
|
paymentFormatCode: void 0,
|
|
@@ -2841,12 +2909,24 @@ function enforceBalance(spec) {
|
|
|
2841
2909
|
for (const claim of claims) {
|
|
2842
2910
|
const claimWarn = checkClaimBalance(claim, position);
|
|
2843
2911
|
if (claimWarn !== void 0) {
|
|
2912
|
+
if (notEvaluable(claimWarn)) {
|
|
2913
|
+
throw new Remit835BuildError(
|
|
2914
|
+
REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_INVALID_SPEC,
|
|
2915
|
+
`build835 refuses a claim whose balance cannot be checked: ${claimWarn.message}`
|
|
2916
|
+
);
|
|
2917
|
+
}
|
|
2844
2918
|
throw new Remit835BuildError(
|
|
2845
2919
|
REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_BALANCE_MISMATCH,
|
|
2846
2920
|
`build835 refuses an out-of-balance claim: ${claimWarn.message}`
|
|
2847
2921
|
);
|
|
2848
2922
|
}
|
|
2849
2923
|
for (const lineWarn of checkServiceLineBalance(claim, position)) {
|
|
2924
|
+
if (notEvaluable(lineWarn)) {
|
|
2925
|
+
throw new Remit835BuildError(
|
|
2926
|
+
REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_INVALID_SPEC,
|
|
2927
|
+
`build835 refuses a service line whose balance cannot be checked: ${lineWarn.message}`
|
|
2928
|
+
);
|
|
2929
|
+
}
|
|
2850
2930
|
throw new Remit835BuildError(
|
|
2851
2931
|
REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_BALANCE_MISMATCH,
|
|
2852
2932
|
`build835 refuses an out-of-balance service line: ${lineWarn.message}`
|
|
@@ -2860,12 +2940,21 @@ function enforceBalance(spec) {
|
|
|
2860
2940
|
position
|
|
2861
2941
|
);
|
|
2862
2942
|
if (totalWarn !== void 0) {
|
|
2943
|
+
if (notEvaluable(totalWarn)) {
|
|
2944
|
+
throw new Remit835BuildError(
|
|
2945
|
+
REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_INVALID_SPEC,
|
|
2946
|
+
`build835 refuses a remit whose balance cannot be checked: ${totalWarn.message}`
|
|
2947
|
+
);
|
|
2948
|
+
}
|
|
2863
2949
|
throw new Remit835BuildError(
|
|
2864
2950
|
REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_BALANCE_MISMATCH,
|
|
2865
2951
|
`build835 refuses an out-of-balance remit: ${totalWarn.message}`
|
|
2866
2952
|
);
|
|
2867
2953
|
}
|
|
2868
2954
|
}
|
|
2955
|
+
function notEvaluable(warn) {
|
|
2956
|
+
return warn.code === WARNING_CODES.X12_835_BALANCE_NOT_EVALUABLE;
|
|
2957
|
+
}
|
|
2869
2958
|
function materializeAdjustment(spec) {
|
|
2870
2959
|
return {
|
|
2871
2960
|
groupCode: spec.groupCode,
|
|
@@ -3239,11 +3328,33 @@ function wireLookup(entries) {
|
|
|
3239
3328
|
return Object.freeze(table);
|
|
3240
3329
|
}
|
|
3241
3330
|
|
|
3331
|
+
// src/transactions/claim/address-segments.ts
|
|
3332
|
+
function emitsStreetLines(address) {
|
|
3333
|
+
return address.lines.length > 0;
|
|
3334
|
+
}
|
|
3335
|
+
function emitsGeographicFields(address) {
|
|
3336
|
+
return address.city !== void 0 || address.state !== void 0 || address.postalCode !== void 0 || address.countryCode !== void 0;
|
|
3337
|
+
}
|
|
3338
|
+
function statesAnAddress(address) {
|
|
3339
|
+
return emitsStreetLines(address) || emitsGeographicFields(address);
|
|
3340
|
+
}
|
|
3341
|
+
|
|
3242
3342
|
// src/transactions/claim/get-837.ts
|
|
3243
3343
|
var VARIANT_BY_ICR = wireLookup({
|
|
3344
|
+
// Health Care Claim: Professional (837).
|
|
3345
|
+
"005010X222": "P",
|
|
3346
|
+
"005010X222A1": "P",
|
|
3244
3347
|
"005010X222A2": "P",
|
|
3348
|
+
// Health Care Claim: Institutional (837).
|
|
3349
|
+
"005010X223": "I",
|
|
3350
|
+
"005010X223A1": "I",
|
|
3351
|
+
"005010X223A2": "I",
|
|
3245
3352
|
"005010X223A3": "I",
|
|
3246
|
-
|
|
3353
|
+
// Health Care Claim: Dental (837).
|
|
3354
|
+
"005010X224": "D",
|
|
3355
|
+
"005010X224A1": "D",
|
|
3356
|
+
"005010X224A2": "D",
|
|
3357
|
+
"005010X224A3": "D"
|
|
3247
3358
|
});
|
|
3248
3359
|
var VARIANT_BY_SV_SEGMENT = wireLookup({
|
|
3249
3360
|
SV1: "P",
|
|
@@ -3279,16 +3390,24 @@ function get837Claims(delimiters, tx, opts) {
|
|
|
3279
3390
|
const explicitType = opts?.type;
|
|
3280
3391
|
const variantFromIcr = implementationConventionReference !== void 0 && implementationConventionReference !== "" ? VARIANT_BY_ICR[implementationConventionReference] : void 0;
|
|
3281
3392
|
let variantFromSegment;
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3393
|
+
let serviceSegmentVariantsConflict = false;
|
|
3394
|
+
if (explicitType === void 0 && variantFromIcr === void 0) {
|
|
3395
|
+
for (const seg of body) {
|
|
3396
|
+
const fromSv = VARIANT_BY_SV_SEGMENT[seg.id];
|
|
3397
|
+
if (fromSv === void 0) continue;
|
|
3398
|
+
if (variantFromSegment === void 0) {
|
|
3399
|
+
variantFromSegment = fromSv;
|
|
3400
|
+
} else if (fromSv !== variantFromSegment) {
|
|
3401
|
+
serviceSegmentVariantsConflict = true;
|
|
3402
|
+
break;
|
|
3403
|
+
}
|
|
3287
3404
|
}
|
|
3288
3405
|
}
|
|
3289
3406
|
const variant = explicitType ?? variantFromIcr ?? variantFromSegment ?? "unknown";
|
|
3290
3407
|
if (variant === "unknown") {
|
|
3291
3408
|
warnings.push(unknown837Variant({ segmentIndex: 0, transactionIndex: 0 }));
|
|
3409
|
+
} else if (serviceSegmentVariantsConflict) {
|
|
3410
|
+
warnings.push(ambiguous837Variant({ segmentIndex: 0, transactionIndex: 0 }));
|
|
3292
3411
|
}
|
|
3293
3412
|
const hierarchies = [];
|
|
3294
3413
|
const hlIndex = /* @__PURE__ */ new Map();
|
|
@@ -3299,6 +3418,8 @@ function get837Claims(delimiters, tx, opts) {
|
|
|
3299
3418
|
let receiver;
|
|
3300
3419
|
let billingProvider;
|
|
3301
3420
|
let payToAddress;
|
|
3421
|
+
let pendingPayToAddress;
|
|
3422
|
+
let payToAddressNamed = false;
|
|
3302
3423
|
let payToPlan;
|
|
3303
3424
|
let currentSubscriberMember;
|
|
3304
3425
|
let currentPayer;
|
|
@@ -3335,6 +3456,10 @@ function get837Claims(delimiters, tx, opts) {
|
|
|
3335
3456
|
if (droppedLineReported) return;
|
|
3336
3457
|
warnings.push(serviceSegmentWithoutLx(position));
|
|
3337
3458
|
};
|
|
3459
|
+
const noteServiceSegment = (acc, position) => {
|
|
3460
|
+
if (acc.serviceSegmentSeen) warnings.push(serviceSegmentRepeated(position));
|
|
3461
|
+
acc.serviceSegmentSeen = true;
|
|
3462
|
+
};
|
|
3338
3463
|
const reportDiscardedEntitySegment = (position) => {
|
|
3339
3464
|
if (!entityLoopClosedByStrayLx) return;
|
|
3340
3465
|
warnings.push(entitySegmentDiscardedAfterLx(position));
|
|
@@ -3385,7 +3510,8 @@ function get837Claims(delimiters, tx, opts) {
|
|
|
3385
3510
|
currentPatientMember = { ...currentPatientMember, entity: next };
|
|
3386
3511
|
},
|
|
3387
3512
|
setPayToAddress: (next) => {
|
|
3388
|
-
|
|
3513
|
+
pendingPayToAddress = next;
|
|
3514
|
+
if (statesAnAddress(next) || payToAddress === void 0) payToAddress = next;
|
|
3389
3515
|
},
|
|
3390
3516
|
setOtherSubscriber: (next) => {
|
|
3391
3517
|
currentOtherSubscriber.otherSubscriber = next;
|
|
@@ -3400,7 +3526,10 @@ function get837Claims(delimiters, tx, opts) {
|
|
|
3400
3526
|
getCurrentSubscriber: () => currentSubscriberMember?.entity,
|
|
3401
3527
|
getCurrentPayer: () => currentPayer,
|
|
3402
3528
|
getCurrentPatient: () => currentPatientMember?.entity,
|
|
3403
|
-
|
|
3529
|
+
// Deliberately the CURRENT occurrence's accumulator, never the committed
|
|
3530
|
+
// slot: this is the getter both address arms build onto, and reading the
|
|
3531
|
+
// committed slot here is exactly what fused two `NM1*87`s.
|
|
3532
|
+
getCurrentPayToAddress: () => pendingPayToAddress,
|
|
3404
3533
|
getCurrentOtherSubscriber: () => currentOtherSubscriber?.otherSubscriber,
|
|
3405
3534
|
getCurrentOtherPayer: () => currentOtherSubscriber?.otherPayer
|
|
3406
3535
|
};
|
|
@@ -3422,6 +3551,8 @@ function get837Claims(delimiters, tx, opts) {
|
|
|
3422
3551
|
currentPatientHl = void 0;
|
|
3423
3552
|
billingProvider = void 0;
|
|
3424
3553
|
payToAddress = void 0;
|
|
3554
|
+
pendingPayToAddress = void 0;
|
|
3555
|
+
payToAddressNamed = false;
|
|
3425
3556
|
payToPlan = void 0;
|
|
3426
3557
|
context = { kind: "loop2000A" };
|
|
3427
3558
|
} else if (hl.levelCode === HL_LEVEL_CODES.SUBSCRIBER) {
|
|
@@ -3485,6 +3616,9 @@ function get837Claims(delimiters, tx, opts) {
|
|
|
3485
3616
|
billingProvider = entity;
|
|
3486
3617
|
activeEntity = { kind: "billingProvider" };
|
|
3487
3618
|
} else if (qualifier === NM1_QUALIFIERS.PAY_TO_ADDRESS && context.kind === "loop2000A") {
|
|
3619
|
+
if (payToAddressNamed) warnings.push(payToAddressRepeated(position));
|
|
3620
|
+
payToAddressNamed = true;
|
|
3621
|
+
pendingPayToAddress = void 0;
|
|
3488
3622
|
activeEntity = { kind: "payToAddress" };
|
|
3489
3623
|
} else if (qualifier === NM1_QUALIFIERS.PAY_TO_PLAN && context.kind === "loop2000A" && variant === "I") {
|
|
3490
3624
|
payToPlan = entity;
|
|
@@ -3618,8 +3752,14 @@ function get837Claims(delimiters, tx, opts) {
|
|
|
3618
3752
|
}
|
|
3619
3753
|
case "AMT": {
|
|
3620
3754
|
const amount = decodeAmt2(seg, delimiters, sink);
|
|
3621
|
-
if (amount === void 0)
|
|
3622
|
-
|
|
3755
|
+
if (amount === void 0) {
|
|
3756
|
+
warnings.push(amountRowDropped(position));
|
|
3757
|
+
break;
|
|
3758
|
+
}
|
|
3759
|
+
if (currentAdjudication !== void 0) {
|
|
3760
|
+
warnings.push(statedAmountDiscarded(position));
|
|
3761
|
+
break;
|
|
3762
|
+
}
|
|
3623
3763
|
if (currentServiceLine !== void 0) currentServiceLine.amounts.push(amount);
|
|
3624
3764
|
else if (currentClaim !== void 0) currentClaim.amounts.push(amount);
|
|
3625
3765
|
break;
|
|
@@ -3662,19 +3802,41 @@ function get837Claims(delimiters, tx, opts) {
|
|
|
3662
3802
|
// before the walk and scans every SVx in the body: an orphan segment
|
|
3663
3803
|
// is eligible for that fallback like any other, which is pre-existing
|
|
3664
3804
|
// and disclosed in KNOWN-LIMITATIONS.md rather than changed here.
|
|
3805
|
+
//
|
|
3806
|
+
// A SECOND service segment inside an already-open Loop 2400 is the
|
|
3807
|
+
// other silence on this segment, and it is a worse one: the line IS
|
|
3808
|
+
// open, so the repeat decodes, and every decoder writes all of the
|
|
3809
|
+
// slots its kind writes. `SV1*HC:99213*8500*…` followed by
|
|
3810
|
+
// `SV1*HC:99999*12*…` left ONE line reading `charge` 12 and
|
|
3811
|
+
// `procedureCode` 99999 through `0.0.13`, with `warnings: []` - money
|
|
3812
|
+
// and a CPT code replaced on the model, on no channel at all.
|
|
3813
|
+
// `noteServiceSegment` reports that, at the repeat itself, and changes
|
|
3814
|
+
// NOTHING about which segment wins.
|
|
3665
3815
|
case "SV1": {
|
|
3666
|
-
if (currentServiceLine
|
|
3667
|
-
|
|
3816
|
+
if (currentServiceLine === void 0) {
|
|
3817
|
+
reportOrphanServiceSegment(position);
|
|
3818
|
+
break;
|
|
3819
|
+
}
|
|
3820
|
+
noteServiceSegment(currentServiceLine, position);
|
|
3821
|
+
decodeSv1(currentServiceLine, seg, delimiters, sink);
|
|
3668
3822
|
break;
|
|
3669
3823
|
}
|
|
3670
3824
|
case "SV2": {
|
|
3671
|
-
if (currentServiceLine
|
|
3672
|
-
|
|
3825
|
+
if (currentServiceLine === void 0) {
|
|
3826
|
+
reportOrphanServiceSegment(position);
|
|
3827
|
+
break;
|
|
3828
|
+
}
|
|
3829
|
+
noteServiceSegment(currentServiceLine, position);
|
|
3830
|
+
decodeSv2(currentServiceLine, seg, delimiters, sink);
|
|
3673
3831
|
break;
|
|
3674
3832
|
}
|
|
3675
3833
|
case "SV3": {
|
|
3676
|
-
if (currentServiceLine
|
|
3677
|
-
|
|
3834
|
+
if (currentServiceLine === void 0) {
|
|
3835
|
+
reportOrphanServiceSegment(position);
|
|
3836
|
+
break;
|
|
3837
|
+
}
|
|
3838
|
+
noteServiceSegment(currentServiceLine, position);
|
|
3839
|
+
decodeSv3(currentServiceLine, seg, delimiters, sink);
|
|
3678
3840
|
break;
|
|
3679
3841
|
}
|
|
3680
3842
|
case "TOO": {
|
|
@@ -3909,7 +4071,7 @@ function decodeCas(seg, delimiters, warnings, position) {
|
|
|
3909
4071
|
groupCode,
|
|
3910
4072
|
reasonCode: code,
|
|
3911
4073
|
reasonDescription: entry?.description,
|
|
3912
|
-
amount
|
|
4074
|
+
amount,
|
|
3913
4075
|
quantity
|
|
3914
4076
|
})
|
|
3915
4077
|
);
|
|
@@ -3955,7 +4117,7 @@ function openClaim2(seg, delimiters, ctx, sink) {
|
|
|
3955
4117
|
payer,
|
|
3956
4118
|
patient,
|
|
3957
4119
|
claimId: elementValue(seg, 1, delimiters),
|
|
3958
|
-
totalCharge:
|
|
4120
|
+
totalCharge: elementDecimal(seg, 2, delimiters, sink),
|
|
3959
4121
|
placeOfServiceCode: componentOptional(seg, 5, 1, delimiters),
|
|
3960
4122
|
facilityCodeQualifier: componentOptional(seg, 5, 2, delimiters),
|
|
3961
4123
|
claimFrequencyCode: componentOptional(seg, 5, 3, delimiters),
|
|
@@ -3981,8 +4143,9 @@ function openServiceLine2(seg, delimiters, variant, position) {
|
|
|
3981
4143
|
lineNumber,
|
|
3982
4144
|
position,
|
|
3983
4145
|
serviceSegmentDecoded: false,
|
|
3984
|
-
|
|
3985
|
-
|
|
4146
|
+
serviceSegmentSeen: false,
|
|
4147
|
+
charge: void 0,
|
|
4148
|
+
units: void 0,
|
|
3986
4149
|
unitOfMeasure: void 0,
|
|
3987
4150
|
placeOfServiceCode: void 0,
|
|
3988
4151
|
dates: [],
|
|
@@ -4043,9 +4206,9 @@ function decodeSv1(acc, seg, delimiters, sink) {
|
|
|
4043
4206
|
if (m !== void 0) mods.push(m);
|
|
4044
4207
|
}
|
|
4045
4208
|
acc.modifiers = mods;
|
|
4046
|
-
acc.charge =
|
|
4209
|
+
acc.charge = elementDecimal(seg, 2, delimiters, sink);
|
|
4047
4210
|
acc.unitOfMeasure = elementOptional(seg, 3, delimiters);
|
|
4048
|
-
acc.units =
|
|
4211
|
+
acc.units = elementDecimal(seg, 4, delimiters, sink);
|
|
4049
4212
|
acc.placeOfServiceCode = elementOptional(seg, 5, delimiters);
|
|
4050
4213
|
const pointers = [];
|
|
4051
4214
|
for (let p = 1; p <= 4; p += 1) {
|
|
@@ -4069,9 +4232,9 @@ function decodeSv2(acc, seg, delimiters, sink) {
|
|
|
4069
4232
|
if (m !== void 0) mods.push(m);
|
|
4070
4233
|
}
|
|
4071
4234
|
acc.modifiers = mods;
|
|
4072
|
-
acc.charge =
|
|
4235
|
+
acc.charge = elementDecimal(seg, 3, delimiters, sink);
|
|
4073
4236
|
acc.unitOfMeasure = elementOptional(seg, 4, delimiters);
|
|
4074
|
-
acc.units =
|
|
4237
|
+
acc.units = elementDecimal(seg, 5, delimiters, sink);
|
|
4075
4238
|
acc.serviceLineRate = elementDecimal(seg, 6, delimiters, sink);
|
|
4076
4239
|
acc.nonCoveredCharge = elementDecimal(seg, 7, delimiters, sink);
|
|
4077
4240
|
}
|
|
@@ -4086,7 +4249,7 @@ function decodeSv3(acc, seg, delimiters, sink) {
|
|
|
4086
4249
|
if (m !== void 0) mods.push(m);
|
|
4087
4250
|
}
|
|
4088
4251
|
acc.modifiers = mods;
|
|
4089
|
-
acc.charge =
|
|
4252
|
+
acc.charge = elementDecimal(seg, 2, delimiters, sink);
|
|
4090
4253
|
acc.placeOfServiceCode = elementOptional(seg, 3, delimiters);
|
|
4091
4254
|
const cavities = [];
|
|
4092
4255
|
for (let p = 1; p <= 5; p += 1) {
|
|
@@ -4095,12 +4258,12 @@ function decodeSv3(acc, seg, delimiters, sink) {
|
|
|
4095
4258
|
}
|
|
4096
4259
|
acc.oralCavityArea = cavities;
|
|
4097
4260
|
acc.prosthesisCrownInlayCode = elementOptional(seg, 5, delimiters);
|
|
4098
|
-
acc.units =
|
|
4261
|
+
acc.units = elementDecimal(seg, 6, delimiters, sink);
|
|
4099
4262
|
}
|
|
4100
4263
|
function openAdjudication(seg, delimiters, sink) {
|
|
4101
4264
|
return {
|
|
4102
4265
|
otherPayerId: elementValue(seg, 1, delimiters),
|
|
4103
|
-
amountPaid:
|
|
4266
|
+
amountPaid: elementDecimal(seg, 2, delimiters, sink),
|
|
4104
4267
|
procedureQualifier: componentOptional(seg, 3, 1, delimiters),
|
|
4105
4268
|
procedureCode: componentOptional(seg, 3, 2, delimiters),
|
|
4106
4269
|
paidUnits: elementDecimal(seg, 5, delimiters, sink),
|
|
@@ -4629,6 +4792,12 @@ function enforceClaim(variant, claim, locator) {
|
|
|
4629
4792
|
`build837${variant}: claim at ${locator} has a service line at index ${String(l)} with an empty ${line.variant === "I" ? "revenue" : "procedure"} code.`
|
|
4630
4793
|
);
|
|
4631
4794
|
}
|
|
4795
|
+
if (line.units === void 0) {
|
|
4796
|
+
throw new Claim837BuildError(
|
|
4797
|
+
CLAIM_837_BUILD_ERROR_CODES.X12_837_BUILD_INVALID_SPEC,
|
|
4798
|
+
`build837${variant}: claim at ${locator} has a service line at index ${String(l)} with no units. This builder will not emit a service unit count the caller did not supply; set units to an X12Decimal rather than leaving it undefined.`
|
|
4799
|
+
);
|
|
4800
|
+
}
|
|
4632
4801
|
}
|
|
4633
4802
|
}
|
|
4634
4803
|
function emitBillingProvider(variant, billing, body, ctx, counter) {
|
|
@@ -4743,7 +4912,7 @@ function emitOtherSubscriber(other, body, ctx) {
|
|
|
4743
4912
|
}
|
|
4744
4913
|
function emitServiceLine2(variant, line, lineNumber, body, ctx) {
|
|
4745
4914
|
body.push(ctx.seg(["LX", ctx.esc(line.lineNumber ?? String(lineNumber))]));
|
|
4746
|
-
const units =
|
|
4915
|
+
const units = decStr(line.units);
|
|
4747
4916
|
if (line.variant === "P") {
|
|
4748
4917
|
const proc = ctx.comp([line.procedureQualifier, line.procedureCode, ...line.modifiers ?? []]);
|
|
4749
4918
|
const pointers = ctx.comp(line.diagnosisPointers ?? []);
|
|
@@ -4895,10 +5064,10 @@ function emitEntity(entity, body, ctx) {
|
|
|
4895
5064
|
for (const ref of entity.references ?? []) body.push(emitRef2(ref, ctx));
|
|
4896
5065
|
}
|
|
4897
5066
|
function emitAddress(address, body, ctx) {
|
|
4898
|
-
if (address
|
|
5067
|
+
if (emitsStreetLines(address)) {
|
|
4899
5068
|
body.push(ctx.seg(["N3", ...address.lines.map(ctx.esc)]));
|
|
4900
5069
|
}
|
|
4901
|
-
if (address
|
|
5070
|
+
if (emitsGeographicFields(address)) {
|
|
4902
5071
|
body.push(
|
|
4903
5072
|
ctx.seg([
|
|
4904
5073
|
"N4",
|
|
@@ -6892,13 +7061,18 @@ function get820Payments(delimiters, tx) {
|
|
|
6892
7061
|
case "RMR": {
|
|
6893
7062
|
if (current === void 0) break;
|
|
6894
7063
|
const item = decodeRmr(seg, delimiters, sink);
|
|
6895
|
-
if (item !== void 0)
|
|
7064
|
+
if (item !== void 0) {
|
|
7065
|
+
current.openItems.push(item);
|
|
7066
|
+
break;
|
|
7067
|
+
}
|
|
7068
|
+
if (statesAnAmount(seg, delimiters)) warnings.push(statedAmountDiscarded(position));
|
|
6896
7069
|
break;
|
|
6897
7070
|
}
|
|
6898
7071
|
case "ADX": {
|
|
6899
7072
|
if (current === void 0) break;
|
|
6900
7073
|
const adjustment = decodeAdx(seg, delimiters, sink);
|
|
6901
|
-
if (adjustment
|
|
7074
|
+
if (adjustment === void 0) warnings.push(amountRowDropped(position));
|
|
7075
|
+
else current.adjustments.push(adjustment);
|
|
6902
7076
|
break;
|
|
6903
7077
|
}
|
|
6904
7078
|
case "REF": {
|
|
@@ -6952,7 +7126,7 @@ function freezeRemittance(acc) {
|
|
|
6952
7126
|
function decodeBpr2(seg, delimiters, sink) {
|
|
6953
7127
|
return Object.freeze({
|
|
6954
7128
|
transactionHandlingCode: elementValue(seg, 1, delimiters),
|
|
6955
|
-
totalPremiumAmount:
|
|
7129
|
+
totalPremiumAmount: elementDecimal(seg, 2, delimiters, sink),
|
|
6956
7130
|
creditDebitFlag: elementValue(seg, 3, delimiters),
|
|
6957
7131
|
method: elementValue(seg, 4, delimiters),
|
|
6958
7132
|
paymentFormatCode: elementOptional(seg, 5, delimiters),
|
|
@@ -7020,6 +7194,9 @@ function decodeNm13(seg, delimiters) {
|
|
|
7020
7194
|
idCode: elementOptional(seg, 9, delimiters)
|
|
7021
7195
|
});
|
|
7022
7196
|
}
|
|
7197
|
+
function statesAnAmount(seg, delimiters) {
|
|
7198
|
+
return elementValue(seg, 4, delimiters) !== "" || elementValue(seg, 5, delimiters) !== "";
|
|
7199
|
+
}
|
|
7023
7200
|
function decodeRmr(seg, delimiters, sink) {
|
|
7024
7201
|
const qualifier = elementValue(seg, 1, delimiters);
|
|
7025
7202
|
const referenceId = elementValue(seg, 2, delimiters);
|
|
@@ -7028,7 +7205,7 @@ function decodeRmr(seg, delimiters, sink) {
|
|
|
7028
7205
|
qualifier,
|
|
7029
7206
|
referenceId,
|
|
7030
7207
|
paymentActionCode: elementOptional(seg, 3, delimiters),
|
|
7031
|
-
amountPaid:
|
|
7208
|
+
amountPaid: elementDecimal(seg, 4, delimiters, sink),
|
|
7032
7209
|
amountDue: elementDecimal(seg, 5, delimiters, sink)
|
|
7033
7210
|
});
|
|
7034
7211
|
}
|
|
@@ -7051,7 +7228,7 @@ var EMPTY_ADDRESS3 = Object.freeze({
|
|
|
7051
7228
|
});
|
|
7052
7229
|
var EMPTY_HEADER2 = Object.freeze({
|
|
7053
7230
|
transactionHandlingCode: "",
|
|
7054
|
-
totalPremiumAmount:
|
|
7231
|
+
totalPremiumAmount: void 0,
|
|
7055
7232
|
creditDebitFlag: "",
|
|
7056
7233
|
method: "",
|
|
7057
7234
|
paymentFormatCode: void 0,
|
|
@@ -7610,7 +7787,8 @@ async function* get834Enrollments(delimiters, tx) {
|
|
|
7610
7787
|
if (currentCoverage === void 0) break;
|
|
7611
7788
|
const sink = { warnings: current.warnings, position };
|
|
7612
7789
|
const amount = decodeAmt3(seg, delimiters, sink);
|
|
7613
|
-
if (amount
|
|
7790
|
+
if (amount === void 0) current.warnings.push(amountRowDropped(position));
|
|
7791
|
+
else currentCoverage.amounts.push(amount);
|
|
7614
7792
|
break;
|
|
7615
7793
|
}
|
|
7616
7794
|
}
|
|
@@ -10192,7 +10370,7 @@ var profiles = Object.freeze({
|
|
|
10192
10370
|
});
|
|
10193
10371
|
|
|
10194
10372
|
// src/index.ts
|
|
10195
|
-
var VERSION = "0.0.
|
|
10373
|
+
var VERSION = "0.0.14";
|
|
10196
10374
|
|
|
10197
10375
|
exports.ACK_BUILD_ERROR_CODES = ACK_BUILD_ERROR_CODES;
|
|
10198
10376
|
exports.ALL_WARNING_MESSAGES = ALL_WARNING_MESSAGES;
|
|
@@ -10300,6 +10478,9 @@ exports.X12ParseError = X12ParseError;
|
|
|
10300
10478
|
exports.X12ProfileError = X12ProfileError;
|
|
10301
10479
|
exports.X12_ACK_DISPOSITION_CODES = X12_ACK_DISPOSITION_CODES;
|
|
10302
10480
|
exports.X12_BUILD_ERROR_CODES = X12_BUILD_ERROR_CODES;
|
|
10481
|
+
exports.ambiguous837Variant = ambiguous837Variant;
|
|
10482
|
+
exports.amountRowDropped = amountRowDropped;
|
|
10483
|
+
exports.balanceNotEvaluable = balanceNotEvaluable;
|
|
10303
10484
|
exports.build271 = build271;
|
|
10304
10485
|
exports.build277 = build277;
|
|
10305
10486
|
exports.build277CA = build277CA;
|
|
@@ -10366,6 +10547,7 @@ exports.parse999 = parse999;
|
|
|
10366
10547
|
exports.parseTA1 = parseTA1;
|
|
10367
10548
|
exports.parseX12 = parseX12;
|
|
10368
10549
|
exports.partitionWarnings = partitionWarnings;
|
|
10550
|
+
exports.payToAddressRepeated = payToAddressRepeated;
|
|
10369
10551
|
exports.pre005010 = pre005010;
|
|
10370
10552
|
exports.profiles = profiles;
|
|
10371
10553
|
exports.readElementDecimal = readElementDecimal;
|
|
@@ -10376,8 +10558,10 @@ exports.segmentCountMismatch = segmentCountMismatch;
|
|
|
10376
10558
|
exports.serializeX12 = serializeX12;
|
|
10377
10559
|
exports.serviceLineDropped = serviceLineDropped;
|
|
10378
10560
|
exports.serviceLineNotDecoded = serviceLineNotDecoded;
|
|
10561
|
+
exports.serviceSegmentRepeated = serviceSegmentRepeated;
|
|
10379
10562
|
exports.serviceSegmentWithoutLx = serviceSegmentWithoutLx;
|
|
10380
10563
|
exports.setDefaultProfile = setDefaultProfile;
|
|
10564
|
+
exports.statedAmountDiscarded = statedAmountDiscarded;
|
|
10381
10565
|
exports.trailingGarbage = trailingGarbage;
|
|
10382
10566
|
exports.transactionCountMismatch = transactionCountMismatch;
|
|
10383
10567
|
exports.unescapeRelease = unescapeRelease;
|