@cosyte/x12 0.0.9 → 0.0.10

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.cjs CHANGED
@@ -140,7 +140,10 @@ var WARNING_CODES = {
140
140
  X12_837_UNKNOWN_VARIANT: "X12_837_UNKNOWN_VARIANT",
141
141
  X12_UNKNOWN_CLAIM_STATUS_CATEGORY: "X12_UNKNOWN_CLAIM_STATUS_CATEGORY",
142
142
  X12_UNKNOWN_CLAIM_STATUS: "X12_UNKNOWN_CLAIM_STATUS",
143
- X12_834_UNKNOWN_MAINTENANCE_TYPE: "X12_834_UNKNOWN_MAINTENANCE_TYPE"
143
+ X12_834_UNKNOWN_MAINTENANCE_TYPE: "X12_834_UNKNOWN_MAINTENANCE_TYPE",
144
+ X12_UNPARSEABLE_DECIMAL: "X12_UNPARSEABLE_DECIMAL",
145
+ X12_837_SERVICE_LINE_NOT_DECODED: "X12_837_SERVICE_LINE_NOT_DECODED",
146
+ X12_837_SERVICE_LINE_DROPPED: "X12_837_SERVICE_LINE_DROPPED"
144
147
  };
145
148
  var CONTROL_NUMBER_PAIRS = {
146
149
  /** ISA-13 against IEA-02. */
@@ -213,7 +216,10 @@ var WARNING_MESSAGES = {
213
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.`,
214
217
  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.",
215
218
  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.",
216
- 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."
219
+ 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
+ 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.",
222
+ 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."
217
223
  };
218
224
  var ALL_WARNING_MESSAGES = new Set(
219
225
  Object.values(WARNING_MESSAGES)
@@ -374,6 +380,20 @@ function unknown837Variant(position) {
374
380
  position
375
381
  };
376
382
  }
383
+ function serviceLineNotDecoded(position) {
384
+ return {
385
+ code: WARNING_CODES.X12_837_SERVICE_LINE_NOT_DECODED,
386
+ message: WARNING_MESSAGES.X12_837_SERVICE_LINE_NOT_DECODED,
387
+ position
388
+ };
389
+ }
390
+ function serviceLineDropped(position) {
391
+ return {
392
+ code: WARNING_CODES.X12_837_SERVICE_LINE_DROPPED,
393
+ message: WARNING_MESSAGES.X12_837_SERVICE_LINE_DROPPED,
394
+ position
395
+ };
396
+ }
377
397
  function unknownClaimStatusCategory(position) {
378
398
  return {
379
399
  code: WARNING_CODES.X12_UNKNOWN_CLAIM_STATUS_CATEGORY,
@@ -395,6 +415,13 @@ function unknownMaintenanceType(position) {
395
415
  position
396
416
  };
397
417
  }
418
+ function unparseableDecimal(position) {
419
+ return {
420
+ code: WARNING_CODES.X12_UNPARSEABLE_DECIMAL,
421
+ message: WARNING_MESSAGES.X12_UNPARSEABLE_DECIMAL,
422
+ position
423
+ };
424
+ }
398
425
 
399
426
  // src/parser/release.ts
400
427
  var RELEASE_CHAR = "?";
@@ -908,13 +935,26 @@ function componentOptional(seg, n, p, delimiters) {
908
935
  const v = getSegmentValue(seg, `${String(n).padStart(2, "0")}-${String(p)}`, delimiters);
909
936
  return v === void 0 || v === "" ? void 0 : v;
910
937
  }
911
- function elementDecimal(seg, n, delimiters) {
938
+ function readElementDecimal(seg, n, delimiters) {
912
939
  const raw = elementOptional(seg, n, delimiters);
913
- if (raw === void 0) return void 0;
914
- return X12Decimal.fromString(raw);
940
+ if (raw === void 0) return { value: void 0, status: "absent" };
941
+ const value = X12Decimal.fromString(raw);
942
+ if (value === void 0) return { value: void 0, status: "unparseable" };
943
+ return { value, status: "decoded" };
915
944
  }
916
- function elementDecimalOrZero(seg, n, delimiters) {
917
- return elementDecimal(seg, n, delimiters) ?? X12Decimal.ZERO;
945
+ function elementDecimal(seg, n, delimiters, sink) {
946
+ const read = readElementDecimal(seg, n, delimiters);
947
+ if (read.status === "unparseable" && sink !== void 0) {
948
+ sink.warnings.push(unparseableDecimal({ ...sink.position, elementIndex: n }));
949
+ }
950
+ return read.value;
951
+ }
952
+ function elementDecimalOrZero(seg, n, delimiters, sink) {
953
+ const read = readElementDecimal(seg, n, delimiters);
954
+ if (read.status === "unparseable" && sink !== void 0) {
955
+ sink.warnings.push(unparseableDecimal({ ...sink.position, elementIndex: n }));
956
+ }
957
+ return read.value ?? X12Decimal.ZERO;
918
958
  }
919
959
  function collectElementValues(seg, start, end, delimiters) {
920
960
  const out = [];
@@ -1420,6 +1460,7 @@ function defineLoopSpec(input) {
1420
1460
  // src/code-lists/meta.ts
1421
1461
  function makeLookup(snapshot) {
1422
1462
  return (code) => {
1463
+ if (!Object.hasOwn(snapshot.codes, code)) return void 0;
1423
1464
  const description = snapshot.codes[code];
1424
1465
  if (description === void 0) return void 0;
1425
1466
  return Object.freeze({ code, description });
@@ -1668,7 +1709,7 @@ var CLAIM_ADJUSTMENT_GROUP_CODES = {
1668
1709
  PI: "PI"
1669
1710
  };
1670
1711
  function isClaimAdjustmentGroupCode(value) {
1671
- return value in CLAIM_ADJUSTMENT_GROUP_CODES;
1712
+ return Object.hasOwn(CLAIM_ADJUSTMENT_GROUP_CODES, value);
1672
1713
  }
1673
1714
 
1674
1715
  // src/code-lists/rarc.ts
@@ -1808,6 +1849,7 @@ var HI_QUALIFIERS = {
1808
1849
  }
1809
1850
  };
1810
1851
  function resolveHiQualifier(qualifier) {
1852
+ if (!Object.hasOwn(HI_QUALIFIERS, qualifier)) return void 0;
1811
1853
  return HI_QUALIFIERS[qualifier];
1812
1854
  }
1813
1855
  function isDiagnosisQualifier(qualifier) {
@@ -1917,9 +1959,10 @@ function get835(delimiters, tx) {
1917
1959
  const seg = body[i];
1918
1960
  if (seg === void 0) continue;
1919
1961
  const position = { segmentIndex: i + 1, transactionIndex: 0 };
1962
+ const sink = { warnings, position };
1920
1963
  switch (seg.id) {
1921
1964
  case "BPR": {
1922
- payment = decodeBpr(seg, delimiters);
1965
+ payment = decodeBpr(seg, delimiters, sink);
1923
1966
  bprSegmentIndex = position.segmentIndex;
1924
1967
  break;
1925
1968
  }
@@ -1994,7 +2037,7 @@ function get835(delimiters, tx) {
1994
2037
  case "CLP": {
1995
2038
  flushClaim();
1996
2039
  lastParty = void 0;
1997
- currentClaim = openClaim(seg, delimiters, position.segmentIndex);
2040
+ currentClaim = openClaim(seg, delimiters, position.segmentIndex, sink);
1998
2041
  break;
1999
2042
  }
2000
2043
  case "CAS": {
@@ -2046,7 +2089,7 @@ function get835(delimiters, tx) {
2046
2089
  break;
2047
2090
  }
2048
2091
  case "AMT": {
2049
- const amount = decodeAmt(seg, delimiters);
2092
+ const amount = decodeAmt(seg, delimiters, sink);
2050
2093
  if (amount === void 0) break;
2051
2094
  if (currentServiceLine !== void 0) currentServiceLine.amounts.push(amount);
2052
2095
  else if (currentClaim !== void 0) currentClaim.amounts.push(amount);
@@ -2065,12 +2108,12 @@ function get835(delimiters, tx) {
2065
2108
  }
2066
2109
  case "SVC": {
2067
2110
  flushServiceLine();
2068
- currentServiceLine = openServiceLine(seg, delimiters);
2111
+ currentServiceLine = openServiceLine(seg, delimiters, sink);
2069
2112
  break;
2070
2113
  }
2071
2114
  case "PLB": {
2072
2115
  flushClaim();
2073
- for (const p of decodePlb(seg, delimiters)) providerAdjustments.push(p);
2116
+ for (const p of decodePlb(seg, delimiters, sink)) providerAdjustments.push(p);
2074
2117
  break;
2075
2118
  }
2076
2119
  }
@@ -2101,10 +2144,10 @@ function get835(delimiters, tx) {
2101
2144
  warnings: Object.freeze(warnings.slice())
2102
2145
  });
2103
2146
  }
2104
- function decodeBpr(seg, delimiters) {
2147
+ function decodeBpr(seg, delimiters, sink) {
2105
2148
  return Object.freeze({
2106
2149
  transactionHandlingCode: elementValue(seg, 1, delimiters),
2107
- totalActualPayment: elementDecimalOrZero(seg, 2, delimiters),
2150
+ totalActualPayment: elementDecimalOrZero(seg, 2, delimiters, sink),
2108
2151
  creditDebitFlag: elementValue(seg, 3, delimiters),
2109
2152
  method: elementValue(seg, 4, delimiters),
2110
2153
  paymentFormatCode: elementOptional(seg, 5, delimiters),
@@ -2163,8 +2206,8 @@ function decodePer(seg, delimiters) {
2163
2206
  communications: Object.freeze(comms)
2164
2207
  });
2165
2208
  }
2166
- function decodeAmt(seg, delimiters) {
2167
- const amount = elementDecimal(seg, 2, delimiters);
2209
+ function decodeAmt(seg, delimiters, sink) {
2210
+ const amount = elementDecimal(seg, 2, delimiters, sink);
2168
2211
  if (amount === void 0) return void 0;
2169
2212
  return Object.freeze({
2170
2213
  qualifier: elementValue(seg, 1, delimiters),
@@ -2200,13 +2243,14 @@ function collectMoaRemarks(seg, delimiters) {
2200
2243
  return out;
2201
2244
  }
2202
2245
  function decodeCasAdjustments(seg, delimiters, warnings, position) {
2246
+ const sink = { warnings, position };
2203
2247
  const groupCode = elementValue(seg, 1, delimiters);
2204
2248
  const out = [];
2205
2249
  for (let triple = 0; triple < 6; triple += 1) {
2206
2250
  const base = 2 + triple * 3;
2207
2251
  const reasonCode = elementOptional(seg, base, delimiters);
2208
- const amount = elementDecimal(seg, base + 1, delimiters);
2209
- const quantity = elementDecimal(seg, base + 2, delimiters);
2252
+ const amount = elementDecimal(seg, base + 1, delimiters, sink);
2253
+ const quantity = elementDecimal(seg, base + 2, delimiters, sink);
2210
2254
  if (reasonCode === void 0 && amount === void 0) continue;
2211
2255
  const code = reasonCode ?? "";
2212
2256
  const entry = code === "" ? void 0 : lookupCarc(code);
@@ -2250,7 +2294,7 @@ function decodeNm1(seg, delimiters) {
2250
2294
  })
2251
2295
  };
2252
2296
  }
2253
- function openClaim(seg, delimiters, clpSegmentIndex) {
2297
+ function openClaim(seg, delimiters, clpSegmentIndex, sink) {
2254
2298
  const claimStatusCode = elementValue(seg, 2, delimiters);
2255
2299
  const claimStatusDescription = lookupClpStatus(claimStatusCode)?.description;
2256
2300
  const facilityTypeCode = componentOptional(seg, 8, 1, delimiters) ?? elementOptional(seg, 8, delimiters);
@@ -2260,9 +2304,9 @@ function openClaim(seg, delimiters, clpSegmentIndex) {
2260
2304
  patientControlNumber: elementValue(seg, 1, delimiters),
2261
2305
  claimStatusCode,
2262
2306
  claimStatusDescription,
2263
- totalChargeAmount: elementDecimalOrZero(seg, 3, delimiters),
2264
- totalPaymentAmount: elementDecimalOrZero(seg, 4, delimiters),
2265
- patientResponsibilityAmount: elementDecimalOrZero(seg, 5, delimiters),
2307
+ totalChargeAmount: elementDecimalOrZero(seg, 3, delimiters, sink),
2308
+ totalPaymentAmount: elementDecimalOrZero(seg, 4, delimiters, sink),
2309
+ patientResponsibilityAmount: elementDecimalOrZero(seg, 5, delimiters, sink),
2266
2310
  claimFilingIndicatorCode: elementOptional(seg, 6, delimiters),
2267
2311
  payerClaimControlNumber: elementOptional(seg, 7, delimiters),
2268
2312
  facilityTypeCode,
@@ -2281,7 +2325,7 @@ function openClaim(seg, delimiters, clpSegmentIndex) {
2281
2325
  serviceLines: []
2282
2326
  };
2283
2327
  }
2284
- function openServiceLine(seg, delimiters) {
2328
+ function openServiceLine(seg, delimiters, sink) {
2285
2329
  const productServiceIdQualifier = componentOptional(seg, 1, 1, delimiters) ?? "";
2286
2330
  const productServiceId = componentOptional(seg, 1, 2, delimiters) ?? "";
2287
2331
  const modifiers = [];
@@ -2289,18 +2333,20 @@ function openServiceLine(seg, delimiters) {
2289
2333
  const m = componentOptional(seg, 1, comp, delimiters);
2290
2334
  if (m !== void 0) modifiers.push(m);
2291
2335
  }
2292
- const revenueCode = elementOptional(seg, 5, delimiters);
2293
- const paidUnitsOfService = elementDecimal(seg, 7, delimiters);
2336
+ const revenueCode = elementOptional(seg, 4, delimiters);
2337
+ const paidUnitsOfService = elementDecimal(seg, 5, delimiters, sink);
2338
+ const originalUnitsOfService = elementDecimal(seg, 7, delimiters, sink);
2294
2339
  const originalServiceIdQualifier = componentOptional(seg, 6, 1, delimiters);
2295
2340
  const originalServiceId = componentOptional(seg, 6, 2, delimiters);
2296
2341
  return {
2297
2342
  productServiceIdQualifier,
2298
2343
  productServiceId,
2299
2344
  modifiers,
2300
- chargeAmount: elementDecimalOrZero(seg, 2, delimiters),
2301
- paymentAmount: elementDecimalOrZero(seg, 3, delimiters),
2345
+ chargeAmount: elementDecimalOrZero(seg, 2, delimiters, sink),
2346
+ paymentAmount: elementDecimalOrZero(seg, 3, delimiters, sink),
2302
2347
  revenueCode,
2303
2348
  paidUnitsOfService,
2349
+ originalUnitsOfService,
2304
2350
  originalServiceId,
2305
2351
  originalServiceIdQualifier,
2306
2352
  serviceDateStart: void 0,
@@ -2326,13 +2372,13 @@ function attachServiceLineDtm(line, seg, delimiters) {
2326
2372
  if (qualifier === "472" || qualifier === "150") line.serviceDateStart = date;
2327
2373
  if (qualifier === "472" || qualifier === "151") line.serviceDateEnd = date;
2328
2374
  }
2329
- function decodePlb(seg, delimiters) {
2375
+ function decodePlb(seg, delimiters, sink) {
2330
2376
  const providerId = elementValue(seg, 1, delimiters);
2331
2377
  const fiscalPeriodDate = elementValue(seg, 2, delimiters);
2332
2378
  const out = [];
2333
2379
  for (let pair = 0; pair < 6; pair += 1) {
2334
2380
  const composite = 3 + pair * 2;
2335
- const amount = elementDecimal(seg, composite + 1, delimiters);
2381
+ const amount = elementDecimal(seg, composite + 1, delimiters, sink);
2336
2382
  const reasonCode = componentOptional(seg, composite, 1, delimiters);
2337
2383
  const subCode = componentOptional(seg, composite, 2, delimiters);
2338
2384
  if (amount === void 0 && reasonCode === void 0) continue;
@@ -2396,6 +2442,7 @@ function freezeServiceLine(acc) {
2396
2442
  paymentAmount: acc.paymentAmount,
2397
2443
  revenueCode: acc.revenueCode,
2398
2444
  paidUnitsOfService: acc.paidUnitsOfService,
2445
+ originalUnitsOfService: acc.originalUnitsOfService,
2399
2446
  originalServiceId: acc.originalServiceId,
2400
2447
  originalServiceIdQualifier: acc.originalServiceIdQualifier,
2401
2448
  serviceDateStart: acc.serviceDateStart,
@@ -2511,6 +2558,49 @@ function isRealArray(value) {
2511
2558
  return Array.isArray(value);
2512
2559
  }
2513
2560
 
2561
+ // src/builder/caller-decimal.ts
2562
+ function describeCallerDecimal(value) {
2563
+ if (value === null) return "null";
2564
+ if (value === void 0) return "undefined";
2565
+ const type = typeof value;
2566
+ if (type === "object") return Array.isArray(value) ? "an array" : "an object";
2567
+ if (type === "function") return "a function";
2568
+ return `a ${type} (${renderCallerValue(value)})`;
2569
+ }
2570
+ function requireCallerDecimal(value, at, refuse) {
2571
+ if (value instanceof X12Decimal) return value;
2572
+ refuse(
2573
+ `${at}: every numeric element value must be an X12Decimal, but received ${describeCallerDecimal(value)}. Values are never coerced - a JavaScript number has no X12 lexical form (0.1 + 0.2 renders as "0.30000000000000004", 1e21 as "1e+21", NaN as "NaN"), and rounding one here would guess a scale the caller never stated. Build it with X12Decimal.fromString() at the call site.`
2574
+ );
2575
+ }
2576
+
2577
+ // src/builder/caller-segment.ts
2578
+ function describeSegmentValue(value) {
2579
+ if (value === null) return "null";
2580
+ if (value === void 0) return "undefined";
2581
+ const type = typeof value;
2582
+ if (type === "object") return Array.isArray(value) ? "an array" : "an object";
2583
+ if (type === "function") return "a function";
2584
+ return `a ${type} (${renderCallerValue(value)})`;
2585
+ }
2586
+ function locateSlot(parts, index) {
2587
+ const id = parts[0];
2588
+ const position = String(index).padStart(2, "0");
2589
+ if (typeof id !== "string" || id.length === 0) return `element ${String(index)}`;
2590
+ return `${renderCallerValue(id)}-${position}`;
2591
+ }
2592
+ function requireCallerSegment(parts, at, refuse) {
2593
+ let index = 0;
2594
+ for (const part of parts) {
2595
+ if (typeof part !== "string") {
2596
+ refuse(
2597
+ `${at}: ${locateSlot(parts, index)} must be a string, but received ${describeSegmentValue(part)}. Values are never coerced - a JavaScript number cannot carry a leading-zero identifier, so converting here could emit a different value than the caller sent. Convert at the call site.`
2598
+ );
2599
+ }
2600
+ index += 1;
2601
+ }
2602
+ }
2603
+
2514
2604
  // src/builder/caller-string.ts
2515
2605
  function describeCallerValue(value) {
2516
2606
  if (value === null) return "null";
@@ -2534,6 +2624,9 @@ function makeCallerEscaper(delimiters, at, refuse) {
2534
2624
  function refuseSpec(message) {
2535
2625
  throw new Remit835BuildError(REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_INVALID_SPEC, message);
2536
2626
  }
2627
+ function escDec(value, esc) {
2628
+ return esc(requireCallerDecimal(value, "build835", refuseSpec).toString());
2629
+ }
2537
2630
  var X221A1_VERSION_RELEASE = "005010X221A1";
2538
2631
  var X12_835_FUNCTIONAL_ID = "HP";
2539
2632
  var X12_AGENCY_CODE = "X";
@@ -2557,6 +2650,7 @@ function build835(spec) {
2557
2650
  };
2558
2651
  const esc = makeCallerEscaper(delimiters, "build835", refuseSpec);
2559
2652
  const seg = (parts) => {
2653
+ requireCallerSegment(parts, "build835", refuseSpec);
2560
2654
  let end = parts.length;
2561
2655
  while (end > 1 && parts[end - 1] === "") end -= 1;
2562
2656
  return parts.slice(0, end).join(elementSeparator) + segmentTerminator;
@@ -2618,9 +2712,9 @@ function build835(spec) {
2618
2712
  // GS-02
2619
2713
  esc(applicationReceiverCode),
2620
2714
  // GS-03
2621
- groupDate,
2715
+ esc(groupDate),
2622
2716
  // GS-04 - CCYYMMDD
2623
- groupTime,
2717
+ esc(groupTime),
2624
2718
  // GS-05 - HHMM
2625
2719
  esc(envelope.groupControlNumber),
2626
2720
  // GS-06
@@ -2636,7 +2730,7 @@ function build835(spec) {
2636
2730
  seg([
2637
2731
  "BPR",
2638
2732
  esc(spec.payment.transactionHandlingCode),
2639
- esc(spec.payment.totalActualPayment.toString()),
2733
+ escDec(spec.payment.totalActualPayment, esc),
2640
2734
  esc(spec.payment.creditDebitFlag),
2641
2735
  esc(spec.payment.method),
2642
2736
  esc(spec.payment.paymentFormatCode ?? ""),
@@ -2771,6 +2865,7 @@ function materializeServiceLine(spec) {
2771
2865
  paymentAmount: spec.paymentAmount,
2772
2866
  revenueCode: spec.revenueCode,
2773
2867
  paidUnitsOfService: spec.paidUnitsOfService,
2868
+ originalUnitsOfService: spec.originalUnitsOfService,
2774
2869
  originalServiceId: spec.originalServiceId,
2775
2870
  originalServiceIdQualifier: spec.originalServiceIdQualifier,
2776
2871
  serviceDateStart: spec.serviceDateStart,
@@ -2866,9 +2961,9 @@ function emitClaim(claim, body, seg, esc, comp) {
2866
2961
  "CLP",
2867
2962
  esc(claim.patientControlNumber),
2868
2963
  esc(claim.claimStatusCode),
2869
- esc(claim.totalChargeAmount.toString()),
2870
- esc(claim.totalPaymentAmount.toString()),
2871
- esc(claim.patientResponsibilityAmount.toString()),
2964
+ escDec(claim.totalChargeAmount, esc),
2965
+ escDec(claim.totalPaymentAmount, esc),
2966
+ escDec(claim.patientResponsibilityAmount, esc),
2872
2967
  esc(claim.claimFilingIndicatorCode ?? ""),
2873
2968
  esc(claim.payerClaimControlNumber ?? ""),
2874
2969
  comp([claim.facilityTypeCode ?? "", "", claim.claimFrequencyCode ?? ""])
@@ -2890,7 +2985,7 @@ function emitClaim(claim, body, seg, esc, comp) {
2890
2985
  }
2891
2986
  for (const ref of claim.references ?? []) body.push(emitRef(ref, seg, esc));
2892
2987
  for (const amt of claim.amounts ?? []) {
2893
- body.push(seg(["AMT", esc(amt.qualifier), esc(amt.amount.toString())]));
2988
+ body.push(seg(["AMT", esc(amt.qualifier), escDec(amt.amount, esc)]));
2894
2989
  }
2895
2990
  for (const remark of claim.remarks ?? []) {
2896
2991
  body.push(seg(["LQ", esc(remark.system), esc(remark.code)]));
@@ -2910,20 +3005,24 @@ function emitServiceLine(line, body, seg, esc, comp) {
2910
3005
  seg([
2911
3006
  "SVC",
2912
3007
  svc01,
2913
- esc(line.chargeAmount.toString()),
2914
- esc(line.paymentAmount.toString()),
2915
- "",
2916
- // SVC-04 - revenue code is SVC-05 in X221A1; SVC-04 unused
3008
+ escDec(line.chargeAmount, esc),
3009
+ escDec(line.paymentAmount, esc),
3010
+ // SVC-04 is the NUBC revenue code (element 234, Product/Service ID) and
3011
+ // SVC-05 is the Units of Service PAID Count (element 380, Quantity).
3012
+ // SVC-07 is the ORIGINAL (submitted) units, a different quantity from
3013
+ // SVC-05 - so the two never share a field. See the SVC note in
3014
+ // KNOWN-LIMITATIONS.md for the sources.
2917
3015
  esc(line.revenueCode ?? ""),
3016
+ line.paidUnitsOfService === void 0 ? "" : escDec(line.paidUnitsOfService, esc),
2918
3017
  svc06,
2919
- line.paidUnitsOfService === void 0 ? "" : esc(line.paidUnitsOfService.toString())
3018
+ line.originalUnitsOfService === void 0 ? "" : escDec(line.originalUnitsOfService, esc)
2920
3019
  ])
2921
3020
  );
2922
3021
  emitServiceLineDtm(line, body, seg, esc);
2923
3022
  emitCasGroup(line.adjustments, "build835: serviceLine.adjustments", body, seg, esc);
2924
3023
  for (const ref of line.references ?? []) body.push(emitRef(ref, seg, esc));
2925
3024
  for (const amt of line.amounts ?? []) {
2926
- body.push(seg(["AMT", esc(amt.qualifier), esc(amt.amount.toString())]));
3025
+ body.push(seg(["AMT", esc(amt.qualifier), escDec(amt.amount, esc)]));
2927
3026
  }
2928
3027
  for (const remark of line.remarks ?? []) {
2929
3028
  body.push(seg(["LQ", esc(remark.system), esc(remark.code)]));
@@ -2955,8 +3054,8 @@ function emitCasGroup(raw, at, body, seg, esc) {
2955
3054
  if (adj === void 0 || adj.groupCode !== groupCode) break;
2956
3055
  parts.push(
2957
3056
  esc(adj.reasonCode),
2958
- esc(adj.amount.toString()),
2959
- adj.quantity === void 0 ? "" : esc(adj.quantity.toString())
3057
+ escDec(adj.amount, esc),
3058
+ adj.quantity === void 0 ? "" : escDec(adj.quantity, esc)
2960
3059
  );
2961
3060
  triples += 1;
2962
3061
  i += 1;
@@ -3014,7 +3113,7 @@ function emitProviderAdjustments(raw, at, body, seg, esc, comp) {
3014
3113
  if (adj === void 0 || adj.providerId !== providerId || adj.fiscalPeriodDate !== fiscalPeriodDate) {
3015
3114
  break;
3016
3115
  }
3017
- parts.push(comp([adj.reasonCode, adj.subCode ?? ""]), esc(adj.amount.toString()));
3116
+ parts.push(comp([adj.reasonCode, adj.subCode ?? ""]), escDec(adj.amount, esc));
3018
3117
  pairs += 1;
3019
3118
  i += 1;
3020
3119
  }
@@ -3112,13 +3211,22 @@ var REMIT_835_LOOP_1000B = defineLoopSpec({
3112
3211
  ]
3113
3212
  });
3114
3213
 
3214
+ // src/parser/lookup.ts
3215
+ function wireLookup(entries) {
3216
+ const table = /* @__PURE__ */ Object.create(null);
3217
+ for (const [key, value] of Object.entries(entries)) {
3218
+ table[key] = value;
3219
+ }
3220
+ return Object.freeze(table);
3221
+ }
3222
+
3115
3223
  // src/transactions/claim/get-837.ts
3116
- var VARIANT_BY_ICR = Object.freeze({
3224
+ var VARIANT_BY_ICR = wireLookup({
3117
3225
  "005010X222A2": "P",
3118
3226
  "005010X223A3": "I",
3119
3227
  "005010X224A2": "D"
3120
3228
  });
3121
- var VARIANT_BY_SV_SEGMENT = Object.freeze({
3229
+ var VARIANT_BY_SV_SEGMENT = wireLookup({
3122
3230
  SV1: "P",
3123
3231
  SV2: "I",
3124
3232
  SV3: "D"
@@ -3139,7 +3247,7 @@ var NM1_QUALIFIERS = Object.freeze({
3139
3247
  PAYER: "PR",
3140
3248
  PATIENT: "QC"
3141
3249
  });
3142
- var EXPECTED_PARENT_LEVEL = Object.freeze({
3250
+ var EXPECTED_PARENT_LEVEL = wireLookup({
3143
3251
  [HL_LEVEL_CODES.INFORMATION_SOURCE]: void 0,
3144
3252
  [HL_LEVEL_CODES.SUBSCRIBER]: HL_LEVEL_CODES.INFORMATION_SOURCE,
3145
3253
  [HL_LEVEL_CODES.DEPENDENT]: HL_LEVEL_CODES.SUBSCRIBER
@@ -3194,6 +3302,9 @@ function get837Claims(delimiters, tx, opts) {
3194
3302
  const flushServiceLine = () => {
3195
3303
  flushAdjudication();
3196
3304
  if (currentServiceLine !== void 0 && currentClaim !== void 0) {
3305
+ if (!currentServiceLine.serviceSegmentDecoded) {
3306
+ warnings.push(serviceLineNotDecoded(currentServiceLine.position));
3307
+ }
3197
3308
  currentClaim.serviceLines.push(freezeServiceLine2(currentServiceLine));
3198
3309
  }
3199
3310
  currentServiceLine = void 0;
@@ -3267,6 +3378,7 @@ function get837Claims(delimiters, tx, opts) {
3267
3378
  const seg = body[i];
3268
3379
  if (seg === void 0) continue;
3269
3380
  const position = { segmentIndex: i + 1, transactionIndex: 0 };
3381
+ const sink = { warnings, position };
3270
3382
  switch (seg.id) {
3271
3383
  case "HL": {
3272
3384
  flushClaim();
@@ -3414,16 +3526,21 @@ function get837Claims(delimiters, tx, opts) {
3414
3526
  warnings.push(missingRequiredLoop(position, REQUIRED_LOOPS.PAYER_NAME_2010BB));
3415
3527
  }
3416
3528
  }
3417
- currentClaim = openClaim2(seg, delimiters, {
3418
- variant,
3419
- hierarchy: currentPatientHl ?? currentSubscriberHl,
3420
- billingProvider,
3421
- payToAddress,
3422
- payToPlan,
3423
- subscriber: currentSubscriberMember,
3424
- payer: currentPayer,
3425
- patient: currentPatientMember
3426
- });
3529
+ currentClaim = openClaim2(
3530
+ seg,
3531
+ delimiters,
3532
+ {
3533
+ variant,
3534
+ hierarchy: currentPatientHl ?? currentSubscriberHl,
3535
+ billingProvider,
3536
+ payToAddress,
3537
+ payToPlan,
3538
+ subscriber: currentSubscriberMember,
3539
+ payer: currentPayer,
3540
+ patient: currentPatientMember
3541
+ },
3542
+ sink
3543
+ );
3427
3544
  activeEntity = void 0;
3428
3545
  context = { kind: "loop2300" };
3429
3546
  break;
@@ -3462,7 +3579,7 @@ function get837Claims(delimiters, tx, opts) {
3462
3579
  break;
3463
3580
  }
3464
3581
  case "AMT": {
3465
- const amount = decodeAmt2(seg, delimiters);
3582
+ const amount = decodeAmt2(seg, delimiters, sink);
3466
3583
  if (amount === void 0) break;
3467
3584
  if (currentAdjudication !== void 0) break;
3468
3585
  if (currentServiceLine !== void 0) currentServiceLine.amounts.push(amount);
@@ -3471,22 +3588,26 @@ function get837Claims(delimiters, tx, opts) {
3471
3588
  }
3472
3589
  case "LX": {
3473
3590
  flushServiceLine();
3474
- if (currentClaim === void 0) break;
3475
- currentServiceLine = openServiceLine2(seg, delimiters, currentClaim.variant);
3591
+ if (currentClaim === void 0) {
3592
+ warnings.push(serviceLineDropped(position));
3593
+ break;
3594
+ }
3595
+ currentServiceLine = openServiceLine2(seg, delimiters, currentClaim.variant, position);
3596
+ if (currentServiceLine === void 0) warnings.push(serviceLineDropped(position));
3476
3597
  activeEntity = void 0;
3477
3598
  context = { kind: "loop2400" };
3478
3599
  break;
3479
3600
  }
3480
3601
  case "SV1": {
3481
- if (currentServiceLine !== void 0) decodeSv1(currentServiceLine, seg, delimiters);
3602
+ if (currentServiceLine !== void 0) decodeSv1(currentServiceLine, seg, delimiters, sink);
3482
3603
  break;
3483
3604
  }
3484
3605
  case "SV2": {
3485
- if (currentServiceLine !== void 0) decodeSv2(currentServiceLine, seg, delimiters);
3606
+ if (currentServiceLine !== void 0) decodeSv2(currentServiceLine, seg, delimiters, sink);
3486
3607
  break;
3487
3608
  }
3488
3609
  case "SV3": {
3489
- if (currentServiceLine !== void 0) decodeSv3(currentServiceLine, seg, delimiters);
3610
+ if (currentServiceLine !== void 0) decodeSv3(currentServiceLine, seg, delimiters, sink);
3490
3611
  break;
3491
3612
  }
3492
3613
  case "TOO": {
@@ -3512,7 +3633,7 @@ function get837Claims(delimiters, tx, opts) {
3512
3633
  }
3513
3634
  currentServiceLine.drug = {
3514
3635
  ...currentServiceLine.drug,
3515
- quantity: elementDecimal(seg, 4, delimiters),
3636
+ quantity: elementDecimal(seg, 4, delimiters, sink),
3516
3637
  unitOfMeasure: componentOptional(seg, 5, 1, delimiters)
3517
3638
  };
3518
3639
  break;
@@ -3520,7 +3641,7 @@ function get837Claims(delimiters, tx, opts) {
3520
3641
  case "SVD": {
3521
3642
  flushAdjudication();
3522
3643
  if (currentServiceLine === void 0) break;
3523
- currentAdjudication = openAdjudication(seg, delimiters);
3644
+ currentAdjudication = openAdjudication(seg, delimiters, sink);
3524
3645
  break;
3525
3646
  }
3526
3647
  case "CAS": {
@@ -3698,19 +3819,20 @@ function decodeNte(seg, delimiters) {
3698
3819
  description: note
3699
3820
  });
3700
3821
  }
3701
- function decodeAmt2(seg, delimiters) {
3702
- const amount = elementDecimal(seg, 2, delimiters);
3822
+ function decodeAmt2(seg, delimiters, sink) {
3823
+ const amount = elementDecimal(seg, 2, delimiters, sink);
3703
3824
  if (amount === void 0) return void 0;
3704
3825
  return Object.freeze({ qualifier: elementValue(seg, 1, delimiters), amount });
3705
3826
  }
3706
3827
  function decodeCas(seg, delimiters, warnings, position) {
3828
+ const sink = { warnings, position };
3707
3829
  const groupCode = elementValue(seg, 1, delimiters);
3708
3830
  const out = [];
3709
3831
  for (let triple = 0; triple < 6; triple += 1) {
3710
3832
  const base = 2 + triple * 3;
3711
3833
  const reasonCode = elementOptional(seg, base, delimiters);
3712
- const amount = elementDecimal(seg, base + 1, delimiters);
3713
- const quantity = elementDecimal(seg, base + 2, delimiters);
3834
+ const amount = elementDecimal(seg, base + 1, delimiters, sink);
3835
+ const quantity = elementDecimal(seg, base + 2, delimiters, sink);
3714
3836
  if (reasonCode === void 0 && amount === void 0) continue;
3715
3837
  const code = reasonCode ?? "";
3716
3838
  const entry = code === "" ? void 0 : lookupCarc(code);
@@ -3745,7 +3867,7 @@ var EMPTY_SUBSCRIBER_INFO = Object.freeze({
3745
3867
  groupName: void 0,
3746
3868
  claimFilingIndicator: void 0
3747
3869
  });
3748
- function openClaim2(seg, delimiters, ctx) {
3870
+ function openClaim2(seg, delimiters, ctx, sink) {
3749
3871
  const {
3750
3872
  variant,
3751
3873
  hierarchy,
@@ -3766,7 +3888,7 @@ function openClaim2(seg, delimiters, ctx) {
3766
3888
  payer,
3767
3889
  patient,
3768
3890
  claimId: elementValue(seg, 1, delimiters),
3769
- totalCharge: elementDecimalOrZero(seg, 2, delimiters),
3891
+ totalCharge: elementDecimalOrZero(seg, 2, delimiters, sink),
3770
3892
  placeOfServiceCode: componentOptional(seg, 5, 1, delimiters),
3771
3893
  facilityCodeQualifier: componentOptional(seg, 5, 2, delimiters),
3772
3894
  claimFrequencyCode: componentOptional(seg, 5, 3, delimiters),
@@ -3786,10 +3908,12 @@ function openClaim2(seg, delimiters, ctx) {
3786
3908
  serviceLines: []
3787
3909
  };
3788
3910
  }
3789
- function openServiceLine2(seg, delimiters, variant) {
3911
+ function openServiceLine2(seg, delimiters, variant, position) {
3790
3912
  const lineNumber = elementValue(seg, 1, delimiters);
3791
3913
  const base = {
3792
3914
  lineNumber,
3915
+ position,
3916
+ serviceSegmentDecoded: false,
3793
3917
  charge: X12Decimal.ZERO,
3794
3918
  units: X12Decimal.ZERO,
3795
3919
  unitOfMeasure: void 0,
@@ -3841,8 +3965,9 @@ function openServiceLine2(seg, delimiters, variant) {
3841
3965
  }
3842
3966
  return void 0;
3843
3967
  }
3844
- function decodeSv1(acc, seg, delimiters) {
3968
+ function decodeSv1(acc, seg, delimiters, sink) {
3845
3969
  if (acc.variant !== "P") return;
3970
+ acc.serviceSegmentDecoded = true;
3846
3971
  acc.procedureQualifier = componentOptional(seg, 1, 1, delimiters) ?? "";
3847
3972
  acc.procedureCode = componentOptional(seg, 1, 2, delimiters) ?? "";
3848
3973
  const mods = [];
@@ -3851,9 +3976,9 @@ function decodeSv1(acc, seg, delimiters) {
3851
3976
  if (m !== void 0) mods.push(m);
3852
3977
  }
3853
3978
  acc.modifiers = mods;
3854
- acc.charge = elementDecimalOrZero(seg, 2, delimiters);
3979
+ acc.charge = elementDecimalOrZero(seg, 2, delimiters, sink);
3855
3980
  acc.unitOfMeasure = elementOptional(seg, 3, delimiters);
3856
- acc.units = elementDecimalOrZero(seg, 4, delimiters);
3981
+ acc.units = elementDecimalOrZero(seg, 4, delimiters, sink);
3857
3982
  acc.placeOfServiceCode = elementOptional(seg, 5, delimiters);
3858
3983
  const pointers = [];
3859
3984
  for (let p = 1; p <= 4; p += 1) {
@@ -3865,8 +3990,9 @@ function decodeSv1(acc, seg, delimiters) {
3865
3990
  acc.epsdtIndicator = elementOptional(seg, 11, delimiters);
3866
3991
  acc.familyPlanningIndicator = elementOptional(seg, 12, delimiters);
3867
3992
  }
3868
- function decodeSv2(acc, seg, delimiters) {
3993
+ function decodeSv2(acc, seg, delimiters, sink) {
3869
3994
  if (acc.variant !== "I") return;
3995
+ acc.serviceSegmentDecoded = true;
3870
3996
  acc.revenueCode = elementValue(seg, 1, delimiters);
3871
3997
  acc.procedureQualifier = componentOptional(seg, 2, 1, delimiters);
3872
3998
  acc.procedureCode = componentOptional(seg, 2, 2, delimiters);
@@ -3876,14 +4002,15 @@ function decodeSv2(acc, seg, delimiters) {
3876
4002
  if (m !== void 0) mods.push(m);
3877
4003
  }
3878
4004
  acc.modifiers = mods;
3879
- acc.charge = elementDecimalOrZero(seg, 3, delimiters);
4005
+ acc.charge = elementDecimalOrZero(seg, 3, delimiters, sink);
3880
4006
  acc.unitOfMeasure = elementOptional(seg, 4, delimiters);
3881
- acc.units = elementDecimalOrZero(seg, 5, delimiters);
3882
- acc.serviceLineRate = elementDecimal(seg, 6, delimiters);
3883
- acc.nonCoveredCharge = elementDecimal(seg, 7, delimiters);
4007
+ acc.units = elementDecimalOrZero(seg, 5, delimiters, sink);
4008
+ acc.serviceLineRate = elementDecimal(seg, 6, delimiters, sink);
4009
+ acc.nonCoveredCharge = elementDecimal(seg, 7, delimiters, sink);
3884
4010
  }
3885
- function decodeSv3(acc, seg, delimiters) {
4011
+ function decodeSv3(acc, seg, delimiters, sink) {
3886
4012
  if (acc.variant !== "D") return;
4013
+ acc.serviceSegmentDecoded = true;
3887
4014
  acc.procedureQualifier = componentOptional(seg, 1, 1, delimiters) ?? "";
3888
4015
  acc.procedureCode = componentOptional(seg, 1, 2, delimiters) ?? "";
3889
4016
  const mods = [];
@@ -3892,7 +4019,7 @@ function decodeSv3(acc, seg, delimiters) {
3892
4019
  if (m !== void 0) mods.push(m);
3893
4020
  }
3894
4021
  acc.modifiers = mods;
3895
- acc.charge = elementDecimalOrZero(seg, 2, delimiters);
4022
+ acc.charge = elementDecimalOrZero(seg, 2, delimiters, sink);
3896
4023
  acc.placeOfServiceCode = elementOptional(seg, 3, delimiters);
3897
4024
  const cavities = [];
3898
4025
  for (let p = 1; p <= 5; p += 1) {
@@ -3901,15 +4028,15 @@ function decodeSv3(acc, seg, delimiters) {
3901
4028
  }
3902
4029
  acc.oralCavityArea = cavities;
3903
4030
  acc.prosthesisCrownInlayCode = elementOptional(seg, 5, delimiters);
3904
- acc.units = elementDecimalOrZero(seg, 6, delimiters);
4031
+ acc.units = elementDecimalOrZero(seg, 6, delimiters, sink);
3905
4032
  }
3906
- function openAdjudication(seg, delimiters) {
4033
+ function openAdjudication(seg, delimiters, sink) {
3907
4034
  return {
3908
4035
  otherPayerId: elementValue(seg, 1, delimiters),
3909
- amountPaid: elementDecimalOrZero(seg, 2, delimiters),
4036
+ amountPaid: elementDecimalOrZero(seg, 2, delimiters, sink),
3910
4037
  procedureQualifier: componentOptional(seg, 3, 1, delimiters),
3911
4038
  procedureCode: componentOptional(seg, 3, 2, delimiters),
3912
- paidUnits: elementDecimal(seg, 5, delimiters),
4039
+ paidUnits: elementDecimal(seg, 5, delimiters, sink),
3913
4040
  adjustments: [],
3914
4041
  dateAdjudicated: void 0
3915
4042
  };
@@ -4201,6 +4328,12 @@ function refuseHierarchy(message) {
4201
4328
  function refuseSpec2(message) {
4202
4329
  throw new Claim837BuildError(CLAIM_837_BUILD_ERROR_CODES.X12_837_BUILD_INVALID_SPEC, message);
4203
4330
  }
4331
+ function decStr(value) {
4332
+ return requireCallerDecimal(value, "build837", refuseSpec2).toString();
4333
+ }
4334
+ function escDec2(value, esc) {
4335
+ return esc(decStr(value));
4336
+ }
4204
4337
  var VERSION_BY_VARIANT = {
4205
4338
  P: "005010X222A2",
4206
4339
  I: "005010X223A3",
@@ -4234,6 +4367,7 @@ function buildClaim837(variant, spec) {
4234
4367
  };
4235
4368
  const esc = makeCallerEscaper(delimiters, "build837", refuseSpec2);
4236
4369
  const seg = (parts) => {
4370
+ requireCallerSegment(parts, "build837", refuseSpec2);
4237
4371
  let end = parts.length;
4238
4372
  while (end > 1 && parts[end - 1] === "") end -= 1;
4239
4373
  return parts.slice(0, end).join(elementSeparator) + segmentTerminator;
@@ -4277,8 +4411,8 @@ function buildClaim837(variant, spec) {
4277
4411
  X12_837_FUNCTIONAL_ID,
4278
4412
  esc(applicationSenderCode),
4279
4413
  esc(applicationReceiverCode),
4280
- groupDate,
4281
- groupTime,
4414
+ esc(groupDate),
4415
+ esc(groupTime),
4282
4416
  esc(envelope.groupControlNumber),
4283
4417
  X12_AGENCY_CODE2,
4284
4418
  versionRelease
@@ -4493,7 +4627,7 @@ function emitClaim2(variant, claim, body, ctx) {
4493
4627
  ctx.seg([
4494
4628
  "CLM",
4495
4629
  ctx.esc(claim.claimId),
4496
- ctx.esc(claim.totalCharge.toString()),
4630
+ escDec2(claim.totalCharge, ctx.esc),
4497
4631
  "",
4498
4632
  "",
4499
4633
  ctx.comp([
@@ -4541,8 +4675,8 @@ function emitOtherSubscriber(other, body, ctx) {
4541
4675
  if (other.otherPayer !== void 0) emitEntity(other.otherPayer, body, ctx);
4542
4676
  }
4543
4677
  function emitServiceLine2(variant, line, lineNumber, body, ctx) {
4544
- body.push(ctx.seg(["LX", line.lineNumber ?? String(lineNumber)]));
4545
- const units = line.units === void 0 ? "0" : line.units.toString();
4678
+ body.push(ctx.seg(["LX", ctx.esc(line.lineNumber ?? String(lineNumber))]));
4679
+ const units = line.units === void 0 ? "0" : decStr(line.units);
4546
4680
  if (line.variant === "P") {
4547
4681
  const proc = ctx.comp([line.procedureQualifier, line.procedureCode, ...line.modifiers ?? []]);
4548
4682
  const pointers = ctx.comp(line.diagnosisPointers ?? []);
@@ -4550,7 +4684,7 @@ function emitServiceLine2(variant, line, lineNumber, body, ctx) {
4550
4684
  ctx.seg([
4551
4685
  "SV1",
4552
4686
  proc,
4553
- ctx.esc(line.charge.toString()),
4687
+ escDec2(line.charge, ctx.esc),
4554
4688
  ctx.esc(line.unitOfMeasure ?? ""),
4555
4689
  ctx.esc(units),
4556
4690
  ctx.esc(line.placeOfServiceCode ?? ""),
@@ -4574,11 +4708,11 @@ function emitServiceLine2(variant, line, lineNumber, body, ctx) {
4574
4708
  "SV2",
4575
4709
  ctx.esc(line.revenueCode),
4576
4710
  proc,
4577
- ctx.esc(line.charge.toString()),
4711
+ escDec2(line.charge, ctx.esc),
4578
4712
  ctx.esc(line.unitOfMeasure ?? ""),
4579
4713
  ctx.esc(units),
4580
- line.serviceLineRate === void 0 ? "" : ctx.esc(line.serviceLineRate.toString()),
4581
- line.nonCoveredCharge === void 0 ? "" : ctx.esc(line.nonCoveredCharge.toString())
4714
+ line.serviceLineRate === void 0 ? "" : escDec2(line.serviceLineRate, ctx.esc),
4715
+ line.nonCoveredCharge === void 0 ? "" : escDec2(line.nonCoveredCharge, ctx.esc)
4582
4716
  ])
4583
4717
  );
4584
4718
  } else {
@@ -4588,7 +4722,7 @@ function emitServiceLine2(variant, line, lineNumber, body, ctx) {
4588
4722
  ctx.seg([
4589
4723
  "SV3",
4590
4724
  proc,
4591
- ctx.esc(line.charge.toString()),
4725
+ escDec2(line.charge, ctx.esc),
4592
4726
  ctx.esc(line.placeOfServiceCode ?? ""),
4593
4727
  cavity,
4594
4728
  ctx.esc(line.prosthesisCrownInlayCode ?? ""),
@@ -4607,7 +4741,7 @@ function emitServiceLine2(variant, line, lineNumber, body, ctx) {
4607
4741
  "",
4608
4742
  "",
4609
4743
  "",
4610
- drug.quantity === void 0 ? "" : ctx.esc(drug.quantity.toString()),
4744
+ drug.quantity === void 0 ? "" : escDec2(drug.quantity, ctx.esc),
4611
4745
  ctx.esc(drug.unitOfMeasure ?? "")
4612
4746
  ])
4613
4747
  );
@@ -4637,10 +4771,10 @@ function emitAdjudication(adj, body, ctx) {
4637
4771
  ctx.seg([
4638
4772
  "SVD",
4639
4773
  ctx.esc(adj.otherPayerId),
4640
- ctx.esc(adj.amountPaid.toString()),
4774
+ escDec2(adj.amountPaid, ctx.esc),
4641
4775
  proc,
4642
4776
  "",
4643
- adj.paidUnits === void 0 ? "" : ctx.esc(adj.paidUnits.toString())
4777
+ adj.paidUnits === void 0 ? "" : escDec2(adj.paidUnits, ctx.esc)
4644
4778
  ])
4645
4779
  );
4646
4780
  emitCasGroup2(adj.adjustments, "build837: adjudication.adjustments", body, ctx);
@@ -4665,8 +4799,8 @@ function emitCasGroup2(raw, at, body, ctx) {
4665
4799
  if (adj === void 0 || adj.groupCode !== groupCode) break;
4666
4800
  parts.push(
4667
4801
  ctx.esc(adj.reasonCode),
4668
- ctx.esc(adj.amount.toString()),
4669
- adj.quantity === void 0 ? "" : ctx.esc(adj.quantity.toString())
4802
+ escDec2(adj.amount, ctx.esc),
4803
+ adj.quantity === void 0 ? "" : escDec2(adj.quantity, ctx.esc)
4670
4804
  );
4671
4805
  triples += 1;
4672
4806
  i += 1;
@@ -4737,7 +4871,7 @@ function emitNote(note, body, ctx) {
4737
4871
  body.push(ctx.seg(["NTE", ctx.esc(note.noteReferenceCode), ctx.esc(note.description)]));
4738
4872
  }
4739
4873
  function emitAmount(amt, body, ctx) {
4740
- body.push(ctx.seg(["AMT", ctx.esc(amt.qualifier), ctx.esc(amt.amount.toString())]));
4874
+ body.push(ctx.seg(["AMT", ctx.esc(amt.qualifier), escDec2(amt.amount, ctx.esc)]));
4741
4875
  }
4742
4876
  function emitHi(hi, body, ctx) {
4743
4877
  const composite = ctx.comp([
@@ -4745,8 +4879,10 @@ function emitHi(hi, body, ctx) {
4745
4879
  hi.code,
4746
4880
  hi.dateQualifier ?? "",
4747
4881
  hi.date ?? "",
4748
- hi.monetaryAmount === void 0 ? "" : hi.monetaryAmount.toString(),
4749
- hi.quantity === void 0 ? "" : hi.quantity.toString(),
4882
+ // `decStr`, not `escDec`: `ctx.comp` maps `esc` over every component, so
4883
+ // escaping here would double-release an active delimiter.
4884
+ hi.monetaryAmount === void 0 ? "" : decStr(hi.monetaryAmount),
4885
+ hi.quantity === void 0 ? "" : decStr(hi.quantity),
4750
4886
  hi.versionId ?? "",
4751
4887
  "",
4752
4888
  hi.poaIndicator ?? ""
@@ -5058,6 +5194,7 @@ function get271Eligibility(delimiters, tx) {
5058
5194
  const seg = body[i];
5059
5195
  if (seg === void 0) continue;
5060
5196
  const position = { segmentIndex: i + 1, transactionIndex: 0 };
5197
+ const sink = { warnings, position };
5061
5198
  switch (seg.id) {
5062
5199
  case "HL": {
5063
5200
  const hl = decodeHl2(seg, delimiters);
@@ -5161,7 +5298,7 @@ function get271Eligibility(delimiters, tx) {
5161
5298
  case "EB": {
5162
5299
  flushBenefit();
5163
5300
  if (currentSubscriber === void 0 && currentDependent === void 0) break;
5164
- currentBenefit = openBenefit(seg, delimiters);
5301
+ currentBenefit = openBenefit(seg, delimiters, sink);
5165
5302
  break;
5166
5303
  }
5167
5304
  case "MSG": {
@@ -5258,7 +5395,7 @@ function decodeDtp2(seg, delimiters) {
5258
5395
  value
5259
5396
  });
5260
5397
  }
5261
- function openBenefit(seg, delimiters) {
5398
+ function openBenefit(seg, delimiters, sink) {
5262
5399
  const serviceTypeCodes = [];
5263
5400
  for (const code of getAllSegmentValues(seg, "03", delimiters)) {
5264
5401
  if (code === "") continue;
@@ -5273,10 +5410,10 @@ function openBenefit(seg, delimiters) {
5273
5410
  insuranceTypeCode: elementOptional(seg, 4, delimiters),
5274
5411
  planCoverageDescription: elementOptional(seg, 5, delimiters),
5275
5412
  timePeriodQualifier: elementOptional(seg, 6, delimiters),
5276
- monetaryAmount: elementDecimal(seg, 7, delimiters),
5277
- percent: elementDecimal(seg, 8, delimiters),
5413
+ monetaryAmount: elementDecimal(seg, 7, delimiters, sink),
5414
+ percent: elementDecimal(seg, 8, delimiters, sink),
5278
5415
  quantityQualifier: elementOptional(seg, 9, delimiters),
5279
- quantity: elementDecimal(seg, 10, delimiters),
5416
+ quantity: elementDecimal(seg, 10, delimiters, sink),
5280
5417
  authorizationRequired: elementOptional(seg, 11, delimiters),
5281
5418
  inPlanNetwork: elementOptional(seg, 12, delimiters),
5282
5419
  references: [],
@@ -5379,6 +5516,9 @@ function refuseSpec3(message) {
5379
5516
  message
5380
5517
  );
5381
5518
  }
5519
+ function escDec3(value, esc) {
5520
+ return esc(requireCallerDecimal(value, "build271", refuseSpec3).toString());
5521
+ }
5382
5522
  var X279A1_VERSION_RELEASE = "005010X279A1";
5383
5523
  var X12_271_FUNCTIONAL_ID = "HB";
5384
5524
  var X12_AGENCY_CODE3 = "X";
@@ -5398,6 +5538,7 @@ function build271(spec) {
5398
5538
  };
5399
5539
  const esc = makeCallerEscaper(delimiters, "build271", refuseSpec3);
5400
5540
  const seg = (parts) => {
5541
+ requireCallerSegment(parts, "build271", refuseSpec3);
5401
5542
  let end = parts.length;
5402
5543
  while (end > 1 && parts[end - 1] === "") end -= 1;
5403
5544
  return parts.slice(0, end).join(elementSeparator) + segmentTerminator;
@@ -5435,8 +5576,8 @@ function build271(spec) {
5435
5576
  X12_271_FUNCTIONAL_ID,
5436
5577
  esc(applicationSenderCode),
5437
5578
  esc(applicationReceiverCode),
5438
- groupDate,
5439
- groupTime,
5579
+ esc(groupDate),
5580
+ esc(groupTime),
5440
5581
  esc(envelope.groupControlNumber),
5441
5582
  X12_AGENCY_CODE3,
5442
5583
  X279A1_VERSION_RELEASE
@@ -5606,10 +5747,10 @@ function emitBenefit(benefit, body, ctx) {
5606
5747
  ctx.esc(benefit.insuranceTypeCode ?? ""),
5607
5748
  ctx.esc(benefit.planCoverageDescription ?? ""),
5608
5749
  ctx.esc(benefit.timePeriodQualifier ?? ""),
5609
- benefit.monetaryAmount === void 0 ? "" : ctx.esc(benefit.monetaryAmount.toString()),
5610
- benefit.percent === void 0 ? "" : ctx.esc(benefit.percent.toString()),
5750
+ benefit.monetaryAmount === void 0 ? "" : escDec3(benefit.monetaryAmount, ctx.esc),
5751
+ benefit.percent === void 0 ? "" : escDec3(benefit.percent, ctx.esc),
5611
5752
  ctx.esc(benefit.quantityQualifier ?? ""),
5612
- benefit.quantity === void 0 ? "" : ctx.esc(benefit.quantity.toString()),
5753
+ benefit.quantity === void 0 ? "" : escDec3(benefit.quantity, ctx.esc),
5613
5754
  ctx.esc(benefit.authorizationRequired ?? ""),
5614
5755
  ctx.esc(benefit.inPlanNetwork ?? "")
5615
5756
  ])
@@ -5816,6 +5957,7 @@ function walk277(delimiters, tx) {
5816
5957
  const seg = body[i];
5817
5958
  if (seg === void 0) continue;
5818
5959
  const position = { segmentIndex: i + 1, transactionIndex: 0 };
5960
+ const sink = { warnings, position };
5819
5961
  switch (seg.id) {
5820
5962
  case "HL": {
5821
5963
  flushClaim();
@@ -5893,7 +6035,7 @@ function walk277(delimiters, tx) {
5893
6035
  case "SVC": {
5894
6036
  flushServiceLine();
5895
6037
  if (currentClaim === void 0) currentClaim = openClaim3();
5896
- currentServiceLine = openServiceLine3(seg, delimiters);
6038
+ currentServiceLine = openServiceLine3(seg, delimiters, sink);
5897
6039
  break;
5898
6040
  }
5899
6041
  case "REF": {
@@ -5967,6 +6109,7 @@ function decodeDtp3(seg, delimiters) {
5967
6109
  });
5968
6110
  }
5969
6111
  function decodeStc(seg, delimiters, warnings, position) {
6112
+ const sink = { warnings, position };
5970
6113
  const statuses = [];
5971
6114
  for (const elementIndex of [1, 10, 11]) {
5972
6115
  const code = decodeStatusComposite(seg, elementIndex, delimiters, warnings, position);
@@ -5975,8 +6118,8 @@ function decodeStc(seg, delimiters, warnings, position) {
5975
6118
  return Object.freeze({
5976
6119
  statusEffectiveDate: elementOptional(seg, 2, delimiters),
5977
6120
  actionCode: elementOptional(seg, 3, delimiters),
5978
- totalChargeAmount: elementDecimal(seg, 4, delimiters),
5979
- paymentAmount: elementDecimal(seg, 5, delimiters),
6121
+ totalChargeAmount: elementDecimal(seg, 4, delimiters, sink),
6122
+ paymentAmount: elementDecimal(seg, 5, delimiters, sink),
5980
6123
  adjudicationDate: elementOptional(seg, 6, delimiters),
5981
6124
  message: elementOptional(seg, 12, delimiters),
5982
6125
  statuses: Object.freeze(statuses)
@@ -6003,7 +6146,7 @@ function decodeStatusComposite(seg, n, delimiters, warnings, position) {
6003
6146
  entityCode
6004
6147
  });
6005
6148
  }
6006
- function openServiceLine3(seg, delimiters) {
6149
+ function openServiceLine3(seg, delimiters, sink) {
6007
6150
  const modifiers = [];
6008
6151
  for (let comp = 3; comp <= 6; comp += 1) {
6009
6152
  const m = componentOptional(seg, 1, comp, delimiters);
@@ -6013,9 +6156,13 @@ function openServiceLine3(seg, delimiters) {
6013
6156
  serviceIdQualifier: componentOptional(seg, 1, 1, delimiters),
6014
6157
  procedureCode: componentOptional(seg, 1, 2, delimiters),
6015
6158
  modifiers,
6016
- lineChargeAmount: elementDecimal(seg, 2, delimiters),
6017
- linePaymentAmount: elementDecimal(seg, 3, delimiters),
6159
+ lineChargeAmount: elementDecimal(seg, 2, delimiters, sink),
6160
+ linePaymentAmount: elementDecimal(seg, 3, delimiters, sink),
6018
6161
  revenueCode: elementOptional(seg, 4, delimiters),
6162
+ // SVC-07. SVC-05 (Quantity) and SVC-06 (a second composite) are NOT USED
6163
+ // in both X212 and X214, so they are deliberately left unread rather than
6164
+ // decoded "for symmetry" with the 835, whose SVC-05 IS the paid count.
6165
+ unitsOfService: elementDecimal(seg, 7, delimiters, sink),
6019
6166
  statuses: [],
6020
6167
  references: [],
6021
6168
  dates: []
@@ -6029,6 +6176,7 @@ function freezeServiceLine3(acc) {
6029
6176
  lineChargeAmount: acc.lineChargeAmount,
6030
6177
  linePaymentAmount: acc.linePaymentAmount,
6031
6178
  revenueCode: acc.revenueCode,
6179
+ unitsOfService: acc.unitsOfService,
6032
6180
  statuses: Object.freeze(acc.statuses.slice()),
6033
6181
  references: Object.freeze(acc.references.slice()),
6034
6182
  dates: Object.freeze(acc.dates.slice())
@@ -6077,6 +6225,9 @@ function refuseSpec4(message) {
6077
6225
  message
6078
6226
  );
6079
6227
  }
6228
+ function escDec4(value, esc) {
6229
+ return esc(requireCallerDecimal(value, "build277", refuseSpec4).toString());
6230
+ }
6080
6231
  var X12_277_FUNCTIONAL_ID = "HN";
6081
6232
  var X12_AGENCY_CODE4 = "X";
6082
6233
  var HL_LEVEL3 = {
@@ -6094,7 +6245,7 @@ function build277CA(spec) {
6094
6245
  }
6095
6246
  function buildClaimStatus(version, spec) {
6096
6247
  const { envelope } = spec;
6097
- enforceStructuralSpec4(spec);
6248
+ enforceStructuralSpec4(version, spec);
6098
6249
  const elementSeparator = envelope.elementSeparator ?? "*";
6099
6250
  const repetitionSeparator = envelope.repetitionSeparator ?? "^";
6100
6251
  const componentSeparator = envelope.componentSeparator ?? ":";
@@ -6107,6 +6258,7 @@ function buildClaimStatus(version, spec) {
6107
6258
  };
6108
6259
  const esc = makeCallerEscaper(delimiters, "build277", refuseSpec4);
6109
6260
  const seg = (parts) => {
6261
+ requireCallerSegment(parts, "build277", refuseSpec4);
6110
6262
  let end = parts.length;
6111
6263
  while (end > 1 && parts[end - 1] === "") end -= 1;
6112
6264
  return parts.slice(0, end).join(elementSeparator) + segmentTerminator;
@@ -6150,8 +6302,8 @@ function buildClaimStatus(version, spec) {
6150
6302
  X12_277_FUNCTIONAL_ID,
6151
6303
  esc(applicationSenderCode),
6152
6304
  esc(applicationReceiverCode),
6153
- groupDate,
6154
- groupTime,
6305
+ esc(groupDate),
6306
+ esc(groupTime),
6155
6307
  esc(envelope.groupControlNumber),
6156
6308
  X12_AGENCY_CODE4,
6157
6309
  version
@@ -6170,7 +6322,7 @@ function buildClaimStatus(version, spec) {
6170
6322
  const raw = isa + gs + st + body.join("") + se + ge + iea;
6171
6323
  return parseX12(raw);
6172
6324
  }
6173
- function enforceStructuralSpec4(spec) {
6325
+ function enforceStructuralSpec4(version, spec) {
6174
6326
  const sources = requireCallerArray(
6175
6327
  spec.informationSources,
6176
6328
  "build277: spec.informationSources",
@@ -6228,13 +6380,13 @@ function enforceStructuralSpec4(spec) {
6228
6380
  for (let u = 0; u < subscribers.length; u += 1) {
6229
6381
  const subscriber = subscribers[u];
6230
6382
  if (subscriber === void 0) continue;
6231
- enforceSubscriber(subscriber, `${locator}.subscriber[${String(u)}]`);
6383
+ enforceSubscriber(version, subscriber, `${locator}.subscriber[${String(u)}]`);
6232
6384
  }
6233
6385
  }
6234
6386
  }
6235
6387
  }
6236
6388
  }
6237
- function enforceSubscriber(subscriber, locator) {
6389
+ function enforceSubscriber(version, subscriber, locator) {
6238
6390
  const claims = requireCallerArray(
6239
6391
  subscriber.claims,
6240
6392
  `build277: subscriber at ${locator}: claims`,
@@ -6253,7 +6405,8 @@ function enforceSubscriber(subscriber, locator) {
6253
6405
  }
6254
6406
  for (let c = 0; c < claims.length; c += 1) {
6255
6407
  const claim = claims[c];
6256
- if (claim !== void 0) enforceClaim2(claim, `${locator}.claim[${String(c)}]`, c === 0);
6408
+ if (claim !== void 0)
6409
+ enforceClaim2(version, claim, `${locator}.claim[${String(c)}]`, c === 0);
6257
6410
  }
6258
6411
  for (let d = 0; d < dependents.length; d += 1) {
6259
6412
  const dependent = dependents[d];
@@ -6272,11 +6425,12 @@ function enforceSubscriber(subscriber, locator) {
6272
6425
  }
6273
6426
  for (let c = 0; c < depClaims.length; c += 1) {
6274
6427
  const claim = depClaims[c];
6275
- if (claim !== void 0) enforceClaim2(claim, `${depLocator}.claim[${String(c)}]`, c === 0);
6428
+ if (claim !== void 0)
6429
+ enforceClaim2(version, claim, `${depLocator}.claim[${String(c)}]`, c === 0);
6276
6430
  }
6277
6431
  }
6278
6432
  }
6279
- function enforceClaim2(claim, locator, isFirst) {
6433
+ function enforceClaim2(version, claim, locator, isFirst) {
6280
6434
  const statuses = requireCallerArray(
6281
6435
  claim.statuses,
6282
6436
  `build277: claim at ${locator}: statuses`,
@@ -6305,6 +6459,12 @@ function enforceClaim2(claim, locator, isFirst) {
6305
6459
  for (let l = 0; l < serviceLines.length; l += 1) {
6306
6460
  const line = serviceLines[l];
6307
6461
  if (line === void 0) continue;
6462
+ if (version === "005010X212" && line.unitsOfService === void 0) {
6463
+ throw new ClaimStatus277BuildError(
6464
+ CLAIM_STATUS_277_BUILD_ERROR_CODES.X12_277_BUILD_INVALID_SPEC,
6465
+ `build277: service line at ${locator}.line[${String(l)}] has no unitsOfService, and SVC-07 is a required element in 005010X212. Supply the submitted count; it is never defaulted. In 005010X214 it is situational, so build277CA accepts the same line.`
6466
+ );
6467
+ }
6308
6468
  const lineStatuses = requireCallerArray(
6309
6469
  line.statuses,
6310
6470
  `build277: claim at ${locator}.line[${String(l)}]: statuses`,
@@ -6398,9 +6558,12 @@ function emitServiceLine3(line, body, ctx) {
6398
6558
  ctx.seg([
6399
6559
  "SVC",
6400
6560
  svc01,
6401
- line.lineChargeAmount === void 0 ? "" : ctx.esc(line.lineChargeAmount.toString()),
6402
- line.linePaymentAmount === void 0 ? "" : ctx.esc(line.linePaymentAmount.toString()),
6403
- ctx.esc(line.revenueCode ?? "")
6561
+ line.lineChargeAmount === void 0 ? "" : escDec4(line.lineChargeAmount, ctx.esc),
6562
+ line.linePaymentAmount === void 0 ? "" : escDec4(line.linePaymentAmount, ctx.esc),
6563
+ ctx.esc(line.revenueCode ?? ""),
6564
+ "",
6565
+ "",
6566
+ line.unitsOfService === void 0 ? "" : escDec4(line.unitsOfService, ctx.esc)
6404
6567
  ])
6405
6568
  );
6406
6569
  for (const status of line.statuses ?? []) emitStatus(status, body, ctx);
@@ -6419,8 +6582,8 @@ function emitStatus(status, body, ctx) {
6419
6582
  composite(0),
6420
6583
  ctx.esc(status.statusEffectiveDate ?? ""),
6421
6584
  ctx.esc(status.actionCode ?? ""),
6422
- status.totalChargeAmount === void 0 ? "" : ctx.esc(status.totalChargeAmount.toString()),
6423
- status.paymentAmount === void 0 ? "" : ctx.esc(status.paymentAmount.toString()),
6585
+ status.totalChargeAmount === void 0 ? "" : escDec4(status.totalChargeAmount, ctx.esc),
6586
+ status.paymentAmount === void 0 ? "" : escDec4(status.paymentAmount, ctx.esc),
6424
6587
  ctx.esc(status.adjudicationDate ?? ""),
6425
6588
  "",
6426
6589
  "",
@@ -6590,9 +6753,11 @@ function get820Payments(delimiters, tx) {
6590
6753
  for (let i = 0; i < body.length; i += 1) {
6591
6754
  const seg = body[i];
6592
6755
  if (seg === void 0) continue;
6756
+ const position = { segmentIndex: i + 1, transactionIndex: 0 };
6757
+ const sink = { warnings, position };
6593
6758
  switch (seg.id) {
6594
6759
  case "BPR": {
6595
- payment = decodeBpr2(seg, delimiters);
6760
+ payment = decodeBpr2(seg, delimiters, sink);
6596
6761
  break;
6597
6762
  }
6598
6763
  case "TRN": {
@@ -6659,13 +6824,13 @@ function get820Payments(delimiters, tx) {
6659
6824
  }
6660
6825
  case "RMR": {
6661
6826
  if (current === void 0) break;
6662
- const item = decodeRmr(seg, delimiters);
6827
+ const item = decodeRmr(seg, delimiters, sink);
6663
6828
  if (item !== void 0) current.openItems.push(item);
6664
6829
  break;
6665
6830
  }
6666
6831
  case "ADX": {
6667
6832
  if (current === void 0) break;
6668
- const adjustment = decodeAdx(seg, delimiters);
6833
+ const adjustment = decodeAdx(seg, delimiters, sink);
6669
6834
  if (adjustment !== void 0) current.adjustments.push(adjustment);
6670
6835
  break;
6671
6836
  }
@@ -6717,10 +6882,10 @@ function freezeRemittance(acc) {
6717
6882
  adjustments: Object.freeze(acc.adjustments.slice())
6718
6883
  });
6719
6884
  }
6720
- function decodeBpr2(seg, delimiters) {
6885
+ function decodeBpr2(seg, delimiters, sink) {
6721
6886
  return Object.freeze({
6722
6887
  transactionHandlingCode: elementValue(seg, 1, delimiters),
6723
- totalPremiumAmount: elementDecimalOrZero(seg, 2, delimiters),
6888
+ totalPremiumAmount: elementDecimalOrZero(seg, 2, delimiters, sink),
6724
6889
  creditDebitFlag: elementValue(seg, 3, delimiters),
6725
6890
  method: elementValue(seg, 4, delimiters),
6726
6891
  paymentFormatCode: elementOptional(seg, 5, delimiters),
@@ -6788,7 +6953,7 @@ function decodeNm13(seg, delimiters) {
6788
6953
  idCode: elementOptional(seg, 9, delimiters)
6789
6954
  });
6790
6955
  }
6791
- function decodeRmr(seg, delimiters) {
6956
+ function decodeRmr(seg, delimiters, sink) {
6792
6957
  const qualifier = elementValue(seg, 1, delimiters);
6793
6958
  const referenceId = elementValue(seg, 2, delimiters);
6794
6959
  if (qualifier === "" && referenceId === "") return void 0;
@@ -6796,12 +6961,12 @@ function decodeRmr(seg, delimiters) {
6796
6961
  qualifier,
6797
6962
  referenceId,
6798
6963
  paymentActionCode: elementOptional(seg, 3, delimiters),
6799
- amountPaid: elementDecimalOrZero(seg, 4, delimiters),
6800
- amountDue: elementDecimal(seg, 5, delimiters)
6964
+ amountPaid: elementDecimalOrZero(seg, 4, delimiters, sink),
6965
+ amountDue: elementDecimal(seg, 5, delimiters, sink)
6801
6966
  });
6802
6967
  }
6803
- function decodeAdx(seg, delimiters) {
6804
- const amount = elementDecimal(seg, 1, delimiters);
6968
+ function decodeAdx(seg, delimiters, sink) {
6969
+ const amount = elementDecimal(seg, 1, delimiters, sink);
6805
6970
  if (amount === void 0) return void 0;
6806
6971
  return Object.freeze({
6807
6972
  amount,
@@ -6862,6 +7027,9 @@ var Premium820BuildError = class extends Error {
6862
7027
  function refuseSpec5(message) {
6863
7028
  throw new Premium820BuildError(PREMIUM_820_BUILD_ERROR_CODES.X12_820_BUILD_INVALID_SPEC, message);
6864
7029
  }
7030
+ function escDec5(value, esc) {
7031
+ return esc(requireCallerDecimal(value, "build820", refuseSpec5).toString());
7032
+ }
6865
7033
  var X218_VERSION_RELEASE = "005010X218";
6866
7034
  var X12_820_FUNCTIONAL_ID = "RA";
6867
7035
  var X12_AGENCY_CODE5 = "X";
@@ -6880,6 +7048,7 @@ function build820(spec) {
6880
7048
  };
6881
7049
  const esc = makeCallerEscaper(delimiters, "build820", refuseSpec5);
6882
7050
  const seg = (parts) => {
7051
+ requireCallerSegment(parts, "build820", refuseSpec5);
6883
7052
  let end = parts.length;
6884
7053
  while (end > 1 && parts[end - 1] === "") end -= 1;
6885
7054
  return parts.slice(0, end).join(elementSeparator) + segmentTerminator;
@@ -6935,9 +7104,9 @@ function build820(spec) {
6935
7104
  // GS-02
6936
7105
  esc(applicationReceiverCode),
6937
7106
  // GS-03
6938
- groupDate,
7107
+ esc(groupDate),
6939
7108
  // GS-04 - CCYYMMDD
6940
- groupTime,
7109
+ esc(groupTime),
6941
7110
  // GS-05 - HHMM
6942
7111
  esc(envelope.groupControlNumber),
6943
7112
  // GS-06
@@ -6953,7 +7122,7 @@ function build820(spec) {
6953
7122
  seg([
6954
7123
  "BPR",
6955
7124
  esc(spec.payment.transactionHandlingCode),
6956
- esc(spec.payment.totalPremiumAmount.toString()),
7125
+ escDec5(spec.payment.totalPremiumAmount, esc),
6957
7126
  esc(spec.payment.creditDebitFlag),
6958
7127
  esc(spec.payment.method),
6959
7128
  esc(spec.payment.paymentFormatCode ?? ""),
@@ -7110,7 +7279,7 @@ function emitRemittance(remittance, body, seg, esc) {
7110
7279
  body.push(
7111
7280
  seg([
7112
7281
  "ADX",
7113
- esc(adjustment.amount.toString()),
7282
+ escDec5(adjustment.amount, esc),
7114
7283
  esc(adjustment.reasonCode),
7115
7284
  esc(adjustment.referenceQualifier ?? ""),
7116
7285
  esc(adjustment.referenceId ?? "")
@@ -7140,8 +7309,8 @@ function emitOpenItem(item, seg, esc) {
7140
7309
  esc(item.qualifier),
7141
7310
  esc(item.referenceId),
7142
7311
  esc(item.paymentActionCode ?? ""),
7143
- esc(item.amountPaid.toString()),
7144
- item.amountDue === void 0 ? "" : esc(item.amountDue.toString())
7312
+ escDec5(item.amountPaid, esc),
7313
+ item.amountDue === void 0 ? "" : escDec5(item.amountDue, esc)
7145
7314
  ]);
7146
7315
  }
7147
7316
  function emitRef5(ref, seg, esc) {
@@ -7372,7 +7541,8 @@ async function* get834Enrollments(delimiters, tx) {
7372
7541
  }
7373
7542
  case "AMT": {
7374
7543
  if (currentCoverage === void 0) break;
7375
- const amount = decodeAmt3(seg, delimiters);
7544
+ const sink = { warnings: current.warnings, position };
7545
+ const amount = decodeAmt3(seg, delimiters, sink);
7376
7546
  if (amount !== void 0) currentCoverage.amounts.push(amount);
7377
7547
  break;
7378
7548
  }
@@ -7498,8 +7668,8 @@ function decodeDtp4(seg, delimiters) {
7498
7668
  value
7499
7669
  });
7500
7670
  }
7501
- function decodeAmt3(seg, delimiters) {
7502
- const amount = elementDecimal(seg, 2, delimiters);
7671
+ function decodeAmt3(seg, delimiters, sink) {
7672
+ const amount = elementDecimal(seg, 2, delimiters, sink);
7503
7673
  if (amount === void 0) return void 0;
7504
7674
  return Object.freeze({
7505
7675
  qualifier: elementValue(seg, 1, delimiters),
@@ -7561,6 +7731,9 @@ function refuseSpec6(message) {
7561
7731
  message
7562
7732
  );
7563
7733
  }
7734
+ function escDec6(value, esc) {
7735
+ return esc(requireCallerDecimal(value, "build834", refuseSpec6).toString());
7736
+ }
7564
7737
  var X220A1_VERSION_RELEASE = "005010X220A1";
7565
7738
  var X12_834_FUNCTIONAL_ID = "BE";
7566
7739
  var X12_AGENCY_CODE6 = "X";
@@ -7581,6 +7754,7 @@ function build834(spec) {
7581
7754
  };
7582
7755
  const esc = makeCallerEscaper(delimiters, "build834", refuseSpec6);
7583
7756
  const seg = (parts) => {
7757
+ requireCallerSegment(parts, "build834", refuseSpec6);
7584
7758
  let end = parts.length;
7585
7759
  while (end > 1 && parts[end - 1] === "") end -= 1;
7586
7760
  return parts.slice(0, end).join(elementSeparator) + segmentTerminator;
@@ -7636,9 +7810,9 @@ function build834(spec) {
7636
7810
  // GS-02
7637
7811
  esc(applicationReceiverCode),
7638
7812
  // GS-03
7639
- groupDate,
7813
+ esc(groupDate),
7640
7814
  // GS-04 - CCYYMMDD
7641
- groupTime,
7815
+ esc(groupTime),
7642
7816
  // GS-05 - HHMM
7643
7817
  esc(envelope.groupControlNumber),
7644
7818
  // GS-06
@@ -7819,7 +7993,7 @@ function emitCoverage(coverage, body, seg, esc) {
7819
7993
  );
7820
7994
  for (const date of coverage.dates ?? []) body.push(emitDate4(date, seg, esc));
7821
7995
  for (const amount of coverage.amounts ?? []) {
7822
- body.push(seg(["AMT", esc(amount.qualifier), esc(amount.amount.toString())]));
7996
+ body.push(seg(["AMT", esc(amount.qualifier), escDec6(amount.amount, esc)]));
7823
7997
  }
7824
7998
  }
7825
7999
  function emitRef6(ref, seg, esc) {
@@ -8248,6 +8422,7 @@ function buildServicesReview(version, direction, spec) {
8248
8422
  };
8249
8423
  const esc = makeCallerEscaper(delimiters, "build278", refuseSpec7);
8250
8424
  const seg = (parts) => {
8425
+ requireCallerSegment(parts, "build278", refuseSpec7);
8251
8426
  let end = parts.length;
8252
8427
  while (end > 1 && parts[end - 1] === "") end -= 1;
8253
8428
  return parts.slice(0, end).join(elementSeparator) + segmentTerminator;
@@ -8291,8 +8466,8 @@ function buildServicesReview(version, direction, spec) {
8291
8466
  X12_278_FUNCTIONAL_ID,
8292
8467
  esc(applicationSenderCode),
8293
8468
  esc(applicationReceiverCode),
8294
- groupDate,
8295
- groupTime,
8469
+ esc(groupDate),
8470
+ esc(groupTime),
8296
8471
  esc(envelope.groupControlNumber),
8297
8472
  X12_AGENCY_CODE7,
8298
8473
  version
@@ -8421,7 +8596,7 @@ function emitReview(review, parentHlId, direction, body, ctx, counter) {
8421
8596
  const nested = review.reviews ?? [];
8422
8597
  const hasChild = nested.length > 0 ? "1" : "0";
8423
8598
  const levelCode = review.levelCode ?? DEFAULT_REVIEW_LEVEL;
8424
- body.push(ctx.seg(["HL", hlId, parentHlId, levelCode, hasChild]));
8599
+ body.push(ctx.seg(["HL", hlId, parentHlId, ctx.esc(levelCode), hasChild]));
8425
8600
  for (const trace of review.traces ?? []) {
8426
8601
  body.push(
8427
8602
  ctx.seg([
@@ -9068,13 +9243,13 @@ function build999(spec) {
9068
9243
  // GS-02
9069
9244
  esc(envelope.receiverId),
9070
9245
  // GS-03
9071
- groupDate,
9246
+ esc(groupDate),
9072
9247
  // GS-04 - CCYYMMDD
9073
- groupTime,
9248
+ esc(groupTime),
9074
9249
  // GS-05 - HHMM
9075
- envelope.groupControlNumber,
9250
+ esc(envelope.groupControlNumber),
9076
9251
  // GS-06
9077
- groupResponsibleAgency,
9252
+ esc(groupResponsibleAgency),
9078
9253
  // GS-07
9079
9254
  X231A1_VERSION_RELEASE
9080
9255
  // GS-08 - the 999 TR3
@@ -9084,7 +9259,7 @@ function build999(spec) {
9084
9259
  );
9085
9260
  const stControlNumber = envelope.transactionSetControlNumber;
9086
9261
  const st = joinSeg(
9087
- ["ST", "999", stControlNumber, X231A1_VERSION_RELEASE],
9262
+ ["ST", "999", esc(stControlNumber), X231A1_VERSION_RELEASE],
9088
9263
  elementSeparator,
9089
9264
  segmentTerminator
9090
9265
  );
@@ -9117,8 +9292,16 @@ function build999(spec) {
9117
9292
  const ak9 = buildAk9(functionalGroup, esc, elementSeparator, segmentTerminator);
9118
9293
  const bodySegments = [st, ak1, ...responseSegments, ak9];
9119
9294
  const seCount = bodySegments.length + 1;
9120
- const se = joinSeg(["SE", String(seCount), stControlNumber], elementSeparator, segmentTerminator);
9121
- const ge = joinSeg(["GE", "1", envelope.groupControlNumber], elementSeparator, segmentTerminator);
9295
+ const se = joinSeg(
9296
+ ["SE", String(seCount), esc(stControlNumber)],
9297
+ elementSeparator,
9298
+ segmentTerminator
9299
+ );
9300
+ const ge = joinSeg(
9301
+ ["GE", "1", esc(envelope.groupControlNumber)],
9302
+ elementSeparator,
9303
+ segmentTerminator
9304
+ );
9122
9305
  const iea = joinSeg(["IEA", "1", interchangeControlNumber], elementSeparator, segmentTerminator);
9123
9306
  const raw = isa + gs + bodySegments.join("") + se + ge + iea;
9124
9307
  return parseX12(raw);
@@ -9240,7 +9423,7 @@ function buildIk4(elemError, esc, componentSeparator, elementSeparator, segmentT
9240
9423
  return joinSeg(parts, elementSeparator, segmentTerminator);
9241
9424
  }
9242
9425
  function buildIk5(response, esc, elementSeparator, segmentTerminator) {
9243
- const parts = ["IK5", response.disposition];
9426
+ const parts = ["IK5", esc(response.disposition)];
9244
9427
  const codes = response.syntaxErrorCodes ?? [];
9245
9428
  if (codes.length > 5) {
9246
9429
  throw new AckBuildError(
@@ -9254,7 +9437,7 @@ function buildIk5(response, esc, elementSeparator, segmentTerminator) {
9254
9437
  function buildAk9(group, esc, elementSeparator, segmentTerminator) {
9255
9438
  const parts = [
9256
9439
  "AK9",
9257
- group.disposition,
9440
+ esc(group.disposition),
9258
9441
  String(group.numberOfTransactionSets),
9259
9442
  String(group.numberOfReceivedTransactionSets),
9260
9443
  String(group.numberOfAcceptedTransactionSets)
@@ -9270,6 +9453,7 @@ function buildAk9(group, esc, elementSeparator, segmentTerminator) {
9270
9453
  return joinSeg(parts, elementSeparator, segmentTerminator);
9271
9454
  }
9272
9455
  function joinSeg(parts, elementSeparator, segmentTerminator) {
9456
+ requireCallerSegment(parts, "build999", refuseSpec8);
9273
9457
  return parts.join(elementSeparator) + segmentTerminator;
9274
9458
  }
9275
9459
  function pad8(value, width) {
@@ -9624,6 +9808,7 @@ function buildTransaction(tx, esc, elementSeparator, segmentTerminator) {
9624
9808
  return st + bodySegments + se;
9625
9809
  }
9626
9810
  function joinSeg2(parts, elementSeparator, segmentTerminator) {
9811
+ requireCallerSegment(parts, "buildInterchange", refuseSpec9);
9627
9812
  return parts.join(elementSeparator) + segmentTerminator;
9628
9813
  }
9629
9814
  function pad9(value, width) {
@@ -9932,7 +10117,7 @@ var profiles = Object.freeze({
9932
10117
  });
9933
10118
 
9934
10119
  // src/index.ts
9935
- var VERSION = "0.0.9";
10120
+ var VERSION = "0.0.10";
9936
10121
 
9937
10122
  exports.ACK_BUILD_ERROR_CODES = ACK_BUILD_ERROR_CODES;
9938
10123
  exports.ALL_WARNING_MESSAGES = ALL_WARNING_MESSAGES;
@@ -10107,11 +10292,14 @@ exports.parseX12 = parseX12;
10107
10292
  exports.partitionWarnings = partitionWarnings;
10108
10293
  exports.pre005010 = pre005010;
10109
10294
  exports.profiles = profiles;
10295
+ exports.readElementDecimal = readElementDecimal;
10110
10296
  exports.remitBalanceMismatch = remitBalanceMismatch;
10111
10297
  exports.renderCallerValue = renderCallerValue;
10112
10298
  exports.resolveHiQualifier = resolveHiQualifier;
10113
10299
  exports.segmentCountMismatch = segmentCountMismatch;
10114
10300
  exports.serializeX12 = serializeX12;
10301
+ exports.serviceLineDropped = serviceLineDropped;
10302
+ exports.serviceLineNotDecoded = serviceLineNotDecoded;
10115
10303
  exports.setDefaultProfile = setDefaultProfile;
10116
10304
  exports.trailingGarbage = trailingGarbage;
10117
10305
  exports.transactionCountMismatch = transactionCountMismatch;
@@ -10124,5 +10312,6 @@ exports.unknownClaimStatusCategory = unknownClaimStatusCategory;
10124
10312
  exports.unknownHiQualifier = unknownHiQualifier;
10125
10313
  exports.unknownMaintenanceType = unknownMaintenanceType;
10126
10314
  exports.unknownRarc = unknownRarc;
10315
+ exports.unparseableDecimal = unparseableDecimal;
10127
10316
  //# sourceMappingURL=index.cjs.map
10128
10317
  //# sourceMappingURL=index.cjs.map