diff --git a/src/main/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/Fnr.kt b/src/main/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/Fnr.kt new file mode 100644 index 0000000..3861822 --- /dev/null +++ b/src/main/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/Fnr.kt @@ -0,0 +1,41 @@ +package no.nav.helsearbeidsgiver.utils.wrapper + +import kotlinx.serialization.Serializable + +/** Sjekker at streng er riktig lengde, kun består av siffer, og at 6 første siffer er gyldig dato. */ +private val fnrRgx = Regex( + "(?:[04][1-9]|[1256]\\d|[37][01])" + // to første siffer er gyldig dag (+40 for D-nummer) + "(?:[048][1-9]|[159][012])" + // to neste siffer er gyldig måned, med støtte for testpersoner (+40 for NAV, +80 for TestNorge) + "\\d{7}" // resten er tall +) + +private val sifferVekter1 = listOf(3, 7, 6, 1, 8, 9, 4, 5, 2) +private val sifferVekter2 = listOf(5, 4, 3, 2, 7, 6, 5, 4, 3, 2) + +@Serializable +@JvmInline +value class Fnr(val verdi: String) { + init { + require(erGyldig(verdi)) { "Ugyldig fødsels- eller d-nummer." } + } + + override fun toString(): String = + verdi + + companion object { + /** Les [her](https://no.wikipedia.org/wiki/F%C3%B8dselsnummer) for forklaring av regler. */ + fun erGyldig(fnr: String): Boolean = + if (fnr.matches(fnrRgx)) { + val fnrSiffer = fnr.toList().map(Char::digitToInt) + + val sjekksum1 = sjekksum(fnrSiffer, sifferVekter1) + val sjekksum2 = sjekksum(fnrSiffer, sifferVekter2) + + 10 !in listOf(sjekksum1, sjekksum2) && + sjekksum1 == fnrSiffer[9] && + sjekksum2 == fnrSiffer[10] + } else { + false + } + } +} diff --git a/src/main/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/Orgnr.kt b/src/main/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/Orgnr.kt new file mode 100644 index 0000000..dfa50ac --- /dev/null +++ b/src/main/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/Orgnr.kt @@ -0,0 +1,34 @@ +package no.nav.helsearbeidsgiver.utils.wrapper + +import kotlinx.serialization.Serializable + +/** Sjekker at streng er riktig lengde og kun består av siffer. */ +private val orgnrRgx = Regex("\\d{9}") + +private val sifferVekter = listOf(3, 2, 7, 6, 5, 4, 3, 2) + +@Serializable +@JvmInline +value class Orgnr(val verdi: String) { + init { + require(erGyldig(verdi)) { "Ugyldig organisasjonsnummer." } + } + + override fun toString(): String = + verdi + + companion object { + /** Les [her](https://www.brreg.no/om-oss/registrene-vare/om-enhetsregisteret/organisasjonsnummeret/) for forklaring av regler. */ + fun erGyldig(orgnr: String): Boolean = + if (orgnr.matches(orgnrRgx)) { + val orgnrSiffer = orgnr.toList().map(Char::digitToInt) + + val sjekksum = sjekksum(orgnrSiffer, sifferVekter) + + sjekksum != 10 && + sjekksum == orgnrSiffer.last() + } else { + false + } + } +} diff --git a/src/main/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/Sjekksum.kt b/src/main/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/Sjekksum.kt new file mode 100644 index 0000000..f4707df --- /dev/null +++ b/src/main/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/Sjekksum.kt @@ -0,0 +1,8 @@ +package no.nav.helsearbeidsgiver.utils.wrapper + +internal fun sjekksum(siffer: List, sifferVekter: List): Int = + siffer.zip(sifferVekter) + .sumOf { (siffer, vekt) -> siffer * vekt } + .mod(11) + .let { 11 - it } + .mod(11) diff --git a/src/test/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/FnrTest.kt b/src/test/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/FnrTest.kt new file mode 100644 index 0000000..44c288e --- /dev/null +++ b/src/test/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/FnrTest.kt @@ -0,0 +1,94 @@ +package no.nav.helsearbeidsgiver.utils.wrapper + +import io.kotest.assertions.throwables.shouldNotThrowAny +import io.kotest.assertions.throwables.shouldThrowExactly +import io.kotest.core.spec.style.FunSpec +import io.kotest.datatest.withData +import io.kotest.matchers.shouldBe + +class FnrTest : FunSpec({ + + context("gyldig") { + withData( + listOf( + "01010012356", + "19092212338", + "29104512346", + "31129912319", + "25056812310", + "11085812345", + "41066712395", // D-nummer + "45066612310", // D-nummer + "50066612306", // D-nummer + "56066612320", // D-nummer + "60066612387", // D-nummer + "69066612309", // D-nummer + "70066712379", // D-nummer + "71066612397", // D-nummer + "01490012369", // Testperson fra NAV + "01500012397", // Testperson fra NAV + "01890012341", // Testperson fra TestNorge + "01900912338" // Testperson fra TestNorge + ) + ) { + shouldNotThrowAny { + Fnr(it) + } + } + } + + context("ugyldig") { + withData( + listOf( + "00010012317", // dag 0, andre siffer feil + "32010012308", // dag 32, andre siffer feil + "40010012300", // dag 40, andre siffer feil (D-nummer) + "72010012482", // dag 72, andre siffer feil (D-nummer) + "80010012485", // dag 80, første siffer feil + "01000012366", // måned 0, fjerde siffer feil + "01130012384", // måned 13, fjerde siffer feil + "01200012352", // måned 20, tredje siffer feil + "01390012310", // måned 39, tredje og fjerde siffer feil + "01400012349", // måned 40, fjerde siffer feil (testperson) + "01530012367", // måned 53, fjerde siffer feil (testperson) + "01790012484", // måned 79, tredje og fjerde siffer feil + "01800012321", // måned 80, fjerde siffer feil (testperson) + "01930012420", // måned 93, fjerde siffer feil (testperson) + "0101001234", // for kort + "010100123567", // for langt + "010100x2345", // med bokstav + "010100 1234", // med mellomrom + "01010012313", // ugyldig kontrollsiffer 1 + "19092212389", // ugyldig kontrollsiffer 1 + "060606123-3", // ugyldig kontrollsiffer 1 (sjekksum = 10) + "060606123103", // ugyldig kontrollsiffer 1 (sjekksum = 10) + "41066712336", // ugyldig kontrollsiffer 1 (D-nummer) + "01490012326", // ugyldig kontrollsiffer 1 (Testperson fra NAV) + "01890012309", // ugyldig kontrollsiffer 1 (Testperson fra TestNorge) + "29104512344", // ugyldig kontrollsiffer 2 + "31129912310", // ugyldig kontrollsiffer 2 + "1403551234-", // ugyldig kontrollsiffer 2 (sjekksum = 10) + "140355123410", // ugyldig kontrollsiffer 2 (sjekksum = 10) + "45066612316", // ugyldig kontrollsiffer 2 (D-nummer) + "01500012390", // ugyldig kontrollsiffer 2 (Testperson fra NAV) + "01900912339" // ugyldig kontrollsiffer 2 (Testperson fra TestNorge) + ) + ) { + shouldThrowExactly { + Fnr(it) + } + } + + test("tom streng") { + shouldThrowExactly { + Fnr("") + } + } + } + + test("toString gir wrappet verdi") { + Fnr("24120612359").let { + it.toString() shouldBe it.verdi + } + } +}) diff --git a/src/test/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/OrgnrTest.kt b/src/test/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/OrgnrTest.kt new file mode 100644 index 0000000..a424aae --- /dev/null +++ b/src/test/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/OrgnrTest.kt @@ -0,0 +1,58 @@ +package no.nav.helsearbeidsgiver.utils.wrapper + +import io.kotest.assertions.throwables.shouldNotThrowAny +import io.kotest.assertions.throwables.shouldThrowExactly +import io.kotest.core.spec.style.FunSpec +import io.kotest.datatest.withData +import io.kotest.matchers.shouldBe + +class OrgnrTest : FunSpec({ + + context("gyldig") { + withData( + listOf( + "161231657", + "908498461", + "135103217", + "684603132", + "167484972", + "796033029" + ) + ) { + shouldNotThrowAny { + Orgnr(it) + } + } + } + + context("ugyldig") { + withData( + listOf( + "1233", // for kort + "1234567895", // for langt + "12x4567890", // med bokstav + "1234 67892", // med mellomrom + "161231654", // ugyldig kontrollsiffer + "908498464", // ugyldig kontrollsiffer + "65432445-", // ugyldig kontrollsiffer (sjekksum = 10) + "6543244510" // ugyldig kontrollsiffer (sjekksum = 10) + ) + ) { + shouldThrowExactly { + Orgnr(it) + } + } + + test("tom streng") { + shouldThrowExactly { + Orgnr("") + } + } + } + + test("toString gir wrappet verdi") { + Orgnr("123456785").let { + it.toString() shouldBe it.verdi + } + } +}) diff --git a/src/test/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/SjekksumTest.kt b/src/test/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/SjekksumTest.kt new file mode 100644 index 0000000..01ce1ac --- /dev/null +++ b/src/test/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/SjekksumTest.kt @@ -0,0 +1,72 @@ +package no.nav.helsearbeidsgiver.utils.wrapper + +import io.kotest.core.spec.style.FunSpec +import io.kotest.datatest.withData +import io.kotest.matchers.shouldBe + +class SjekksumTest : FunSpec({ + context("gyldig") { + withData( + mapOf( + "siffer og sifferVekter er tomme" to TestData( + siffer = emptyList(), + sifferVekter = emptyList(), + forventetSjekksum = 0 + ), + "siffer er tom" to TestData( + siffer = emptyList(), + sifferVekter = listOf(1, 2, 3), + forventetSjekksum = 0 + ), + "sifferVekter er tom" to TestData( + siffer = listOf(1, 2, 3), + sifferVekter = emptyList(), + forventetSjekksum = 0 + ), + "siffer er kortere enn sifferVekter" to TestData( + siffer = listOf(3), + sifferVekter = listOf(5, 7, 9), + forventetSjekksum = 7 + ), + "sifferVekter er kortere enn siffer" to TestData( + siffer = listOf(2, 4, 6), + sifferVekter = listOf(8), + forventetSjekksum = 6 + ), + "krever ikke enkeltsiffer" to TestData( + siffer = listOf(13), + sifferVekter = listOf(14), + forventetSjekksum = 5 + ), + "sjekksum kalkuleres korrekt (1 av 2)" to TestData( + siffer = listOf(4, 4, 9), + sifferVekter = listOf(9, 3, 4), + forventetSjekksum = 4 + ), + "sjekksum kalkuleres korrekt (2 av 2)" to TestData( + siffer = listOf(4, 8, 4, 8), + sifferVekter = listOf(3, 8, 2, 1), + forventetSjekksum = 7 + ), + "sjekksum er tosifret" to TestData( + siffer = listOf(3, 1, 0, 5, 3), + sifferVekter = listOf(6, 8, 4, 2, 3), + forventetSjekksum = 10 + ), + "sjekksum er 0" to TestData( + siffer = listOf(7, 5, 6, 5, 1, 2, 1), + sifferVekter = listOf(0, 6, 0, 9, 9, 2, 0), + forventetSjekksum = 0 + ) + ) + ) { + sjekksum(it.siffer, it.sifferVekter) shouldBe it.forventetSjekksum + } + } +}) + +private data class TestData( + val siffer: List, + val sifferVekter: List, + val forventetSjekksum: Int +)