Skip to content

Commit

Permalink
Legg til wrappere for fnr og orgnr
Browse files Browse the repository at this point in the history
  • Loading branch information
bjerga committed Jan 11, 2024
1 parent 2fbb410 commit 4339dc5
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 0 deletions.
42 changes: 42 additions & 0 deletions src/main/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/Fnr.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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))
}

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
}
}
}
34 changes: 34 additions & 0 deletions src/main/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/Orgnr.kt
Original file line number Diff line number Diff line change
@@ -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))
}

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
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package no.nav.helsearbeidsgiver.utils.wrapper

internal fun sjekksum(siffer: List<Int>, sifferVekter: List<Int>): Int =
siffer.zip(sifferVekter)
.sumOf { (siffer, vekt) -> siffer * vekt }
.mod(11)
.let { 11 - it }
.mod(11)
81 changes: 81 additions & 0 deletions src/test/kotlin/no/nav/helsearbeidsgiver/utils/wrapper/FnrTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
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(
// Sjekker ikke kontrollsiffer
"01010012345",
"19092212345",
"29104512345",
"31129912345",
"25056712345",
"11085812345",
"41066612345", // D-nummer
"45066612345", // D-nummer
"50066612345", // D-nummer
"57066612345", // D-nummer
"60066612345", // D-nummer
"69066612345", // D-nummer
"70066612345", // D-nummer
"71066612345", // D-nummer
"01490012345", // Testperson fra NAV
"01500012345", // Testperson fra NAV
"01890012345", // Testperson fra TestNorge
"01900012345" // Testperson fra TestNorge
)
) {
shouldNotThrowAny {
Fnr(it)
}
}
}

context("ugyldig") {
withData(
listOf(
"00010012345", // dag 0, andre siffer feil
"32010012345", // dag 32, andre siffer feil
"40010012345", // dag 40, andre siffer feil (D-nummer)
"72010012345", // dag 72, andre siffer feil (D-nummer)
"80010012345", // dag 80, første siffer feil
"01000012345", // måned 0, fjerde siffer feil
"01130012345", // måned 13, fjerde siffer feil
"01200012345", // måned 20, tredje siffer feil
"01390012345", // måned 39, tredje og fjerde siffer feil
"01400012345", // måned 40, fjerde siffer feil (testperson)
"01530012345", // måned 53, fjerde siffer feil (testperson)
"01790012345", // måned 79, tredje og fjerde siffer feil
"01800012345", // måned 80, fjerde siffer feil (testperson)
"01930012345", // måned 93, fjerde siffer feil (testperson)
"0101001234", // for kort
"010100123456", // for langt
"010100x2345", // med bokstav
"010100 1234" // med mellomrom
)
) {
shouldThrowExactly<IllegalArgumentException> {
Fnr(it)
}
}

test("tom streng") {
shouldThrowExactly<IllegalArgumentException> {
Fnr("")
}
}
}

test("toString gir wrappet verdi") {
Fnr("24120612345").let {
it.toString() shouldBe it.verdi
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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(
"161231654",
"908498460",
"135103210",
"684603132",
"167484979",
"796033020"
)
) {
shouldNotThrowAny {
Orgnr(it)
}
}
}

context("ugyldig") {
withData(
listOf(
"123", // for kort
"0123456789", // for langt
"12x456789", // med bokstav
"1234 6789" // med mellomrom
)
) {
shouldThrowExactly<IllegalArgumentException> {
Orgnr(it)
}
}

test("tom streng") {
shouldThrowExactly<IllegalArgumentException> {
Orgnr("")
}
}
}

test("toString gir wrappet verdi") {
Orgnr("123456789").let {
it.toString() shouldBe it.verdi
}
}
})
Original file line number Diff line number Diff line change
@@ -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<Int>,
val sifferVekter: List<Int>,
val forventetSjekksum: Int
)

0 comments on commit 4339dc5

Please sign in to comment.