diff --git a/common/src/main/resources/2024QuarterlyEditsDescriptions.txt b/common/src/main/resources/2024QuarterlyEditsDescriptions.txt index 870f567c10..2fb4691efa 100644 --- a/common/src/main/resources/2024QuarterlyEditsDescriptions.txt +++ b/common/src/main/resources/2024QuarterlyEditsDescriptions.txt @@ -256,8 +256,8 @@ Q610|If Action Taken equals 1, Lien Status equals 1, and Rate Spread is greater Q611|If Action Taken equals 1, Lien Status equals 2, and Rate Spread is greater than 8.5%, then HOEPA Status generally should be 1.|Action Taken; Lien Status; Rate Spread; HOEPA Status Q612|If Type of Purchaser equals 1 or 3, then HOEPA Status generally should be 2 or 3.|Type of Purchaser; HOEPA Status Q613|If Business or Commercial Purpose equals 1, then Loan Purpose generally should equal 1, 2, 31, 32, or 5.|Business or Commercial Purpose; Loan Purpose -Q614-1|The Age of Applicant or Borrower generally should be between 18 and 100 unless the Age of Applicant or Borrower is reported 8888 indicating NA. Your data indicates a number outside of this range.|Universal Loan Identifier (ULI) -Q614-2|The Age of Co-Applicant or Co-Borrower generally should be between 18 and 100 unless the Age of Co-Applicant or Co-Borrower is reported 8888 indicating NA or 9999 indicating no co-applicant or co-borrower. Your data indicates a number outside of this range.|Universal Loan Identifier (ULI) +Q614-1|The Age of Applicant or Borrower generally should be between 18 and 100 unless the Age of Applicant or Borrower is reported 8888 indicating NA. Your data indicates a number outside of this range.|Universal Loan Identifier (ULI); Age of Applicant or +Q614-2|The Age of Co-Applicant or Co-Borrower generally should be between 18 and 100 unless the Age of Co-Applicant or Co-Borrower is reported 8888 indicating NA or 9999 indicating no co-applicant or co-borrower. Your data indicates a number outside of this range.|Universal Loan Identifier (ULI); Age of Applicant or Q615-1|If Total Loan Costs and Origination Charges are not reported NA or Exempt, and are both nonzero numbers, then Total Loan Costs generally should be greater than Origination Charges.|Origination Charges; Total Loan Costs; Total Points and Fees Q615-2|If Total Points and Fees and Origination Charges are not reported NA or Exempt, and are both nonzero numbers, then Total Points and Fees generally should be greater than Origination Charges.|Origination Charges; Total Loan Costs; Total Points and Fees Q616-1|If Total Loan Costs and Discount Points are not reported NA or Exempt, and are both nonzero numbers, then Total Loan Costs generally should be greater than Discount Points.|Discount Points; Total Loan Costs; Total Points and Fees diff --git a/hmda/src/main/scala/hmda/validation/engine/2024/LarEngine2024.scala b/hmda/src/main/scala/hmda/validation/engine/2024/LarEngine2024.scala index 61faae7e7d..2e2aa4f779 100644 --- a/hmda/src/main/scala/hmda/validation/engine/2024/LarEngine2024.scala +++ b/hmda/src/main/scala/hmda/validation/engine/2024/LarEngine2024.scala @@ -286,8 +286,6 @@ private[engine] object LarEngine2024 extends ValidationEngine[LoanApplicationReg Q644, Q645_1, Q645_2, - quality2025.Q646, - quality2025.Q647.withContext(ctx), quality2020.Q648, quality2020.Q649_1, quality2020.Q649_2, diff --git a/hmda/src/main/scala/hmda/validation/engine/2024Qscala/LarEngine2024Q.scala b/hmda/src/main/scala/hmda/validation/engine/2024Qscala/LarEngine2024Q.scala index 65caf56d12..a926b5af68 100644 --- a/hmda/src/main/scala/hmda/validation/engine/2024Qscala/LarEngine2024Q.scala +++ b/hmda/src/main/scala/hmda/validation/engine/2024Qscala/LarEngine2024Q.scala @@ -285,8 +285,6 @@ private[engine] object LarEngine2024Q extends ValidationEngine[LoanApplicationRe Q644, Q645_1, Q645_2, - quality2025.Q647.withContext(ctx), - quality2025.Q646, quality2020.Q648, quality2020.Q649_1, quality2020.Q649_2, diff --git a/hmda/src/main/scala/hmda/validation/engine/2025Qscala/LarEngine2025Q.scala b/hmda/src/main/scala/hmda/validation/engine/2025Qscala/LarEngine2025Q.scala new file mode 100644 index 0000000000..c15f163120 --- /dev/null +++ b/hmda/src/main/scala/hmda/validation/engine/2025Qscala/LarEngine2025Q.scala @@ -0,0 +1,312 @@ +package hmda.validation.engine + +// $COVERAGE-OFF$ +import hmda.model.filing.lar.LoanApplicationRegister +import hmda.validation.context.ValidationContext +import hmda.validation.rules.lar.quality._2019._ +import hmda.validation.rules.lar.quality.{_2020 => quality2020, _2021 => quality2021, _2022 => quality2022, _2025 => quality2025} +import hmda.validation.rules.lar.quality.common._ +import hmda.validation.rules.lar.syntactical.{S300, S301} +import hmda.validation.rules.lar.validity.{_2020, _2022, _} +import hmda.census.records.CensusRecords +import hmda.validation.rules.lar.quality._2024._ + +private[engine] object LarEngine2025Q extends ValidationEngine[LoanApplicationRegister] { + + override def syntacticalChecks(ctx: ValidationContext) = Vector( + S300, + S301.withContext(ctx) + ) + + override def validityChecks(ctx: ValidationContext) = Vector( + V600, + V608_1, + _2022.V608_2, + V609, + V610_1, + V610_2, + V611, + V612_1, + V612_2, + V613_1, + V613_2, + V613_3, + V613_4, + V614_1, + V614_2, + V614_3, + V614_4, + V615_1, + V615_2, + V615_3, + V616, + V617, + V618, + V619_1, + _2024.V619_2, + V619_3, + V620, + V621, + V622, + V623, + V624, + V625_1, + V625_2.withIndexedTracts(CensusRecords.indexedTract2024), + V626.withIndexedCounties(CensusRecords.indexedCounty2024), + V628_1, + V628_2, + V628_3, + V628_4, + V629_1, + V629_2, + V629_3, + V630, + V631_1, + V631_2, + V631_3, + V631_4, + V632_1, + V632_2, + V632_3, + V633, + V634, + V635_1, + V635_2, + V635_3, + V635_4, + V636_1, + V636_2, + V636_3, + V637, + V638_1, + V638_2, + V638_3, + V638_4, + V639_1, + V639_2, + V639_3, + V640, + V641, + V642_1, + V642_2, + V643, + V644_1, + _2020.V644_2, + V645, + V646_1, + V646_2, + V647, + V648_1, + _2020.V648_2, + V649, + V650, + V651_1, + V651_2, + V652_1, + V652_2, + V654_1, + V654_2, + V655_1, + V655_2, + V656_1, + V656_2, + V657_1, + V657_2, + V657_3, + V658_1, + V658_2, + V659, + _2024.V660_1, + _2024.V660_2, + V661, + _2024.V662_1, + _2024.V662_2, + V663, + V664, + _2024.V665_1, + _2024.V665_2, + V666_1, + V666_2, + V667_1, + V667_2, + V668_1, + V668_2, + V669_1, + V669_2, + V669_3, + V669_4, + V670_1, + V670_2, + V670_3, + V670_4, + V671_1, + V671_2, + V672_1, + V672_2, + V672_3, + V672_4, + V672_5, + V672_6, + V673_1, + V673_2, + V673_3, + V673_4, + V673_5, + V674_1, + V674_2, + V674_3, + V674_4, + V674_5, + V675_1, + V675_2, + V675_3, + V675_4, + V675_5, + V676_1, + V676_2, + V676_3, + V676_4, + V676_5, + _2020.V677_1, + V677_2, + V678_1, + V678_2, + V678_3, + V678_4, + V678_5, + V679_1, + V679_2, + V679_3, + V680_1, + V680_2, + V681_1, + V681_2, + V682_1, + V682_2, + V683, + V684, + V685, + V686, + V687, + V688_1, + V688_2, + V689_1, + V689_2, + V689_3, + V690_1, + V690_2, + V690_3, + V691, + V692_1, + V692_2, + V692_3, + V693_1, + V693_2, + V693_3, + V694_1, + V694_2, + V694_3, + _2021.V695_1, + _2021.V695_2, + _2021.V696_1, + _2021.V696_2, + V696_3, + _2020.V699, + V700_1, + V700_2, + V701, + V702_1, + V702_2, + V703_1, + V703_2, + V704_1, + V704_2, + V705_1, + V705_2, + V706, + V707, + V708, + V709, + V710, + V711, + V712, + V713, + V714, + V715, + _2020.V716.withIndexedCounties(CensusRecords.indexedCounty2024), + _2022.V721_1, + _2022.V721_2, + _2024.V720_1, + _2024.V720_2, + _2024.V720_3, + _2024.V720_4 + ) + + override def qualityChecks(ctx: ValidationContext) = Vector( + Q601, + Q602, + Q603.withIndexedSmallCounties(CensusRecords.indexedSmallCounty2024), + Q605_1, + Q605_2, + quality2020.Q606, + Q609, + Q607, + Q608, + Q610, + Q611, + Q612, + Q613, + quality2020.Q614_1, + quality2020.Q614_2, + quality2021.Q615_1, + quality2021.Q615_2, + Q616_1, + Q616_2, + quality2020.Q617, + Q618, + Q619, + Q620, + Q621, + Q622, + Q623, + Q624, + Q625, + Q626, + Q627, + Q628, + Q629, + Q630, + Q631, + Q632, + Q633, + Q642_1, + Q642_2, + Q643, + Q644, + Q645_1, + Q645_2, + quality2025.Q646, + quality2025.Q647.withContext(ctx), + quality2020.Q648, + quality2020.Q649_1, + quality2020.Q649_2, + quality2020.Q651, + quality2020.Q652, + quality2021.Q650_1, + quality2021.Q650_2, + quality2020.Q653_1, + quality2020.Q653_2, + quality2022.Q654, + quality2021.Q655, + quality2021.Q656, + quality2021.Q657, + quality2022.Q658, + quality2022.Q659_1, + quality2022.Q659_2, + Q660_1, + Q660_2, + Q660_3, + Q660_4 + ) +} +// $COVERAGE-ON$ diff --git a/hmda/src/main/scala/hmda/validation/engine/2025Qscala/TsEngine2025Q.scala b/hmda/src/main/scala/hmda/validation/engine/2025Qscala/TsEngine2025Q.scala new file mode 100644 index 0000000000..844c3303c9 --- /dev/null +++ b/hmda/src/main/scala/hmda/validation/engine/2025Qscala/TsEngine2025Q.scala @@ -0,0 +1,31 @@ +package hmda.validation.engine + +import hmda.model.filing.ts.TransmittalSheet +import hmda.validation.context.ValidationContext +import hmda.validation.rules.ts.syntactical.{ S300, S303, S302 } +import hmda.validation.rules.ts.validity._ +// $COVERAGE-OFF$ +private[engine] object TsEngine2025Q extends ValidationEngine[TransmittalSheet] { + + override def syntacticalChecks(ctx: ValidationContext) = Vector( + S300, + S302.withContext(ctx), + S303.withContext(ctx) + ) + + override def validityChecks(ctx: ValidationContext) = Vector( + V600, + V601, + V603, + V604, + V605, + V606, + V607, + V718.withContext(ctx), + V717, + V719_1, + V719_2 + ) + +} +// $COVERAGE-ON$ \ No newline at end of file diff --git a/hmda/src/main/scala/hmda/validation/engine/2025Qscala/TsLarEngine2025Q.scala b/hmda/src/main/scala/hmda/validation/engine/2025Qscala/TsLarEngine2025Q.scala new file mode 100644 index 0000000000..4653a14d00 --- /dev/null +++ b/hmda/src/main/scala/hmda/validation/engine/2025Qscala/TsLarEngine2025Q.scala @@ -0,0 +1,23 @@ +package hmda.validation.engine + +import hmda.model.filing.ts.TransmittalLar +import hmda.validation.context.ValidationContext +import hmda.validation.rules.lar.quality.common.Q600 +import hmda.validation.rules.lar.syntactical.{S304, S305, S306} +// $COVERAGE-OFF$ +private[engine] object TsLarEngine2025Q extends ValidationEngine[TransmittalLar] { + + override def syntacticalChecks(ctx: ValidationContext) = Vector( + S304, + S305, + S306, + S307 + ) + + override def qualityChecks(ctx: ValidationContext) = Vector( + Q600 + ) + +} + +// $COVERAGE-ON$ \ No newline at end of file diff --git a/hmda/src/main/scala/hmda/validation/filing/MacroValidationFlow.scala b/hmda/src/main/scala/hmda/validation/filing/MacroValidationFlow.scala index 12691f3c6f..0e076a2b53 100644 --- a/hmda/src/main/scala/hmda/validation/filing/MacroValidationFlow.scala +++ b/hmda/src/main/scala/hmda/validation/filing/MacroValidationFlow.scala @@ -135,7 +135,9 @@ object MacroValidationFlow { Q637(totalCount), Q638, Q639, - Q640 + Q640, + Q646, + Q647(tsSource) ) case Period(2025, _) => List(