@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/dist/index.mjs CHANGED
@@ -136,6 +136,7 @@ var WARNING_CODES = {
136
136
  X12_UNKNOWN_HI_QUALIFIER: "X12_UNKNOWN_HI_QUALIFIER",
137
137
  X12_MISSING_REQUIRED_LOOP: "X12_MISSING_REQUIRED_LOOP",
138
138
  X12_837_UNKNOWN_VARIANT: "X12_837_UNKNOWN_VARIANT",
139
+ X12_837_AMBIGUOUS_VARIANT: "X12_837_AMBIGUOUS_VARIANT",
139
140
  X12_UNKNOWN_CLAIM_STATUS_CATEGORY: "X12_UNKNOWN_CLAIM_STATUS_CATEGORY",
140
141
  X12_UNKNOWN_CLAIM_STATUS: "X12_UNKNOWN_CLAIM_STATUS",
141
142
  X12_834_UNKNOWN_MAINTENANCE_TYPE: "X12_834_UNKNOWN_MAINTENANCE_TYPE",
@@ -143,7 +144,12 @@ var WARNING_CODES = {
143
144
  X12_837_SERVICE_LINE_NOT_DECODED: "X12_837_SERVICE_LINE_NOT_DECODED",
144
145
  X12_837_SERVICE_LINE_DROPPED: "X12_837_SERVICE_LINE_DROPPED",
145
146
  X12_837_SERVICE_SEGMENT_WITHOUT_LX: "X12_837_SERVICE_SEGMENT_WITHOUT_LX",
146
- X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX: "X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX"
147
+ X12_837_SERVICE_SEGMENT_REPEATED: "X12_837_SERVICE_SEGMENT_REPEATED",
148
+ X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX: "X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX",
149
+ X12_837_PAY_TO_ADDRESS_REPEATED: "X12_837_PAY_TO_ADDRESS_REPEATED",
150
+ X12_835_BALANCE_NOT_EVALUABLE: "X12_835_BALANCE_NOT_EVALUABLE",
151
+ X12_AMOUNT_ROW_DROPPED: "X12_AMOUNT_ROW_DROPPED",
152
+ X12_STATED_AMOUNT_DISCARDED: "X12_STATED_AMOUNT_DISCARDED"
147
153
  };
148
154
  var CONTROL_NUMBER_PAIRS = {
149
155
  /** ISA-13 against IEA-02. */
@@ -204,6 +210,9 @@ var WARNING_MESSAGES = {
204
210
  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.",
205
211
  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.",
206
212
  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.",
213
+ 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.",
214
+ 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.",
215
+ 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.",
207
216
  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.",
208
217
  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.",
209
218
  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.",
@@ -213,15 +222,20 @@ var WARNING_MESSAGES = {
213
222
  X12_MISSING_REQUIRED_LOOP_2000B: 'Missing required loop "2000B" (Subscriber HL): no Subscriber HL precedes the CLM.',
214
223
  X12_MISSING_REQUIRED_LOOP_2010BA: 'Missing required loop "2010BA" (Subscriber Name): no Subscriber Name follows the Subscriber HL.',
215
224
  X12_MISSING_REQUIRED_LOOP_2010BB: 'Missing required loop "2010BB" (Payer Name): no Payer Name follows the Subscriber HL.',
216
- X12_837_UNKNOWN_VARIANT: `837 variant could not be resolved: ST-03's implementation convention reference is not one of "005010X222A2" / "005010X223A3" / "005010X224A2", and no SVx service-line segment was present to fall back on. The verbatim reference is preserved on the model.`,
225
+ 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.",
226
+ 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.",
217
227
  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.",
218
228
  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.",
219
229
  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.",
220
230
  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.",
221
- 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` hold 0 as a stand-in and are NOT values the sender supplied; 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.",
231
+ 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.",
222
232
  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.",
223
233
  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.",
224
- 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."
234
+ 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.",
235
+ 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.",
236
+ 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.",
237
+ 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.",
238
+ 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."
225
239
  };
226
240
  var ALL_WARNING_MESSAGES = new Set(
227
241
  Object.values(WARNING_MESSAGES)
@@ -243,6 +257,11 @@ var BALANCE_INVARIANT_MESSAGES = {
243
257
  [BALANCE_INVARIANTS.SERVICE_LINE]: WARNING_MESSAGES.X12_835_REMIT_BALANCE_MISMATCH_SERVICE_LINE,
244
258
  [BALANCE_INVARIANTS.REMIT_TOTAL]: WARNING_MESSAGES.X12_835_REMIT_BALANCE_MISMATCH_REMIT_TOTAL
245
259
  };
260
+ var BALANCE_NOT_EVALUABLE_MESSAGES = {
261
+ [BALANCE_INVARIANTS.CLAIM]: WARNING_MESSAGES.X12_835_BALANCE_NOT_EVALUABLE_CLAIM,
262
+ [BALANCE_INVARIANTS.SERVICE_LINE]: WARNING_MESSAGES.X12_835_BALANCE_NOT_EVALUABLE_SERVICE_LINE,
263
+ [BALANCE_INVARIANTS.REMIT_TOTAL]: WARNING_MESSAGES.X12_835_BALANCE_NOT_EVALUABLE_REMIT_TOTAL
264
+ };
246
265
  var REQUIRED_LOOP_MESSAGES = {
247
266
  [REQUIRED_LOOPS.BILLING_PROVIDER_2000A]: WARNING_MESSAGES.X12_MISSING_REQUIRED_LOOP_2000A,
248
267
  [REQUIRED_LOOPS.SUBSCRIBER_2000B]: WARNING_MESSAGES.X12_MISSING_REQUIRED_LOOP_2000B,
@@ -333,6 +352,13 @@ function remitBalanceMismatch(position, invariant) {
333
352
  position
334
353
  };
335
354
  }
355
+ function balanceNotEvaluable(position, invariant) {
356
+ return {
357
+ code: WARNING_CODES.X12_835_BALANCE_NOT_EVALUABLE,
358
+ message: BALANCE_NOT_EVALUABLE_MESSAGES[invariant],
359
+ position
360
+ };
361
+ }
336
362
  function unknownCarc(position) {
337
363
  return {
338
364
  code: WARNING_CODES.X12_UNKNOWN_CARC,
@@ -382,6 +408,13 @@ function unknown837Variant(position) {
382
408
  position
383
409
  };
384
410
  }
411
+ function ambiguous837Variant(position) {
412
+ return {
413
+ code: WARNING_CODES.X12_837_AMBIGUOUS_VARIANT,
414
+ message: WARNING_MESSAGES.X12_837_AMBIGUOUS_VARIANT,
415
+ position
416
+ };
417
+ }
385
418
  function serviceLineNotDecoded(position) {
386
419
  return {
387
420
  code: WARNING_CODES.X12_837_SERVICE_LINE_NOT_DECODED,
@@ -403,6 +436,13 @@ function serviceSegmentWithoutLx(position) {
403
436
  position
404
437
  };
405
438
  }
439
+ function serviceSegmentRepeated(position) {
440
+ return {
441
+ code: WARNING_CODES.X12_837_SERVICE_SEGMENT_REPEATED,
442
+ message: WARNING_MESSAGES.X12_837_SERVICE_SEGMENT_REPEATED,
443
+ position
444
+ };
445
+ }
406
446
  function entitySegmentDiscardedAfterLx(position) {
407
447
  return {
408
448
  code: WARNING_CODES.X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX,
@@ -410,6 +450,13 @@ function entitySegmentDiscardedAfterLx(position) {
410
450
  position
411
451
  };
412
452
  }
453
+ function payToAddressRepeated(position) {
454
+ return {
455
+ code: WARNING_CODES.X12_837_PAY_TO_ADDRESS_REPEATED,
456
+ message: WARNING_MESSAGES.X12_837_PAY_TO_ADDRESS_REPEATED,
457
+ position
458
+ };
459
+ }
413
460
  function unknownClaimStatusCategory(position) {
414
461
  return {
415
462
  code: WARNING_CODES.X12_UNKNOWN_CLAIM_STATUS_CATEGORY,
@@ -438,6 +485,20 @@ function unparseableDecimal(position) {
438
485
  position
439
486
  };
440
487
  }
488
+ function amountRowDropped(position) {
489
+ return {
490
+ code: WARNING_CODES.X12_AMOUNT_ROW_DROPPED,
491
+ message: WARNING_MESSAGES.X12_AMOUNT_ROW_DROPPED,
492
+ position
493
+ };
494
+ }
495
+ function statedAmountDiscarded(position) {
496
+ return {
497
+ code: WARNING_CODES.X12_STATED_AMOUNT_DISCARDED,
498
+ message: WARNING_MESSAGES.X12_STATED_AMOUNT_DISCARDED,
499
+ position
500
+ };
501
+ }
441
502
 
442
503
  // src/parser/release.ts
443
504
  var RELEASE_CHAR = "?";
@@ -1898,11 +1959,13 @@ function isProcedureQualifier(qualifier) {
1898
1959
  // src/transactions/remit/balance.ts
1899
1960
  function checkClaimBalance(claim, position) {
1900
1961
  const claimCasSum = sumAmounts(claim.adjustments);
1901
- const lineCasSum = claim.serviceLines.reduce(
1902
- (acc, sl) => acc.add(sumAmounts(sl.adjustments)),
1903
- X12Decimal.ZERO
1962
+ const lineCasSum = sumOptional(
1963
+ claim.serviceLines.map((sl) => sumAmounts(sl.adjustments))
1904
1964
  );
1905
- const computed = claim.totalPaymentAmount.add(claimCasSum).add(lineCasSum);
1965
+ const computed = sumOptional([claim.totalPaymentAmount, claimCasSum, lineCasSum]);
1966
+ if (computed === void 0 || claim.totalChargeAmount === void 0) {
1967
+ return balanceNotEvaluable(position, BALANCE_INVARIANTS.CLAIM);
1968
+ }
1906
1969
  if (computed.equals(claim.totalChargeAmount)) return void 0;
1907
1970
  return remitBalanceMismatch(position, BALANCE_INVARIANTS.CLAIM);
1908
1971
  }
@@ -1912,35 +1975,37 @@ function checkServiceLineBalance(claim, position) {
1912
1975
  const line = claim.serviceLines[i];
1913
1976
  if (line === void 0) continue;
1914
1977
  const lineCasSum = sumAmounts(line.adjustments);
1915
- const computed = line.paymentAmount.add(lineCasSum);
1978
+ const linePosition = { ...position, segmentIndex: position.segmentIndex + i + 1 };
1979
+ const computed = sumOptional([line.paymentAmount, lineCasSum]);
1980
+ if (computed === void 0 || line.chargeAmount === void 0) {
1981
+ out.push(balanceNotEvaluable(linePosition, BALANCE_INVARIANTS.SERVICE_LINE));
1982
+ continue;
1983
+ }
1916
1984
  if (computed.equals(line.chargeAmount)) continue;
1917
- out.push(
1918
- remitBalanceMismatch(
1919
- { ...position, segmentIndex: position.segmentIndex + i + 1 },
1920
- BALANCE_INVARIANTS.SERVICE_LINE
1921
- )
1922
- );
1985
+ out.push(remitBalanceMismatch(linePosition, BALANCE_INVARIANTS.SERVICE_LINE));
1923
1986
  }
1924
1987
  return out;
1925
1988
  }
1926
1989
  function checkRemitTotalBalance(bpr02, claims, providerAdjustments, position) {
1927
- const claimSum = claims.reduce(
1928
- (acc, c) => acc.add(c.totalPaymentAmount),
1929
- X12Decimal.ZERO
1930
- );
1931
- const plbSum = providerAdjustments.reduce(
1932
- (acc, p) => acc.add(p.amount),
1933
- X12Decimal.ZERO
1934
- );
1990
+ const claimSum = sumOptional(claims.map((c) => c.totalPaymentAmount));
1991
+ const plbSum = sumOptional(providerAdjustments.map((p) => p.amount));
1992
+ if (bpr02 === void 0 || claimSum === void 0 || plbSum === void 0) {
1993
+ return balanceNotEvaluable(position, BALANCE_INVARIANTS.REMIT_TOTAL);
1994
+ }
1935
1995
  const computed = claimSum.subtract(plbSum);
1936
1996
  if (computed.equals(bpr02)) return void 0;
1937
1997
  return remitBalanceMismatch(position, BALANCE_INVARIANTS.REMIT_TOTAL);
1938
1998
  }
1939
1999
  function sumAmounts(adjustments) {
1940
- return adjustments.reduce(
1941
- (acc, a) => acc.add(a.amount),
1942
- X12Decimal.ZERO
1943
- );
2000
+ return sumOptional(adjustments.map((a) => a.amount));
2001
+ }
2002
+ function sumOptional(terms) {
2003
+ let acc = X12Decimal.ZERO;
2004
+ for (const term of terms) {
2005
+ if (term === void 0) return void 0;
2006
+ acc = acc.add(term);
2007
+ }
2008
+ return acc;
1944
2009
  }
1945
2010
 
1946
2011
  // src/transactions/remit/get-835.ts
@@ -2106,7 +2171,10 @@ function get835(delimiters, tx) {
2106
2171
  }
2107
2172
  case "AMT": {
2108
2173
  const amount = decodeAmt(seg, delimiters, sink);
2109
- if (amount === void 0) break;
2174
+ if (amount === void 0) {
2175
+ warnings.push(amountRowDropped(position));
2176
+ break;
2177
+ }
2110
2178
  if (currentServiceLine !== void 0) currentServiceLine.amounts.push(amount);
2111
2179
  else if (currentClaim !== void 0) currentClaim.amounts.push(amount);
2112
2180
  break;
@@ -2163,7 +2231,7 @@ function get835(delimiters, tx) {
2163
2231
  function decodeBpr(seg, delimiters, sink) {
2164
2232
  return Object.freeze({
2165
2233
  transactionHandlingCode: elementValue(seg, 1, delimiters),
2166
- totalActualPayment: elementDecimalOrZero(seg, 2, delimiters, sink),
2234
+ totalActualPayment: elementDecimal(seg, 2, delimiters, sink),
2167
2235
  creditDebitFlag: elementValue(seg, 3, delimiters),
2168
2236
  method: elementValue(seg, 4, delimiters),
2169
2237
  paymentFormatCode: elementOptional(seg, 5, delimiters),
@@ -2276,7 +2344,7 @@ function decodeCasAdjustments(seg, delimiters, warnings, position) {
2276
2344
  groupCode,
2277
2345
  reasonCode: code,
2278
2346
  reasonDescription: entry?.description,
2279
- amount: amount ?? X12Decimal.ZERO,
2347
+ amount,
2280
2348
  quantity
2281
2349
  })
2282
2350
  );
@@ -2320,9 +2388,9 @@ function openClaim(seg, delimiters, clpSegmentIndex, sink) {
2320
2388
  patientControlNumber: elementValue(seg, 1, delimiters),
2321
2389
  claimStatusCode,
2322
2390
  claimStatusDescription,
2323
- totalChargeAmount: elementDecimalOrZero(seg, 3, delimiters, sink),
2324
- totalPaymentAmount: elementDecimalOrZero(seg, 4, delimiters, sink),
2325
- patientResponsibilityAmount: elementDecimalOrZero(seg, 5, delimiters, sink),
2391
+ totalChargeAmount: elementDecimal(seg, 3, delimiters, sink),
2392
+ totalPaymentAmount: elementDecimal(seg, 4, delimiters, sink),
2393
+ patientResponsibilityAmount: elementDecimal(seg, 5, delimiters, sink),
2326
2394
  claimFilingIndicatorCode: elementOptional(seg, 6, delimiters),
2327
2395
  payerClaimControlNumber: elementOptional(seg, 7, delimiters),
2328
2396
  facilityTypeCode,
@@ -2358,8 +2426,8 @@ function openServiceLine(seg, delimiters, sink) {
2358
2426
  productServiceIdQualifier,
2359
2427
  productServiceId,
2360
2428
  modifiers,
2361
- chargeAmount: elementDecimalOrZero(seg, 2, delimiters, sink),
2362
- paymentAmount: elementDecimalOrZero(seg, 3, delimiters, sink),
2429
+ chargeAmount: elementDecimal(seg, 2, delimiters, sink),
2430
+ paymentAmount: elementDecimal(seg, 3, delimiters, sink),
2363
2431
  revenueCode,
2364
2432
  paidUnitsOfService,
2365
2433
  originalUnitsOfService,
@@ -2404,7 +2472,7 @@ function decodePlb(seg, delimiters, sink) {
2404
2472
  fiscalPeriodDate,
2405
2473
  reasonCode: reasonCode ?? "",
2406
2474
  subCode,
2407
- amount: amount ?? X12Decimal.ZERO
2475
+ amount
2408
2476
  })
2409
2477
  );
2410
2478
  }
@@ -2419,7 +2487,7 @@ var EMPTY_ADDRESS = Object.freeze({
2419
2487
  });
2420
2488
  var EMPTY_HEADER = Object.freeze({
2421
2489
  transactionHandlingCode: "",
2422
- totalActualPayment: X12Decimal.ZERO,
2490
+ totalActualPayment: void 0,
2423
2491
  creditDebitFlag: "",
2424
2492
  method: "",
2425
2493
  paymentFormatCode: void 0,
@@ -2839,12 +2907,24 @@ function enforceBalance(spec) {
2839
2907
  for (const claim of claims) {
2840
2908
  const claimWarn = checkClaimBalance(claim, position);
2841
2909
  if (claimWarn !== void 0) {
2910
+ if (notEvaluable(claimWarn)) {
2911
+ throw new Remit835BuildError(
2912
+ REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_INVALID_SPEC,
2913
+ `build835 refuses a claim whose balance cannot be checked: ${claimWarn.message}`
2914
+ );
2915
+ }
2842
2916
  throw new Remit835BuildError(
2843
2917
  REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_BALANCE_MISMATCH,
2844
2918
  `build835 refuses an out-of-balance claim: ${claimWarn.message}`
2845
2919
  );
2846
2920
  }
2847
2921
  for (const lineWarn of checkServiceLineBalance(claim, position)) {
2922
+ if (notEvaluable(lineWarn)) {
2923
+ throw new Remit835BuildError(
2924
+ REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_INVALID_SPEC,
2925
+ `build835 refuses a service line whose balance cannot be checked: ${lineWarn.message}`
2926
+ );
2927
+ }
2848
2928
  throw new Remit835BuildError(
2849
2929
  REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_BALANCE_MISMATCH,
2850
2930
  `build835 refuses an out-of-balance service line: ${lineWarn.message}`
@@ -2858,12 +2938,21 @@ function enforceBalance(spec) {
2858
2938
  position
2859
2939
  );
2860
2940
  if (totalWarn !== void 0) {
2941
+ if (notEvaluable(totalWarn)) {
2942
+ throw new Remit835BuildError(
2943
+ REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_INVALID_SPEC,
2944
+ `build835 refuses a remit whose balance cannot be checked: ${totalWarn.message}`
2945
+ );
2946
+ }
2861
2947
  throw new Remit835BuildError(
2862
2948
  REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_BALANCE_MISMATCH,
2863
2949
  `build835 refuses an out-of-balance remit: ${totalWarn.message}`
2864
2950
  );
2865
2951
  }
2866
2952
  }
2953
+ function notEvaluable(warn) {
2954
+ return warn.code === WARNING_CODES.X12_835_BALANCE_NOT_EVALUABLE;
2955
+ }
2867
2956
  function materializeAdjustment(spec) {
2868
2957
  return {
2869
2958
  groupCode: spec.groupCode,
@@ -3237,11 +3326,33 @@ function wireLookup(entries) {
3237
3326
  return Object.freeze(table);
3238
3327
  }
3239
3328
 
3329
+ // src/transactions/claim/address-segments.ts
3330
+ function emitsStreetLines(address) {
3331
+ return address.lines.length > 0;
3332
+ }
3333
+ function emitsGeographicFields(address) {
3334
+ return address.city !== void 0 || address.state !== void 0 || address.postalCode !== void 0 || address.countryCode !== void 0;
3335
+ }
3336
+ function statesAnAddress(address) {
3337
+ return emitsStreetLines(address) || emitsGeographicFields(address);
3338
+ }
3339
+
3240
3340
  // src/transactions/claim/get-837.ts
3241
3341
  var VARIANT_BY_ICR = wireLookup({
3342
+ // Health Care Claim: Professional (837).
3343
+ "005010X222": "P",
3344
+ "005010X222A1": "P",
3242
3345
  "005010X222A2": "P",
3346
+ // Health Care Claim: Institutional (837).
3347
+ "005010X223": "I",
3348
+ "005010X223A1": "I",
3349
+ "005010X223A2": "I",
3243
3350
  "005010X223A3": "I",
3244
- "005010X224A2": "D"
3351
+ // Health Care Claim: Dental (837).
3352
+ "005010X224": "D",
3353
+ "005010X224A1": "D",
3354
+ "005010X224A2": "D",
3355
+ "005010X224A3": "D"
3245
3356
  });
3246
3357
  var VARIANT_BY_SV_SEGMENT = wireLookup({
3247
3358
  SV1: "P",
@@ -3277,16 +3388,24 @@ function get837Claims(delimiters, tx, opts) {
3277
3388
  const explicitType = opts?.type;
3278
3389
  const variantFromIcr = implementationConventionReference !== void 0 && implementationConventionReference !== "" ? VARIANT_BY_ICR[implementationConventionReference] : void 0;
3279
3390
  let variantFromSegment;
3280
- for (const seg of body) {
3281
- const fromSv = VARIANT_BY_SV_SEGMENT[seg.id];
3282
- if (fromSv !== void 0) {
3283
- variantFromSegment = fromSv;
3284
- break;
3391
+ let serviceSegmentVariantsConflict = false;
3392
+ if (explicitType === void 0 && variantFromIcr === void 0) {
3393
+ for (const seg of body) {
3394
+ const fromSv = VARIANT_BY_SV_SEGMENT[seg.id];
3395
+ if (fromSv === void 0) continue;
3396
+ if (variantFromSegment === void 0) {
3397
+ variantFromSegment = fromSv;
3398
+ } else if (fromSv !== variantFromSegment) {
3399
+ serviceSegmentVariantsConflict = true;
3400
+ break;
3401
+ }
3285
3402
  }
3286
3403
  }
3287
3404
  const variant = explicitType ?? variantFromIcr ?? variantFromSegment ?? "unknown";
3288
3405
  if (variant === "unknown") {
3289
3406
  warnings.push(unknown837Variant({ segmentIndex: 0, transactionIndex: 0 }));
3407
+ } else if (serviceSegmentVariantsConflict) {
3408
+ warnings.push(ambiguous837Variant({ segmentIndex: 0, transactionIndex: 0 }));
3290
3409
  }
3291
3410
  const hierarchies = [];
3292
3411
  const hlIndex = /* @__PURE__ */ new Map();
@@ -3297,6 +3416,8 @@ function get837Claims(delimiters, tx, opts) {
3297
3416
  let receiver;
3298
3417
  let billingProvider;
3299
3418
  let payToAddress;
3419
+ let pendingPayToAddress;
3420
+ let payToAddressNamed = false;
3300
3421
  let payToPlan;
3301
3422
  let currentSubscriberMember;
3302
3423
  let currentPayer;
@@ -3333,6 +3454,10 @@ function get837Claims(delimiters, tx, opts) {
3333
3454
  if (droppedLineReported) return;
3334
3455
  warnings.push(serviceSegmentWithoutLx(position));
3335
3456
  };
3457
+ const noteServiceSegment = (acc, position) => {
3458
+ if (acc.serviceSegmentSeen) warnings.push(serviceSegmentRepeated(position));
3459
+ acc.serviceSegmentSeen = true;
3460
+ };
3336
3461
  const reportDiscardedEntitySegment = (position) => {
3337
3462
  if (!entityLoopClosedByStrayLx) return;
3338
3463
  warnings.push(entitySegmentDiscardedAfterLx(position));
@@ -3383,7 +3508,8 @@ function get837Claims(delimiters, tx, opts) {
3383
3508
  currentPatientMember = { ...currentPatientMember, entity: next };
3384
3509
  },
3385
3510
  setPayToAddress: (next) => {
3386
- payToAddress = next;
3511
+ pendingPayToAddress = next;
3512
+ if (statesAnAddress(next) || payToAddress === void 0) payToAddress = next;
3387
3513
  },
3388
3514
  setOtherSubscriber: (next) => {
3389
3515
  currentOtherSubscriber.otherSubscriber = next;
@@ -3398,7 +3524,10 @@ function get837Claims(delimiters, tx, opts) {
3398
3524
  getCurrentSubscriber: () => currentSubscriberMember?.entity,
3399
3525
  getCurrentPayer: () => currentPayer,
3400
3526
  getCurrentPatient: () => currentPatientMember?.entity,
3401
- getCurrentPayToAddress: () => payToAddress,
3527
+ // Deliberately the CURRENT occurrence's accumulator, never the committed
3528
+ // slot: this is the getter both address arms build onto, and reading the
3529
+ // committed slot here is exactly what fused two `NM1*87`s.
3530
+ getCurrentPayToAddress: () => pendingPayToAddress,
3402
3531
  getCurrentOtherSubscriber: () => currentOtherSubscriber?.otherSubscriber,
3403
3532
  getCurrentOtherPayer: () => currentOtherSubscriber?.otherPayer
3404
3533
  };
@@ -3420,6 +3549,8 @@ function get837Claims(delimiters, tx, opts) {
3420
3549
  currentPatientHl = void 0;
3421
3550
  billingProvider = void 0;
3422
3551
  payToAddress = void 0;
3552
+ pendingPayToAddress = void 0;
3553
+ payToAddressNamed = false;
3423
3554
  payToPlan = void 0;
3424
3555
  context = { kind: "loop2000A" };
3425
3556
  } else if (hl.levelCode === HL_LEVEL_CODES.SUBSCRIBER) {
@@ -3483,6 +3614,9 @@ function get837Claims(delimiters, tx, opts) {
3483
3614
  billingProvider = entity;
3484
3615
  activeEntity = { kind: "billingProvider" };
3485
3616
  } else if (qualifier === NM1_QUALIFIERS.PAY_TO_ADDRESS && context.kind === "loop2000A") {
3617
+ if (payToAddressNamed) warnings.push(payToAddressRepeated(position));
3618
+ payToAddressNamed = true;
3619
+ pendingPayToAddress = void 0;
3486
3620
  activeEntity = { kind: "payToAddress" };
3487
3621
  } else if (qualifier === NM1_QUALIFIERS.PAY_TO_PLAN && context.kind === "loop2000A" && variant === "I") {
3488
3622
  payToPlan = entity;
@@ -3616,8 +3750,14 @@ function get837Claims(delimiters, tx, opts) {
3616
3750
  }
3617
3751
  case "AMT": {
3618
3752
  const amount = decodeAmt2(seg, delimiters, sink);
3619
- if (amount === void 0) break;
3620
- if (currentAdjudication !== void 0) break;
3753
+ if (amount === void 0) {
3754
+ warnings.push(amountRowDropped(position));
3755
+ break;
3756
+ }
3757
+ if (currentAdjudication !== void 0) {
3758
+ warnings.push(statedAmountDiscarded(position));
3759
+ break;
3760
+ }
3621
3761
  if (currentServiceLine !== void 0) currentServiceLine.amounts.push(amount);
3622
3762
  else if (currentClaim !== void 0) currentClaim.amounts.push(amount);
3623
3763
  break;
@@ -3660,19 +3800,41 @@ function get837Claims(delimiters, tx, opts) {
3660
3800
  // before the walk and scans every SVx in the body: an orphan segment
3661
3801
  // is eligible for that fallback like any other, which is pre-existing
3662
3802
  // and disclosed in KNOWN-LIMITATIONS.md rather than changed here.
3803
+ //
3804
+ // A SECOND service segment inside an already-open Loop 2400 is the
3805
+ // other silence on this segment, and it is a worse one: the line IS
3806
+ // open, so the repeat decodes, and every decoder writes all of the
3807
+ // slots its kind writes. `SV1*HC:99213*8500*…` followed by
3808
+ // `SV1*HC:99999*12*…` left ONE line reading `charge` 12 and
3809
+ // `procedureCode` 99999 through `0.0.13`, with `warnings: []` - money
3810
+ // and a CPT code replaced on the model, on no channel at all.
3811
+ // `noteServiceSegment` reports that, at the repeat itself, and changes
3812
+ // NOTHING about which segment wins.
3663
3813
  case "SV1": {
3664
- if (currentServiceLine !== void 0) decodeSv1(currentServiceLine, seg, delimiters, sink);
3665
- else reportOrphanServiceSegment(position);
3814
+ if (currentServiceLine === void 0) {
3815
+ reportOrphanServiceSegment(position);
3816
+ break;
3817
+ }
3818
+ noteServiceSegment(currentServiceLine, position);
3819
+ decodeSv1(currentServiceLine, seg, delimiters, sink);
3666
3820
  break;
3667
3821
  }
3668
3822
  case "SV2": {
3669
- if (currentServiceLine !== void 0) decodeSv2(currentServiceLine, seg, delimiters, sink);
3670
- else reportOrphanServiceSegment(position);
3823
+ if (currentServiceLine === void 0) {
3824
+ reportOrphanServiceSegment(position);
3825
+ break;
3826
+ }
3827
+ noteServiceSegment(currentServiceLine, position);
3828
+ decodeSv2(currentServiceLine, seg, delimiters, sink);
3671
3829
  break;
3672
3830
  }
3673
3831
  case "SV3": {
3674
- if (currentServiceLine !== void 0) decodeSv3(currentServiceLine, seg, delimiters, sink);
3675
- else reportOrphanServiceSegment(position);
3832
+ if (currentServiceLine === void 0) {
3833
+ reportOrphanServiceSegment(position);
3834
+ break;
3835
+ }
3836
+ noteServiceSegment(currentServiceLine, position);
3837
+ decodeSv3(currentServiceLine, seg, delimiters, sink);
3676
3838
  break;
3677
3839
  }
3678
3840
  case "TOO": {
@@ -3907,7 +4069,7 @@ function decodeCas(seg, delimiters, warnings, position) {
3907
4069
  groupCode,
3908
4070
  reasonCode: code,
3909
4071
  reasonDescription: entry?.description,
3910
- amount: amount ?? X12Decimal.ZERO,
4072
+ amount,
3911
4073
  quantity
3912
4074
  })
3913
4075
  );
@@ -3953,7 +4115,7 @@ function openClaim2(seg, delimiters, ctx, sink) {
3953
4115
  payer,
3954
4116
  patient,
3955
4117
  claimId: elementValue(seg, 1, delimiters),
3956
- totalCharge: elementDecimalOrZero(seg, 2, delimiters, sink),
4118
+ totalCharge: elementDecimal(seg, 2, delimiters, sink),
3957
4119
  placeOfServiceCode: componentOptional(seg, 5, 1, delimiters),
3958
4120
  facilityCodeQualifier: componentOptional(seg, 5, 2, delimiters),
3959
4121
  claimFrequencyCode: componentOptional(seg, 5, 3, delimiters),
@@ -3979,8 +4141,9 @@ function openServiceLine2(seg, delimiters, variant, position) {
3979
4141
  lineNumber,
3980
4142
  position,
3981
4143
  serviceSegmentDecoded: false,
3982
- charge: X12Decimal.ZERO,
3983
- units: X12Decimal.ZERO,
4144
+ serviceSegmentSeen: false,
4145
+ charge: void 0,
4146
+ units: void 0,
3984
4147
  unitOfMeasure: void 0,
3985
4148
  placeOfServiceCode: void 0,
3986
4149
  dates: [],
@@ -4041,9 +4204,9 @@ function decodeSv1(acc, seg, delimiters, sink) {
4041
4204
  if (m !== void 0) mods.push(m);
4042
4205
  }
4043
4206
  acc.modifiers = mods;
4044
- acc.charge = elementDecimalOrZero(seg, 2, delimiters, sink);
4207
+ acc.charge = elementDecimal(seg, 2, delimiters, sink);
4045
4208
  acc.unitOfMeasure = elementOptional(seg, 3, delimiters);
4046
- acc.units = elementDecimalOrZero(seg, 4, delimiters, sink);
4209
+ acc.units = elementDecimal(seg, 4, delimiters, sink);
4047
4210
  acc.placeOfServiceCode = elementOptional(seg, 5, delimiters);
4048
4211
  const pointers = [];
4049
4212
  for (let p = 1; p <= 4; p += 1) {
@@ -4067,9 +4230,9 @@ function decodeSv2(acc, seg, delimiters, sink) {
4067
4230
  if (m !== void 0) mods.push(m);
4068
4231
  }
4069
4232
  acc.modifiers = mods;
4070
- acc.charge = elementDecimalOrZero(seg, 3, delimiters, sink);
4233
+ acc.charge = elementDecimal(seg, 3, delimiters, sink);
4071
4234
  acc.unitOfMeasure = elementOptional(seg, 4, delimiters);
4072
- acc.units = elementDecimalOrZero(seg, 5, delimiters, sink);
4235
+ acc.units = elementDecimal(seg, 5, delimiters, sink);
4073
4236
  acc.serviceLineRate = elementDecimal(seg, 6, delimiters, sink);
4074
4237
  acc.nonCoveredCharge = elementDecimal(seg, 7, delimiters, sink);
4075
4238
  }
@@ -4084,7 +4247,7 @@ function decodeSv3(acc, seg, delimiters, sink) {
4084
4247
  if (m !== void 0) mods.push(m);
4085
4248
  }
4086
4249
  acc.modifiers = mods;
4087
- acc.charge = elementDecimalOrZero(seg, 2, delimiters, sink);
4250
+ acc.charge = elementDecimal(seg, 2, delimiters, sink);
4088
4251
  acc.placeOfServiceCode = elementOptional(seg, 3, delimiters);
4089
4252
  const cavities = [];
4090
4253
  for (let p = 1; p <= 5; p += 1) {
@@ -4093,12 +4256,12 @@ function decodeSv3(acc, seg, delimiters, sink) {
4093
4256
  }
4094
4257
  acc.oralCavityArea = cavities;
4095
4258
  acc.prosthesisCrownInlayCode = elementOptional(seg, 5, delimiters);
4096
- acc.units = elementDecimalOrZero(seg, 6, delimiters, sink);
4259
+ acc.units = elementDecimal(seg, 6, delimiters, sink);
4097
4260
  }
4098
4261
  function openAdjudication(seg, delimiters, sink) {
4099
4262
  return {
4100
4263
  otherPayerId: elementValue(seg, 1, delimiters),
4101
- amountPaid: elementDecimalOrZero(seg, 2, delimiters, sink),
4264
+ amountPaid: elementDecimal(seg, 2, delimiters, sink),
4102
4265
  procedureQualifier: componentOptional(seg, 3, 1, delimiters),
4103
4266
  procedureCode: componentOptional(seg, 3, 2, delimiters),
4104
4267
  paidUnits: elementDecimal(seg, 5, delimiters, sink),
@@ -4627,6 +4790,12 @@ function enforceClaim(variant, claim, locator) {
4627
4790
  `build837${variant}: claim at ${locator} has a service line at index ${String(l)} with an empty ${line.variant === "I" ? "revenue" : "procedure"} code.`
4628
4791
  );
4629
4792
  }
4793
+ if (line.units === void 0) {
4794
+ throw new Claim837BuildError(
4795
+ CLAIM_837_BUILD_ERROR_CODES.X12_837_BUILD_INVALID_SPEC,
4796
+ `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.`
4797
+ );
4798
+ }
4630
4799
  }
4631
4800
  }
4632
4801
  function emitBillingProvider(variant, billing, body, ctx, counter) {
@@ -4741,7 +4910,7 @@ function emitOtherSubscriber(other, body, ctx) {
4741
4910
  }
4742
4911
  function emitServiceLine2(variant, line, lineNumber, body, ctx) {
4743
4912
  body.push(ctx.seg(["LX", ctx.esc(line.lineNumber ?? String(lineNumber))]));
4744
- const units = line.units === void 0 ? "0" : decStr(line.units);
4913
+ const units = decStr(line.units);
4745
4914
  if (line.variant === "P") {
4746
4915
  const proc = ctx.comp([line.procedureQualifier, line.procedureCode, ...line.modifiers ?? []]);
4747
4916
  const pointers = ctx.comp(line.diagnosisPointers ?? []);
@@ -4893,10 +5062,10 @@ function emitEntity(entity, body, ctx) {
4893
5062
  for (const ref of entity.references ?? []) body.push(emitRef2(ref, ctx));
4894
5063
  }
4895
5064
  function emitAddress(address, body, ctx) {
4896
- if (address.lines.length > 0) {
5065
+ if (emitsStreetLines(address)) {
4897
5066
  body.push(ctx.seg(["N3", ...address.lines.map(ctx.esc)]));
4898
5067
  }
4899
- if (address.city !== void 0 || address.state !== void 0 || address.postalCode !== void 0 || address.countryCode !== void 0) {
5068
+ if (emitsGeographicFields(address)) {
4900
5069
  body.push(
4901
5070
  ctx.seg([
4902
5071
  "N4",
@@ -6890,13 +7059,18 @@ function get820Payments(delimiters, tx) {
6890
7059
  case "RMR": {
6891
7060
  if (current === void 0) break;
6892
7061
  const item = decodeRmr(seg, delimiters, sink);
6893
- if (item !== void 0) current.openItems.push(item);
7062
+ if (item !== void 0) {
7063
+ current.openItems.push(item);
7064
+ break;
7065
+ }
7066
+ if (statesAnAmount(seg, delimiters)) warnings.push(statedAmountDiscarded(position));
6894
7067
  break;
6895
7068
  }
6896
7069
  case "ADX": {
6897
7070
  if (current === void 0) break;
6898
7071
  const adjustment = decodeAdx(seg, delimiters, sink);
6899
- if (adjustment !== void 0) current.adjustments.push(adjustment);
7072
+ if (adjustment === void 0) warnings.push(amountRowDropped(position));
7073
+ else current.adjustments.push(adjustment);
6900
7074
  break;
6901
7075
  }
6902
7076
  case "REF": {
@@ -6950,7 +7124,7 @@ function freezeRemittance(acc) {
6950
7124
  function decodeBpr2(seg, delimiters, sink) {
6951
7125
  return Object.freeze({
6952
7126
  transactionHandlingCode: elementValue(seg, 1, delimiters),
6953
- totalPremiumAmount: elementDecimalOrZero(seg, 2, delimiters, sink),
7127
+ totalPremiumAmount: elementDecimal(seg, 2, delimiters, sink),
6954
7128
  creditDebitFlag: elementValue(seg, 3, delimiters),
6955
7129
  method: elementValue(seg, 4, delimiters),
6956
7130
  paymentFormatCode: elementOptional(seg, 5, delimiters),
@@ -7018,6 +7192,9 @@ function decodeNm13(seg, delimiters) {
7018
7192
  idCode: elementOptional(seg, 9, delimiters)
7019
7193
  });
7020
7194
  }
7195
+ function statesAnAmount(seg, delimiters) {
7196
+ return elementValue(seg, 4, delimiters) !== "" || elementValue(seg, 5, delimiters) !== "";
7197
+ }
7021
7198
  function decodeRmr(seg, delimiters, sink) {
7022
7199
  const qualifier = elementValue(seg, 1, delimiters);
7023
7200
  const referenceId = elementValue(seg, 2, delimiters);
@@ -7026,7 +7203,7 @@ function decodeRmr(seg, delimiters, sink) {
7026
7203
  qualifier,
7027
7204
  referenceId,
7028
7205
  paymentActionCode: elementOptional(seg, 3, delimiters),
7029
- amountPaid: elementDecimalOrZero(seg, 4, delimiters, sink),
7206
+ amountPaid: elementDecimal(seg, 4, delimiters, sink),
7030
7207
  amountDue: elementDecimal(seg, 5, delimiters, sink)
7031
7208
  });
7032
7209
  }
@@ -7049,7 +7226,7 @@ var EMPTY_ADDRESS3 = Object.freeze({
7049
7226
  });
7050
7227
  var EMPTY_HEADER2 = Object.freeze({
7051
7228
  transactionHandlingCode: "",
7052
- totalPremiumAmount: X12Decimal.ZERO,
7229
+ totalPremiumAmount: void 0,
7053
7230
  creditDebitFlag: "",
7054
7231
  method: "",
7055
7232
  paymentFormatCode: void 0,
@@ -7608,7 +7785,8 @@ async function* get834Enrollments(delimiters, tx) {
7608
7785
  if (currentCoverage === void 0) break;
7609
7786
  const sink = { warnings: current.warnings, position };
7610
7787
  const amount = decodeAmt3(seg, delimiters, sink);
7611
- if (amount !== void 0) currentCoverage.amounts.push(amount);
7788
+ if (amount === void 0) current.warnings.push(amountRowDropped(position));
7789
+ else currentCoverage.amounts.push(amount);
7612
7790
  break;
7613
7791
  }
7614
7792
  }
@@ -10190,8 +10368,8 @@ var profiles = Object.freeze({
10190
10368
  });
10191
10369
 
10192
10370
  // src/index.ts
10193
- var VERSION = "0.0.12";
10371
+ var VERSION = "0.0.14";
10194
10372
 
10195
- export { ACK_BUILD_ERROR_CODES, ALL_WARNING_MESSAGES, AUTH_278_BUILD_ERROR_CODES, AUTH_278_LOOP_2000A, AUTH_278_LOOP_2000B, AUTH_278_LOOP_2000C, AUTH_278_LOOP_2000D, AUTH_278_LOOP_2000E, AUTH_278_LOOP_2000F, AckBuildError, BALANCE_INVARIANTS, BUILD_REFUSAL_VALUE_MAX_LENGTH, BUILD_REFUSAL_VALUE_MAX_RENDERED, CARC, CLAIM_837D_LOOP_2000A, CLAIM_837D_LOOP_2300, CLAIM_837D_LOOP_2400, CLAIM_837I_LOOP_2000A, CLAIM_837I_LOOP_2300, CLAIM_837I_LOOP_2400, CLAIM_837P_LOOP_2000A, CLAIM_837P_LOOP_2300, CLAIM_837P_LOOP_2400, CLAIM_837P_LOOP_2410, CLAIM_837_BUILD_ERROR_CODES, CLAIM_837_LOOP_1000A, CLAIM_837_LOOP_1000B, CLAIM_837_LOOP_2010AA, CLAIM_837_LOOP_2010BA, CLAIM_837_LOOP_2010BB, CLAIM_837_LOOP_2010CA, CLAIM_837_LOOP_2430, CLAIM_ADJUSTMENT_GROUP_CODES, CLAIM_STATUS_277_BUILD_ERROR_CODES, CLAIM_STATUS_CATEGORY_CODES, CLAIM_STATUS_CODES, CLP_STATUS, CONTROL_NUMBER_PAIRS, Claim837BuildError, ClaimStatus277BuildError, DELIMITER_POSITIONS, ELIGIBILITY_271_BUILD_ERROR_CODES, ELIGIBILITY_271_LOOP_2000A, ELIGIBILITY_271_LOOP_2000B, ELIGIBILITY_271_LOOP_2000C, ELIGIBILITY_271_LOOP_2000D, ELIGIBILITY_271_LOOP_2100C, ELIGIBILITY_271_LOOP_2100D, ELIGIBILITY_271_LOOP_2110, ENROLLMENT_834_BUILD_ERROR_CODES, ENROLLMENT_834_LOOP_1000A, ENROLLMENT_834_LOOP_1000B, ENROLLMENT_834_LOOP_2000, ENROLLMENT_834_LOOP_2100A, ENROLLMENT_834_LOOP_2300, ENROLLMENT_834_LOOP_2320, Eligibility271BuildError, Enrollment834BuildError, FATAL_CODES, HI_QUALIFIERS, HL_LEVEL_CODES, IK3_SYNTAX_ERROR_CODES, IK4_SYNTAX_ERROR_CODES, ISA_MIN_LENGTH, LoopSpecDefinitionError, MAINTENANCE_TYPE_CODES, NM1_QUALIFIERS, NON_SPEC_SEGMENT_ID, PREMIUM_820_BUILD_ERROR_CODES, PREMIUM_820_LOOP_1000A, PREMIUM_820_LOOP_1000B, PREMIUM_820_LOOP_2000A, PREMIUM_820_LOOP_2100A, PREMIUM_820_LOOP_2300A, PREMIUM_820_LOOP_2310A, Premium820BuildError, RARC, RELEASE_CHAR, REMIT_835_BUILD_ERROR_CODES, REMIT_835_LOOP_1000A, REMIT_835_LOOP_1000B, REMIT_835_LOOP_2000, REMIT_835_LOOP_2100, REMIT_835_LOOP_2110, REQUIRED_LOOPS, Remit835BuildError, SERVICE_TYPE_CODES, STATUS_277_LOOP_2000A, STATUS_277_LOOP_2000B, STATUS_277_LOOP_2000C, STATUS_277_LOOP_2000D, STATUS_277_LOOP_2000E, STATUS_277_LOOP_2200, STATUS_277_LOOP_2220, ServicesReview278BuildError, TA1_ACK_CODES, TA1_NOTE_CODES, UNEXPECTED_SEGMENT_CONTEXTS, VERSION, WARNING_CODES, X12BuildError, X12Decimal, X12ParseError, X12ProfileError, X12_ACK_DISPOSITION_CODES, X12_BUILD_ERROR_CODES, build271, build277, build277CA, build278Request, build278Response, build820, build834, build835, build837D, build837I, build837P, build999, buildInterchange, buildTA1, checkClaimBalance, checkRemitTotalBalance, checkServiceLineBalance, collectElementValues, componentOptional, controlNumberMismatch, danglingReleaseChar, decodeSegment, defineLoopSpec, defineProfile, detectDelimiters, elementDecimal, elementDecimalOrZero, elementOptional, elementValue, entitySegmentDiscardedAfterLx, escapeRelease, get271Eligibility, get277CADisposition, get277Status, get278Request, get278Response, get820Payments, get834Enrollments, get834Header, get835, get837Claims, getAllSegmentValues, getDefaultProfile, getSegmentValue, groupCountMismatch, hlParentLevelInvalid, hlParentMismatch, isAcceptDisposition, isClaimAdjustmentGroupCode, isDiagnosisQualifier, isProcedureQualifier, lookupCarc, lookupClaimStatus, lookupClaimStatusCategory, lookupClpStatus, lookupMaintenanceType, lookupRarc, lookupServiceType, missingGe, missingIea, missingRequiredLoop, missingSe, parse999, parseTA1, parseX12, partitionWarnings, pre005010, profiles, readElementDecimal, remitBalanceMismatch, renderCallerValue, resolveHiQualifier, segmentCountMismatch, serializeX12, serviceLineDropped, serviceLineNotDecoded, serviceSegmentWithoutLx, setDefaultProfile, trailingGarbage, transactionCountMismatch, unescapeRelease, unexpectedSegment, unknown837Variant, unknownCarc, unknownClaimStatus, unknownClaimStatusCategory, unknownHiQualifier, unknownMaintenanceType, unknownRarc, unparseableDecimal };
10373
+ export { ACK_BUILD_ERROR_CODES, ALL_WARNING_MESSAGES, AUTH_278_BUILD_ERROR_CODES, AUTH_278_LOOP_2000A, AUTH_278_LOOP_2000B, AUTH_278_LOOP_2000C, AUTH_278_LOOP_2000D, AUTH_278_LOOP_2000E, AUTH_278_LOOP_2000F, AckBuildError, BALANCE_INVARIANTS, BUILD_REFUSAL_VALUE_MAX_LENGTH, BUILD_REFUSAL_VALUE_MAX_RENDERED, CARC, CLAIM_837D_LOOP_2000A, CLAIM_837D_LOOP_2300, CLAIM_837D_LOOP_2400, CLAIM_837I_LOOP_2000A, CLAIM_837I_LOOP_2300, CLAIM_837I_LOOP_2400, CLAIM_837P_LOOP_2000A, CLAIM_837P_LOOP_2300, CLAIM_837P_LOOP_2400, CLAIM_837P_LOOP_2410, CLAIM_837_BUILD_ERROR_CODES, CLAIM_837_LOOP_1000A, CLAIM_837_LOOP_1000B, CLAIM_837_LOOP_2010AA, CLAIM_837_LOOP_2010BA, CLAIM_837_LOOP_2010BB, CLAIM_837_LOOP_2010CA, CLAIM_837_LOOP_2430, CLAIM_ADJUSTMENT_GROUP_CODES, CLAIM_STATUS_277_BUILD_ERROR_CODES, CLAIM_STATUS_CATEGORY_CODES, CLAIM_STATUS_CODES, CLP_STATUS, CONTROL_NUMBER_PAIRS, Claim837BuildError, ClaimStatus277BuildError, DELIMITER_POSITIONS, ELIGIBILITY_271_BUILD_ERROR_CODES, ELIGIBILITY_271_LOOP_2000A, ELIGIBILITY_271_LOOP_2000B, ELIGIBILITY_271_LOOP_2000C, ELIGIBILITY_271_LOOP_2000D, ELIGIBILITY_271_LOOP_2100C, ELIGIBILITY_271_LOOP_2100D, ELIGIBILITY_271_LOOP_2110, ENROLLMENT_834_BUILD_ERROR_CODES, ENROLLMENT_834_LOOP_1000A, ENROLLMENT_834_LOOP_1000B, ENROLLMENT_834_LOOP_2000, ENROLLMENT_834_LOOP_2100A, ENROLLMENT_834_LOOP_2300, ENROLLMENT_834_LOOP_2320, Eligibility271BuildError, Enrollment834BuildError, FATAL_CODES, HI_QUALIFIERS, HL_LEVEL_CODES, IK3_SYNTAX_ERROR_CODES, IK4_SYNTAX_ERROR_CODES, ISA_MIN_LENGTH, LoopSpecDefinitionError, MAINTENANCE_TYPE_CODES, NM1_QUALIFIERS, NON_SPEC_SEGMENT_ID, PREMIUM_820_BUILD_ERROR_CODES, PREMIUM_820_LOOP_1000A, PREMIUM_820_LOOP_1000B, PREMIUM_820_LOOP_2000A, PREMIUM_820_LOOP_2100A, PREMIUM_820_LOOP_2300A, PREMIUM_820_LOOP_2310A, Premium820BuildError, RARC, RELEASE_CHAR, REMIT_835_BUILD_ERROR_CODES, REMIT_835_LOOP_1000A, REMIT_835_LOOP_1000B, REMIT_835_LOOP_2000, REMIT_835_LOOP_2100, REMIT_835_LOOP_2110, REQUIRED_LOOPS, Remit835BuildError, SERVICE_TYPE_CODES, STATUS_277_LOOP_2000A, STATUS_277_LOOP_2000B, STATUS_277_LOOP_2000C, STATUS_277_LOOP_2000D, STATUS_277_LOOP_2000E, STATUS_277_LOOP_2200, STATUS_277_LOOP_2220, ServicesReview278BuildError, TA1_ACK_CODES, TA1_NOTE_CODES, UNEXPECTED_SEGMENT_CONTEXTS, VERSION, WARNING_CODES, X12BuildError, X12Decimal, X12ParseError, X12ProfileError, X12_ACK_DISPOSITION_CODES, X12_BUILD_ERROR_CODES, ambiguous837Variant, amountRowDropped, balanceNotEvaluable, build271, build277, build277CA, build278Request, build278Response, build820, build834, build835, build837D, build837I, build837P, build999, buildInterchange, buildTA1, checkClaimBalance, checkRemitTotalBalance, checkServiceLineBalance, collectElementValues, componentOptional, controlNumberMismatch, danglingReleaseChar, decodeSegment, defineLoopSpec, defineProfile, detectDelimiters, elementDecimal, elementDecimalOrZero, elementOptional, elementValue, entitySegmentDiscardedAfterLx, escapeRelease, get271Eligibility, get277CADisposition, get277Status, get278Request, get278Response, get820Payments, get834Enrollments, get834Header, get835, get837Claims, getAllSegmentValues, getDefaultProfile, getSegmentValue, groupCountMismatch, hlParentLevelInvalid, hlParentMismatch, isAcceptDisposition, isClaimAdjustmentGroupCode, isDiagnosisQualifier, isProcedureQualifier, lookupCarc, lookupClaimStatus, lookupClaimStatusCategory, lookupClpStatus, lookupMaintenanceType, lookupRarc, lookupServiceType, missingGe, missingIea, missingRequiredLoop, missingSe, parse999, parseTA1, parseX12, partitionWarnings, payToAddressRepeated, pre005010, profiles, readElementDecimal, remitBalanceMismatch, renderCallerValue, resolveHiQualifier, segmentCountMismatch, serializeX12, serviceLineDropped, serviceLineNotDecoded, serviceSegmentRepeated, serviceSegmentWithoutLx, setDefaultProfile, statedAmountDiscarded, trailingGarbage, transactionCountMismatch, unescapeRelease, unexpectedSegment, unknown837Variant, unknownCarc, unknownClaimStatus, unknownClaimStatusCategory, unknownHiQualifier, unknownMaintenanceType, unknownRarc, unparseableDecimal };
10196
10374
  //# sourceMappingURL=index.mjs.map
10197
10375
  //# sourceMappingURL=index.mjs.map