Skip to content

Commit 714aea6

Browse files
authored
Lagt til utils for å finne ut av virkedager og fnrgenerator (#7)
* Lagt til utils for å finne ut av virkedager og fnrgenerator * Endrer til spotless då det andre pluginet ikke håndterer specialtegn i filnavn
1 parent b2734b1 commit 714aea6

File tree

12 files changed

+319
-6
lines changed

12 files changed

+319
-6
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.idea
22
.gradle
3+
*.iml
34

45
# Ignore Gradle build output directory
56
build

Diff for: README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# tilleggsstonader-libs
2+
3+
Fellesbiblioteker for Tilleggsstønader, med inspirasjon fra https://github.com/navikt/familie-felles

Diff for: build.gradle.kts

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
1-
import org.jlleitschuh.gradle.ktlint.KtlintExtension
2-
31
val javaVersion = JavaLanguageVersion.of(17)
42

53
plugins {
64
kotlin("jvm") version "1.9.10"
75
`maven-publish`
86
`java-library`
9-
id("org.jlleitschuh.gradle.ktlint") version "11.5.1"
7+
id("com.diffplug.spotless") version "6.20.0"
108
}
119

1210
allprojects {
1311
repositories {
1412
mavenCentral()
1513
}
1614

17-
apply(plugin = "org.jlleitschuh.gradle.ktlint")
18-
configure<KtlintExtension> {
19-
version.set("0.50.0")
15+
apply(plugin = "com.diffplug.spotless")
16+
spotless {
17+
kotlin {
18+
ktlint("0.50.0")
19+
}
2020
}
2121

2222
configurations.all {
@@ -82,6 +82,10 @@ subprojects {
8282
}
8383
}
8484

85+
if (project.hasProperty("skipLint")) {
86+
gradle.startParameter.excludedTaskNames += "spotlessKotlinCheck"
87+
}
88+
8589
kotlin.sourceSets["main"].kotlin.srcDirs("main")
8690
kotlin.sourceSets["test"].kotlin.srcDirs("test")
8791
sourceSets["main"].resources.srcDirs("main")

Diff for: settings.gradle.kts

+2
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ include("http-client")
88
include("log")
99
include("pdl")
1010
include("sikkerhet")
11+
include("util")
12+
include("test-util")

Diff for: test-util/build.gradle.kts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
dependencies {
2+
testImplementation(project(":util"))
3+
}
4+
5+
tasks.sourcesJar {
6+
duplicatesStrategy = DuplicatesStrategy.WARN
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package no.nav.tilleggsstonader.libs.test.fnr
2+
3+
import java.time.LocalDate
4+
5+
/**
6+
* Fødselsnummergenerator. Genererer tilfeldige fødselsnumre med mulighet for å spesifisere år, måned, dato og D-nummer.
7+
*/
8+
object FnrGenerator {
9+
10+
private val k1Vekting = intArrayOf(3, 7, 6, 1, 8, 9, 4, 5, 2)
11+
private val k2Vekting = intArrayOf(5, 4, 3, 2, 7, 6, 5, 4, 3, 2)
12+
13+
fun generer(dato: LocalDate, erDnummer: Boolean = false): String =
14+
generer(dato.year, dato.monthValue, dato.dayOfMonth, erDnummer)
15+
16+
fun generer(
17+
år: Int = (1854..2039).random(),
18+
ned: Int = (1..12).random(),
19+
dag: Int = (1..28).random(),
20+
erDnummer: Boolean = false,
21+
): String {
22+
if (år > 2039 || år < 1854) {
23+
error("Ugyldig årstall. Lovlige verdier er mellom 1854 og 2039")
24+
}
25+
26+
val datoString = formater(år, måned, dag, erDnummer)
27+
28+
while (true) {
29+
val fødselsnummer = lagFødselsnummer(år, datoString)
30+
if (fødselsnummer.length == 11) {
31+
return fødselsnummer
32+
}
33+
}
34+
}
35+
36+
private fun lagFødselsnummerr: Int, datoString: String): String {
37+
val personnummerUtenSjekksiffer = when (år / 100) {
38+
19 -> (0..499).random().toString().padStart(3, '0')
39+
18 -> (500..749).random().toString().padStart(3, '0')
40+
20 -> (500..999).random().toString().padStart(3, '0')
41+
else -> error("Ugylidg århundre")
42+
}
43+
val verdi = datoString + personnummerUtenSjekksiffer
44+
45+
val siffer = verdi.chunked(1).map { it.toInt() }
46+
47+
val kontrollMod1 = 11 - (0..8).sumOf { k1Vekting[it] * siffer[it] } % 11
48+
val kontrollsiffer1 = kontrollSiffer(kontrollMod1)
49+
50+
val sifferMedEttKontrollsiffer = siffer + kontrollsiffer1
51+
val kontrollMod2 = 11 - (0..9).sumOf { k2Vekting[it] * sifferMedEttKontrollsiffer[it] } % 11
52+
val kontrollsiffer2 = kontrollSiffer(kontrollMod2)
53+
54+
return verdi + kontrollsiffer1 + kontrollsiffer2
55+
}
56+
57+
private fun formaterr: Int, måned: Int, dag: Int, erDnummer: Boolean): String {
58+
val dagString = if (erDnummer) (dag + 40).toString() else dag.toString().padStart(2, '0')
59+
val månedString = måned.toString().padStart(2, '0')
60+
val årString = (år % 100).toString().padStart(2, '0')
61+
return dagString + månedString + årString
62+
}
63+
64+
private fun kontrollSiffer(kontrollMod: Int): Int {
65+
if (kontrollMod == 11) {
66+
return 0
67+
}
68+
return kontrollMod
69+
}
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package no.nav.tilleggsstonader.libs.utils
2+
3+
import no.nav.tilleggsstonader.libs.test.fnr.FnrGenerator
4+
import no.nav.tilleggsstonader.libs.utils.fnr.Fødselsnummer
5+
import org.junit.jupiter.api.Test
6+
7+
class FnrGeneratorTest {
8+
9+
@Test
10+
fun `generer genererer kun gyldige fødselsnumre`() {
11+
repeat(10000) {
12+
Fødselsnummer(FnrGenerator.generer())
13+
}
14+
}
15+
}

Diff for: util/build.gradle.kts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
tasks.sourcesJar {
2+
duplicatesStrategy = DuplicatesStrategy.WARN
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package no.nav.tilleggsstonader.libs.utils
2+
3+
import java.time.DayOfWeek
4+
import java.time.LocalDate
5+
import java.time.MonthDay
6+
7+
object VirkedagerProvider {
8+
9+
private val FASTE_HELLIGDAGER = setOf(
10+
MonthDay.of(1, 1),
11+
MonthDay.of(5, 1),
12+
MonthDay.of(5, 17),
13+
MonthDay.of(12, 25),
14+
MonthDay.of(12, 26),
15+
)
16+
17+
private fun beregnBevegeligeHelligdagerr: Int): Set<MonthDay> {
18+
val påskedag = utledPåskedag(år)
19+
val skjærTorsdag = påskedag.minusDays(3)
20+
val langfredag = påskedag.minusDays(2)
21+
val andrePåskedag = påskedag.plusDays(1)
22+
val kristiHimmelfartsdag = påskedag.plusDays(39)
23+
val førstePinsedag = påskedag.plusDays(49)
24+
val andrePinsedag = påskedag.plusDays(50)
25+
26+
return setOf(
27+
MonthDay.from(påskedag),
28+
MonthDay.from(skjærTorsdag),
29+
MonthDay.from(langfredag),
30+
MonthDay.from(andrePåskedag),
31+
MonthDay.from(kristiHimmelfartsdag),
32+
MonthDay.from(førstePinsedag),
33+
MonthDay.from(andrePinsedag),
34+
)
35+
}
36+
37+
fun nesteVirkedag(dagensDato: LocalDate): LocalDate {
38+
var nesteDag = dagensDato.plusDays(1)
39+
40+
while (erHelgEllerHelligdag(nesteDag)) {
41+
nesteDag = nesteDag.plusDays(1)
42+
}
43+
return nesteDag
44+
}
45+
46+
private fun erHelgEllerHelligdag(dato: LocalDate): Boolean {
47+
val helligDager = FASTE_HELLIGDAGER + beregnBevegeligeHelligdager(dato.year)
48+
49+
return dato.dayOfWeek == DayOfWeek.SATURDAY ||
50+
dato.dayOfWeek == DayOfWeek.SUNDAY ||
51+
helligDager.contains(MonthDay.from(dato))
52+
}
53+
54+
// Butcher-Meeus algoritm
55+
private fun utledPåskedagr: Int): LocalDate {
56+
val a = år % 19
57+
val b = år / 100
58+
val c = år % 100
59+
val d = b / 4
60+
val e = b % 4
61+
val f = (b + 8) / 25
62+
val g = (b - f + 1) / 3
63+
val h = (19 * a + b - d - g + 15) % 30
64+
val i = c / 4
65+
val k = c % 4
66+
val l = (32 + 2 * e + 2 * i - h - k) % 7
67+
val m = (a + 11 * h + 22 * l) / 451
68+
val n = (h + l - 7 * m + 114) / 31
69+
val p = (h + l - 7 * m + 114) % 31
70+
return LocalDate.of(år, n, p + 1)
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package no.nav.tilleggsstonader.libs.utils.fnr
2+
3+
import java.time.LocalDate
4+
5+
data class Fødselsnummer(val verdi: String) {
6+
7+
init {
8+
check(gyldig(), verdi::toString)
9+
}
10+
11+
val erDNummer get() = verdi.substring(0, 1).toInt() > 3
12+
val erNAVSyntetisk get() = verdi.substring(2, 3).toInt() >= 4 && verdi.substring(2, 3).toInt() < 8
13+
val erSkattSyntetisk get() = verdi.substring(2, 3).toInt() >= 8
14+
val fødselsdato get() = beregnFødselsdato()
15+
16+
private fun beregnFødselsdato(): LocalDate {
17+
val dag = verdi.substring(0, 2).toInt() - (if (erDNummer) 40 else 0)
18+
val måned = verdi.substring(2, 4).toInt() - (if (erNAVSyntetisk) 40 else if (erSkattSyntetisk) 80 else 0)
19+
val år = verdi.substring(4, 6).toInt()
20+
val datoUtenÅrhundre = LocalDate.of(år, måned, dag)
21+
val individnummer = verdi.substring(6, 9).toInt()
22+
when {
23+
individnummer in 0..499 -> return datoUtenÅrhundre.plusYears(1900)
24+
individnummer in 500..749 && år >= 54 && år <= 99 -> return datoUtenÅrhundre.plusYears(1800)
25+
individnummer in 900..999 && år >= 40 && år <= 99 -> return datoUtenÅrhundre.plusYears(1900)
26+
individnummer in 500..999 && år >= 0 && år <= 39 -> return datoUtenÅrhundre.plusYears(2000)
27+
}
28+
throw IllegalArgumentException()
29+
}
30+
31+
private fun gyldig(): Boolean {
32+
if (verdi.length != 11 || verdi.toLongOrNull() == null) {
33+
return false
34+
}
35+
36+
val siffer = verdi.chunked(1).map { it.toInt() }
37+
val k1Vekting = intArrayOf(3, 7, 6, 1, 8, 9, 4, 5, 2)
38+
val k2Vekting = intArrayOf(5, 4, 3, 2, 7, 6, 5, 4, 3, 2)
39+
40+
val kontrollMod1 = 11 - (0..8).sumOf { k1Vekting[it] * siffer[it] } % 11
41+
val kontrollMod2 = 11 - (0..9).sumOf { k2Vekting[it] * siffer[it] } % 11
42+
val kontrollsiffer1 = siffer[9]
43+
val kontrollsiffer2 = siffer[10]
44+
45+
return gyldigKontrollSiffer(kontrollMod1, kontrollsiffer1) && gyldigKontrollSiffer(
46+
kontrollMod2,
47+
kontrollsiffer2,
48+
)
49+
}
50+
51+
private fun gyldigKontrollSiffer(kontrollMod: Int, kontrollsiffer: Int): Boolean {
52+
return kontrollMod == kontrollsiffer ||
53+
kontrollMod == 11 && kontrollsiffer == 0
54+
}
55+
56+
override fun toString(): String {
57+
return "***********"
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package no.nav.tilleggsstonader.libs.utils
2+
3+
import org.junit.jupiter.api.Assertions.assertEquals
4+
import org.junit.jupiter.api.Test
5+
import java.time.LocalDate
6+
7+
class VirkedagerProviderTest {
8+
val skjærTorsdag2021 = LocalDate.of(2021, 4, 1)
9+
val skjærTorsdag2022 = LocalDate.of(2022, 4, 14)
10+
11+
@Test
12+
fun `Hent virkedag allmenlig måndag`() {
13+
val allmenligMåndag = LocalDate.of(2020, 10, 26)
14+
assertEquals(VirkedagerProvider.nesteVirkedag(allmenligMåndag), allmenligMåndag.plusDays(1))
15+
}
16+
17+
@Test
18+
fun `Hent virkedag allmenlig fredag`() {
19+
val allmenligFredag = LocalDate.of(2020, 10, 30)
20+
assertEquals(VirkedagerProvider.nesteVirkedag(allmenligFredag), allmenligFredag.plusDays(3))
21+
}
22+
23+
@Test
24+
fun `Hent virkedag skjærtorsdag 2021`() {
25+
assertEquals(VirkedagerProvider.nesteVirkedag(skjærTorsdag2021), skjærTorsdag2021.plusDays(5))
26+
}
27+
28+
@Test
29+
fun `Hent virkedag skjærtorsdag 2022`() {
30+
assertEquals(VirkedagerProvider.nesteVirkedag(skjærTorsdag2022), skjærTorsdag2022.plusDays(5))
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package no.nav.tilleggsstonader.libs.utils.fnr
2+
3+
import org.assertj.core.api.Assertions.assertThat
4+
import org.junit.jupiter.api.Test
5+
import java.time.LocalDate
6+
7+
internal class FødselsnummerTest {
8+
9+
@Test
10+
internal fun `skal tillate helsyntetiske nummer fra dolly`() {
11+
val listeAvBrukere = listOf(
12+
SyntetiskBruker("15507600333", "55507608360", "Mann", LocalDate.of(1976, 10, 15)),
13+
SyntetiskBruker("29422059278", "69422056629", "Kvinne", LocalDate.of(2020, 2, 29)),
14+
SyntetiskBruker("15507600333", "55507608360", "Mann", LocalDate.of(1976, 10, 15)),
15+
SyntetiskBruker("29422059278", "69422056629", "Kvinne", LocalDate.of(2020, 2, 29)),
16+
SyntetiskBruker("05440355678", "45440356293", "Kvinne", LocalDate.of(2003, 4, 5)),
17+
SyntetiskBruker("12429400544", "52429405181", "Mann", LocalDate.of(1994, 2, 12)),
18+
SyntetiskBruker("12505209719", "52505209540", "Mann", LocalDate.of(1952, 10, 12)),
19+
SyntetiskBruker("21483609245", "61483601467", "Kvinne", LocalDate.of(1936, 8, 21)),
20+
SyntetiskBruker("17912099997", "57912075186", "Mann", LocalDate.of(2020, 11, 17)),
21+
SyntetiskBruker("29822099635", "69822075096", "Kvinne", LocalDate.of(2020, 2, 29)),
22+
SyntetiskBruker("05840399895", "45840375084", "Kvinne", LocalDate.of(2003, 4, 5)),
23+
SyntetiskBruker("12829499914", "52829400197", "Mann", LocalDate.of(1994, 2, 12)),
24+
SyntetiskBruker("12905299938", "52905200100", "Mann", LocalDate.of(1952, 10, 12)),
25+
SyntetiskBruker("21883649874", "61883600222", "Kvinne", LocalDate.of(1936, 8, 21)),
26+
)
27+
28+
listeAvBrukere.forEach {
29+
assertThat(it.fnr).isEqualTo(Fødselsnummer(it.fnr).verdi)
30+
.withFailMessage { "Fødselsnummer ${it.fnr} er gyldig" }
31+
assertThat(it.dnr).isEqualTo(Fødselsnummer(it.dnr).verdi)
32+
.withFailMessage { "Dnr ${it.dnr} er gyldig" }
33+
assertThat(it.fødselsdato).isEqualTo(Fødselsnummer(it.fnr).fødselsdato)
34+
.withFailMessage { "Finner dato for ${it.fnr}" }
35+
assertThat(it.fødselsdato).isEqualTo(Fødselsnummer(it.dnr).fødselsdato)
36+
.withFailMessage { "Finner dato for ${it.dnr}" }
37+
assertThat(false).isEqualTo(Fødselsnummer(it.fnr).erDNummer)
38+
.withFailMessage { "${it.fnr} er ikke D-nummer" }
39+
assertThat(true).isEqualTo(Fødselsnummer(it.dnr).erDNummer)
40+
.withFailMessage { "${it.dnr} er D-nummer" }
41+
}
42+
}
43+
44+
data class SyntetiskBruker(val fnr: String, val dnr: String, val kjønn: String, valdselsdato: LocalDate)
45+
}

0 commit comments

Comments
 (0)