Skip to content

Commit

Permalink
Adding more CSV 2.2.0 tests
Browse files Browse the repository at this point in the history
Add tests for modifier-only rows.
Add tests for requiring estimated allowed amount when payer-specific
percentage or algorithm, but not dollar amount, are present.
Add tests for requiring drug information when NDC code type is used.
  • Loading branch information
mint-thompson committed Nov 26, 2024
1 parent 4385f78 commit 265f882
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 47 deletions.
11 changes: 11 additions & 0 deletions src/errors/csv/DrugInformationRequiredError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { CsvValidationError } from "./CsvValidationError";

export class DrugInformationRequiredError extends CsvValidationError {
constructor(row: number, column: number) {
super(
row,
column,
"If code type is NDC, then the corresponding drug unit of measure and drug type of measure data element must be encoded."
);
}
}
11 changes: 11 additions & 0 deletions src/errors/csv/ModifierMissingInfoError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { CsvValidationError } from "./CsvValidationError";

export class ModifierMissingInfoError extends CsvValidationError {
constructor(row: number, column: number) {
super(
row,
column,
"If a modifier is encoded without an item or service, then a description and one of the following is the minimum information required: additional_payer_notes, standard_charge | negotiated_dollar, standard_charge | negotiated_percentage, or standard_charge | negotiated_algorithm."
);
}
}
11 changes: 11 additions & 0 deletions src/errors/csv/PercentageAlgorithmEstimateError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { CsvValidationError } from "./CsvValidationError";

export class PercentageAlgorithmEstimateError extends CsvValidationError {
constructor(row: number, column: number) {
super(
row,
column,
'If a "payer specific negotiated charge" can only be expressed as a percentage or algorithm, then a corresponding "Estimated Allowed Amount" must also be encoded.'
);
}
}
3 changes: 3 additions & 0 deletions src/errors/csv/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from "./AmbiguousFormatError";
export * from "./CodePairMissingError";
export * from "./ColumnMissingError";
export * from "./DollarNeedsMinMaxError";
export * from "./DrugInformationRequiredError";
export * from "./DuplicateColumnError";
export * from "./DuplicateHeaderColumnError";
export * from "./HeaderBlankError";
Expand All @@ -14,6 +15,8 @@ export * from "./InvalidStateCodeError";
export * from "./InvalidVersionError";
export * from "./ItemRequiresChargeError";
export * from "./MinRowsError";
export * from "./ModifierMissingInfoError";
export * from "./OtherMethodologyNotesError";
export * from "./PercentageAlgorithmEstimateError";
export * from "./ProblemsInHeaderError";
export * from "./RequiredValueError";
93 changes: 80 additions & 13 deletions src/validators/CsvValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
ColumnMissingError,
CsvValidationError,
DollarNeedsMinMaxError,
DrugInformationRequiredError,
DuplicateColumnError,
DuplicateHeaderColumnError,
HeaderBlankError,
Expand All @@ -26,13 +27,15 @@ import {
InvalidVersionError,
ItemRequiresChargeError,
MinRowsError,
ModifierMissingInfoError,
OtherMethodologyNotesError,
PercentageAlgorithmEstimateError,
ProblemsInHeaderError,
RequiredValueError,
} from "../errors/csv";
import { range, partial } from "lodash";
import _ from "lodash";
import { ToastyValidator, DynaReadyValidator } from "./CsvFieldTypes";
import { ToastyValidator } from "./CsvFieldTypes";

export const AFFIRMATION =
"To the best of its knowledge and belief, the hospital has included all applicable standard charge information in accordance with the requirements of 45 CFR 180.50, and the information encoded is true, accurate, and complete as of the date indicated.";
Expand Down Expand Up @@ -280,15 +283,6 @@ export function dynaValidateRequiredField(
return [];
}

export function isOneOfPresent(
normalizedColumns: string[],
enteredColumns: string[],
fields: string[],
dataRow: { [key: string]: string }
) {
return fields.some;
}

export class CsvValidator extends BaseValidator {
public index = 0;
public isTall: boolean = false;
Expand Down Expand Up @@ -418,8 +412,6 @@ export class CsvValidator extends BaseValidator {
});
});

// this.rowValidators.push(...standardChargeChecks);

const nonModifierChecks: ToastyValidator[] = [];

if (semver.gte(this.version, "2.1.0")) {
Expand Down Expand Up @@ -532,7 +524,6 @@ export class CsvValidator extends BaseValidator {
}

if (semver.gte(this.version, "2.2.0")) {
// checks diverge based on whether this is a modifier row
this.rowValidators.push(
{
name: "drug_unit_of_measurement",
Expand Down Expand Up @@ -562,6 +553,82 @@ export class CsvValidator extends BaseValidator {
),
}
);
// If code type is NDC, then the corresponding drug unit of measure and
// drug type of measure data elements must be encoded. new in v2.2.0
this.rowValidators.push({
name: "NDC code requires drug information",
validator: (dataRow, row) => {
const hasNDC = range(1, this.codeCount + 1).some((codeIndex) => {
return matchesString(
dataRow[`code | ${codeIndex} | type`],
"NDC"
);
});
if (hasNDC) {
const missingDrugFields = [
"drug_unit_of_measurement",
"drug_type_of_measurement",
].filter((field) => !Boolean(dataRow[field]));
if (missingDrugFields.length > 0) {
return [
new DrugInformationRequiredError(
row,
this.normalizedColumns.indexOf(missingDrugFields[0])
),
];
}
}
return [];
},
});
// some checks diverge based on whether this is a modifier row
// If a modifier is encoded without an item or service, then a description and one of the following
// is the minimum information required:
// additional_generic_notes, standard_charge | negotiated_dollar, standard_charge | negotiated_percentage, or standard_charge | negotiated_algorithm
modifierChecks.push({
name: "extra info for modifier row",
validator: (dataRow, row) => {
if (
![
"additional_generic_notes",
"standard_charge | negotiated_dollar",
"standard_charge | negotiated_percentage",
"standard_charge | negotiated_algorithm",
].some((field) => Boolean(dataRow[field]))
) {
return [
new ModifierMissingInfoError(
row,
this.normalizedColumns.indexOf("additional_generic_notes")
),
];
}
return [];
},
});
// If a "payer specific negotiated charge" can only be expressed as a percentage or algorithm,
// then a corresponding "Estimated Allowed Amount" must also be encoded. new in v2.2.0
nonModifierChecks.push({
name: "estimated allowed amount required when charge is only percentage or algorithm",
validator: (dataRow, row) => {
if (
!dataRow["standard_charge | negotiated_dollar"] &&
(dataRow["standard_charge | negotiated_percentage"] ||
dataRow["standard_charge | negotiated_algorithm"]) &&
!dataRow.estimated_amount
) {
return [
new PercentageAlgorithmEstimateError(
row,
this.normalizedColumns.indexOf("estimated_amount")
),
];
}
return [];
},
});
// that's all for the conditional checks. so now build the tree out, branching on whether
// the row is modifier-only.
const isModifierPresent: ToastyValidator = {
name: "is a modifier present",
predicate: (row) => {
Expand Down
Loading

0 comments on commit 265f882

Please sign in to comment.