From 1c70ee382597184911375ed9fefa11f098d76217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristen=20H=C3=A6rum?= Date: Wed, 7 Aug 2024 14:01:06 +0200 Subject: [PATCH 1/6] Update config.test.yml Lagt til outbound rule for skattekort-service --- apps/dolly-backend/config.test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/dolly-backend/config.test.yml b/apps/dolly-backend/config.test.yml index 23eb2c39428..79877d0ed8b 100644 --- a/apps/dolly-backend/config.test.yml +++ b/apps/dolly-backend/config.test.yml @@ -37,6 +37,7 @@ spec: - application: testnav-organisasjon-service - application: testnav-pdl-forvalter-dev - application: testnav-person-service + - application: testnav-skattekort-service - application: testnav-sykemelding-api - application: testnav-synt-sykemelding-api - application: testnav-tps-messaging-service @@ -107,4 +108,4 @@ spec: name: testnav-dolly-backend-dev databases: - name: testnav-dolly-backend-dev - autoBackupHour: 3 \ No newline at end of file + autoBackupHour: 3 From 63de518655e3fe576ff56346f04d9504f126c9c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristen=20H=C3=A6rum?= Date: Fri, 9 Aug 2024 10:09:27 +0200 Subject: [PATCH 2/6] Bugfix/skattekort service bugfix (#3577) * Remove Trekktype and refactor related code #deploy-skatte-service Removed the Trekktype class and refactored related code to use Forskuddstrekk directly. Updated mapping strategies and utility to align with the new structure. This change simplifies the data model and improves maintainability. * Rename and refactor mapping strategy classes #deploy-skattekort-service Renamed `TrekktypeMappingStrategy` to `ForskuddstrekkMappingStrategy` for better clarity. Updated variable names inside the class to align with the new name. Modified `Forskuddstrekk` class by adding `AllArgsConstructor` and changing `SuperBuilder` to `Builder`. --- .../SkattekortServiceApplicationStarter.java | 2 +- .../dto/SkattekortResponsIntermediate.java | 61 +++++++------------ .../mapper/ArbeidsgiverMappingStrategy.java | 24 +++----- ...ava => ForskuddstrekkMappingStrategy.java} | 58 +++++++----------- .../utility/SkattekortValidator.java | 2 +- .../skattekortservice/v1/Forskuddstrekk.java | 35 ++++++++++- .../dto/skattekortservice/v1/Skattekort.java | 10 +-- .../dto/skattekortservice/v1/Trekktype.java | 47 -------------- 8 files changed, 96 insertions(+), 143 deletions(-) rename apps/skattekort-service/src/main/java/no/nav/skattekortservice/mapper/{TrekktypeMappingStrategy.java => ForskuddstrekkMappingStrategy.java} (52%) delete mode 100644 libs/data-transfer-objects/src/main/java/no/nav/testnav/libs/dto/skattekortservice/v1/Trekktype.java diff --git a/apps/skattekort-service/src/main/java/no/nav/skattekortservice/SkattekortServiceApplicationStarter.java b/apps/skattekort-service/src/main/java/no/nav/skattekortservice/SkattekortServiceApplicationStarter.java index 1c714ebb8b7..0d5a91d6a10 100644 --- a/apps/skattekort-service/src/main/java/no/nav/skattekortservice/SkattekortServiceApplicationStarter.java +++ b/apps/skattekort-service/src/main/java/no/nav/skattekortservice/SkattekortServiceApplicationStarter.java @@ -10,4 +10,4 @@ public static void main(String[] args) { SpringApplication.run(SkattekortServiceApplicationStarter.class, args); } -} +} \ No newline at end of file diff --git a/apps/skattekort-service/src/main/java/no/nav/skattekortservice/dto/SkattekortResponsIntermediate.java b/apps/skattekort-service/src/main/java/no/nav/skattekortservice/dto/SkattekortResponsIntermediate.java index dcf4a91927e..efeef1ffbc1 100644 --- a/apps/skattekort-service/src/main/java/no/nav/skattekortservice/dto/SkattekortResponsIntermediate.java +++ b/apps/skattekort-service/src/main/java/no/nav/skattekortservice/dto/SkattekortResponsIntermediate.java @@ -1,11 +1,15 @@ package no.nav.skattekortservice.dto; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; +import lombok.experimental.SuperBuilder; import no.nav.testnav.libs.dto.skattekortservice.v1.IdentifikatorForEnhetEllerPerson; import java.time.LocalDate; @@ -88,9 +92,6 @@ public static class Skattekort { private LocalDate utstedtDato; private Long skattekortidentifikator; private List forskuddstrekk; - private List frikort; - private List trekktabell; - private List trekkprosent; public List getForskuddstrekk() { @@ -99,59 +100,40 @@ public List getForskuddstrekk() { } return forskuddstrekk; } - - public List getFrikort() { - - if (isNull(frikort)) { - frikort = new ArrayList<>(); - } - return frikort; - } - - public List getTrekktabell() { - - if (isNull(trekktabell)) { - trekktabell = new ArrayList<>(); - } - return trekktabell; - } - - public List getTrekkprosent() { - - if (isNull(trekkprosent)) { - trekkprosent = new ArrayList<>(); - } - return trekkprosent; - } } @Data - @Builder + @SuperBuilder @NoArgsConstructor @AllArgsConstructor - public static class Forskuddstrekk { + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "xsi:type") + @JsonSubTypes({ + @JsonSubTypes.Type(value = Frikort.class, name = "Frikort"), + @JsonSubTypes.Type(value = Trekktabell.class, name = "Trekktabell"), + @JsonSubTypes.Type(value = Trekkprosent.class, name = "Trekkprosent") + }) + public abstract static class Forskuddstrekk { private Trekkode trekkode; + private String type; } + @EqualsAndHashCode(callSuper = true) @Data - @Builder + @SuperBuilder @NoArgsConstructor @AllArgsConstructor - public static class Frikort { - - private Trekkode trekkode; + public static class Frikort extends Forskuddstrekk { private Integer frikortbeloep; } + @EqualsAndHashCode(callSuper = true) @Data - @Builder + @SuperBuilder @NoArgsConstructor @AllArgsConstructor - public static class Trekktabell { - - private Trekkode trekkode; + public static class Trekktabell extends Forskuddstrekk { private Tabelltype tabelltype; private String tabellnummer; @@ -159,11 +141,12 @@ public static class Trekktabell { private Integer antallMaanederForTrekk; } + @EqualsAndHashCode(callSuper = true) @Data - @Builder + @SuperBuilder @NoArgsConstructor @AllArgsConstructor - public static class Trekkprosent { + public static class Trekkprosent extends Forskuddstrekk { private Trekkode trekkode; diff --git a/apps/skattekort-service/src/main/java/no/nav/skattekortservice/mapper/ArbeidsgiverMappingStrategy.java b/apps/skattekort-service/src/main/java/no/nav/skattekortservice/mapper/ArbeidsgiverMappingStrategy.java index 28348b1e7ff..6d3f64c1a3b 100644 --- a/apps/skattekort-service/src/main/java/no/nav/skattekortservice/mapper/ArbeidsgiverMappingStrategy.java +++ b/apps/skattekort-service/src/main/java/no/nav/skattekortservice/mapper/ArbeidsgiverMappingStrategy.java @@ -5,7 +5,6 @@ import ma.glasnost.orika.MapperFactory; import ma.glasnost.orika.MappingContext; import no.nav.skattekortservice.dto.SkattekortResponsIntermediate; -import no.nav.testnav.libs.dto.skattekortservice.v1.Forskuddstrekk; import no.nav.testnav.libs.dto.skattekortservice.v1.Frikort; import no.nav.testnav.libs.dto.skattekortservice.v1.Resultatstatus; import no.nav.testnav.libs.dto.skattekortservice.v1.Skattekort; @@ -17,6 +16,8 @@ import no.nav.testnav.libs.dto.skattekortservice.v1.Trekktabell; import org.springframework.stereotype.Component; +import static java.util.Objects.nonNull; + @Slf4j @Component public class ArbeidsgiverMappingStrategy implements MappingStrategy { @@ -39,22 +40,12 @@ public void mapAtoB(SkattekortResponsIntermediate.Skattekortmelding source, Skat }) .register(); - factory.classMap(SkattekortResponsIntermediate.Forskuddstrekk.class, Forskuddstrekk.class) - .customize(new CustomMapper<>() { - @Override - public void mapAtoB(SkattekortResponsIntermediate.Forskuddstrekk source, Forskuddstrekk target, MappingContext context) { - - target.setTrekkode(Trekkode.valueOf(source.getTrekkode().getValue())); - } - }) - .register(); - factory.classMap(SkattekortResponsIntermediate.Frikort.class, Frikort.class) .customize(new CustomMapper<>() { @Override public void mapAtoB(SkattekortResponsIntermediate.Frikort source, Frikort target, MappingContext context) { - target.setTrekkode(Trekkode.valueOf(source.getTrekkode().getValue())); + target.setTrekkode(getTrekkodeValue(source.getTrekkode())); target.setFrikortbeloep(source.getFrikortbeloep()); } }) @@ -65,7 +56,7 @@ public void mapAtoB(SkattekortResponsIntermediate.Frikort source, Frikort target @Override public void mapAtoB(SkattekortResponsIntermediate.Trekktabell source, Trekktabell target, MappingContext context) { - target.setTrekkode(Trekkode.valueOf(source.getTrekkode().getValue())); + target.setTrekkode(getTrekkodeValue(source.getTrekkode())); target.setTabelltype(Tabelltype.valueOf(source.getTabelltype().getValue())); target.setTabellnummer(source.getTabellnummer()); target.setProsentsats(source.getProsentsats()); @@ -79,11 +70,16 @@ public void mapAtoB(SkattekortResponsIntermediate.Trekktabell source, Trekktabel @Override public void mapAtoB(SkattekortResponsIntermediate.Trekkprosent source, Trekkprosent target, MappingContext context) { - target.setTrekkode(Trekkode.valueOf(target.getTrekkode().getValue())); + target.setTrekkode(getTrekkodeValue(source.getTrekkode())); target.setProsentsats(source.getProsentsats()); target.setAntallMaanederForTrekk(source.getAntallMaanederForTrekk()); } }) .register(); } + + private static Trekkode getTrekkodeValue(SkattekortResponsIntermediate.Trekkode trekkode) { + + return nonNull(trekkode.getValue()) ? Trekkode.valueOf(trekkode.getValue()) : null; + } } diff --git a/apps/skattekort-service/src/main/java/no/nav/skattekortservice/mapper/TrekktypeMappingStrategy.java b/apps/skattekort-service/src/main/java/no/nav/skattekortservice/mapper/ForskuddstrekkMappingStrategy.java similarity index 52% rename from apps/skattekort-service/src/main/java/no/nav/skattekortservice/mapper/TrekktypeMappingStrategy.java rename to apps/skattekort-service/src/main/java/no/nav/skattekortservice/mapper/ForskuddstrekkMappingStrategy.java index 0d6d81dfd77..5e6c1273f11 100644 --- a/apps/skattekort-service/src/main/java/no/nav/skattekortservice/mapper/TrekktypeMappingStrategy.java +++ b/apps/skattekort-service/src/main/java/no/nav/skattekortservice/mapper/ForskuddstrekkMappingStrategy.java @@ -9,7 +9,6 @@ import no.nav.testnav.libs.dto.skattekortservice.v1.Skattekort; import no.nav.testnav.libs.dto.skattekortservice.v1.Trekkprosent; import no.nav.testnav.libs.dto.skattekortservice.v1.Trekktabell; -import no.nav.testnav.libs.dto.skattekortservice.v1.Trekktype; import org.springframework.stereotype.Component; import java.util.Objects; @@ -17,7 +16,7 @@ import static java.util.Objects.nonNull; @Component -public class TrekktypeMappingStrategy implements MappingStrategy { +public class ForskuddstrekkMappingStrategy implements MappingStrategy { @Override public void register(MapperFactory factory) { @@ -26,20 +25,17 @@ public void register(MapperFactory factory) { @Override public void mapAtoB(Skattekort skattekort, no.skatteetaten.fastsetting.formueinntekt.forskudd.skattekorttilarbeidsgiver.v3.Skattekort skattekort2, MappingContext context) { - skattekort2.getForskuddstrekk().addAll(skattekort.getTrekktype().stream() - .map(trekktype -> { - if (nonNull(trekktype.getFrikort())) { - return mapperFacade.map(trekktype.getFrikort(), + skattekort2.getForskuddstrekk().addAll(skattekort.getForskuddstrekk().stream() + .map(forskuddstrekk -> { + if (nonNull(forskuddstrekk.getFrikort())) { + return mapperFacade.map(forskuddstrekk.getFrikort(), no.skatteetaten.fastsetting.formueinntekt.forskudd.skattekorttilarbeidsgiver.v3.Frikort.class); - } else if (nonNull(trekktype.getTrekktabell())) { - return mapperFacade.map(trekktype.getTrekktabell(), + } else if (nonNull(forskuddstrekk.getTrekktabell())) { + return mapperFacade.map(forskuddstrekk.getTrekktabell(), no.skatteetaten.fastsetting.formueinntekt.forskudd.skattekorttilarbeidsgiver.v3.Trekktabell.class); - } else if (nonNull(trekktype.getTrekkprosent())) { - return mapperFacade.map(trekktype.getTrekkprosent(), + } else if (nonNull(forskuddstrekk.getTrekkprosent())) { + return mapperFacade.map(forskuddstrekk.getTrekkprosent(), no.skatteetaten.fastsetting.formueinntekt.forskudd.skattekorttilarbeidsgiver.v3.Trekkprosent.class); - } else if (nonNull(trekktype.getForskuddstrekk())) { - return mapperFacade.map(trekktype.getForskuddstrekk(), - no.skatteetaten.fastsetting.formueinntekt.forskudd.skattekorttilarbeidsgiver.v3.Forskuddstrekk.class); } else { return null; } @@ -48,7 +44,7 @@ public void mapAtoB(Skattekort skattekort, no.skatteetaten.fastsetting.formueinn .toList()); } }) - .exclude("trekktype") + .exclude("forskuddstrekk") .byDefault() .register(); @@ -57,29 +53,21 @@ public void mapAtoB(Skattekort skattekort, no.skatteetaten.fastsetting.formueinn @Override public void mapAtoB(SkattekortResponsIntermediate.Skattekort skattekort, Skattekort skattekort2, MappingContext context) { - skattekort2.getTrekktype().addAll(skattekort.getFrikort().stream() - .map(frikort -> Trekktype.builder() - .frikort(mapperFacade.map(frikort, Frikort.class)) - .build()) - .toList()); - skattekort2.getTrekktype().addAll(skattekort.getForskuddstrekk().stream() - .map(forskuddstrekk -> Trekktype.builder() - .forskuddstrekk(mapperFacade.map(forskuddstrekk, Forskuddstrekk.class)) - .build()) - .toList()); - skattekort2.getTrekktype().addAll(skattekort.getTrekkprosent().stream() - .map(trekkprosent -> Trekktype.builder() - .trekkprosent(mapperFacade.map(trekkprosent, Trekkprosent.class)) - .build()) - .toList()); - skattekort2.getTrekktype().addAll(skattekort.getTrekktabell().stream() - .map(trekktabell -> Trekktype.builder() - .trekktabell(mapperFacade.map(trekktabell, Trekktabell.class)) - .build()) - .toList()); + skattekort2.setSkattekortidentifikator(skattekort.getSkattekortidentifikator()); + skattekort2.setUtstedtDato(skattekort.getUtstedtDato()); + skattekort.getForskuddstrekk() + .forEach(forskuddstrekk -> skattekort2.getForskuddstrekk().add( + Forskuddstrekk.builder() + .frikort(forskuddstrekk instanceof SkattekortResponsIntermediate.Frikort ? + mapperFacade.map(forskuddstrekk, Frikort.class) : null) + .trekktabell(forskuddstrekk instanceof SkattekortResponsIntermediate.Trekktabell ? + mapperFacade.map(forskuddstrekk, Trekktabell.class) : null) + .trekkprosent(forskuddstrekk instanceof SkattekortResponsIntermediate.Trekkprosent ? + mapperFacade.map(forskuddstrekk, Trekkprosent.class) : null) + .build() + )); } }) - .byDefault() .register(); } } \ No newline at end of file diff --git a/apps/skattekort-service/src/main/java/no/nav/skattekortservice/utility/SkattekortValidator.java b/apps/skattekort-service/src/main/java/no/nav/skattekortservice/utility/SkattekortValidator.java index 2ead001896d..e039d549028 100644 --- a/apps/skattekort-service/src/main/java/no/nav/skattekortservice/utility/SkattekortValidator.java +++ b/apps/skattekort-service/src/main/java/no/nav/skattekortservice/utility/SkattekortValidator.java @@ -28,7 +28,7 @@ private static void validateSkattekort(SkattekortRequestDTO skattekort) { .map(ArbeidsgiverSkatt::getArbeidstaker) .flatMap(Collection::stream) .map(Skattekortmelding::getSkattekort) - .map(Skattekort::getTrekktype) + .map(Skattekort::getForskuddstrekk) .flatMap(Collection::stream) .forEach(trekktype -> { if (trekktype.isAllEmpty()) { diff --git a/libs/data-transfer-objects/src/main/java/no/nav/testnav/libs/dto/skattekortservice/v1/Forskuddstrekk.java b/libs/data-transfer-objects/src/main/java/no/nav/testnav/libs/dto/skattekortservice/v1/Forskuddstrekk.java index 0d2dc40687a..aeed4c1f671 100644 --- a/libs/data-transfer-objects/src/main/java/no/nav/testnav/libs/dto/skattekortservice/v1/Forskuddstrekk.java +++ b/libs/data-transfer-objects/src/main/java/no/nav/testnav/libs/dto/skattekortservice/v1/Forskuddstrekk.java @@ -1,15 +1,48 @@ package no.nav.testnav.libs.dto.skattekortservice.v1; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.util.Arrays; +import java.util.Objects; + @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Forskuddstrekk { - private Trekkode trekkode; + private Frikort frikort; + private Trekktabell trekktabell; + private Trekkprosent trekkprosent; + + @JsonIgnore + public boolean isAllEmpty() { + + return contentsCount() == 0; + } + + @JsonIgnore + public boolean isAmbiguous() { + + return contentsCount() > 1; + } + + private long contentsCount() { + + return Arrays.stream(getClass().getDeclaredFields()) + .map(field -> { + try { + field.setAccessible(true); + return field.get(this); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }) + .filter(Objects::nonNull) + .count(); + } } diff --git a/libs/data-transfer-objects/src/main/java/no/nav/testnav/libs/dto/skattekortservice/v1/Skattekort.java b/libs/data-transfer-objects/src/main/java/no/nav/testnav/libs/dto/skattekortservice/v1/Skattekort.java index b7f872e60a1..94a1bb58fa3 100644 --- a/libs/data-transfer-objects/src/main/java/no/nav/testnav/libs/dto/skattekortservice/v1/Skattekort.java +++ b/libs/data-transfer-objects/src/main/java/no/nav/testnav/libs/dto/skattekortservice/v1/Skattekort.java @@ -19,14 +19,14 @@ public class Skattekort { private LocalDate utstedtDato; private Long skattekortidentifikator; - private List trekktype; + private List forskuddstrekk; - public List getTrekktype() { + public List getForskuddstrekk() { - if (isNull(trekktype)) { - trekktype = new ArrayList<>(); + if (isNull(forskuddstrekk)) { + forskuddstrekk = new ArrayList<>(); } - return trekktype; + return forskuddstrekk; } } diff --git a/libs/data-transfer-objects/src/main/java/no/nav/testnav/libs/dto/skattekortservice/v1/Trekktype.java b/libs/data-transfer-objects/src/main/java/no/nav/testnav/libs/dto/skattekortservice/v1/Trekktype.java deleted file mode 100644 index 5042a2e4870..00000000000 --- a/libs/data-transfer-objects/src/main/java/no/nav/testnav/libs/dto/skattekortservice/v1/Trekktype.java +++ /dev/null @@ -1,47 +0,0 @@ -package no.nav.testnav.libs.dto.skattekortservice.v1; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; - -import java.util.Arrays; -import java.util.Objects; - -@Data -@SuperBuilder -@NoArgsConstructor -public class Trekktype { - - private Forskuddstrekk forskuddstrekk; - private Frikort frikort; - private Trekktabell trekktabell; - private Trekkprosent trekkprosent; - - @JsonIgnore - public boolean isAllEmpty() { - - return contentsCount() == 0; - } - - @JsonIgnore - public boolean isAmbiguous() { - - return contentsCount() > 1; - } - - private long contentsCount() { - - return Arrays.stream(getClass().getDeclaredFields()) - .map(field -> { - try { - field.setAccessible(true); - return field.get(this); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - }) - .filter(Objects::nonNull) - .count(); - } -} From 24250f092d488686c533cd7b05a99d6d046b3a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristen=20H=C3=A6rum?= Date: Tue, 13 Aug 2024 11:23:37 +0200 Subject: [PATCH 3/6] Feature/opprett pensjonsavtale (#3575) * Add Pensjonsavtale feature to PensjonforvalterClient #deploy-test-dolly-backend Integrate support for Pensjonsavtale in PensjonforvalterClient, including new domain requests, consumer commands, and mapping strategy. This enables the system to handle and store Pensjonsavtale data efficiently. * Lagt til bestillingsfelter for pensjonsavtale * Add support for Pensjonsavtale field #deploy-test-dolly-backend Included the Pensjonsavtale field in mapping strategies and status report mappings. This ensures that data related to Pensjonsavtale is correctly processed and reported. * Add pension agreement fields to order summary Included new fields for the pension agreement in the order summary to capture product designation, agreement category, start and end ages, and payment periods. Updated helper text in the related form to reflect these changes. * Add support for Pensjonsavtale field #deploy-test-dolly-backend Included the Pensjonsavtale field in mapping strategies and status report mappings. This ensures that data related to Pensjonsavtale is correctly processed and reported. * deploy #deploy-test-dolly-backend * Add support for Pensjonsavtale data Enhanced the application to handle Pensjonsavtale data by adding new components and necessary validation logic. Updated existing components and services to integrate the new functionality seamlessly. * deploy #deploy-test-dolly-backend * Update pension agreement data structure and endpoint #deploy-test-dolly-backend Removed unused fields `startAlderAar` and `sluttAlderAar` from `PensjonsavtaleRequest` and `PensjonData` classes. Changed field names from `startAlderMaaneder` and `sluttAlderMaaneder` to `startAlderMaaned` and `sluttAlderMaaned` for consistency. Updated pension agreement endpoint to v2. * Add support for deleting pension agreements #deploy-test-dolly-backend Introduced functionality to delete pension agreements via the new `SlettePensjonsavtaleCommand`. Updated relevant methods in `PensjonforvalterClient` and `PensjonforvalterConsumer` to call the new command. * Add pension agreement fields to order summary #deploy-test-frontend Included new fields for the pension agreement in the order summary to capture product designation, agreement category, start and end ages, and payment periods. Updated helper text and validation rules in the related form to reflect these changes. Removed unnecessary fields and standardized month labels in the dropdown options. * Fix typo in validation error message Corrected a typo from "nellom" to "mellom" in the age validation message. This ensures clear communication of the validation rules to users. * Update initial values in pensjonsavtale component #deploy-test-frontend Changed `avtaleKategori` to 'PRIVAT_TJENESTEPENSJON' and adjusted `aarligUtbetaling` in initialPensjonsavtale. These updates enhance consistency with the new pension agreement specifications. * Adjust panel visibility condition in Pensjonsavtale component #deploy-test-frontend Modified the `isPanelOpen` condition to trigger when data length is less than 3. This change ensures better alignment with the requirements for displaying the pension agreement panel. * Fix UtbetalingsperioderForm initialization and update labels #deploy-test-frontend Removed the initialization parameter for `initialUtbetalingsperiode` from `UtbetalingsperioderForm`. Updated month labels in `SelectOptions.tsx` to start with an uppercase letter. Added a condition to show 'Pensjonsavtale' information in certain environments. * Swap start and slutt ages in initialValues.tsx #deploy-test-frontend Corrected the start and end ages for the initial payment period in the `initialValues.tsx` file to align with the expected configuration. This ensures the age parameters accurately reflect the intended pension distribution timeline. * Remove Pensjonsavtaler form and visning components #deploy-test-frontend Deleted `Pensjonsavtaler.tsx` and `PensjonsavtaleVisning.tsx` components due to refactoring and removal of deprecated functionality. Updated the `initalValues.tsx` file to streamline the initial payment period data structure. --- .../PensjonforvalterClient.java | 27 ++++- .../PensjonforvalterConsumer.java | 20 +++ .../command/LagrePensjonsavtaleCommand.java | 66 ++++++++++ .../command/SlettePensjonsavtaleCommand.java | 56 +++++++++ .../domain/PensjonsavtaleRequest.java | 57 +++++++++ .../mapper/PensjonsavtaleMappingStrategy.java | 33 +++++ .../service/DollyBestillingService.java | 2 +- .../dolly/domain/resultset/SystemTyper.java | 1 + .../domain/resultset/pensjon/PensjonData.java | 57 +++++++++ ...estillingPensjonforvalterStatusMapper.java | 3 + ...yRequest2MalBestillingMappingStrategy.java | 3 +- .../src/main/js/playwright/globalSetup.tsx | 5 + .../main/js/playwright/mocks/BasicMocks.tsx | 2 + .../kriterier/BestillingKriterieMapper.tsx | 28 +++++ .../stegVelger/steg/steg1/paneler/Pensjon.tsx | 13 ++ .../stegVelger/steg/steg2/Steg2.tsx | 2 + .../fagsystem/pensjon/form/validation.tsx | 2 + .../fagsystem/pensjonsavtale/form/Form.tsx | 64 ++++++++++ .../form/partials/Utbetalingsperioder.tsx | 45 +++++++ .../pensjonsavtale/form/validation.tsx | 56 +++++++++ .../fagsystem/pensjonsavtale/initalValues.tsx | 15 +++ .../visning/PensjonsavtaleVisning.tsx | 114 ++++++++++++++++++ .../src/components/miljoVelger/MiljoeInfo.tsx | 6 + .../js/src/components/ui/form/formUtils.tsx | 1 + .../gruppe/PersonVisning/PersonVisning.tsx | 14 +++ .../src/main/js/src/service/SelectOptions.tsx | 26 ++++ .../src/main/js/src/utils/DataFormatter.tsx | 6 +- .../js/src/utils/SjekkBestillingFagsystem.tsx | 10 ++ .../js/src/utils/hooks/useFagsystemer.tsx | 24 ++++ 29 files changed, 752 insertions(+), 6 deletions(-) create mode 100644 apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/command/LagrePensjonsavtaleCommand.java create mode 100644 apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/command/SlettePensjonsavtaleCommand.java create mode 100644 apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/domain/PensjonsavtaleRequest.java create mode 100644 apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/mapper/PensjonsavtaleMappingStrategy.java create mode 100644 apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/form/Form.tsx create mode 100644 apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/form/partials/Utbetalingsperioder.tsx create mode 100644 apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/form/validation.tsx create mode 100644 apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/initalValues.tsx create mode 100644 apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/visning/PensjonsavtaleVisning.tsx diff --git a/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/PensjonforvalterClient.java b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/PensjonforvalterClient.java index 3f34286411f..ea4f68fb40a 100644 --- a/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/PensjonforvalterClient.java +++ b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/PensjonforvalterClient.java @@ -21,6 +21,7 @@ import no.nav.dolly.bestilling.pensjonforvalter.domain.PensjonTpYtelseRequest; import no.nav.dolly.bestilling.pensjonforvalter.domain.PensjonUforetrygdRequest; import no.nav.dolly.bestilling.pensjonforvalter.domain.PensjonforvalterResponse; +import no.nav.dolly.bestilling.pensjonforvalter.domain.PensjonsavtaleRequest; import no.nav.dolly.bestilling.personservice.PersonServiceConsumer; import no.nav.dolly.consumer.norg2.Norg2Consumer; import no.nav.dolly.consumer.norg2.dto.Norg2EnhetResponse; @@ -83,6 +84,7 @@ public class PensjonforvalterClient implements ClientRegister { private static final String TP_FORHOLD = "TpForhold#"; private static final String PEN_ALDERSPENSJON = "AP#"; private static final String PEN_UFORETRYGD = "Ufoer#"; + private static final String PEN_PENSJONSAVTALE = "Pensjonsavtale#"; private static final String PERIODE = "/periode/"; private final PensjonforvalterConsumer pensjonforvalterConsumer; @@ -154,7 +156,10 @@ public Flux gjenopprett(RsDollyUtvidetBestilling bestilling, Dolly .map(response -> POPP_INNTEKTSREGISTER + decodeStatus(response, dollyPerson.getIdent())), lagreTpForhold(pensjon, dollyPerson.getIdent(), bestilteMiljoer.get()) - .map(response -> TP_FORHOLD + decodeStatus(response, dollyPerson.getIdent())) + .map(response -> TP_FORHOLD + decodeStatus(response, dollyPerson.getIdent())), + + lagrePensjonsavtale(pensjon, dollyPerson.getIdent(), bestilteMiljoer.get()) + .map(response -> PEN_PENSJONSAVTALE + decodeStatus(response, dollyPerson.getIdent())) ) .collectList() .doOnNext(statusResultat::addAll) @@ -197,9 +202,10 @@ public Flux gjenopprett(RsDollyUtvidetBestilling bestilling, Dolly @Override public void release(List identer) { - // Pensjonforvalter / POPP støtter pt ikke sletting + // Pensjonforvalter / POPP, AP, UT støtter pt ikke sletting pensjonforvalterConsumer.sletteTpForhold(identer); + pensjonforvalterConsumer.slettePensjonsavtale(identer); } public static PensjonforvalterResponse mergePensjonforvalterResponses(List responser) { @@ -490,6 +496,23 @@ private Mono lagreTpForhold(PensjonData pensjonData, S .map(PensjonforvalterClient::mergePensjonforvalterResponses); } + private Flux lagrePensjonsavtale(PensjonData pensjon, String ident, Set miljoer) { + + return Flux.just(pensjon) + .filter(PensjonData::hasPensjonsavtale) + .map(PensjonData::getPensjonsavtale) + .flatMap(pensjonsavtaler -> Flux.fromIterable(pensjonsavtaler) + .flatMap(pensjonsavtale -> { + + var context = MappingContextUtils.getMappingContext(); + context.setProperty(IDENT, ident); + context.setProperty(MILJOER, miljoer); + + var pensjonsavtaleRequest = mapperFacade.map(pensjonsavtale, PensjonsavtaleRequest.class, context); + return pensjonforvalterConsumer.lagrePensjonsavtale(pensjonsavtaleRequest); + })); + } + private String decodeStatus(PensjonforvalterResponse response, String ident) { log.info("Mottatt status på {} fra Pensjon-Testdata-Facade: {}", ident, response); diff --git a/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/PensjonforvalterConsumer.java b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/PensjonforvalterConsumer.java index c22bc5da68e..d2f8fbfca78 100644 --- a/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/PensjonforvalterConsumer.java +++ b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/PensjonforvalterConsumer.java @@ -7,12 +7,14 @@ import no.nav.dolly.bestilling.pensjonforvalter.command.HentMiljoerCommand; import no.nav.dolly.bestilling.pensjonforvalter.command.HentSamboerCommand; import no.nav.dolly.bestilling.pensjonforvalter.command.LagreAlderspensjonCommand; +import no.nav.dolly.bestilling.pensjonforvalter.command.LagrePensjonsavtaleCommand; import no.nav.dolly.bestilling.pensjonforvalter.command.LagrePoppInntektCommand; import no.nav.dolly.bestilling.pensjonforvalter.command.LagreSamboerCommand; import no.nav.dolly.bestilling.pensjonforvalter.command.LagreTpForholdCommand; import no.nav.dolly.bestilling.pensjonforvalter.command.LagreTpYtelseCommand; import no.nav.dolly.bestilling.pensjonforvalter.command.LagreUforetrygdCommand; import no.nav.dolly.bestilling.pensjonforvalter.command.OpprettPersonCommand; +import no.nav.dolly.bestilling.pensjonforvalter.command.SlettePensjonsavtaleCommand; import no.nav.dolly.bestilling.pensjonforvalter.command.SletteTpForholdCommand; import no.nav.dolly.bestilling.pensjonforvalter.domain.AlderspensjonRequest; import no.nav.dolly.bestilling.pensjonforvalter.domain.PensjonPersonRequest; @@ -23,6 +25,7 @@ import no.nav.dolly.bestilling.pensjonforvalter.domain.PensjonTpYtelseRequest; import no.nav.dolly.bestilling.pensjonforvalter.domain.PensjonUforetrygdRequest; import no.nav.dolly.bestilling.pensjonforvalter.domain.PensjonforvalterResponse; +import no.nav.dolly.bestilling.pensjonforvalter.domain.PensjonsavtaleRequest; import no.nav.dolly.config.Consumers; import no.nav.dolly.metrics.Timed; import no.nav.testnav.libs.securitycore.domain.ServerProperties; @@ -148,6 +151,23 @@ public Flux lagreTpYtelse(PensjonTpYtelseRequest pensj .flatMapMany(token -> new LagreTpYtelseCommand(webClient, token.getTokenValue(), pensjonTpYtelseRequest).call()); } + @Timed(name = "providers", tags = { "operation", "pen_lagrePensjpnsavtale" }) + public Flux lagrePensjonsavtale(PensjonsavtaleRequest pensjonsavtaleRequest) { + + return tokenService.exchange(serverProperties) + .flatMapMany(token -> new LagrePensjonsavtaleCommand(webClient, pensjonsavtaleRequest, token.getTokenValue()).call()); + } + + @Timed(name = "providers", tags = { "operation", "pen_slettePensjpnsavtale" }) + public void slettePensjonsavtale(List identer) { + + var test = tokenService.exchange(serverProperties) + .flatMapMany(token -> Flux.fromIterable(identer) + .flatMap(ident -> new SlettePensjonsavtaleCommand(webClient, ident, token.getTokenValue()).call())) + .collectList() + .subscribe(resultat -> log.info("Slettet pensjonsavtaler (PEN), alle miljøer")); + } + @Override public String serviceUrl() { return serverProperties.getUrl(); diff --git a/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/command/LagrePensjonsavtaleCommand.java b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/command/LagrePensjonsavtaleCommand.java new file mode 100644 index 00000000000..81e8b513b7a --- /dev/null +++ b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/command/LagrePensjonsavtaleCommand.java @@ -0,0 +1,66 @@ +package no.nav.dolly.bestilling.pensjonforvalter.command; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import no.nav.dolly.bestilling.pensjonforvalter.domain.PensjonforvalterResponse; +import no.nav.dolly.bestilling.pensjonforvalter.domain.PensjonsavtaleRequest; +import no.nav.testnav.libs.reactivecore.utils.WebClientFilter; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.retry.Retry; + +import java.time.Duration; +import java.util.concurrent.Callable; + +import static no.nav.dolly.domain.CommonKeysAndUtils.CONSUMER; +import static no.nav.dolly.domain.CommonKeysAndUtils.HEADER_NAV_CALL_ID; +import static no.nav.dolly.domain.CommonKeysAndUtils.HEADER_NAV_CONSUMER_ID; +import static no.nav.dolly.util.CallIdUtil.generateCallId; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; + +@Slf4j +@RequiredArgsConstructor +public class LagrePensjonsavtaleCommand implements Callable> { + + private static final String PENSJONSAVTALE_URL = "/api/v2/pensjonsavtale/opprett"; + + private final WebClient webClient; + private final PensjonsavtaleRequest pensjonsavtaleRequest; + private final String token; + + @Override + public Flux call() { + + var callId = generateCallId(); + log.info("Pensjonsavtale lagre inntekt {}, callId: {}", pensjonsavtaleRequest, callId); + + return webClient.post() + .uri(uriBuilder -> uriBuilder.path(PENSJONSAVTALE_URL).build()) + .header(AUTHORIZATION, "Bearer " + token) + .header(HEADER_NAV_CALL_ID, callId) + .header(HEADER_NAV_CONSUMER_ID, CONSUMER) + .bodyValue(pensjonsavtaleRequest) + .retrieve() + .bodyToFlux(PensjonforvalterResponse.class) + .doOnError(WebClientFilter::logErrorMessage) + .retryWhen(Retry.backoff(3, Duration.ofSeconds(5)) + .filter(WebClientFilter::is5xxException)) + .onErrorResume(error -> + Mono.just(PensjonforvalterResponse.builder() + .status(pensjonsavtaleRequest.getMiljoer().stream() + .map(miljoe -> PensjonforvalterResponse.ResponseEnvironment.builder() + .miljo(miljoe) + .response(PensjonforvalterResponse.Response.builder() + .httpStatus(PensjonforvalterResponse.HttpStatus.builder() + .status(WebClientFilter.getStatus(error).value()) + .reasonPhrase(WebClientFilter.getStatus(error).getReasonPhrase()) + .build()) + .message(WebClientFilter.getMessage(error)) + .path(PENSJONSAVTALE_URL) + .build()) + .build()) + .toList()) + .build())); + } +} diff --git a/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/command/SlettePensjonsavtaleCommand.java b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/command/SlettePensjonsavtaleCommand.java new file mode 100644 index 00000000000..67358421be6 --- /dev/null +++ b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/command/SlettePensjonsavtaleCommand.java @@ -0,0 +1,56 @@ +package no.nav.dolly.bestilling.pensjonforvalter.command; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import no.nav.dolly.bestilling.pensjonforvalter.domain.PensjonforvalterResponse; +import no.nav.testnav.libs.reactivecore.utils.WebClientFilter; +import no.nav.testnav.libs.securitycore.config.UserConstant; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.retry.Retry; + +import java.time.Duration; +import java.util.concurrent.Callable; + +import static no.nav.dolly.domain.CommonKeysAndUtils.CONSUMER; +import static no.nav.dolly.domain.CommonKeysAndUtils.HEADER_NAV_CALL_ID; +import static no.nav.dolly.domain.CommonKeysAndUtils.HEADER_NAV_CONSUMER_ID; +import static no.nav.dolly.util.CallIdUtil.generateCallId; +import static no.nav.dolly.util.TokenXUtil.getUserJwt; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; + +@Slf4j +@RequiredArgsConstructor +public class SlettePensjonsavtaleCommand implements Callable> { + + private static final String PENSJON_TP_PERSON_FORHOLD_URL = "/api/v1/pensjonsavtale/delete"; + + private final WebClient webClient; + private final String ident; + private final String token; + + + public Flux call() { + + var callId = generateCallId(); + log.info("Pensjon slette pensjonsavtale callId: {}", callId); + + return webClient + .delete() + .uri(uriBuilder -> uriBuilder + .path(PENSJON_TP_PERSON_FORHOLD_URL) + .build()) + .header(AUTHORIZATION, "Bearer " + token) + .header(UserConstant.USER_HEADER_JWT, getUserJwt()) + .header(HEADER_NAV_CALL_ID, callId) + .header(HEADER_NAV_CONSUMER_ID, CONSUMER) + .header("ident", ident) + .retrieve() + .bodyToFlux(PensjonforvalterResponse.class) + .retryWhen(Retry.backoff(3, Duration.ofSeconds(5)) + .filter(WebClientFilter::is5xxException)) + .doOnError(WebClientFilter::logErrorMessage) + .onErrorResume(Exception.class, error -> Mono.empty()); + } +} \ No newline at end of file diff --git a/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/domain/PensjonsavtaleRequest.java b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/domain/PensjonsavtaleRequest.java new file mode 100644 index 00000000000..06f9ae6d4a9 --- /dev/null +++ b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/domain/PensjonsavtaleRequest.java @@ -0,0 +1,57 @@ +package no.nav.dolly.bestilling.pensjonforvalter.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.Objects.isNull; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PensjonsavtaleRequest { + + public enum AvtaleKategori { + NONE, UNKNOWN, INDIVIDUELL_ORDNING, PRIVAT_AFP, + PRIVAT_TJENESTEPENSJON, OFFENTLIG_TJENESTEPENSJON, FOLKETRYGD + } + + private String ident; + private String produktBetegnelse; + private AvtaleKategori avtaleKategori; + private List utbetalingsperioder; + private List miljoer; + + public List getUtbetalingsperioder() { + + if (isNull(utbetalingsperioder)) { + utbetalingsperioder = new ArrayList<>(); + } + return utbetalingsperioder; + } + + public List getMiljoer() { + + if (isNull(miljoer)) { + miljoer = new ArrayList<>(); + } + return miljoer; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class OpprettUtbetalingsperiodeDTO { + private Integer startAlderAar; + private Integer startAlderMaaned; + private Integer sluttAlderAar; + private Integer sluttAlderMaaned; + private Integer aarligUtbetaling; + } +} diff --git a/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/mapper/PensjonsavtaleMappingStrategy.java b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/mapper/PensjonsavtaleMappingStrategy.java new file mode 100644 index 00000000000..44f2511bdf5 --- /dev/null +++ b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/pensjonforvalter/mapper/PensjonsavtaleMappingStrategy.java @@ -0,0 +1,33 @@ +package no.nav.dolly.bestilling.pensjonforvalter.mapper; + +import ma.glasnost.orika.CustomMapper; +import ma.glasnost.orika.MapperFactory; +import ma.glasnost.orika.MappingContext; +import no.nav.dolly.bestilling.pensjonforvalter.domain.PensjonsavtaleRequest; +import no.nav.dolly.domain.resultset.pensjon.PensjonData; +import no.nav.dolly.mapper.MappingStrategy; +import org.springframework.stereotype.Component; + +import java.util.Set; + +@Component +public class PensjonsavtaleMappingStrategy implements MappingStrategy { + + @Override + public void register(MapperFactory factory) { + factory.classMap(PensjonData.Pensjonsavtale.class, PensjonsavtaleRequest.class) + .customize(new CustomMapper<>() { + @Override + public void mapAtoB(PensjonData.Pensjonsavtale pensjonsavtale, PensjonsavtaleRequest pensjonsavtaleRequest, MappingContext context) { + + var ident = (String) context.getProperty("ident"); + var miljoer = (Set) context.getProperty("miljoer"); + + pensjonsavtaleRequest.setIdent(ident); + pensjonsavtaleRequest.setMiljoer(miljoer.stream().toList()); + } + }) + .byDefault() + .register(); + } +} diff --git a/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/service/DollyBestillingService.java b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/service/DollyBestillingService.java index 7e1639dcf84..8befa07bc47 100644 --- a/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/service/DollyBestillingService.java +++ b/apps/dolly-backend/src/main/java/no/nav/dolly/bestilling/service/DollyBestillingService.java @@ -154,7 +154,7 @@ protected void leggIdentTilGruppe(BestillingProgress progress, String beskrivels protected void leggIdentTilGruppe(String ident, BestillingProgress progress, String beskrivelse) { identService.saveIdentTilGruppe(isNotBlank(ident) ? ident : progress.getIdent(), progress.getBestilling().getGruppe(), progress.getMaster(), beskrivelse); - log.info("Ident {} lagt til gruppe {}", progress.getIdent(), progress.getBestilling().getGruppe().getId()); + log.info("Ident {} lagt til gruppe {}", isNotBlank(ident) ? ident : progress.getIdent(), progress.getBestilling().getGruppe().getId()); } protected Flux opprettDollyPerson(BestillingProgress progress, Bruker bruker) { diff --git a/apps/dolly-backend/src/main/java/no/nav/dolly/domain/resultset/SystemTyper.java b/apps/dolly-backend/src/main/java/no/nav/dolly/domain/resultset/SystemTyper.java index 20381dbac04..5bb1cdb6edb 100644 --- a/apps/dolly-backend/src/main/java/no/nav/dolly/domain/resultset/SystemTyper.java +++ b/apps/dolly-backend/src/main/java/no/nav/dolly/domain/resultset/SystemTyper.java @@ -32,6 +32,7 @@ public enum SystemTyper { PEN_AP("Alderspensjon (AP)"), PEN_FORVALTER("Pensjon persondata (PEN)"), PEN_INNTEKT("Pensjonsopptjening (POPP)"), + PEN_PENSJONSAVTALE("Pensjonsavtale (PEN)"), PEN_SAMBOER("Pensjon samboerregister"), PEN_UT("Uføretrygd (UT)"), SIGRUNSTUB("Skatteinntekt grunnlag (SIGRUN)"), diff --git a/apps/dolly-backend/src/main/java/no/nav/dolly/domain/resultset/pensjon/PensjonData.java b/apps/dolly-backend/src/main/java/no/nav/dolly/domain/resultset/pensjon/PensjonData.java index 8f90d121877..836e548a9b7 100644 --- a/apps/dolly-backend/src/main/java/no/nav/dolly/domain/resultset/pensjon/PensjonData.java +++ b/apps/dolly-backend/src/main/java/no/nav/dolly/domain/resultset/pensjon/PensjonData.java @@ -1,5 +1,6 @@ package no.nav.dolly.domain.resultset.pensjon; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; @@ -30,28 +31,49 @@ public class PensjonData { @Schema(description = "Data for tjenestepensjon (TP)") private List tp; + @Schema(description = "Data for pensjonsavtale") + private List pensjonsavtale; + @Schema(description = "Data for alderspensjon (AP)") private Alderspensjon alderspensjon; @Schema(description = "Data for uføretrygd (UT)") private Uforetrygd uforetrygd; + @JsonIgnore public boolean hasInntekt() { return nonNull(inntekt); } + @JsonIgnore public boolean hasTp() { return !getTp().isEmpty(); } + @JsonIgnore public boolean hasAlderspensjon() { return nonNull(alderspensjon); } + @JsonIgnore public boolean hasUforetrygd() { return nonNull(uforetrygd); } + @JsonIgnore + public boolean hasPensjonsavtale() { + + return !getPensjonsavtale().isEmpty(); + } + + public List getPensjonsavtale() { + + if (isNull(pensjonsavtale)) { + pensjonsavtale = new ArrayList<>(); + } + return pensjonsavtale; + } + public List getTp() { if (isNull(tp)) { @@ -131,6 +153,41 @@ public static class TpYtelse { private LocalDate datoYtelseIverksattTom; } + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Pensjonsavtale { + public enum AvtaleKategori { + NONE, UNKNOWN, INDIVIDUELL_ORDNING, PRIVAT_AFP, + PRIVAT_TJENESTEPENSJON, OFFENTLIG_TJENESTEPENSJON, FOLKETRYGD + } + + private String produktBetegnelse; + private AvtaleKategori avtaleKategori; + private List utbetalingsperioder; + + public List getUtbetalingsperioder() { + + if (isNull(utbetalingsperioder)) { + utbetalingsperioder = new ArrayList<>(); + } + return utbetalingsperioder; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class OpprettUtbetalingsperiodeDTO { + private Integer startAlderAar; + private Integer startAlderMaaned; + private Integer sluttAlderAar; + private Integer sluttAlderMaaned; + private Integer aarligUtbetaling; + } + } + @Data @Builder @NoArgsConstructor diff --git a/apps/dolly-backend/src/main/java/no/nav/dolly/mapper/BestillingPensjonforvalterStatusMapper.java b/apps/dolly-backend/src/main/java/no/nav/dolly/mapper/BestillingPensjonforvalterStatusMapper.java index 807422d2fb6..54aaf40538b 100644 --- a/apps/dolly-backend/src/main/java/no/nav/dolly/mapper/BestillingPensjonforvalterStatusMapper.java +++ b/apps/dolly-backend/src/main/java/no/nav/dolly/mapper/BestillingPensjonforvalterStatusMapper.java @@ -18,6 +18,7 @@ import static no.nav.dolly.domain.resultset.SystemTyper.PEN_AP; import static no.nav.dolly.domain.resultset.SystemTyper.PEN_FORVALTER; import static no.nav.dolly.domain.resultset.SystemTyper.PEN_INNTEKT; +import static no.nav.dolly.domain.resultset.SystemTyper.PEN_PENSJONSAVTALE; import static no.nav.dolly.domain.resultset.SystemTyper.PEN_SAMBOER; import static no.nav.dolly.domain.resultset.SystemTyper.PEN_UT; import static no.nav.dolly.domain.resultset.SystemTyper.TP_FORVALTER; @@ -33,6 +34,7 @@ public final class BestillingPensjonforvalterStatusMapper { private static final String ALDERSPENSJON = "AP"; private static final String UFORETRYGD = "Ufoer"; private static final String SAMBOER = "Samboer"; + private static final String PENSJONSAVTALE = "Pensjonsavtale"; public static List buildPensjonforvalterStatusMap(List progressList) { @@ -65,6 +67,7 @@ public static List buildPensjonforvalterStatusMap(List() { @Override public void mapAtoB(PensjonData pensjonData, PensjonData akkumulert, MappingContext context) { akkumulert.getTp().addAll(pensjonData.getTp()); + akkumulert.getPensjonsavtale().addAll(pensjonData.getPensjonsavtale()); } }) .register(); diff --git a/apps/dolly-frontend/src/main/js/playwright/globalSetup.tsx b/apps/dolly-frontend/src/main/js/playwright/globalSetup.tsx index 827112dc97f..ec20a4276de 100644 --- a/apps/dolly-frontend/src/main/js/playwright/globalSetup.tsx +++ b/apps/dolly-frontend/src/main/js/playwright/globalSetup.tsx @@ -29,6 +29,7 @@ import { organisasjonFraMiljoeMock, paginerteGrupperMock, pensjonMock, + pensjonPensjonsavtaleMock, pensjonTpMock, personFragmentNavigerMock, personFragmentSearchMock, @@ -82,6 +83,9 @@ const skjerming = new RegExp(/dolly-backend\/api\/v1\/skjerming/) const pensjon = new RegExp(/testnav-pensjon-testdata-facade-proxy\/api\/v1\/inntekt/) const pensjonMiljoer = new RegExp(/testnav-pensjon-testdata-facade-proxy\/api\/v1\/miljo/) const pensjonTp = new RegExp(/testnav-pensjon-testdata-facade-proxy\/api\/v1\/tp(.*?)q1/) +const pensjonPensjonsavtale = new RegExp( + /testnav-pensjon-testdata-facade-proxy\/api\/v2\/pensjonsavtale\/hent/, +) const krrstub = new RegExp(/testnav-krrstub-proxy\/api\/v2/) const udistub = new RegExp(/testnav-udistub-proxy\/api\/v1/) const brregstub = new RegExp(/testnav-brregstub/) @@ -142,6 +146,7 @@ const mockRoutes: RouteInfo[] = [ { url: inst, response: instMock }, { url: pensjon, response: pensjonMock }, { url: pensjonTp, response: pensjonTpMock }, + { url: pensjonPensjonsavtale, response: pensjonPensjonsavtaleMock }, { url: sigrunstub, response: sigrunstubMock }, { url: udistub, response: udistubMock }, { url: kodeverk, response: kodeverkMock }, diff --git a/apps/dolly-frontend/src/main/js/playwright/mocks/BasicMocks.tsx b/apps/dolly-frontend/src/main/js/playwright/mocks/BasicMocks.tsx index 857d74b72ad..f861792b266 100644 --- a/apps/dolly-frontend/src/main/js/playwright/mocks/BasicMocks.tsx +++ b/apps/dolly-frontend/src/main/js/playwright/mocks/BasicMocks.tsx @@ -881,6 +881,8 @@ export const pensjonMock = [ export const pensjonTpMock = [{ ordning: '4095' }, { ordning: '3010' }] +export const pensjonPensjonsavtaleMock = [{}, {}] + export const tagsMock = [{ tag: 'DUMMY', beskrivelse: 'Dummy' }] export const kontoregisterMock = { diff --git a/apps/dolly-frontend/src/main/js/src/components/bestilling/sammendrag/kriterier/BestillingKriterieMapper.tsx b/apps/dolly-frontend/src/main/js/src/components/bestilling/sammendrag/kriterier/BestillingKriterieMapper.tsx index d2b993d48fc..432c2203aee 100644 --- a/apps/dolly-frontend/src/main/js/src/components/bestilling/sammendrag/kriterier/BestillingKriterieMapper.tsx +++ b/apps/dolly-frontend/src/main/js/src/components/bestilling/sammendrag/kriterier/BestillingKriterieMapper.tsx @@ -1903,6 +1903,34 @@ const mapPensjon = (bestillingData, data, navEnheter) => { data.push(pensjonforvalterPopp) } + if (pensjonKriterier.pensjonsavtale && pensjonKriterier.pensjonsavtale?.length > 0) { + const penPensjonsavtale = { + header: 'Pensjonsavtale (PEN)', + itemRows: [], + } + + pensjonKriterier.pensjonsavtale?.forEach((pensjonsavtale, i) => { + penPensjonsavtale.itemRows.push([ + { numberHeader: `Pensjonsavtale ${i + 1}` }, + obj('Produktbetegnelse', pensjonsavtale.produktBetegnelse), + obj('Avtalekategori', showLabel('avtaleKategori', pensjonsavtale.avtaleKategori)), + ]) + + pensjonsavtale.utbetalingsperioder?.forEach((periode, j) => { + penPensjonsavtale.itemRows.push([ + { numberHeader: `Utbetalingsperiode ${j + 1}` }, + obj('Startalder År', periode.startAlderAar), + obj('Startalder Måned', showLabel('maanedsvelger', periode.startAlderMaaned)), + obj('Sluttalder År', periode.sluttAlderAar), + obj('Sluttalder Måned', showLabel('maanedsvelger', periode.sluttAlderMaaned)), + obj('Årlig Utbetaling', periode.aarligUtbetaling), + ]) + }) + }) + + data.push(penPensjonsavtale) + } + if (pensjonKriterier.tp && pensjonKriterier.tp?.length > 0) { const hentTpOrdningNavn = (tpnr) => { if (Options('tpOrdninger')?.length) { diff --git a/apps/dolly-frontend/src/main/js/src/components/bestillingsveileder/stegVelger/steg/steg1/paneler/Pensjon.tsx b/apps/dolly-frontend/src/main/js/src/components/bestillingsveileder/stegVelger/steg/steg1/paneler/Pensjon.tsx index 913c27b9586..c4b4a9621a6 100644 --- a/apps/dolly-frontend/src/main/js/src/components/bestillingsveileder/stegVelger/steg/steg1/paneler/Pensjon.tsx +++ b/apps/dolly-frontend/src/main/js/src/components/bestillingsveileder/stegVelger/steg/steg1/paneler/Pensjon.tsx @@ -15,6 +15,7 @@ import { runningE2ETest } from '@/service/services/Request' import _ from 'lodash' import { alderspensjonPath } from '@/components/fagsystem/alderspensjon/form/Form' import { uforetrygdPath } from '@/components/fagsystem/uforetrygd/form/Form' +import { initialPensjonsavtale } from '@/components/fagsystem/pensjonsavtale/initalValues' export const PensjonPanel = ({ stateModifier, formValues }: any) => { const sm = stateModifier(PensjonPanel.initialValues) @@ -92,6 +93,9 @@ export const PensjonPanel = ({ stateModifier, formValues }: any) => { + + + @@ -121,6 +125,7 @@ PensjonPanel.initialValues = ({ set, del, has }: any) => { tp: 'pensjonforvalter.tp', alderspensjon: 'pensjonforvalter.alderspensjon', uforetrygd: 'pensjonforvalter.uforetrygd', + pensjonsavtale: 'pensjonforvalter.pensjonsavtale', } return { inntekt: { @@ -160,5 +165,13 @@ PensjonPanel.initialValues = ({ set, del, has }: any) => { }, remove: () => del(paths.uforetrygd), }, + pensjonsavtale: { + label: 'Har pensjonsavtale', + checked: has(paths.pensjonsavtale), + add: () => { + set(paths.pensjonsavtale, initialPensjonsavtale) + }, + remove: () => del(paths.pensjonsavtale), + }, } } diff --git a/apps/dolly-frontend/src/main/js/src/components/bestillingsveileder/stegVelger/steg/steg2/Steg2.tsx b/apps/dolly-frontend/src/main/js/src/components/bestillingsveileder/stegVelger/steg/steg2/Steg2.tsx index 80b2d3e006f..1b8684a167e 100644 --- a/apps/dolly-frontend/src/main/js/src/components/bestillingsveileder/stegVelger/steg/steg2/Steg2.tsx +++ b/apps/dolly-frontend/src/main/js/src/components/bestillingsveileder/stegVelger/steg/steg2/Steg2.tsx @@ -24,6 +24,7 @@ import { UforetrygdForm } from '@/components/fagsystem/uforetrygd/form/Form' import { SigrunstubPensjonsgivendeForm } from '@/components/fagsystem/sigrunstubPensjonsgivende/form/Form' import { KrrstubForm } from '@/components/fagsystem/krrstub/form/KrrForm' import { useFormContext } from 'react-hook-form' +import { PensjonsavtaleForm } from '@/components/fagsystem/pensjonsavtale/form/Form' const gruppeNavn = (gruppe) => {gruppe.navn} @@ -70,6 +71,7 @@ export const Steg2 = () => { + diff --git a/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjon/form/validation.tsx b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjon/form/validation.tsx index 2fe0f29ca5b..2c37aae7643 100644 --- a/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjon/form/validation.tsx +++ b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjon/form/validation.tsx @@ -4,6 +4,7 @@ import { ifPresent, requiredNumber } from '@/utils/YupValidations' import { TjenestepensjonForm } from '@/components/fagsystem/tjenestepensjon/form/Form' import { AlderspensjonForm } from '@/components/fagsystem/alderspensjon/form/Form' import { UforetrygdForm } from '@/components/fagsystem/uforetrygd/form/Form' +import { PensjonsavtaleForm } from '@/components/fagsystem/pensjonsavtale/form/Form' function calculate_age(dob) { const diff_ms = Date.now() - dob.getTime() @@ -195,6 +196,7 @@ export const validation = { redusertMedGrunnbelop: Yup.boolean(), }), ), + ...PensjonsavtaleForm.validation, ...TjenestepensjonForm.validation, ...AlderspensjonForm.validation, ...UforetrygdForm.validation, diff --git a/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/form/Form.tsx b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/form/Form.tsx new file mode 100644 index 00000000000..2a0138f0a45 --- /dev/null +++ b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/form/Form.tsx @@ -0,0 +1,64 @@ +import React from 'react' +import { validation } from '@/components/fagsystem/pensjonsavtale/form/validation' +import { useFormContext } from 'react-hook-form' +import { Vis } from '@/components/bestillingsveileder/VisAttributt' +import Panel from '@/components/ui/panel/Panel' +import { erForsteEllerTest, panelError } from '@/components/ui/form/formUtils' +import { FormTextInput } from '@/components/ui/form/inputs/textInput/TextInput' +import { FormSelect } from '@/components/ui/form/inputs/select/Select' +import { SelectOptionsManager as Options } from '@/service/SelectOptions' +import { UtbetalingsperioderForm } from '@/components/fagsystem/pensjonsavtale/form/partials/Utbetalingsperioder' +import { FormDollyFieldArray } from '@/components/ui/form/fieldArray/DollyFieldArray' +import { initialPensjonsavtale } from '@/components/fagsystem/pensjonsavtale/initalValues' + +export const avtalePath = 'pensjonforvalter.pensjonsavtale' +const hjelpetekst = 'Pensjonsavtale beskriver type av pensjon, samt utbetalingsperioder og beløp.' + +export const PensjonsavtaleForm = () => { + const formMethods = useFormContext() + + return ( + + + + {(formPath, idx) => ( + + +
+ + + + + +
+
+
+ )} +
+
+
+ ) +} + +PensjonsavtaleForm.validation = validation diff --git a/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/form/partials/Utbetalingsperioder.tsx b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/form/partials/Utbetalingsperioder.tsx new file mode 100644 index 00000000000..9e4b28eb6da --- /dev/null +++ b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/form/partials/Utbetalingsperioder.tsx @@ -0,0 +1,45 @@ +import { FormDollyFieldArray } from '@/components/ui/form/fieldArray/DollyFieldArray' +import * as React from 'react' +import { FormTextInput } from '@/components/ui/form/inputs/textInput/TextInput' +import { SelectOptionsManager as Options } from '@/service/SelectOptions' +import { FormSelect } from '@/components/ui/form/inputs/select/Select' +import { initialUtbetalingsperiode } from '@/components/fagsystem/pensjonsavtale/initalValues' + +export const UtbetalingsperioderForm = ({ path }: any) => { + return ( + + {(path: any, idx: React.Key) => ( + +
+ + + + + +
+
+ )} +
+ ) +} diff --git a/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/form/validation.tsx b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/form/validation.tsx new file mode 100644 index 00000000000..55fe1a047cd --- /dev/null +++ b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/form/validation.tsx @@ -0,0 +1,56 @@ +import * as Yup from 'yup' +import { ifPresent } from '@/utils/YupValidations' + +export const validation = { + pensjonsavtale: ifPresent( + '$pensjonforvalter.pensjonsavtale', + Yup.array().of( + Yup.object({ + produktBetegnelse: Yup.string().required('Feltet er påkrevd'), + avtaleKategori: Yup.string().required('Feltet er påkrevd'), + utbetalingsperioder: Yup.array().of( + Yup.object({ + startAlderAar: Yup.number() + .min(62, 'Minimum 62 år') + .max(72, 'Maksimum 72 år') + .required('Feltet er påkrevd') + .typeError('Feltet er påkrevd og må ha verdi mellom 62 og 72 år'), + startAlderMaaned: Yup.number() + .min(1, 'Minimum 1 (januar)') + .max(12, 'Maksimum 12 (desember)') + .required('Feltet er påkrevd') + .typeError('Feltet er påkrevd og må ha verdi mellom 1 og 12 (januar-desember)'), + sluttAlderAar: Yup.number() + .nullable() + .min(72, 'Minimum 72 år') + .max(100, 'Maksimum 100 år') + .transform((value) => (Number.isNaN(value) ? null : value)) + .test( + 'has-value-if-sluttAlderMaaned-is-stated', + 'Sluttalder År må være satt når Sluttalder Måned har verdi', + (sluttAlderAar, context) => { + return sluttAlderAar || (!sluttAlderAar && !context.parent.sluttAlderMaaned) + }, + ), + sluttAlderMaaned: Yup.number() + .nullable() + .min(1, 'Minimum 1 (januar)') + .max(12, 'Maksimum 12 (desember)') + .typeError('Verdi kan angis, og må da ha verdi mellom 1 og 12 (januar-desember)') + .test( + 'has-value-if-sluttAlderAar-is-stated', + 'Feltet er påkrevd når Sluttalder År er angitt', + (sluttAlderMaaned, context) => { + return sluttAlderMaaned || (!sluttAlderMaaned && !context.parent.sluttAlderAar) + }, + ), + aarligUtbetaling: Yup.number() + .required() + .min(1000, 'Minimum 1000 kr') + .typeError('Verdi kan angis, og må da ha verdi minimum 1000 kr'), + }), + ), + }), + ), + ), +} diff --git a/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/initalValues.tsx b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/initalValues.tsx new file mode 100644 index 00000000000..9f017676237 --- /dev/null +++ b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/initalValues.tsx @@ -0,0 +1,15 @@ +export const initialUtbetalingsperiode = { + startAlderAar: 62, + startAlderMaaned: 1, + sluttAlderAar: 72, + sluttAlderMaaned: 12, + aarligUtbetaling: 30000, +} + +export const initialPensjonsavtale = [ + { + produktBetegnelse: 'Test av pensjonsavtale', + avtaleKategori: 'PRIVAT_TJENESTEPENSJON', + utbetalingsperioder: [initialUtbetalingsperiode], + }, +] diff --git a/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/visning/PensjonsavtaleVisning.tsx b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/visning/PensjonsavtaleVisning.tsx new file mode 100644 index 00000000000..c94b07d6c09 --- /dev/null +++ b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pensjonsavtale/visning/PensjonsavtaleVisning.tsx @@ -0,0 +1,114 @@ +import SubOverskrift from '@/components/ui/subOverskrift/SubOverskrift' +import { TitleValue } from '@/components/ui/titleValue/TitleValue' +import Loading from '@/components/ui/loading/Loading' +import { DollyFieldArray } from '@/components/ui/form/fieldArray/DollyFieldArray' +import { ErrorBoundary } from '@/components/ui/appError/ErrorBoundary' +import Panel from '@/components/ui/panel/Panel' +import { runningE2ETest } from '@/service/services/Request' +import { Alert } from '@navikt/ds-react' +import { MiljoTabs } from '@/components/ui/miljoTabs/MiljoTabs' +import { useBestilteMiljoer } from '@/utils/hooks/useBestilling' +import { showLabel } from '@/utils/DataFormatter' + +export const sjekkManglerPensjonavtaleData = (pensjonData) => { + return pensjonData?.length < 1 +} + +const Utbetalingsperioder = ({ utbetalingsperioder }) => { + if (!utbetalingsperioder) return null + + return ( + + {(utbetalingsperiode, idx) => ( +
+ + + + + +
+ )} +
+ ) +} + +const Pensjonsavtale = ({ data, setPanelOpen }) => { + if (!data) return null + + const isPanelOpen = data?.length < 3 + + return ( + + + {(pensjonsavtale, idx) => ( +
+ + + +
+ )} +
+
+ ) +} + +export const PensjonsavtaleVisning = ({ data, loading, bestillingIdListe, tilgjengeligMiljoe }) => { + const { bestilteMiljoer } = useBestilteMiljoer(bestillingIdListe, 'PEN_PENSJONSAVTALE') + + if (loading) { + return + } + if (!data) { + return null + } + + const manglerFagsystemdata = sjekkManglerPensjonavtaleData(data) + + const miljoerMedData = data?.map((miljoData) => miljoData.data?.length > 0 && miljoData.miljo) + const errorMiljoer = bestilteMiljoer.filter((miljo) => !miljoerMedData?.includes(miljo)) + + const forsteMiljo = data.find((miljoData) => miljoData?.data)?.miljo + + const filteredData = + tilgjengeligMiljoe && data.filter((item) => tilgjengeligMiljoe.includes(item.miljo)) + + return ( + + + {manglerFagsystemdata ? ( + + Fant ikke pensjonsavtale-data for person + + ) : ( + + + + )} + + ) +} diff --git a/apps/dolly-frontend/src/main/js/src/components/miljoVelger/MiljoeInfo.tsx b/apps/dolly-frontend/src/main/js/src/components/miljoVelger/MiljoeInfo.tsx index 77752138cda..01cf948b258 100644 --- a/apps/dolly-frontend/src/main/js/src/components/miljoVelger/MiljoeInfo.tsx +++ b/apps/dolly-frontend/src/main/js/src/components/miljoVelger/MiljoeInfo.tsx @@ -82,6 +82,12 @@ export const MiljoeInfo = ({ bestillingsdata, dollyEnvironments }) => { {getMiljoer(pensjonEnvironments, loadingPensjon, errorPensjon)} )} + {pensjonforvalter?.pensjonsavtale && ( +
  • + Pensjonsavtale (PEN):  + {getMiljoer(pensjonEnvironments, loadingPensjon, errorPensjon)} +
  • + )} {sykemelding &&
  • Sykemelding: q1
  • } diff --git a/apps/dolly-frontend/src/main/js/src/components/ui/form/formUtils.tsx b/apps/dolly-frontend/src/main/js/src/components/ui/form/formUtils.tsx index 7076a8ca7cc..d344c0a6d27 100644 --- a/apps/dolly-frontend/src/main/js/src/components/ui/form/formUtils.tsx +++ b/apps/dolly-frontend/src/main/js/src/components/ui/form/formUtils.tsx @@ -70,6 +70,7 @@ const getValgteAttributter = (values) => { 'inntektsmelding', 'arbeidsplassenCV', 'pensjonforvalter.inntekt', + 'pensjonforvalter.pensjonsavtale', 'pensjonforvalter.tp', 'pensjonforvalter.alderspensjon', 'pensjonforvalter.uforetrygd', diff --git a/apps/dolly-frontend/src/main/js/src/pages/gruppe/PersonVisning/PersonVisning.tsx b/apps/dolly-frontend/src/main/js/src/pages/gruppe/PersonVisning/PersonVisning.tsx index 5d8bbc2c9b5..346f553afde 100644 --- a/apps/dolly-frontend/src/main/js/src/pages/gruppe/PersonVisning/PersonVisning.tsx +++ b/apps/dolly-frontend/src/main/js/src/pages/gruppe/PersonVisning/PersonVisning.tsx @@ -42,6 +42,7 @@ import { useDokarkivData, useHistarkData, useInstData, + usePensjonsavtaleData, usePoppData, useTpData, useTransaksjonIdData, @@ -58,6 +59,7 @@ import { harInntektsmeldingBestilling, harInstBestilling, harMedlBestilling, + harPensjonavtaleBestilling, harPoppBestilling, harSykemeldingBestilling, harTpBestilling, @@ -90,6 +92,7 @@ import { useTenorIdent } from '@/utils/hooks/useTenorSoek' import { SkatteetatenVisning } from '@/components/fagsystem/skatteetaten/visning/SkatteetatenVisning' import PdlVisningConnector from '@/components/fagsystem/pdl/visning/PdlVisningConnector' import { useOrganisasjonMiljoe } from '@/utils/hooks/useOrganisasjonTilgang' +import { PensjonsavtaleVisning } from '@/components/fagsystem/pensjonsavtale/visning/PensjonsavtaleVisning' const getIdenttype = (ident) => { if (parseInt(ident.charAt(0)) > 3) { @@ -164,6 +167,11 @@ export default ({ harTpBestilling(bestillingerFagsystemer), ) + const { loading: loadingPensjonsavtaleData, pensjonsavtaleData } = usePensjonsavtaleData( + ident.ident, + harPensjonavtaleBestilling(bestillingerFagsystemer), + ) + const { loading: loadingPoppData, poppData } = usePoppData( ident.ident, harPoppBestilling(bestillingerFagsystemer), @@ -466,6 +474,12 @@ export default ({ bestillingIdListe={bestillingIdListe} tilgjengeligMiljoe={tilgjengeligMiljoe} /> + { optionsGruppe.includes('partner') && (copyOptionsGruppe = optionsGruppe.replace('partner_', '')) optionsGruppe.includes('barn') && (copyOptionsGruppe = optionsGruppe.replace('barn_', '')) - const obj = Options(copyOptionsGruppe).filter( - (options) => options.value.toUpperCase() === value.toUpperCase(), + const obj = Options(copyOptionsGruppe).filter((options) => + typeof value === 'string' + ? options.value.toUpperCase() === value.toUpperCase() + : options.value === value, ) if (_.get(obj, 'label') || _.get(obj, '[0].label')) { diff --git a/apps/dolly-frontend/src/main/js/src/utils/SjekkBestillingFagsystem.tsx b/apps/dolly-frontend/src/main/js/src/utils/SjekkBestillingFagsystem.tsx index bee10ce752d..0e02fd81ede 100644 --- a/apps/dolly-frontend/src/main/js/src/utils/SjekkBestillingFagsystem.tsx +++ b/apps/dolly-frontend/src/main/js/src/utils/SjekkBestillingFagsystem.tsx @@ -58,6 +58,16 @@ export const harApBestilling = (bestillingerFagsystemer) => { return alderspensjon } +export const harPensjonavtaleBestilling = (bestillingerFagsystemer) => { + let pensjonavtale = false + bestillingerFagsystemer?.forEach((i) => { + if (i?.pensjonforvalter?.pensjonsavtale) { + pensjonavtale = true + } + }) + return pensjonavtale +} + export const harUforetrygdBestilling = (bestillingerFagsystemer) => { let uforetrygd = false bestillingerFagsystemer?.forEach((i) => { diff --git a/apps/dolly-frontend/src/main/js/src/utils/hooks/useFagsystemer.tsx b/apps/dolly-frontend/src/main/js/src/utils/hooks/useFagsystemer.tsx index 8a01a5b3e37..e3a77c29037 100644 --- a/apps/dolly-frontend/src/main/js/src/utils/hooks/useFagsystemer.tsx +++ b/apps/dolly-frontend/src/main/js/src/utils/hooks/useFagsystemer.tsx @@ -22,6 +22,12 @@ const poppUrl = (ident, miljoer) => miljo: miljo, })) +const pensjonsavtaleUrl = (miljoer) => + miljoer?.map((miljo) => ({ + url: `/testnav-pensjon-testdata-facade-proxy/api/v2/pensjonsavtale/hent?miljo=${miljo}`, + miljo: miljo, + })) + const tpUrl = (ident, miljoer) => miljoer?.map((miljo) => ({ url: `/testnav-pensjon-testdata-facade-proxy/api/v1/tp/forhold?fnr=${ident}&miljo=${miljo}`, @@ -87,6 +93,24 @@ export const usePoppData = (ident, harPoppBestilling) => { } } +export const usePensjonsavtaleData = (ident, harPensjonsavtaleBestilling) => { + const { pensjonEnvironments } = usePensjonEnvironments() + + const { data, isLoading, error } = useSWR( + [ + harPensjonsavtaleBestilling ? pensjonsavtaleUrl(pensjonEnvironments) : null, + { 'Nav-Call-Id': 'dolly', 'Nav-Consumer-Id': 'dolly', Authorization: 'dolly', ident: ident }, + ], + ([url, headers]) => multiFetcherPensjon(url, headers), + ) + + return { + pensjonsavtaleData: data?.sort((a, b) => a.miljo.localeCompare(b.miljo)), + loading: isLoading, + error: error, + } +} + export const useTpData = (ident, harTpBestilling) => { const { pensjonEnvironments } = usePensjonEnvironments() From c2fedc7d023d8bbe56ce4299b0ee554d5f93961b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristen=20H=C3=A6rum?= Date: Tue, 13 Aug 2024 11:24:11 +0200 Subject: [PATCH 4/6] Update condition for displaying existing person component (#3579) Changed condition to display the PdlEksisterendePerson component when `ansvar` equals 'ANDRE' instead of only `kanHaForeldreansvar`. This ensures the correct UI behavior based on the specified responsibility criteria. --- .../familierelasjoner/foreldreansvar/Foreldreansvar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dolly-frontend/src/main/js/src/components/fagsystem/pdlf/form/partials/familierelasjoner/foreldreansvar/Foreldreansvar.tsx b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pdlf/form/partials/familierelasjoner/foreldreansvar/Foreldreansvar.tsx index 5275e65456d..0040bdc942c 100644 --- a/apps/dolly-frontend/src/main/js/src/components/fagsystem/pdlf/form/partials/familierelasjoner/foreldreansvar/Foreldreansvar.tsx +++ b/apps/dolly-frontend/src/main/js/src/components/fagsystem/pdlf/form/partials/familierelasjoner/foreldreansvar/Foreldreansvar.tsx @@ -167,7 +167,7 @@ export const ForeldreansvarForm = ({ eksisterendeNyPerson={eksisterendeNyPerson} /> )} - {kanHaForeldreansvar && opts?.personFoerLeggTil && ( + {ansvar === 'ANDRE' && kanHaForeldreansvar && opts?.personFoerLeggTil && ( Date: Tue, 13 Aug 2024 11:25:21 +0200 Subject: [PATCH 5/6] Add support for person identification in tests (#3578) #deploy-test-pdl-forvalter #deploy-pdl-forvalter Added support for setting person identification (FNR_IDENT and DNR_IDENT) in various tests to ensure correct data handling. Enhanced validation logic for address handling based on the type of identification in service classes. --- .../nav/pdl/forvalter/service/AdresseService.java | 6 ++++++ .../pdl/forvalter/service/BostedAdresseService.java | 13 ++++++++++--- .../forvalter/service/KontaktAdresseService.java | 2 +- .../forvalter/service/OppholdsadresseService.java | 5 ++--- .../no/nav/pdl/forvalter/utils/ArtifactUtils.java | 4 +++- .../service/AdressebeskyttelseServiceTest.java | 2 ++ .../FolkeregisterPersonstatusServiceTest.java | 4 ++++ .../forvalter/service/InnflyttingServiceTest.java | 3 +++ .../forvalter/service/UtflyttingServiceTest.java | 2 ++ 9 files changed, 33 insertions(+), 8 deletions(-) diff --git a/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/AdresseService.java b/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/AdresseService.java index 35a4c0936a6..44120e286d6 100644 --- a/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/AdresseService.java +++ b/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/AdresseService.java @@ -18,6 +18,8 @@ import static java.util.Objects.nonNull; import static no.nav.pdl.forvalter.utils.ArtifactUtils.getKilde; import static no.nav.pdl.forvalter.utils.ArtifactUtils.getMaster; +import static no.nav.pdl.forvalter.utils.IdenttypeUtility.isNotNpidIdent; +import static no.nav.pdl.forvalter.utils.TestnorgeIdentUtility.isTestnorgeIdent; import static org.apache.commons.lang3.BooleanUtils.isFalse; import static org.apache.commons.lang3.BooleanUtils.isTrue; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -225,4 +227,8 @@ private void sortAdresser(List adresser) { adresser.get(i - 1).setId(adresser.size() - i + 1); } } + protected boolean isIdSupported(AdresseDTO adresse, String ident) { + + return isNotNpidIdent(ident) && !isTestnorgeIdent(ident) && !adresse.isPdlMaster(); + } } diff --git a/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/BostedAdresseService.java b/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/BostedAdresseService.java index 6876f23e2cb..4f3b7410a36 100644 --- a/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/BostedAdresseService.java +++ b/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/BostedAdresseService.java @@ -24,6 +24,7 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static no.nav.pdl.forvalter.utils.IdenttypeUtility.getIdenttype; +import static no.nav.pdl.forvalter.utils.TestnorgeIdentUtility.isTestnorgeIdent; import static no.nav.testnav.libs.data.pdlforvalter.v1.AdressebeskyttelseDTO.AdresseBeskyttelse.STRENGT_FORTROLIG; import static no.nav.testnav.libs.data.pdlforvalter.v1.Identtype.FNR; import static org.apache.commons.lang3.BooleanUtils.isNotTrue; @@ -115,7 +116,13 @@ private void handle(BostedadresseDTO bostedadresse, PersonDTO person) { } } else if (bostedadresse.countAdresser() == 0) { - bostedadresse.setVegadresse(new VegadresseDTO()); + + if (isTestnorgeIdent(person.getIdent())) { + bostedadresse.setUtenlandskAdresse(new UtenlandskAdresseDTO()); + + } else { + bostedadresse.setVegadresse(new VegadresseDTO()); + } } } else if (bostedadresse.countAdresser() == 0) { @@ -139,14 +146,14 @@ private void buildBoadresse(BostedadresseDTO bostedadresse, PersonDTO person) { var vegadresse = adresseServiceConsumer.getVegadresse(bostedadresse.getVegadresse(), bostedadresse.getAdresseIdentifikatorFraMatrikkelen()); - bostedadresse.setAdresseIdentifikatorFraMatrikkelen(vegadresse.getMatrikkelId()); + bostedadresse.setAdresseIdentifikatorFraMatrikkelen(isIdSupported(bostedadresse, person.getIdent()) ? vegadresse.getMatrikkelId() : null); mapperFacade.map(vegadresse, bostedadresse.getVegadresse()); } else if (nonNull(bostedadresse.getMatrikkeladresse())) { var matrikkeladresse = adresseServiceConsumer.getMatrikkeladresse(bostedadresse.getMatrikkeladresse(), bostedadresse.getAdresseIdentifikatorFraMatrikkelen()); - bostedadresse.setAdresseIdentifikatorFraMatrikkelen(matrikkeladresse.getMatrikkelId()); + bostedadresse.setAdresseIdentifikatorFraMatrikkelen(isIdSupported(bostedadresse, person.getIdent()) ? matrikkeladresse.getMatrikkelId() : null); mapperFacade.map(matrikkeladresse, bostedadresse.getMatrikkeladresse()); } else if (nonNull(bostedadresse.getUtenlandskAdresse())) { diff --git a/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/KontaktAdresseService.java b/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/KontaktAdresseService.java index c2f50a46293..9ae0e8f07f9 100644 --- a/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/KontaktAdresseService.java +++ b/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/KontaktAdresseService.java @@ -114,7 +114,7 @@ private void handle(KontaktadresseDTO kontaktadresse, PersonDTO person) { if (nonNull(kontaktadresse.getVegadresse())) { var vegadresse = adresseServiceConsumer.getVegadresse(kontaktadresse.getVegadresse(), kontaktadresse.getAdresseIdentifikatorFraMatrikkelen()); - kontaktadresse.setAdresseIdentifikatorFraMatrikkelen(kontaktadresse.getMaster() == Master.FREG ? vegadresse.getMatrikkelId() : null); + kontaktadresse.setAdresseIdentifikatorFraMatrikkelen(isIdSupported(kontaktadresse, person.getIdent()) ? vegadresse.getMatrikkelId() : null); mapperFacade.map(vegadresse, kontaktadresse.getVegadresse()); kontaktadresse.getVegadresse().setKommunenummer(null); diff --git a/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/OppholdsadresseService.java b/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/OppholdsadresseService.java index 85144a803b5..49ee8f10ea4 100644 --- a/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/OppholdsadresseService.java +++ b/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/service/OppholdsadresseService.java @@ -6,7 +6,6 @@ import no.nav.pdl.forvalter.exception.InvalidRequestException; import no.nav.pdl.forvalter.utils.IdenttypeUtility; import no.nav.testnav.libs.data.pdlforvalter.v1.AdressebeskyttelseDTO; -import no.nav.testnav.libs.data.pdlforvalter.v1.DbVersjonDTO.Master; import no.nav.testnav.libs.data.pdlforvalter.v1.OppholdsadresseDTO; import no.nav.testnav.libs.data.pdlforvalter.v1.PersonDTO; import no.nav.testnav.libs.data.pdlforvalter.v1.StatsborgerskapDTO; @@ -114,14 +113,14 @@ protected void handle(OppholdsadresseDTO oppholdsadresse, PersonDTO person) { if (nonNull(oppholdsadresse.getVegadresse())) { var vegadresse = adresseServiceConsumer.getVegadresse(oppholdsadresse.getVegadresse(), oppholdsadresse.getAdresseIdentifikatorFraMatrikkelen()); - oppholdsadresse.setAdresseIdentifikatorFraMatrikkelen(oppholdsadresse.getMaster() == Master.FREG ? + oppholdsadresse.setAdresseIdentifikatorFraMatrikkelen(isIdSupported(oppholdsadresse, person.getIdent()) ? vegadresse.getMatrikkelId() : null); mapperFacade.map(vegadresse, oppholdsadresse.getVegadresse()); } else if (nonNull(oppholdsadresse.getMatrikkeladresse())) { var matrikkeladresse = adresseServiceConsumer.getMatrikkeladresse(oppholdsadresse.getMatrikkeladresse(), oppholdsadresse.getAdresseIdentifikatorFraMatrikkelen()); - oppholdsadresse.setAdresseIdentifikatorFraMatrikkelen(matrikkeladresse.getMatrikkelId()); + oppholdsadresse.setAdresseIdentifikatorFraMatrikkelen(isIdSupported(oppholdsadresse, person.getIdent()) ? matrikkeladresse.getMatrikkelId() : null); mapperFacade.map(matrikkeladresse, oppholdsadresse.getMatrikkeladresse()); } else if (nonNull(oppholdsadresse.getUtenlandskAdresse())) { diff --git a/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/utils/ArtifactUtils.java b/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/utils/ArtifactUtils.java index bb3889ac76a..f7dc682e76e 100644 --- a/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/utils/ArtifactUtils.java +++ b/apps/pdl-forvalter/src/main/java/no/nav/pdl/forvalter/utils/ArtifactUtils.java @@ -9,6 +9,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static java.util.Objects.nonNull; +import static no.nav.pdl.forvalter.utils.TestnorgeIdentUtility.isTestnorgeIdent; import static org.apache.commons.lang3.StringUtils.isNotBlank; @UtilityClass @@ -30,7 +31,8 @@ public static boolean hasSpraak(String spraak) { public static DbVersjonDTO.Master getMaster(DbVersjonDTO artifact, PersonDTO person) { - return getMaster(artifact, person.getIdenttype()); + return isTestnorgeIdent(person.getIdent()) ? DbVersjonDTO.Master.PDL : + getMaster(artifact, person.getIdenttype()); } public static DbVersjonDTO.Master getMaster(DbVersjonDTO artifact, Identtype identtype) { diff --git a/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/AdressebeskyttelseServiceTest.java b/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/AdressebeskyttelseServiceTest.java index 115b1027850..e80dde56edd 100644 --- a/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/AdressebeskyttelseServiceTest.java +++ b/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/AdressebeskyttelseServiceTest.java @@ -70,6 +70,7 @@ void whenStrengtFortroligOrFortroligAndIdentypeDnr_thenThrowExecption() { void whenStrengtFortroligUtland_thenSetMasterToPdl() { var request = PersonDTO.builder() + .ident(DNR_IDENT) .adressebeskyttelse(List.of(AdressebeskyttelseDTO.builder() .gradering(STRENGT_FORTROLIG_UTLAND) .isNew(true) @@ -85,6 +86,7 @@ void whenStrengtFortroligUtland_thenSetMasterToPdl() { void whenStrengtFortrolig_thenSetKontaktadresseClearOtherAdresses() { var request = PersonDTO.builder() + .ident(DNR_IDENT) .adressebeskyttelse(List.of(AdressebeskyttelseDTO.builder() .gradering(STRENGT_FORTROLIG) .isNew(true) diff --git a/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/FolkeregisterPersonstatusServiceTest.java b/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/FolkeregisterPersonstatusServiceTest.java index 6ce12812e04..b87cdb75662 100644 --- a/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/FolkeregisterPersonstatusServiceTest.java +++ b/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/FolkeregisterPersonstatusServiceTest.java @@ -41,6 +41,7 @@ void whenValueProvided_thenKeepValue() { var target = folkeregisterPersonstatusService.convert( PersonDTO.builder() + .ident(FNR_IDENT) .folkeregisterPersonstatus(List.of(FolkeregisterPersonstatusDTO.builder() .status(FORSVUNNET) .isNew(true) @@ -55,6 +56,7 @@ void whenDoedsfallExists_thenUseDoedsfall() { var target = folkeregisterPersonstatusService.convert( PersonDTO.builder() + .ident(FNR_IDENT) .folkeregisterPersonstatus( List.of(FolkeregisterPersonstatusDTO.builder() .isNew(true) @@ -73,6 +75,7 @@ void whenOppholdExists_thenUseOpphold() { var target = folkeregisterPersonstatusService.convert( PersonDTO.builder() + .ident(FNR_IDENT) .folkeregisterPersonstatus( List.of(FolkeregisterPersonstatusDTO.builder() .isNew(true) @@ -90,6 +93,7 @@ void whenUtflyttingExists_thenUseUpphold() { var target = folkeregisterPersonstatusService.convert( PersonDTO.builder() + .ident(FNR_IDENT) .folkeregisterPersonstatus( List.of(FolkeregisterPersonstatusDTO.builder() .isNew(true) diff --git a/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/InnflyttingServiceTest.java b/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/InnflyttingServiceTest.java index 2efcadbae0a..18767d9b462 100644 --- a/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/InnflyttingServiceTest.java +++ b/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/InnflyttingServiceTest.java @@ -23,6 +23,8 @@ @ExtendWith(MockitoExtension.class) class InnflyttingServiceTest { + private static final String DNR_IDENT = "45023412345"; + @Mock private KodeverkConsumer kodeverkConsumer; @@ -52,6 +54,7 @@ void whenEmptyLandkode_thenProvideRandomCountry() { when(kodeverkConsumer.getTilfeldigLand()).thenReturn("IND"); var request = PersonDTO.builder() + .ident(DNR_IDENT) .innflytting(List.of(InnflyttingDTO.builder() .isNew(true) .build())) diff --git a/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/UtflyttingServiceTest.java b/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/UtflyttingServiceTest.java index 1b923df5319..e10e6318c06 100644 --- a/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/UtflyttingServiceTest.java +++ b/apps/pdl-forvalter/src/test/java/no/nav/pdl/forvalter/service/UtflyttingServiceTest.java @@ -23,6 +23,7 @@ @ExtendWith(MockitoExtension.class) class UtflyttingServiceTest { + private static final String FNR_IDENT = "03012312345"; @Mock private KodeverkConsumer kodeverkConsumer; @@ -52,6 +53,7 @@ void whenEmptyLandkode_thenProvideCountryFromGeografiskeKodeverkConsumer() { when(kodeverkConsumer.getTilfeldigLand()).thenReturn("TGW"); var request = PersonDTO.builder() + .ident(FNR_IDENT) .utflytting(List.of(UtflyttingDTO.builder().isNew(true).build())) .build(); From b77ff6f6530c729ed40d2fcbae131d8a8a2d295e Mon Sep 17 00:00:00 2001 From: Ulrik <94615322+ulrikHesmyr@users.noreply.github.com> Date: Wed, 14 Aug 2024 09:01:52 +0200 Subject: [PATCH 6/6] Feature/sommeroppgave app (#3532) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Lagt til app for levende arbeidsforhold #deploy-levende-arbeidsforhold-service * Liten folder fix #deploy-levende-arbeidsforhold-service * Liten name fix #deploy-levende-arbeidsforhold-service * Liten name fix #deploy-levende-arbeidsforhold-service * Revertert endringer i generer-navn-service #deploy-levende-arbeidsforhold-service * #deploy-levende-arbeidsforhold-service * test tilgang * Fungerer å kjøre lokalt * Fjernet develocity * renamet applikasjonen * Lagt til KafkaConfig.java * Test tilgang * Lagt til kafke implementation * Lagt til kafke implementationLagt til settings * Kommentert ut // implementation 'io.confluent:kafka-avro-serializer:7.5.1' i build.gradle * Kommentert ut // implementation 'io.confluent:kafka-avro-serializer:7.5.1' i build.gradle * Fungerer * FUNGERER * #deploy-levende-arbeidsforhold-service * #deploy-levende-arbeidsforhold-service * Fjerne ubrukt kode fra template * Start på å hente PDL hendelser * Slettet ting som var til generer navn service * Spring module method helloworld * La til annotation i DoedsfallListener.java * La til kafka group id i application.yaml og eventlistener * Add dev .yml file and changes to KafkaConfig.java #deploy-levende-arbeidsforhold-service * Endret typo for KafkaConfig group-id #deploy-levende-arbeidsforhold-service * Troubleshooting fikse logger, endre til =dev i Dockerfile #deploy-levende-arbeidsforhold-service * Troubleshooting fikse logger, endre til =prod i Dockerfile #deploy-levende-arbeidsforhold-service * Fjernet Profile("dev") i listener filen #deploy-levende-arbeidsforhold-service * Fjernet Profile("dev") i KafkaConfig.java filen #deploy-levende-arbeidsforhold-service * Endret til Profile("dev", "prod") i KafkaConfig.java filen #deploy-levende-arbeidsforhold-service * Kommenterer ut logging som spammer elastic #deploy-levende-arbeidsforhold-service * Init skrive ut records #deploy-levende-arbeidsforhold-service * Kopier over .avdl-filer fra pdl-repoet * Slettet .avdl-filer, la til .avsc-filer * Endre objekt-type til records * Skriver ut records * Skriver ut records #deploy-levende-arbeidsforhold-service * Lagt til Consumers.java * Lagt til HentArbeidsforholdCommand.java * Lagt til HentArbeidsforholdConsumer.java * Lagt service mappe og laget en tom service klasse * Se om consumer fungerer nåt deployet #deploy-levende-arbeidsforhold-service * Se om consumer fungerer nåt deployet * Se om jeg får kjørt consumer #deploy * Se om jeg får kjørt consumer #deploy-levende-arbeidsforhold-service * Se om jeg får kjørt consumer #deploy-levende-arbeidsforhold-service * Se om jeg får kjørt consumer #deploy-levende-arbeidsforhold-service * Se om jeg får kjørt consumer #deploy-levende-arbeidsforhold-service * Forandret fra å logge token * Forandret fra å logge token #deploy-levende-arbeidsforhold-service * Lagt til controller * Skriver ut kun doedsfall #deploy-levende-arbeidsforhold-service * Skriver ut til konsoll når dødsfall-hendelser forekommer * La til en tilfeldig suffix for group-id sånn at vi ikke kun har 1 konsument * Fått hjelp av Stian #deploy-levende-arbeidsforhold-service * Fått hjelp av Stian #deploy-levende-arbeidsforhold-service * Fått hjelp av Stian #deploy-levende-arbeidsforhold-service * Tror at man får hentet arbeidsforhold * Lagt til endre arbeidsforhold command og consumer * Lagt til endre arbeidsforhold command og consumer * Lagt til endre arbeidsforhold command og consumer * Se om jeg får kjørt consumer #deploy-levende-arbeidsforhold-service * Se om jeg får kjørt consumer #deploy-levende-arbeidsforhold-service * Forandret fra å logge token * Forandret fra å logge token #deploy-levende-arbeidsforhold-service * Rebaset forandringen * Fått hjelp av Stian #deploy-levende-arbeidsforhold-service * Fått hjelp av Stian #deploy-levende-arbeidsforhold-service * Tror at man får hentet arbeidsforhold * Limet fra endre-arbeidsforhold * Lagt inn standalone.insecure * Limt inn fra endre-arbeidsforhold * Limt inn fra endre-arbeidsforhold * Limt inn fra endre-arbeidsforhold * Endret fra ArbeidsforholdDTO til Arbeidsforhold klassen * Prøvet å lage en EndreArbeidsforholdCommand * La to EndreArbeidsforholdCommand i HentArbeidsforholdConsumer.java og endret navnet til AaregConsumer.java * Lagt til endre arbeidsforhold command og consumer * Forandret fra å logge token #deploy-levende-arbeidsforhold-service * Rebaset forandringen * Fått hjelp av Stian #deploy-levende-arbeidsforhold-service * Limt inn fra endre-arbeidsforhold * Endret fra ArbeidsforholdDTO til Arbeidsforhold klassen * Må endre i EndreArbeidsforholdCommand.java * Sender spørring med aktørid * Endret fra ArbeidsforholdDTO til Arbeidsforhold * Setter opp riktig endepunkt ved bruk av proxy * Lager metode for å håndtere arbeidsforhold ved dødsfall * Avslutter nå alle arbeidsforhold for en person på dødsfall * Endret EndreArbeidsforholdCommand.java til å logge annerledes slik at det er lettere å sjekke hvor feilen kommer fra * Nå kræsjer den ikke * Forandret put meldingen * La til ny/riktig Arbeidsforhold klasse * Forandre til put meldingen til å ha header * Endrer arbeidsforhold med gyldig sluttårsak- og varslings-kode * Legger til toString for Arbeidsforhold * Forandret EndreArbeidsforholdCommand.java slik at den ikke gir feilmelding * Fungerer å endrearbeidsforhold. Endret put kallet i EndreArbeidsforholdCommand.java. * La til .subscribe() * Rydder opp og fjerner ubrukte filer * Legger til SupressWarning på SonarCloud * Fjernet logging av suksess-scenarier. Logger kun for feilscenarier #deploy-levende-arbeidsforhold-service * La til doxygen dokumentasjon for /listener mappen * Skrev readme for /domain.v1 folderen * Deployer for å se feilmeldinger #deploy-levende-arbeidsforhold-service * Legger til sjekk om arbeidsforholdet allerede er avsluttet * Lagt til outbound.external.host: testnav-aareg-proxy.dev-fss-pub.nais.io i config,.yml * Lagt på lenger maxLength av stacktrace og prøver mindre endringer for KafkaConfig #deploy-levende-arbeidsforhold-service * Lagt på lenger maxLength av stacktrace og prøver mindre endringer for KafkaConfig #deploy-levende-arbeidsforhold-service * Whoops #deploy-levende-arbeidsforhold-service * Revert "Whoops" This reverts commit 05647ef9464408f840da01476c1a7a5cfab29a7e. * Revert "Lagt på lenger maxLength av stacktrace og prøver mindre endringer for KafkaConfig" This reverts commit fcb78907ba26c8c37f62c4ddf3d5565e9e051eaf. * Revert "Lagt på lenger maxLength av stacktrace og prøver mindre endringer for KafkaConfig" This reverts commit 07d8748269b1a7dc63eb6aa5e2b3c8a7de6a8dcb. * Lagt til kafka.groupid i config.yml #deploy-levende-arbeidsforhold-service * lagt til pool i application.yml #deploy-levende-arbeidsforhold-service * Fjernet det jeg gjorde #deploy-levende-arbeidsforhold-service * Deployer hoved-branch #deploy-levende-arbeidsforhold-service * Refactor application dependencies and configs #deploy-levende-arbeidsforhold-service Removed unnecessary dependencies and configuration entries across multiple files for better clarity and efficiency. Updated dependency paths and module includes to align with the current project structure. * Remove @AllArgsConstructor annotations from domain classes #deploy-levende-arbeidsforhold-service This commit removes the @AllArgsConstructor annotations from several domain classes, including Gyldighetsperiode, Person, and Arbeidsavtale among others. The decision to remove these constructors may be aimed at simplifying object creation or enforcing the use of builder patterns. * Switch to SecureRandom for consumer group ID generation This commit replaces Math.random() with SecureRandom for generating random suffixes for Kafka consumer group IDs. This change improves the security and unpredictability of generated IDs in the KafkaConfig class. --------- Co-authored-by: stigus Co-authored-by: Andrea Devold Fjeld Co-authored-by: Andrea Devold Fjeld Co-authored-by: Martine.Erdal.Mansaker Co-authored-by: kristenhaerum --- .../app.levende-arbeidsforhold-service.yml | 24 ++ .../src/main/resources/application.yml | 5 +- .../levende-arbeidsforhold-service/Dockerfile | 8 + apps/levende-arbeidsforhold-service/README.md | 26 ++ .../build.gradle | 84 +++++++ .../levende-arbeidsforhold-service/config.yml | 51 ++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59536 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + apps/levende-arbeidsforhold-service/gradlew | 234 ++++++++++++++++++ .../gradlew.bat | 89 +++++++ .../gradlewUpdate.sh | 3 + .../settings.gradle | 20 ++ ...beidsforholdServiceApplicationStarter.java | 13 + .../config/ApplicationConfig.java | 15 ++ .../config/Consumers.java | 29 +++ .../config/DevConfig.java | 36 +++ .../config/KafkaConfig.java | 95 +++++++ .../config/OpenApiConfig.java | 55 ++++ .../config/SecurityConfig.java | 37 +++ .../consumers/AaregConsumer.java | 77 ++++++ .../command/EndreArbeidsforholdCommand.java | 56 +++++ .../command/HentArbeidsforholdCommand.java | 75 ++++++ .../domain/v1/Ansettelsesperiode.java | 42 ++++ .../domain/v1/AntallTimerForTimeloennet.java | 54 ++++ .../domain/v1/Arbeidsavtale.java | 101 ++++++++ .../domain/v1/Arbeidsavtaletype.java | 9 + .../domain/v1/Arbeidsforhold.java | 121 +++++++++ .../domain/v1/Arbeidsforholdoversikt.java | 107 ++++++++ .../ArbeidsgiverArbeidsforholdoversikter.java | 42 ++++ .../domain/v1/Arbeidsgiveroversikt.java | 32 +++ .../domain/v1/Bruksperiode.java | 69 ++++++ ...orenkletOppgjoersordningArbeidsavtale.java | 40 +++ .../domain/v1/FrilanserArbeidsavtale.java | 40 +++ .../domain/v1/Gyldighetsperiode.java | 21 ++ .../domain/v1/MaritimArbeidsavtale.java | 54 ++++ .../v1/OpplysningspliktigArbeidsgiver.java | 28 +++ .../OpplysningspliktigArbeidsgiverType.java | 6 + .../domain/v1/OrdinaerArbeidsavtale.java | 40 +++ .../domain/v1/Organisasjon.java | 34 +++ .../domain/v1/Periode.java | 67 +++++ .../domain/v1/PermisjonPermittering.java | 42 ++++ .../domain/v1/Person.java | 36 +++ .../domain/v1/Persontype.java | 6 + .../levendearbeidsforhold/domain/v1/README.md | 6 + .../domain/v1/Sporingsinformasjon.java | 87 +++++++ .../domain/v1/Utenlandsopphold.java | 56 +++++ .../domain/v1/Varsel.java | 29 +++ .../domain/v1/Varselentitet.java | 11 + .../listener/DoedsfallListener.java | 49 ++++ .../service/ArbeidsforholdService.java | 51 ++++ .../util/JavaTimeUtil.java | 38 +++ .../src/main/resources/application-dev.yml | 2 + .../src/main/resources/application.yml | 55 ++++ .../src/main/resources/logback-spring.xml | 40 +++ .../ApplicationContextTest.java | 20 ++ .../repository/CarEntity.java | 2 + .../src/main/avro/Personhendelse.avsc | 81 ++++++ settings.gradle | 4 +- 58 files changed, 2556 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/app.levende-arbeidsforhold-service.yml create mode 100644 apps/levende-arbeidsforhold-service/Dockerfile create mode 100644 apps/levende-arbeidsforhold-service/README.md create mode 100644 apps/levende-arbeidsforhold-service/build.gradle create mode 100644 apps/levende-arbeidsforhold-service/config.yml create mode 100644 apps/levende-arbeidsforhold-service/gradle/wrapper/gradle-wrapper.jar create mode 100644 apps/levende-arbeidsforhold-service/gradle/wrapper/gradle-wrapper.properties create mode 100755 apps/levende-arbeidsforhold-service/gradlew create mode 100644 apps/levende-arbeidsforhold-service/gradlew.bat create mode 100755 apps/levende-arbeidsforhold-service/gradlewUpdate.sh create mode 100644 apps/levende-arbeidsforhold-service/settings.gradle create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/LevendeArbeidsforholdServiceApplicationStarter.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/ApplicationConfig.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/Consumers.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/DevConfig.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/KafkaConfig.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/OpenApiConfig.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/SecurityConfig.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/consumers/AaregConsumer.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/consumers/command/EndreArbeidsforholdCommand.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/consumers/command/HentArbeidsforholdCommand.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Ansettelsesperiode.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/AntallTimerForTimeloennet.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsavtale.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsavtaletype.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsforhold.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsforholdoversikt.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/ArbeidsgiverArbeidsforholdoversikter.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsgiveroversikt.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Bruksperiode.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/ForenkletOppgjoersordningArbeidsavtale.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/FrilanserArbeidsavtale.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Gyldighetsperiode.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/MaritimArbeidsavtale.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/OpplysningspliktigArbeidsgiver.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/OpplysningspliktigArbeidsgiverType.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/OrdinaerArbeidsavtale.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Organisasjon.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Periode.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/PermisjonPermittering.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Person.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Persontype.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/README.md create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Sporingsinformasjon.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Utenlandsopphold.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Varsel.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Varselentitet.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/listener/DoedsfallListener.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/service/ArbeidsforholdService.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/util/JavaTimeUtil.java create mode 100644 apps/levende-arbeidsforhold-service/src/main/resources/application-dev.yml create mode 100644 apps/levende-arbeidsforhold-service/src/main/resources/application.yml create mode 100644 apps/levende-arbeidsforhold-service/src/main/resources/logback-spring.xml create mode 100644 apps/levende-arbeidsforhold-service/src/test/java/no/nav/registre/testnorge/levendearbeidsforhold/ApplicationContextTest.java create mode 100644 libs/avro-schema/src/main/avro/Personhendelse.avsc diff --git a/.github/workflows/app.levende-arbeidsforhold-service.yml b/.github/workflows/app.levende-arbeidsforhold-service.yml new file mode 100644 index 00000000000..5c2c2af3571 --- /dev/null +++ b/.github/workflows/app.levende-arbeidsforhold-service.yml @@ -0,0 +1,24 @@ +name: levende-arbeidsforhold-service + +on: + push: + paths: + - libs/kafka-config/** + - libs/avro-schema/** + - libs/security-core/** + - libs/servlet-core/** + - libs/servlet-insecure-security/** + - libs/reactive-core/** + - apps/levende-arbeidsforhold-service/** + - .github/workflows/app.levende-arbeidsforhold-service.yml + +jobs: + workflow: + uses: ./.github/workflows/common.workflow.backend.yml + with: + working-directory: "apps/levende-arbeidsforhold-service" + deploy-tag: "#deploy-levende-arbeidsforhold-service" + permissions: + contents: read + id-token: write + secrets: inherit diff --git a/apps/arbeidsforhold-service/src/main/resources/application.yml b/apps/arbeidsforhold-service/src/main/resources/application.yml index 0cebd9bc87e..1ede0997de3 100644 --- a/apps/arbeidsforhold-service/src/main/resources/application.yml +++ b/apps/arbeidsforhold-service/src/main/resources/application.yml @@ -27,7 +27,10 @@ consumers: namespace: dolly url: https://testnav-aareg-proxy.dev-fss-pub.nais.io cluster: dev-fss - +spec: + azure: + application: + enabled: true management: endpoints: enabled-by-default: true diff --git a/apps/levende-arbeidsforhold-service/Dockerfile b/apps/levende-arbeidsforhold-service/Dockerfile new file mode 100644 index 00000000000..4a36f93546f --- /dev/null +++ b/apps/levende-arbeidsforhold-service/Dockerfile @@ -0,0 +1,8 @@ +FROM ghcr.io/navikt/baseimages/temurin:21 +LABEL maintainer="Team Dolly" + +ENV JAVA_OPTS="-Dspring.profiles.active=prod" + +ADD /build/libs/app.jar /app/app.jar + +EXPOSE 8080 diff --git a/apps/levende-arbeidsforhold-service/README.md b/apps/levende-arbeidsforhold-service/README.md new file mode 100644 index 00000000000..8f4c27a0910 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/README.md @@ -0,0 +1,26 @@ +# Levende arbeidsforhold-service + +Fyll inn: + +## Swagger + +Swagger finnes under [/swagger](https://levende-arbeidsforhold-service.intern.dev.nav.no/swagger) -endepunktet til +applikasjonen. + +## Lokal kjøring + +Ha naisdevice kjørende og kjør GenererNavnServiceApplicationStarter med følgende argumenter: + +``` +-Dspring.cloud.vault.token=[vault-token] +-Dspring.profiles.active=dev +``` + +### Utviklerimage + +I utviklerimage brukes ikke naisdevice og du må legge til følgende ekstra argumenter: + +``` +-Djavax.net.ssl.trustStore=[path til lokal truststore] +-Djavax.net.ssl.trustStorePassword=[passord til lokal truststore] +``` diff --git a/apps/levende-arbeidsforhold-service/build.gradle b/apps/levende-arbeidsforhold-service/build.gradle new file mode 100644 index 00000000000..18246d51a1e --- /dev/null +++ b/apps/levende-arbeidsforhold-service/build.gradle @@ -0,0 +1,84 @@ +plugins { + id 'java' + id "org.sonarqube" version "5.0.0.4638" + id 'org.springframework.boot' version "3.2.6" + id 'io.spring.dependency-management' version "1.1.5" + id "jacoco" +} + +test { + useJUnitPlatform() +} + +sonarqube { + properties { + property "sonar.dynamicAnalysis", "reuseReports" + property "sonar.host.url", "https://sonarcloud.io" + property "sonar.java.coveragePlugin", "jacoco" + property "sonar.language", "java" + property "sonar.token", System.getenv("SONAR_TOKEN") + property "sonar.organization", "navikt" + property "sonar.project.monorepo.enabled", true + property "sonar.projectKey", "testnav-levende-arbeidsforhold-service" + property "sonar.projectName", "testnav-levende-arbeidsforhold-service" + property "sonar.sourceEncoding", "UTF-8" + } +} +bootJar { + archiveFileName = "app.jar" +} + +dependencyManagement { + applyMavenExclusions = false + imports { + mavenBom 'org.springframework.cloud:spring-cloud-dependencies:2023.0.1' + } +} + +repositories { + mavenCentral() + mavenLocal() + maven { + url = uri('https://packages.confluent.io/maven/') + } +} + +dependencies { + implementation 'no.nav.testnav.libs:avro-schema' + implementation 'no.nav.testnav.libs:kafka-config' + implementation 'no.nav.testnav.libs:security-core' + implementation 'no.nav.testnav.libs:servlet-insecure-security' + implementation 'no.nav.testnav.libs:servlet-core' + implementation 'no.nav.testnav.libs:reactive-core' + + implementation 'org.springframework.boot:spring-boot-starter-webflux' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + implementation 'org.springframework.boot:spring-boot-starter-security' + + implementation 'org.springframework.cloud:spring-cloud-starter-vault-config' + implementation 'org.springframework.boot:spring-boot-starter-actuator' + + implementation 'io.micrometer:micrometer-registry-prometheus' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' + implementation 'io.swagger.core.v3:swagger-annotations-jakarta:2.2.21' + + implementation 'net.logstash.logback:logstash-logback-encoder:7.4' + implementation 'org.hibernate.validator:hibernate-validator' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.cloud:spring-cloud-contract-wiremock' + + implementation 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + + implementation 'org.apache.kafka:kafka-clients:3.7.0' + implementation 'io.confluent:kafka-avro-serializer:7.6.0' + implementation 'org.springframework.kafka:spring-kafka' +} +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} diff --git a/apps/levende-arbeidsforhold-service/config.yml b/apps/levende-arbeidsforhold-service/config.yml new file mode 100644 index 00000000000..31957652a8f --- /dev/null +++ b/apps/levende-arbeidsforhold-service/config.yml @@ -0,0 +1,51 @@ +apiVersion: "nais.io/v1alpha1" +kind: "Application" +metadata: + name: testnav-levende-arbeidsforhold-service + namespace: dolly + labels: + team: dolly +spec: + image: "{{image}}" + port: 8080 + accessPolicy: + inbound: + rules: + - application: team-dolly-lokal-app + cluster: dev-gcp + - application: testnav-oversikt-frontend + cluster: dev-gcp + outbound: + external: + - host: testnav-aareg-proxy.dev-fss-pub.nais.io + azure: + application: + allowAllUsers: true + enabled: true + tenant: nav.no + liveness: + path: /internal/isAlive + initialDelay: 4 + periodSeconds: 5 + failureThreshold: 500 + readiness: + path: /internal/isReady + initialDelay: 4 + periodSeconds: 5 + failureThreshold: 500 + prometheus: + enabled: true + path: /internal/metrics + replicas: + min: 1 + max: 1 + resources: + requests: + cpu: 200m + memory: 1024Mi + limits: + memory: 2048Mi + kafka: + pool: nav-dev + ingresses: + - "https://testnav-levende-arbeidsforhold-service.intern.dev.nav.no" diff --git a/apps/levende-arbeidsforhold-service/gradle/wrapper/gradle-wrapper.jar b/apps/levende-arbeidsforhold-service/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..7454180f2ae8848c63b8b4dea2cb829da983f2fa GIT binary patch literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL literal 0 HcmV?d00001 diff --git a/apps/levende-arbeidsforhold-service/gradle/wrapper/gradle-wrapper.properties b/apps/levende-arbeidsforhold-service/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..48c0a02ca41 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/apps/levende-arbeidsforhold-service/gradlew b/apps/levende-arbeidsforhold-service/gradlew new file mode 100755 index 00000000000..3da45c161b0 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright ? 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions ?$var?, ?${var}?, ?${var:-default}?, ?${var+SET}?, +# ?${var#prefix}?, ?${var%suffix}?, and ?$( cmd )?; +# * compound commands having a testable exit status, especially ?case?; +# * various built-in commands including ?command?, ?set?, and ?ulimit?. +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/apps/levende-arbeidsforhold-service/gradlew.bat b/apps/levende-arbeidsforhold-service/gradlew.bat new file mode 100644 index 00000000000..107acd32c4e --- /dev/null +++ b/apps/levende-arbeidsforhold-service/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/apps/levende-arbeidsforhold-service/gradlewUpdate.sh b/apps/levende-arbeidsforhold-service/gradlewUpdate.sh new file mode 100755 index 00000000000..e5ee6361152 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/gradlewUpdate.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +gradle wrapper \ No newline at end of file diff --git a/apps/levende-arbeidsforhold-service/settings.gradle b/apps/levende-arbeidsforhold-service/settings.gradle new file mode 100644 index 00000000000..b8c8778a068 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/settings.gradle @@ -0,0 +1,20 @@ +plugins { + id "com.gradle.develocity" version "3.17.4" +} + +rootProject.name = 'levende-arbeidsforhold-service' + +includeBuild "../../libs/kafka-config" +includeBuild "../../libs/avro-schema" +includeBuild '../../libs/security-core' +includeBuild '../../libs/servlet-core' +includeBuild '../../libs/reactive-core' +includeBuild '../../libs/servlet-insecure-security' +includeBuild '../../.github/workflows' + +develocity { + buildScan { + termsOfUseUrl = "https://gradle.com/terms-of-service" + termsOfUseAgree = "yes" + } +} \ No newline at end of file diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/LevendeArbeidsforholdServiceApplicationStarter.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/LevendeArbeidsforholdServiceApplicationStarter.java new file mode 100644 index 00000000000..4c1bc1f8bd4 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/LevendeArbeidsforholdServiceApplicationStarter.java @@ -0,0 +1,13 @@ +package no.nav.registre.testnorge.levendearbeidsforhold; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + + +@SpringBootApplication +public class LevendeArbeidsforholdServiceApplicationStarter { + + public static void main(String[] args) { + SpringApplication.run(LevendeArbeidsforholdServiceApplicationStarter.class, args); + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/ApplicationConfig.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/ApplicationConfig.java new file mode 100644 index 00000000000..80a0da22609 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/ApplicationConfig.java @@ -0,0 +1,15 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.config; + +import no.nav.testnav.libs.servletcore.config.ApplicationCoreConfig; +import no.nav.testnav.libs.standalone.servletsecurity.config.InsecureJwtServerToServerConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@Import({ + ApplicationCoreConfig.class, + InsecureJwtServerToServerConfiguration.class +}) +public class ApplicationConfig { + +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/Consumers.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/Consumers.java new file mode 100644 index 00000000000..c5654aff14d --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/Consumers.java @@ -0,0 +1,29 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.config; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import no.nav.testnav.libs.securitycore.domain.ServerProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import static lombok.AccessLevel.PACKAGE; + +/** + * Samler alle placeholders for ulike {@code consumers.*}-konfigurasjon her, dvs. subklasser av {@code ServerProperties}. + *

    + * Husk at Spring Boot bruker
    relaxed binding + * mellom configuration properties og field names. + * + * @see ServerProperties + */ +@Configuration +@ConfigurationProperties(prefix = "consumers") +@NoArgsConstructor(access = PACKAGE) +@Getter +@Setter(PACKAGE) +public class Consumers { + + private ServerProperties testnavAaregProxy; + +} \ No newline at end of file diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/DevConfig.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/DevConfig.java new file mode 100644 index 00000000000..a5b2cad3c88 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/DevConfig.java @@ -0,0 +1,36 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.vault.annotation.VaultPropertySource; +import org.springframework.vault.authentication.ClientAuthentication; +import org.springframework.vault.authentication.TokenAuthentication; +import org.springframework.vault.client.VaultEndpoint; +import org.springframework.vault.config.AbstractVaultConfiguration; + +import static io.micrometer.common.util.StringUtils.isBlank; + +@Configuration +@Profile("dev") +@VaultPropertySource(value = "secret/dolly/lokal", ignoreSecretNotFound = false) +public class DevConfig extends AbstractVaultConfiguration { + + private static final String VAULT_TOKEN = "spring.cloud.vault.token"; + + @Override + public VaultEndpoint vaultEndpoint() { + return VaultEndpoint.create("vault.adeo.no", 443); + } + + @Override + public ClientAuthentication clientAuthentication() { + if (System.getenv().containsKey("VAULT_TOKEN")) { + System.setProperty(VAULT_TOKEN, System.getenv("VAULT_TOKEN")); + } + var token = System.getProperty(VAULT_TOKEN); + if (isBlank(token)) { + throw new IllegalArgumentException("Påkrevet property 'spring.cloud.vault.token' er ikke satt."); + } + return new TokenAuthentication(System.getProperty(VAULT_TOKEN)); + } +} \ No newline at end of file diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/KafkaConfig.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/KafkaConfig.java new file mode 100644 index 00000000000..5fab1fac11f --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/KafkaConfig.java @@ -0,0 +1,95 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.config; + + +import io.confluent.kafka.serializers.AbstractKafkaSchemaSerDeConfig; +import io.confluent.kafka.serializers.KafkaAvroDeserializer; +import io.confluent.kafka.serializers.KafkaAvroDeserializerConfig; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.CommonClientConfigs; +import org.apache.kafka.clients.consumer.Consumer; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.config.SslConfigs; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.listener.CommonLoggingErrorHandler; + +import java.net.InetSocketAddress; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Random; + +@Slf4j +@EnableKafka +@Configuration +@Profile({"dev", "prod"}) +public class KafkaConfig { + + private static final Random RANDOM = new SecureRandom(); + private final String groupId; + + public KafkaConfig(@Value("${spring.kafka.consumer.group-id}") String groupId) { + this.groupId = groupId; + } + + public ConsumerFactory consumerFactory() { + + var randomSuffixGroupID = String.valueOf((int)(RANDOM.nextFloat() * 1000)); + + var inetSocketAddress = new InetSocketAddress(0); + var props = new HashMap(); + + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, System.getenv("KAFKA_BROKERS")); + props.put(SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG, System.getenv("KAFKA_KEYSTORE_PATH")); + props.put(SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG, System.getenv("KAFKA_CREDSTORE_PASSWORD")); + props.put(SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG, System.getenv("KAFKA_TRUSTSTORE_PATH")); + props.put(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG, System.getenv("KAFKA_CREDSTORE_PASSWORD")); + props.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SSL"); + props.put(KafkaAvroDeserializerConfig.SPECIFIC_AVRO_READER_CONFIG, true); + + props.put(AbstractKafkaSchemaSerDeConfig.BASIC_AUTH_CREDENTIALS_SOURCE, "USER_INFO"); + var username = System.getenv("KAFKA_SCHEMA_REGISTRY_USER"); + var password = System.getenv("KAFKA_SCHEMA_REGISTRY_PASSWORD"); + + props.put(AbstractKafkaSchemaSerDeConfig.USER_INFO_CONFIG, username + ":" + password); + props.put(AbstractKafkaSchemaSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG, System.getenv("KAFKA_SCHEMA_REGISTRY")); + + props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId + randomSuffixGroupID); + props.put(ConsumerConfig.CLIENT_ID_CONFIG, groupId + inetSocketAddress.getHostString()); + props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true); + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest"); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 1000 * 60 * 10); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, KafkaAvroDeserializer.class); + + return new DefaultKafkaConsumerFactory<>(props); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { + + var factory = new ConcurrentKafkaListenerContainerFactory(); + var consumerFactory = consumerFactory(); + consumerFactory.addListener(new ConsumerFactory.Listener<>() { + @Override + public void consumerAdded(String id, Consumer consumer) { + log.info("Legger til consumer med id: {}", id); + } + + @Override + public void consumerRemoved(String id, Consumer consumer) { + log.warn("Fjerner consumer med id: {}. Restarter app...", id); + } + }); + factory.setBatchListener(true); + factory.setConsumerFactory(consumerFactory); + factory.setCommonErrorHandler(new CommonLoggingErrorHandler()); + return factory; + } +} \ No newline at end of file diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/OpenApiConfig.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/OpenApiConfig.java new file mode 100644 index 00000000000..d2dc2940c31 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/OpenApiConfig.java @@ -0,0 +1,55 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.config; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.Arrays; + +import no.nav.testnav.libs.servletcore.config.ApplicationProperties; + +@Configuration +public class OpenApiConfig implements WebMvcConfigurer { + + @Bean + public OpenAPI openApi(ApplicationProperties applicationProperties) { + return new OpenAPI() + .components(new Components().addSecuritySchemes("bearer-jwt", new SecurityScheme() + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT") + .in(SecurityScheme.In.HEADER) + .name("Authorization") + )) + .addSecurityItem( + new SecurityRequirement().addList("bearer-jwt", Arrays.asList("read", "write"))) + .info(new Info() + .title(applicationProperties.getName()) + .version(applicationProperties.getVersion()) + .description(applicationProperties.getDescription()) + .termsOfService("https://nav.no") + .contact(new Contact() + .url("https://nav-it.slack.com/archives/CA3P9NGA2") + .email("dolly@nav.no") + .name("Team Dolly") + ) + .license(new License() + .name("MIT License") + .url("https://opensource.org/licenses/MIT") + ) + ); + } + + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/swagger").setViewName("redirect:/swagger-ui.html"); + } +} \ No newline at end of file diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/SecurityConfig.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/SecurityConfig.java new file mode 100644 index 00000000000..ae501af8a05 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/config/SecurityConfig.java @@ -0,0 +1,37 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; + +@EnableWebSecurity +@Configuration +public class SecurityConfig { + + @Bean + @SuppressWarnings("java:S4502") + public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { + + httpSecurity.sessionManagement(sessionConfig -> sessionConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(authorizeConfig -> authorizeConfig.requestMatchers( + "/internal/**", + "/webjars/**", + "/swagger-resources/**", + "/v3/api-docs/**", + "/swagger-ui/**", + "/swagger", + "/error", + "/swagger-ui.html" + ).permitAll().requestMatchers("/api/**").fullyAuthenticated()) + .oauth2ResourceServer(oauth2RSConfig -> oauth2RSConfig.jwt(Customizer.withDefaults())); + + return httpSecurity.build(); + } +} + diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/consumers/AaregConsumer.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/consumers/AaregConsumer.java new file mode 100644 index 00000000000..9b3f46be9a2 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/consumers/AaregConsumer.java @@ -0,0 +1,77 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.consumers; + +import lombok.extern.slf4j.Slf4j; + +import no.nav.registre.testnorge.levendearbeidsforhold.consumers.command.EndreArbeidsforholdCommand; +import no.nav.registre.testnorge.levendearbeidsforhold.consumers.command.HentArbeidsforholdCommand; +import no.nav.registre.testnorge.levendearbeidsforhold.domain.v1.Arbeidsforhold; +import no.nav.registre.testnorge.levendearbeidsforhold.config.Consumers; +import no.nav.testnav.libs.securitycore.domain.ServerProperties; +import no.nav.testnav.libs.standalone.servletsecurity.exchange.TokenExchange; + +import org.springframework.http.MediaType; +import org.springframework.http.codec.json.Jackson2JsonDecoder; +import org.springframework.http.codec.json.Jackson2JsonEncoder; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.ExchangeStrategies; +import org.springframework.web.reactive.function.client.WebClient; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.Objects.nonNull; + +import com.fasterxml.jackson.databind.ObjectMapper; + +@Slf4j +@Component +public class AaregConsumer { + + private final WebClient webClient; + private final ServerProperties serverProperties; + private final TokenExchange tokenExchange; + + public AaregConsumer( + Consumers consumers, + TokenExchange tokenExchange, + ObjectMapper objectMapper) { + + this.serverProperties = consumers.getTestnavAaregProxy(); + this.tokenExchange = tokenExchange; + + ExchangeStrategies jacksonStrategy = ExchangeStrategies + .builder() + .codecs( + config -> { + config + .defaultCodecs() + .jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON)); + config + .defaultCodecs() + .jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON)); + }) + .build(); + + this.webClient = WebClient + .builder() + .exchangeStrategies(jacksonStrategy) + .baseUrl(serverProperties.getUrl()) + .build(); + } + + public List hentArbeidsforhold(String ident) { + var token = tokenExchange.exchange(serverProperties).block(); + if (nonNull(token)) { + return new HentArbeidsforholdCommand(webClient, token.getTokenValue(), ident).call(); + } + return new ArrayList<>(); + } + + public void endreArbeidsforhold(Arbeidsforhold requests) { + var token = tokenExchange.exchange(serverProperties).block(); + if (nonNull(token)) { + new EndreArbeidsforholdCommand(webClient, requests, token.getTokenValue()).call(); + } + } +} + diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/consumers/command/EndreArbeidsforholdCommand.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/consumers/command/EndreArbeidsforholdCommand.java new file mode 100644 index 00000000000..60aceb2f3f7 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/consumers/command/EndreArbeidsforholdCommand.java @@ -0,0 +1,56 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.consumers.command; + +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import no.nav.registre.testnorge.levendearbeidsforhold.domain.v1.Arbeidsforhold; +import no.nav.testnav.libs.reactivecore.utils.WebClientFilter; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; +import reactor.util.retry.Retry; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.Callable; + +@Slf4j +@RequiredArgsConstructor +public class EndreArbeidsforholdCommand implements Callable> { + + private static final String navArbeidsforholdKilde = "Dolly-doedsfall-hendelse" ; + private static final String miljoe = "q2"; + + private final WebClient webClient; + private final Arbeidsforhold requests; + private final String token; + + @SneakyThrows + @Override + public Mono call() { + + Mono request = webClient + .put() + .uri(builder -> builder.path("/{miljoe}/api/v1/arbeidsforhold/{navArbeidsforholdId}") + .build(miljoe, requests.getNavArbeidsforholdId())) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + token) + .header("Nav-Arbeidsforhold-Kildereferanse", navArbeidsforholdKilde) + .header("Nav-Arbeidsforhold-Periode", LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"))) + .body(BodyInserters.fromValue(requests)) + .retrieve() + .bodyToMono(Arbeidsforhold.class) + .retryWhen(Retry.backoff(3, Duration.ofSeconds(5)) + .filter(WebClientFilter::is5xxException)) + .map(arbeidsforhold1 -> Arbeidsforhold.builder().build()); + + request.subscribe(response -> {}, error -> log.error("Feil ved endring av arbeidsforhold: {}", error.getMessage())); + + return request; + } +} + + diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/consumers/command/HentArbeidsforholdCommand.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/consumers/command/HentArbeidsforholdCommand.java new file mode 100644 index 00000000000..cf84f0f4848 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/consumers/command/HentArbeidsforholdCommand.java @@ -0,0 +1,75 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.consumers.command; + +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import no.nav.registre.testnorge.levendearbeidsforhold.domain.v1.Arbeidsforhold; +import no.nav.testnav.libs.reactivecore.utils.WebClientFilter; +import no.nav.testnav.libs.servletcore.headers.NavHeaders; +import org.springframework.http.HttpHeaders; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.util.retry.Retry; + +import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.stream.Collectors; + +import static java.lang.String.format; + + +@Slf4j +@RequiredArgsConstructor +public class HentArbeidsforholdCommand implements Callable> { + private static final String miljoe = "q2"; + private static final String NAV_PERSON_IDENT = "Nav-Personident"; + private static final String CONSUMER = "Dolly"; + + private final WebClient webClient; + private final String token; + private final String ident; + + private static String getNavCallId() { + return format("%s %s", CONSUMER, UUID.randomUUID()); + } + + @SneakyThrows + @Override + public List call(){ + + try { + var arbeidsforhold = webClient + .get() + .uri(builder -> builder + .path("/{miljoe}/api/v1/arbeidstaker/arbeidsforhold") + .queryParam("arbeidsforholdtype", "forenkletOppgjoersordning", + "frilanserOppdragstakerHonorarPersonerMm", "maritimtArbeidsforhold", + "ordinaertArbeidsforhold") + .build(miljoe)) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + token) + .header(NAV_PERSON_IDENT, ident) + .header(NavHeaders.NAV_CONSUMER_ID, CONSUMER) + .header(NavHeaders.NAV_CALL_ID, getNavCallId()) + .retrieve() + .bodyToMono(Arbeidsforhold[].class) + .retryWhen(Retry + .backoff(3, Duration.ofSeconds(5)) + .filter(WebClientFilter::is5xxException)) + .block(); + + return Arrays.stream(arbeidsforhold).collect(Collectors.toList()); + } catch (WebClientResponseException.NotFound e) { + return Collections.emptyList(); + } catch (WebClientResponseException e) { + log.error( + "Klarer ikke å hente arbeidsforhold. Feilmelding: {}.", + e.getResponseBodyAsString() + ); + throw e; + } + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Ansettelsesperiode.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Ansettelsesperiode.java new file mode 100644 index 00000000000..b2580aa002b --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Ansettelsesperiode.java @@ -0,0 +1,42 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonPropertyOrder({ + "periode", + "sluttaarsak", + "varslingskode", + "bruksperiode", + "sporingsinformasjon" +}) +@Schema(description = "Informasjon knyttet til ansettelsesperioden") +public class Ansettelsesperiode { + + private Periode periode; + + @Schema(description = "Årsak for avsluttet ansettelsesperiode (kodeverk: Slutt%C3%A5rsakAareg)", example = "arbeidstakerHarSagtOppSelv") + private String sluttaarsak; + + @Schema(description = "Varslingskode (kodeverk: Varslingskode_5fAa-registeret) - benyttes hvis ansettelsesperiode er lukket maskinelt", example = "ERKONK") + private String varslingskode; + + private Bruksperiode bruksperiode; + + private Sporingsinformasjon sporingsinformasjon; + + @Override + public String toString() { + return ("Ansettelsesforhold: [" + periode.toString() + ", " + sluttaarsak + ", " + varslingskode); + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/AntallTimerForTimeloennet.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/AntallTimerForTimeloennet.java new file mode 100644 index 00000000000..9f3dc79cfba --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/AntallTimerForTimeloennet.java @@ -0,0 +1,54 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import no.nav.registre.testnorge.levendearbeidsforhold.util.JavaTimeUtil; + +import java.time.YearMonth; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonPropertyOrder({ + "periode", + "antallTimer", + "rapporteringsperiode", + "sporingsinformasjon" +}) +@Schema(description = "Informasjon om antall timer med timelønn") +public class AntallTimerForTimeloennet { + + private Periode periode; + + @Schema(description = "Antall timer", example = "37.5") + private Double antallTimer; + + private YearMonth rapporteringsperiode; + + private Sporingsinformasjon sporingsinformasjon; + + @JsonIgnore + public YearMonth getRapporteringsperiode() { + return rapporteringsperiode; + } + + @JsonProperty("rapporteringsperiode") + @Schema(description = "Rapporteringsperiode for antall timer med timelønn, format (ISO-8601): yyyy-MM", example = "2018-05") + public String getRapporteringsperiodeAsString() { + return JavaTimeUtil.toString(rapporteringsperiode); + } + + @JsonProperty("rapporteringsperiode") + public void setRapporteringsperiodeAsString(String rapporteringsperiode) { + this.rapporteringsperiode = JavaTimeUtil.toYearMonth(rapporteringsperiode); + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsavtale.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsavtale.java new file mode 100644 index 00000000000..c9855e0b2e0 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsavtale.java @@ -0,0 +1,101 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.swagger.v3.oas.annotations.media.DiscriminatorMapping; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import no.nav.registre.testnorge.levendearbeidsforhold.util.JavaTimeUtil; + +import java.time.LocalDate; + +@Data +@NoArgsConstructor +@SuperBuilder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = OrdinaerArbeidsavtale.class, name = OrdinaerArbeidsavtale.TYPE), + @JsonSubTypes.Type(value = MaritimArbeidsavtale.class, name = MaritimArbeidsavtale.TYPE), + @JsonSubTypes.Type(value = ForenkletOppgjoersordningArbeidsavtale.class, name = ForenkletOppgjoersordningArbeidsavtale.TYPE), + @JsonSubTypes.Type(value = FrilanserArbeidsavtale.class, name = FrilanserArbeidsavtale.TYPE) +}) +@Schema(description = "Informasjon om arbeidsavtale/ansettelsesdetaljer", + discriminatorProperty = "type", + discriminatorMapping = { + @DiscriminatorMapping(value = OrdinaerArbeidsavtale.TYPE, schema = OrdinaerArbeidsavtale.class), + @DiscriminatorMapping(value = MaritimArbeidsavtale.TYPE, schema = MaritimArbeidsavtale.class), + @DiscriminatorMapping(value = ForenkletOppgjoersordningArbeidsavtale.TYPE, schema = ForenkletOppgjoersordningArbeidsavtale.class), + @DiscriminatorMapping(value = FrilanserArbeidsavtale.TYPE, schema = FrilanserArbeidsavtale.class) + } +) +public abstract class Arbeidsavtale implements Arbeidsavtaletype { + + @Schema(description = "Arbeidstidsordning (kodeverk: Arbeidstidsordninger)", example = "ikkeSkift") + private String arbeidstidsordning; + + @Schema(description = "Ansettelsesform (kodeverk: AnsettelsesformAareg)", example = "fast") + private String ansettelsesform; + + @Schema(description = "Yrke (kodeverk: Yrker)", example = "2130123") + private String yrke; + + @Schema(description = "Stillingsprosent", example = "49.5") + private Double stillingsprosent; + + @Schema(description = "Antall timer per uke", example = "37.5") + private Double antallTimerPrUke; + + @Schema(description = "Beregnet antall timer per uke", example = "37.5") + private Double beregnetAntallTimerPrUke; + + @Schema(description = "Dato for siste lønnsendring, format (ISO-8601): yyyy-MM-dd", example = "2014-07-15") + private LocalDate sistLoennsendring; + + @Schema(description = "Dato for siste stillingsendring, format (ISO-8601): yyyy-MM-dd", example = "2015-12-15") + private LocalDate sistStillingsendring; + + private Bruksperiode bruksperiode; + + private Gyldighetsperiode gyldighetsperiode; + + private Sporingsinformasjon sporingsinformasjon; + + @JsonIgnore + public LocalDate getSistLoennsendring() { + return sistLoennsendring; + } + + @JsonIgnore + public LocalDate getSistStillingsendring() { + return sistStillingsendring; + } + + @JsonProperty("sistLoennsendring") + public String getSistLoennsendringAsString() { + return JavaTimeUtil.toString(sistLoennsendring); + } + + @JsonProperty("sistLoennsendring") + public void setSistLoennsendringAsString(String sistLoennsendring) { + this.sistLoennsendring = JavaTimeUtil.toLocalDate(sistLoennsendring); + } + + @JsonProperty("sistStillingsendring") + public String getSistStillingsendringAsString() { + return JavaTimeUtil.toString(sistStillingsendring); + } + + @JsonProperty("sistStillingsendring") + public void setSistStillingsendringAsString(String sistStillingsendring) { + this.sistStillingsendring = JavaTimeUtil.toLocalDate(sistStillingsendring); + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsavtaletype.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsavtaletype.java new file mode 100644 index 00000000000..8192cf72ad5 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsavtaletype.java @@ -0,0 +1,9 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import io.swagger.v3.oas.annotations.media.Schema; + +public interface Arbeidsavtaletype { + + @Schema(description = "Type for arbeidsavtale", allowableValues = "Ordinaer,Maritim,Forenklet,Frilanser") + String getType(); +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsforhold.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsforhold.java new file mode 100644 index 00000000000..a340d6fdf1f --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsforhold.java @@ -0,0 +1,121 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import no.nav.registre.testnorge.levendearbeidsforhold.util.JavaTimeUtil; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonPropertyOrder({ + "navArbeidsforholdId", + "arbeidsforholdId", + "arbeidstaker", + "arbeidsgiver", + "opplysningspliktig", + "type", + "ansettelsesperiode", + "arbeidsavtaler", + "permisjonPermitteringer", + "antallTimerForTimeloennet", + "utenlandsopphold", + "varsler", + "innrapportertEtterAOrdningen", + "registrert", + "sistBekreftet", + "sporingsinformasjon" +}) +@Schema(description = "Informasjon om arbeidsforhold") +@SuppressWarnings({"pmd:TooManyFields", "fb-contrib:CC_CYCLOMATIC_COMPLEXITY"}) +public class Arbeidsforhold { + + @Schema(description = "Arbeidsforhold-id i AAREG", example = "123456") + private Long navArbeidsforholdId; + + @Schema(description = "Arbeidsforhold-id fra opplysningspliktig", example = "abc-321") + private String arbeidsforholdId; + + private LocalDateTime registrert; + + private Person arbeidstaker; + + private OpplysningspliktigArbeidsgiver arbeidsgiver; + + private OpplysningspliktigArbeidsgiver opplysningspliktig; + + @Schema(description = "Arbeidsforholdtype (kodeverk: Arbeidsforholdtyper)", example = "ordinaertArbeidsforhold") + private String type; + + private Ansettelsesperiode ansettelsesperiode; + + @Schema(description = "Liste av arbeidsavtaler - gjeldende og evt. med historikk") + private List arbeidsavtaler; + + @Schema(description = "Liste av permisjoner og/eller permitteringer") + private List permisjonPermitteringer; + + @Schema(description = "Liste av antall timer med timelønn") + private List antallTimerForTimeloennet; + + @Schema(description = "Liste av utenlandsopphold") + private List utenlandsopphold; + + @Schema(description = "Liste av unike varsler for ulike entiter") + private List varsler; + + @Schema(description = "Er arbeidsforholdet innrapportert via a-ordningen?") + private Boolean innrapportertEtterAOrdningen; + + private LocalDateTime sistBekreftet; + + private Sporingsinformasjon sporingsinformasjon; + + @JsonIgnore + public LocalDateTime getRegistrert() { + return registrert; + } + + @JsonProperty("registrert") + @Schema(description = "Tidspunkt for registrering av arbeidsforhold, format (ISO-8601): yyyy-MM-dd'T'HH:mm[:ss[.SSSSSSSSS]]", example = "2018-09-18T11:12:29") + public String getRegistrertAsString() { + return JavaTimeUtil.toString(registrert); + } + + @JsonProperty("registrert") + public void setRegistrertAsString(String registrert) { + this.registrert = JavaTimeUtil.toLocalDateTime(registrert); + } + + @JsonIgnore + public LocalDateTime getSistBekreftet() { + return sistBekreftet; + } + + @JsonProperty("sistBekreftet") + @Schema(description = "Tidspunkt for siste bekreftelse av arbeidsforhold, format (ISO-8601): yyyy-MM-dd'T'HH:mm[:ss[.SSSSSSSSS]]", example = "2018-09-19T12:10:31") + public String getSistBekreftetAsString() { + return JavaTimeUtil.toString(sistBekreftet); + } + + @JsonProperty("sistBekreftet") + public void setSistBekreftetAsString(String sistBekreftet) { + this.sistBekreftet = JavaTimeUtil.toLocalDateTime(sistBekreftet); + } + + @Override + public String toString() { + return ("Arbeidsforhold: [" + navArbeidsforholdId + ", " + arbeidsforholdId + ", " + ansettelsesperiode + ", " + arbeidsavtaler); + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsforholdoversikt.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsforholdoversikt.java new file mode 100644 index 00000000000..eb51335d943 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsforholdoversikt.java @@ -0,0 +1,107 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import no.nav.registre.testnorge.levendearbeidsforhold.util.JavaTimeUtil; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonPropertyOrder({ + "navArbeidsforholdId", + "arbeidstaker", + "arbeidsgiver", + "opplysningspliktig", + "type", + "ansattFom", + "ansattTom", + "yrke", + "stillingsprosent", + "permisjonPermitteringsprosent", + "innrapportertEtterAOrdningen", + "sistBekreftet", + "varsler" +}) +@Schema(description = "Informasjon om arbeidsforhold (overordnet)") +@SuppressWarnings("fb-contrib:CC_CYCLOMATIC_COMPLEXITY") +public class Arbeidsforholdoversikt { + + @Schema(description = "Arbeidsforhold-id i AAREG", example = "123456") + private Long navArbeidsforholdId; + + private Person arbeidstaker; + + private OpplysningspliktigArbeidsgiver arbeidsgiver; + + private OpplysningspliktigArbeidsgiver opplysningspliktig; + + @Schema(description = "Arbeidsforholdtype (kodeverk: Arbeidsforholdtyper)", example = "ordinaertArbeidsforhold") + private String type; + + private LocalDate ansattFom; + + private LocalDate ansattTom; + + @Schema(description = "Yrke (kodeverk: Yrker)", example = "2130123") + private String yrke; + + @Schema(description = "Stillingsprosent", example = "49.5") + private Double stillingsprosent; + + @Schema(description = "Prosent for permisjon eller permittering (aggregert)", example = "50.5") + private Double permisjonPermitteringsprosent; + + @Schema(description = "Er arbeidsforholdet innrapportert via a-ordningen?") + private Boolean innrapportertEtterAOrdningen; + + private LocalDateTime sistBekreftet; + + @Schema(description = "Liste av unike varsler for ulike entiter") + private List varsler; + + @JsonIgnore + public LocalDate getAnsattFom() { + return ansattFom; + } + + @JsonProperty("ansattFom") + @Schema(description = "Fra-og-med-dato for ansettelsesperiode, format (ISO-8601): yyyy-MM-dd", example = "2014-07-01") + public String getAnsattFomAsString() { + return JavaTimeUtil.toString(ansattFom); + } + + @JsonIgnore + public LocalDate getAnsattTom() { + return ansattTom; + } + + @JsonProperty("ansattTom") + @Schema(description = "Til-og-med-dato for ansettelsesperiode, format (ISO-8601): yyyy-MM-dd", example = "2015-12-31") + public String getAnsattTomAsString() { + return JavaTimeUtil.toString(ansattTom); + } + + @JsonIgnore + public LocalDateTime getSistBekreftet() { + return sistBekreftet; + } + + @JsonProperty("sistBekreftet") + @Schema(description = "Tidspunkt for siste bekreftelse av arbeidsforhold, format (ISO-8601): yyyy-MM-dd'T'HH:mm[:ss[.SSSSSSSSS]]", example = "2018-09-19T12:10:31") + public String getSistBekreftetAsString() { + return JavaTimeUtil.toString(sistBekreftet); + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/ArbeidsgiverArbeidsforholdoversikter.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/ArbeidsgiverArbeidsforholdoversikter.java new file mode 100644 index 00000000000..674475f7258 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/ArbeidsgiverArbeidsforholdoversikter.java @@ -0,0 +1,42 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "arbeidsforholdoversikter", + "startrad", + "antall", + "totalAntall" +}) +@Schema(description = """ + Resultatobjekt for finn-arbeidsforholdoversikter-per-arbeidsgiver + + Arbeidsforholdoversikter er filtrert grunnet tilgangskontroll hvis størrelse på liste er mindre enn (total) antall (forutsatt at antall- og/eller startrad-filter ikke er angitt)""" +) +public class ArbeidsgiverArbeidsforholdoversikter { + + @Schema(description = "Liste av arbeidsforholdoversikter") + private List arbeidsforholdoversikter; + + @Schema(description = "Nummer for første rad i resultatsett (ikke angitt hvis antall er 0)") + private Integer startrad; + + @Schema(description = "Antall arbeidsforholdoversikter i resultatsett - der det er siste resultatsett hvis antall er mindre enn forespurt antall") + private Integer antall; + + @Schema(description = "Total antall arbeidsforholdoversikter") + private Integer totalAntall; +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsgiveroversikt.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsgiveroversikt.java new file mode 100644 index 00000000000..f1be4637bbf --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Arbeidsgiveroversikt.java @@ -0,0 +1,32 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "arbeidsgiver", + "aktiveArbeidsforhold", + "inaktiveArbeidsforhold" +}) +@Schema(description = "Informasjon knyttet til arbeidsgiver (overordnet)") +public class Arbeidsgiveroversikt { + + private OpplysningspliktigArbeidsgiver arbeidsgiver; + + @Schema(description = "Antall aktive arbeidsforhold, dvs. de som har en gjeldende ansettelsesperiode") + private Integer aktiveArbeidsforhold; + + @Schema(description = "Antall inaktive arbeidsforhold, dvs. de som har en historisk ansettelsesperiode") + private Integer inaktiveArbeidsforhold; + +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Bruksperiode.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Bruksperiode.java new file mode 100644 index 00000000000..fd3fcb620ee --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Bruksperiode.java @@ -0,0 +1,69 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import no.nav.registre.testnorge.levendearbeidsforhold.util.JavaTimeUtil; + +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonPropertyOrder({ + "fom", + "tom" +}) +@Schema(description = "Inneholder informasjon om bruksperiode til objektet") +public class Bruksperiode { + + private LocalDateTime fom; + + private LocalDateTime tom; + + @JsonIgnore + public LocalDateTime getFom() { + return fom; + } + + @JsonIgnore + public LocalDateTime getTom() { + return tom; + } + + @JsonProperty("fom") + @Schema(description = "Fra-tidsstempel for bruksperiode, format (ISO-8601): yyyy-MM-dd'T'HH:mm[:ss[.SSSSSSSSS]]", example = "2015-01-06T21:44:04.748") + public String getFomAsString() { + return JavaTimeUtil.toString(fom); + } + + @JsonProperty("fom") + public void setFomAsString(String fom) { + this.fom = JavaTimeUtil.toLocalDateTime(fom); + } + + @JsonProperty("tom") + @Schema(description = "Til-tidsstempel for bruksperiode, format (ISO-8601): yyyy-MM-dd'T'HH:mm[:ss[.SSSSSSSSS]]", example = "2015-12-06T19:45:04") + public String getTomAsString() { + return JavaTimeUtil.toString(tom); + } + + @JsonProperty("tom") + public void setTomAsString(String tom) { + this.tom = JavaTimeUtil.toLocalDateTime(tom); + } + + @Override + @SuppressWarnings({"pmd:ConsecutiveLiteralAppends", "pmd:ConsecutiveAppendsShouldReuse", "fb-contrib:UCPM_USE_CHARACTER_PARAMETERIZED_METHOD", "pmd:AppendCharacterWithChar"}) + public String toString() { + return "Bruksperiode{" + "fom=" + getFomAsString() + ", tom=" + getTomAsString() + "}"; + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/ForenkletOppgjoersordningArbeidsavtale.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/ForenkletOppgjoersordningArbeidsavtale.java new file mode 100644 index 00000000000..2cfa5113756 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/ForenkletOppgjoersordningArbeidsavtale.java @@ -0,0 +1,40 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +@Data +@NoArgsConstructor +@SuperBuilder +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@JsonPropertyOrder({ + "arbeidstidsordning", + "ansettelsesform", + "yrke", + "stillingsprosent", + "antallTimerPrUke", + "beregnetAntallTimerPrUke", + "sistLoennsendring", + "sistStillingsendring", + "bruksperiode", + "gyldighetsperiode", + "sporingsinformasjon" +}) +@Schema(description = "Arbeidsavtale/ansettelsesdetaljer for forenklet oppgjørsordning arbeidsforhold") +public class ForenkletOppgjoersordningArbeidsavtale extends Arbeidsavtale { + + public static final String TYPE = "Forenklet"; + + @Override + @JsonIgnore + public String getType() { + return TYPE; + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/FrilanserArbeidsavtale.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/FrilanserArbeidsavtale.java new file mode 100644 index 00000000000..db5a22093c9 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/FrilanserArbeidsavtale.java @@ -0,0 +1,40 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +@Data +@NoArgsConstructor +@SuperBuilder +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@JsonPropertyOrder({ + "arbeidstidsordning", + "ansettelsesform", + "yrke", + "stillingsprosent", + "antallTimerPrUke", + "beregnetAntallTimerPrUke", + "sistLoennsendring", + "sistStillingsendring", + "bruksperiode", + "gyldighetsperiode", + "sporingsinformasjon" +}) +@Schema(description = "Arbeidsavtale/ansettelsesdetaljer for frilanser arbeidsforhold", allOf = Arbeidsavtale.class) +public class FrilanserArbeidsavtale extends Arbeidsavtale { + + public static final String TYPE = "Frilanser"; + + @Override + @JsonIgnore + public String getType() { + return TYPE; + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Gyldighetsperiode.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Gyldighetsperiode.java new file mode 100644 index 00000000000..35a6cd3690f --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Gyldighetsperiode.java @@ -0,0 +1,21 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@NoArgsConstructor +@SuperBuilder +@EqualsAndHashCode(callSuper = true) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class Gyldighetsperiode extends Periode { + + @Override + @SuppressWarnings({"pmd:ConsecutiveLiteralAppends", "pmd:ConsecutiveAppendsShouldReuse", "fb-contrib:UCPM_USE_CHARACTER_PARAMETERIZED_METHOD", "pmd:AppendCharacterWithChar"}) + public String toString() { + return "Gyldighetsperiode{" + "fom=" + getFomAsString() + ", tom=" + getTomAsString() + "}"; + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/MaritimArbeidsavtale.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/MaritimArbeidsavtale.java new file mode 100644 index 00000000000..4f1a78bcf12 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/MaritimArbeidsavtale.java @@ -0,0 +1,54 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@JsonPropertyOrder({ + "fartsomraade", + "skipsregister", + "fartoeystype", + "arbeidstidsordning", + "ansettelsesform", + "yrke", + "stillingsprosent", + "antallTimerPrUke", + "beregnetAntallTimerPrUke", + "sistLoennsendring", + "sistStillingsendring", + "bruksperiode", + "gyldighetsperiode", + "sporingsinformasjon" +}) +@Schema(description = "Arbeidsavtale/ansettelsesdetaljer for maritime arbeidsforhold", allOf = Arbeidsavtale.class) +public class MaritimArbeidsavtale extends Arbeidsavtale { + + public static final String TYPE = "Maritim"; + + @Schema(description = "Fartsområde (kodeverk: Fartsomraader)", example = "utenriks") + private String fartsomraade; + + @Schema(description = "Skipsregister (kodeverk: Skipsregistre)", example = "nis") + private String skipsregister; + + @Schema(description = "Skipstype (kodeverk: Skipstyper)", example = "turist") + private String skipstype; + + @Override + @JsonIgnore + public String getType() { + return TYPE; + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/OpplysningspliktigArbeidsgiver.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/OpplysningspliktigArbeidsgiver.java new file mode 100644 index 00000000000..b565cae50b8 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/OpplysningspliktigArbeidsgiver.java @@ -0,0 +1,28 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@NoArgsConstructor +@SuperBuilder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = Organisasjon.class, name = "Organisasjon"), + @JsonSubTypes.Type(value = Person.class, name = "Person") +}) +@Schema(description = "Informasjon om opplysningspliktig eller arbeidsgiver (organisasjon eller person)", oneOf = {Organisasjon.class, Person.class}) +@SuppressWarnings("squid:S1610") +public abstract class OpplysningspliktigArbeidsgiver { + + public abstract String getType(); +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/OpplysningspliktigArbeidsgiverType.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/OpplysningspliktigArbeidsgiverType.java new file mode 100644 index 00000000000..5c97c433b06 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/OpplysningspliktigArbeidsgiverType.java @@ -0,0 +1,6 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +public interface OpplysningspliktigArbeidsgiverType { + + String getType(); +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/OrdinaerArbeidsavtale.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/OrdinaerArbeidsavtale.java new file mode 100644 index 00000000000..42cd8b828d9 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/OrdinaerArbeidsavtale.java @@ -0,0 +1,40 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +@Data +@NoArgsConstructor +@SuperBuilder +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@JsonPropertyOrder({ + "arbeidstidsordning", + "ansettelsesform", + "yrke", + "stillingsprosent", + "antallTimerPrUke", + "beregnetAntallTimerPrUke", + "sistLoennsendring", + "sistStillingsendring", + "bruksperiode", + "gyldighetsperiode", + "sporingsinformasjon" +}) +@Schema(description = "Arbeidsavtale/ansettelsesdetaljer for ordinære arbeidsforhold", allOf = Arbeidsavtale.class) +public class OrdinaerArbeidsavtale extends Arbeidsavtale { + + public static final String TYPE = "Ordinaer"; + + @Override + @JsonIgnore + public String getType() { + return TYPE; + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Organisasjon.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Organisasjon.java new file mode 100644 index 00000000000..440607caed7 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Organisasjon.java @@ -0,0 +1,34 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@EqualsAndHashCode(callSuper = true) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonPropertyOrder({ + "type", + "organisasjonsnummer" +}) +@Schema(description = "Informasjon om organisasjon (arbeidsgiver/opplysningspliktig)") +public class Organisasjon extends OpplysningspliktigArbeidsgiver implements OpplysningspliktigArbeidsgiverType { + + @Schema(description = "Organisasjonsnummer fra Enhetsregisteret", example = "987654321") + private String organisasjonsnummer; + + @Override + @JsonIgnore + public String getType() { + return this.getClass().getSimpleName(); + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Periode.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Periode.java new file mode 100644 index 00000000000..087f903a9e8 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Periode.java @@ -0,0 +1,67 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import no.nav.registre.testnorge.levendearbeidsforhold.util.JavaTimeUtil; + +import java.time.LocalDate; + +@Data +@NoArgsConstructor +@SuperBuilder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonPropertyOrder({ + "fom", + "tom" +}) +@Schema(description = "Inneholder informasjon om periode") +public class Periode { + + private LocalDate fom; + + private LocalDate tom; + + @JsonIgnore + public LocalDate getFom() { + return fom; + } + + @JsonIgnore + public LocalDate getTom() { + return tom; + } + + @JsonProperty("fom") + @Schema(description = "Fra-og-med-dato for periode, format (ISO-8601): yyyy-MM-dd", example = "2014-07-01") + public String getFomAsString() { + return JavaTimeUtil.toString(fom); + } + + @JsonProperty("fom") + public void setFomAsString(String fom) { + this.fom = JavaTimeUtil.toLocalDate(fom); + } + + @JsonProperty("tom") + @Schema(description = "Til-og-med-dato for periode, format (ISO-8601): yyyy-MM-dd", example = "2015-12-31") + public String getTomAsString() { + return JavaTimeUtil.toString(tom); + } + + @JsonProperty("tom") + public void setTomAsString(String tom) { + this.tom = JavaTimeUtil.toLocalDate(tom); + } + + @Override + @SuppressWarnings({"pmd:ConsecutiveLiteralAppends", "pmd:ConsecutiveAppendsShouldReuse", "fb-contrib:UCPM_USE_CHARACTER_PARAMETERIZED_METHOD", "pmd:AppendCharacterWithChar"}) + public String toString() { + return "Periode{" + "fom=" + getFomAsString() + ", tom=" + getTomAsString() + "}"; + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/PermisjonPermittering.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/PermisjonPermittering.java new file mode 100644 index 00000000000..93f4898e9f6 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/PermisjonPermittering.java @@ -0,0 +1,42 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonPropertyOrder({ + "permisjonPermitteringId", + "periode", + "prosent", + "type", + "sporingsinformasjon" +}) +@Schema(description = "Informasjon om permisjon eller permittering") +public class PermisjonPermittering { + + @Schema(description = "Id fra opplysningspliktig", example = "123-xyz") + private String permisjonPermitteringId; + + private Periode periode; + + @Schema(description = "Prosent for permisjon eller permittering", example = "50.5") + private Double prosent; + + @Schema(description = "Permisjon-/permitteringstype (kodeverk: PermisjonsOgPermitteringsBeskrivelse)", example = "permisjonMedForeldrepenger") + private String type; + + @Schema(description = "Varslingskode (kodeverk: Varslingskode_5fAa-registeret) - benyttes hvis permisjon/permittering er lukket maskinelt") + private String varslingskode; + + @Schema(description = "Informasjon om opprettelse og endring av objekt") + private Sporingsinformasjon sporingsinformasjon; +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Person.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Person.java new file mode 100644 index 00000000000..bc79a98d9a8 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Person.java @@ -0,0 +1,36 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@NoArgsConstructor +@SuperBuilder +@EqualsAndHashCode(callSuper = true) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonPropertyOrder({ + "type", + "offentligIdent", + "aktoerId" +}) +@Schema(description = "Informasjon om person (arbeidstaker/arbeidsgiver/opplysningspliktig)") +public class Person extends OpplysningspliktigArbeidsgiver implements Persontype { + + @Schema(description = "Gjeldende offentlig ident", example = "31126700000") + private String offentligIdent; + + @Schema(description = "Aktør-id", example = "1234567890") + private String aktoerId; + + @Override + @JsonIgnore + public String getType() { + return this.getClass().getSimpleName(); + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Persontype.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Persontype.java new file mode 100644 index 00000000000..516b09ee60f --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Persontype.java @@ -0,0 +1,6 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +public interface Persontype { + + String getType(); +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/README.md b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/README.md new file mode 100644 index 00000000000..3df8cc38724 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/README.md @@ -0,0 +1,6 @@ +# /Domain.v1 + +Denne mappen inneholder alle klassene for å hente ut og parse +dokumentene av skjermaet fra [GET](https://aareg-services-q2.intern.dev.nav.no/swagger-ui/index.html#/arbeidstaker/finnArbeidsforholdPrArbeidstaker_1) +og [PUT](https://aareg-vedlikehold-q2.dev.intern.nav.no/swagger-ui/index.html#/arbeidsforhold/endreArbeidsforhold) +endepunktene til AA-reg. Klassene er en eksakt kopi av [AA-reg sin modell](https://github.com/navikt/aareg-services/tree/main/src/main/java/no/nav/aareg/services/provider/rs/api/contract/arbeidsforhold/v1) diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Sporingsinformasjon.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Sporingsinformasjon.java new file mode 100644 index 00000000000..a5f3dc789eb --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Sporingsinformasjon.java @@ -0,0 +1,87 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import no.nav.registre.testnorge.levendearbeidsforhold.util.JavaTimeUtil; + +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonPropertyOrder({ + "opprettetTidspunkt", + "opprettetAv", + "opprettetKilde", + "opprettetKildereferanse", + "endretTidspunkt", + "endretAv", + "endretKilde", + "endretKildereferanse" +}) +@Schema(description = "Informasjon om opprettelse og endring av objekt. MERK: Skal IKKE eksponeres i selvbetjeningssonen (SBS).") +public class Sporingsinformasjon { + + private LocalDateTime opprettetTidspunkt; + + @Schema(description = "Brukernavn for opprettelse", example = "srvappserver") + private String opprettetAv; + + @Schema(description = "Kilde for opprettelse", example = "EDAG") + private String opprettetKilde; + + @Schema(description = "Kildereferanse for opprettelse", example = "22a26849-aeef-4b81-9174-e238c11e1081") + private String opprettetKildereferanse; + + private LocalDateTime endretTidspunkt; + + @Schema(description = "Brukernavn for endring", example = "Z990693") + private String endretAv; + + @Schema(description = "Kilde for endring", example = "AAREG") + private String endretKilde; + + @Schema(description = "Kildereferanse for endring", example = "referanse-fra-kilde") + private String endretKildereferanse; + + @JsonIgnore + public LocalDateTime getOpprettetTidspunkt() { + return opprettetTidspunkt; + } + + @JsonIgnore + public LocalDateTime getEndretTidspunkt() { + return endretTidspunkt; + } + + @JsonProperty("opprettetTidspunkt") + @Schema(description = "Tidspunkt for opprettelse, format (ISO-8601): yyyy-MM-dd'T'HH:mm[:ss[.SSSSSSSSS]]", example = "2018-09-19T12:10:58.059") + public String getOpprettetTidspunktAsString() { + return JavaTimeUtil.toString(opprettetTidspunkt); + } + + @JsonProperty("opprettetTidspunkt") + public void setOpprettetTidspunktAsString(String opprettetTidspunkt) { + this.opprettetTidspunkt = JavaTimeUtil.toLocalDateTime(opprettetTidspunkt); + } + + @JsonProperty("endretTidspunkt") + @Schema(description = "Tidspunkt for endring, format (ISO-8601): yyyy-MM-dd'T'HH:mm[:ss[.SSSSSSSSS]]", example = "2018-09-19T12:11:20.79") + public String getEndretTidspunktAsString() { + return JavaTimeUtil.toString(endretTidspunkt); + } + + @JsonProperty("endretTidspunkt") + public void setEndretTidspunktAsString(String endretTidspunkt) { + this.endretTidspunkt = JavaTimeUtil.toLocalDateTime(endretTidspunkt); + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Utenlandsopphold.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Utenlandsopphold.java new file mode 100644 index 00000000000..a6d61508b40 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Utenlandsopphold.java @@ -0,0 +1,56 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import no.nav.registre.testnorge.levendearbeidsforhold.util.JavaTimeUtil; + +import java.time.YearMonth; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonPropertyOrder({ + "periode", + "landkode", + "rapporteringsperiode", + "sporingsinformasjon" +}) +@Schema(description = "Informasjon om utenlandsopphold") +public class Utenlandsopphold { + + @Schema(description = "Periode for utenlandsopphold") + private Periode periode; + + @Schema(description = "Landkode (kodeverk: Landkoder)", example = "JPN") + private String landkode; + + private YearMonth rapporteringsperiode; + + @Schema(description = "Informasjon om opprettelse og endring av objekt") + private Sporingsinformasjon sporingsinformasjon; + + @JsonIgnore + public YearMonth getRapporteringsperiode() { + return rapporteringsperiode; + } + + @JsonProperty("rapporteringsperiode") + @Schema(description = "Rapporteringsperiode for utenlandsopphold, format (ISO-8601): yyyy-MM", example = "2017-12") + public String getRapporteringsperiodeAsString() { + return JavaTimeUtil.toString(rapporteringsperiode); + } + + @JsonProperty("rapporteringsperiode") + public void setRapporteringsperiodeAsString(String rapporteringsperiode) { + this.rapporteringsperiode = JavaTimeUtil.toYearMonth(rapporteringsperiode); + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Varsel.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Varsel.java new file mode 100644 index 00000000000..a7df47d6b06 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Varsel.java @@ -0,0 +1,29 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonPropertyOrder({ + "entitet", + "type", + "varslingskode" +}) +@Schema(description = "Informasjon om varsel") +public class Varsel { + + @Schema(description = "Entitet for varsel") + private Varselentitet entitet; + + @Schema(description = "Varslingskode (kodeverk: Varslingskode_5fAa-registeret)") + private String varslingskode; +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Varselentitet.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Varselentitet.java new file mode 100644 index 00000000000..73abecce19e --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/domain/v1/Varselentitet.java @@ -0,0 +1,11 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.domain.v1; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "Entitet for varsel") +public enum Varselentitet { + + ARBEIDSFORHOLD, + ANSETTELSESPERIODE, + PERMISJONPERMITTERING +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/listener/DoedsfallListener.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/listener/DoedsfallListener.java new file mode 100644 index 00000000000..fa123f50c6f --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/listener/DoedsfallListener.java @@ -0,0 +1,49 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.listener; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import no.nav.person.pdl.leesah.Personhendelse; +import no.nav.registre.testnorge.levendearbeidsforhold.service.ArbeidsforholdService; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class DoedsfallListener { + private static final String doedsfallTopic = "pdl.leesah-v1"; + private static final String oensketHendelsestype = "DOEDSFALL_V1"; + + private final ArbeidsforholdService arbeidsforholdService; + + /** + * Lytter til og konsumerer hendelser fra Kafka hendelsesstrømmen på et gitt topic. + * Behandler også alle hendelser av en gitt hendelsestype. + * @param hendelser - Alle konsumerte hendelser fra hendelsesstrømmen + */ + @KafkaListener(topics = doedsfallTopic) + public void getHendelser(List> hendelser) { + for (ConsumerRecord hendelse: hendelser){ + + var aktoerId = hendelse.key().split("\u001A")[1]; + var hendelsestype = hendelse.value().get(4).toString(); + + if (validerHendelse(hendelsestype)){ + arbeidsforholdService.arbeidsforholdService(aktoerId); + } + } + } + + /** + * Validerer om hendelsen er av ønsket hendelsestype + * @param personhendelse - Hendelse/opplysningstype, f.eks: FOLKEREGISTERIDENTIFIKATOR_V1, NAVN_V1, SIVILSTAND_V1, etc. + * @return true dersom det er av ønsket hendelsestype, false hvis ikke + */ + private Boolean validerHendelse(String personhendelse) { + return personhendelse.equals(oensketHendelsestype); + } +} + diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/service/ArbeidsforholdService.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/service/ArbeidsforholdService.java new file mode 100644 index 00000000000..4822569213e --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/service/ArbeidsforholdService.java @@ -0,0 +1,51 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.service; + + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import no.nav.registre.testnorge.levendearbeidsforhold.consumers.AaregConsumer; +import no.nav.registre.testnorge.levendearbeidsforhold.domain.v1.Arbeidsforhold; + +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ArbeidsforholdService { + + private final AaregConsumer aaregConsumer; + private final String sluttAarsaksKode = "arbeidstakerHarSagtOppSelv"; + private final String varslingsKode = "NAVEND"; + + public void arbeidsforholdService(String aktoerId) { + List arbeidsforholdListe = hentArbeidsforhold(aktoerId); + if (!arbeidsforholdListe.isEmpty()) { + arbeidsforholdListe.forEach( + arbeidsforhold -> { + if (arbeidsforhold.getAnsettelsesperiode().getPeriode().getTom() == null){ + endreArbeidsforhold(arbeidsforhold); + } + } + ); + } + } + + public List hentArbeidsforhold(String ident) { + return aaregConsumer.hentArbeidsforhold(ident); + } + + public void endreArbeidsforhold(Arbeidsforhold arbeidsforhold){ + + arbeidsforhold.getAnsettelsesperiode().getPeriode().setTom(LocalDate.now()); + arbeidsforhold.getAnsettelsesperiode().setSluttaarsak(sluttAarsaksKode); + arbeidsforhold.getAnsettelsesperiode().setVarslingskode(varslingsKode); + arbeidsforhold.getArbeidsavtaler().forEach( + arbeidsavtale -> arbeidsavtale.setStillingsprosent(null)); + + aaregConsumer.endreArbeidsforhold(arbeidsforhold); + } +} diff --git a/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/util/JavaTimeUtil.java b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/util/JavaTimeUtil.java new file mode 100644 index 00000000000..589b1396fc6 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/java/no/nav/registre/testnorge/levendearbeidsforhold/util/JavaTimeUtil.java @@ -0,0 +1,38 @@ +package no.nav.registre.testnorge.levendearbeidsforhold.util; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; + +import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE; +import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME; + +public class JavaTimeUtil { + + private static final String YEAR_MONTH_PATTERN = "yyyy-MM"; + + public static String toString(LocalDate date) { + return date != null ? date.format(ISO_LOCAL_DATE) : null; + } + + public static String toString(LocalDateTime dateTime) { + return dateTime != null ? dateTime.format(ISO_LOCAL_DATE_TIME) : null; + } + + public static String toString(YearMonth yearMonth) { + return yearMonth != null ? yearMonth.format(DateTimeFormatter.ofPattern(YEAR_MONTH_PATTERN)) : null; + } + + public static LocalDate toLocalDate(String date) { + return date != null ? LocalDate.parse(date, ISO_LOCAL_DATE) : null; + } + + public static LocalDateTime toLocalDateTime(String dateTime) { + return dateTime != null ? LocalDateTime.parse(dateTime, ISO_LOCAL_DATE_TIME) : null; + } + + public static YearMonth toYearMonth(String yearMonth) { + return yearMonth != null ? YearMonth.parse(yearMonth, DateTimeFormatter.ofPattern(YEAR_MONTH_PATTERN)) : null; + } +} \ No newline at end of file diff --git a/apps/levende-arbeidsforhold-service/src/main/resources/application-dev.yml b/apps/levende-arbeidsforhold-service/src/main/resources/application-dev.yml new file mode 100644 index 00000000000..7df314e0e54 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/resources/application-dev.yml @@ -0,0 +1,2 @@ +kafka: + group-id: testnav-levende-arbeidsforhold-service diff --git a/apps/levende-arbeidsforhold-service/src/main/resources/application.yml b/apps/levende-arbeidsforhold-service/src/main/resources/application.yml new file mode 100644 index 00000000000..0e91756066c --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/resources/application.yml @@ -0,0 +1,55 @@ +AAD_ISSUER_URI: https://login.microsoftonline.com/62366534-1ec3-4962-8869-9b5535279d0b + +spring: + main: + banner-mode: off + application: + name: testnav-levende-arbeidsforhold-service + version: 1 + description: App for å avslutte arbeidsforhold ved dødsfall. + security: + oauth2: + client: + resourceserver: + aad: + issuer-uri: ${AAD_ISSUER_URI}/v2.0 + jwk-set-uri: ${AAD_ISSUER_URI}/discovery/v2.0/keys + accepted-audience: ${azure.app.client.id}, api://${azure.app.client.id} + cloud: + vault: + enabled: false + kafka: + consumer: + group-id: testnav-levende-arbeidsforhold-service +springdoc: + swagger-ui: + disable-swagger-default-url: true + url: /v3/api-docs + +management: + endpoints: + enabled-by-default: true + web: + base-path: /internal + exposure.include: prometheus,heapdump,health + path-mapping: + prometheus: metrics + endpoint: + prometheus.enabled: true + heapdump.enabled: true + prometheus: + metrics: + export: + enabled: true +server: + servlet: + encoding: + charset: UTF-8 + error: + include-message: always +consumers: + testnav-aareg-proxy: + name: testnav-aareg-proxy + namespace: dolly + url: https://testnav-aareg-proxy.dev-fss-pub.nais.io + cluster: dev-fss diff --git a/apps/levende-arbeidsforhold-service/src/main/resources/logback-spring.xml b/apps/levende-arbeidsforhold-service/src/main/resources/logback-spring.xml new file mode 100644 index 00000000000..9583ff2d427 --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/main/resources/logback-spring.xml @@ -0,0 +1,40 @@ + + + + + + + true + 256 + 10280 + 20 + ^sun\.reflect\..*\.invoke + ^net\.sf\.cglib\.proxy\.MethodProxy\.invoke + java\.util\.concurrent\..* + org\.apache\.catalina\..* + org\.apache\.coyote\..* + org\.apache\.tomcat\..* + + + + + + + + + + + + + %d{HH:mm:ss.SSS} | %5p | %logger{25} | %m%n + + utf8 + + + + + + + + + \ No newline at end of file diff --git a/apps/levende-arbeidsforhold-service/src/test/java/no/nav/registre/testnorge/levendearbeidsforhold/ApplicationContextTest.java b/apps/levende-arbeidsforhold-service/src/test/java/no/nav/registre/testnorge/levendearbeidsforhold/ApplicationContextTest.java new file mode 100644 index 00000000000..4c6f896873e --- /dev/null +++ b/apps/levende-arbeidsforhold-service/src/test/java/no/nav/registre/testnorge/levendearbeidsforhold/ApplicationContextTest.java @@ -0,0 +1,20 @@ +package no.nav.registre.testnorge.levendearbeidsforhold; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.test.context.ActiveProfiles; + +@SpringBootTest +@ActiveProfiles("test") +class ApplicationContextTest { + + @MockBean + public JwtDecoder jwtDecoder; + + @Test + @SuppressWarnings("java:S2699") + void load_app_context() { + } +} diff --git a/examples/reactive-rest-example/src/main/java/no/nav/testnav/examples/reactiverestexample/repository/CarEntity.java b/examples/reactive-rest-example/src/main/java/no/nav/testnav/examples/reactiverestexample/repository/CarEntity.java index ee096aed4dd..dda3cadb6d7 100644 --- a/examples/reactive-rest-example/src/main/java/no/nav/testnav/examples/reactiverestexample/repository/CarEntity.java +++ b/examples/reactive-rest-example/src/main/java/no/nav/testnav/examples/reactiverestexample/repository/CarEntity.java @@ -49,3 +49,5 @@ public boolean isNew() { + + diff --git a/libs/avro-schema/src/main/avro/Personhendelse.avsc b/libs/avro-schema/src/main/avro/Personhendelse.avsc new file mode 100644 index 00000000000..24c9a2cd292 --- /dev/null +++ b/libs/avro-schema/src/main/avro/Personhendelse.avsc @@ -0,0 +1,81 @@ +{ + "type" : "record", + "name" : "Personhendelse", + "namespace" : "no.nav.person.pdl.leesah", + "fields" : [ { + "name" : "hendelseId", + "type" : "string" + }, { + "name" : "personidenter", + "type" : { + "type" : "array", + "items" : "string" + } + }, { + "name" : "master", + "type" : "string" + }, { + "name" : "opprettet", + "type" : { + "type" : "long", + "logicalType" : "timestamp-millis" + } + }, { + "name" : "opplysningstype", + "type" : "string" + }, { + "name" : "endringstype", + "type" : { + "type" : "enum", + "name" : "Endringstype", + "symbols" : [ "OPPRETTET", "KORRIGERT", "ANNULLERT", "OPPHOERT" ] + } + }, { + "name" : "tidligereHendelseId", + "type" : [ "null", "string" ], + "default" : null + }, { + "name" : "navn", + "type" : [ "null", { + "type" : "record", + "name" : "Navn", + "namespace" : "no.nav.person.pdl.leesah.navn", + "fields" : [ { + "name" : "fornavn", + "type" : "string" + }, { + "name" : "mellomnavn", + "type" : [ "null", "string" ], + "default" : null + }, { + "name" : "etternavn", + "type" : "string" + }, { + "name" : "forkortetNavn", + "type" : [ "null", "string" ], + "default" : null + }, { + "name" : "originaltNavn", + "type" : [ "null", { + "type" : "record", + "name" : "OriginaltNavn", + "fields" : [ { + "name" : "fornavn", + "type" : [ "null", "string" ], + "default" : null + }, { + "name" : "mellomnavn", + "type" : [ "null", "string" ], + "default" : null + }, { + "name" : "etternavn", + "type" : [ "null", "string" ], + "default" : null + } ] + } ], + "default" : null + } ] + } ], + "default" : null + } ] +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 21529e70cd8..17a285fbdea 100644 --- a/settings.gradle +++ b/settings.gradle @@ -95,6 +95,7 @@ includeBuild './apps/inntektsmelding-service' includeBuild './apps/jenkins-batch-status-service' includeBuild './apps/joark-dokument-service' includeBuild './apps/kodeverk-service' +includeBuild './apps/levende-arbeidsforhold-service' includeBuild './apps/miljoer-service' includeBuild './apps/oppsummeringsdokument-service' includeBuild './apps/organisasjon-bestilling-service' @@ -112,6 +113,7 @@ includeBuild './apps/person-organisasjon-tilgang-service' includeBuild './apps/person-search-service' includeBuild './apps/person-service' includeBuild './apps/profil-api' +includeBuild './apps/skattekort-service' includeBuild './apps/sykemelding-api' includeBuild './apps/synt-sykemelding-api' includeBuild './apps/synt-vedtakshistorikk-service' @@ -122,8 +124,6 @@ includeBuild './apps/tilbakemelding-api' includeBuild './apps/tps-messaging-service' includeBuild './apps/udi-stub' includeBuild './apps/varslinger-service' -includeBuild './apps/dollystatus' -includeBuild './apps/skattekort-service' includeBuild './xsd/arbeidsforhold-xsd'