Skip to content

Commit 02bb18a

Browse files
authored
Feature/bestilling fra skattekort service (#3573)
* Add new applications in config.yml #deploy-skattekort-service Updated the inbound rules in the skattekort-service config.yml to include additional applications: dolly-backend-dev, dolly-frontend-dev, and team-dolly-lokal-app. This allows better control and access for these new applications. * Add skattekort support to bestilling This update introduces skattekort processing in the bestilling service. New functionalities include mapping skattekort status, handling skattekort requests, and updating related configurations. * Add skattekort error handling in bestilling #deploy-test-dolly-backend Enhance bestilling service to process skattekort errors. Modify the handling of skattekort statuses, returning detailed error reports when errors occur. Integrate new dependency imports and clean up deprecated code. * Add null check for skattekort in bestilling #deploy-test-dolly-backend Enhanced bestilling service by filtering out null skattekort entries before processing. This prevents null pointers and ensures only valid skattekort data moves forward in the pipeline. Added necessary imports to support the new check. * Add SkattekortRequestDTO and mapping strategy #deploy-test-dolly-backend Introduced SkattekortRequestDTO and integrated it into existing packages. Added a new mapping strategy to map DTO fields for skattekort service requests. This enhances the flexibility and extensibility of our code related to skattekort functionality. * Add SkattekortRequestDTO and mapping strategy #deploy-test-dolly-backend Introduced SkattekortRequestDTO and integrated it into existing packages. Added a new mapping strategy to map DTO fields for skattekort service requests. This enhances the flexibility and extensibility of our code related to skattekort functionality. * Rename Arbeidsgiver class to ArbeidsgiverSkatt #deploy-skattekort-service #deploy-test-dolly-backend Renamed the Arbeidsgiver class to ArbeidsgiverSkatt for clarity and improved domain specificity. Updated all occurrences and references in DTOs to reflect the change. This ensures consistency across the codebase and improves readability. * Rename Arbeidsgiver to ArbeidsgiverSkatt #deploy-skattekort-service Replaced all occurrences of "Arbeidsgiver" with "ArbeidsgiverSkatt" in service, utility, and DTO classes. This change aligns the codebase with updated naming conventions and ensures consistency across different modules.
1 parent a68106e commit 02bb18a

25 files changed

+380
-16
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package no.nav.dolly.bestilling.skattekort;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import ma.glasnost.orika.MapperFacade;
5+
import no.nav.dolly.bestilling.ClientFuture;
6+
import no.nav.dolly.bestilling.ClientRegister;
7+
import no.nav.dolly.domain.jpa.BestillingProgress;
8+
import no.nav.dolly.domain.resultset.RsDollyUtvidetBestilling;
9+
import no.nav.dolly.domain.resultset.dolly.DollyPerson;
10+
import no.nav.dolly.util.TransactionHelperService;
11+
import no.nav.testnav.libs.dto.skattekortservice.v1.SkattekortRequestDTO;
12+
import org.springframework.stereotype.Service;
13+
import reactor.core.publisher.Flux;
14+
15+
import java.util.List;
16+
import java.util.stream.Collectors;
17+
18+
import static java.util.Objects.nonNull;
19+
20+
@Service
21+
@RequiredArgsConstructor
22+
public class SkattekortClient implements ClientRegister {
23+
24+
private final SkattekortConsumer skattekortConsumer;
25+
private final MapperFacade mapperFacade;
26+
private final TransactionHelperService transactionHelperService;
27+
28+
@Override
29+
public Flux<ClientFuture> gjenopprett(RsDollyUtvidetBestilling bestilling, DollyPerson dollyPerson,
30+
BestillingProgress progress, boolean isOpprettEndre) {
31+
32+
return Flux.just(bestilling)
33+
.filter(bestilling1 -> nonNull(bestilling1.getSkattekort()))
34+
.map(bestilling1 -> mapperFacade.map(bestilling1.getSkattekort(), SkattekortRequestDTO.class))
35+
.doOnNext(skattekort ->
36+
skattekort.getArbeidsgiver()
37+
.forEach(arbeidsgiver -> arbeidsgiver.getArbeidstaker()
38+
.forEach(arbeidstaker -> arbeidstaker.setArbeidstakeridentifikator(dollyPerson.getIdent()))))
39+
.flatMap(skattkort -> Flux.fromIterable(skattkort.getArbeidsgiver())
40+
.map(arbeidsgiver -> SkattekortRequestDTO.builder()
41+
.arbeidsgiver(List.of(arbeidsgiver))
42+
.build())
43+
.flatMap(skattekortConsumer::sendSkattekort)
44+
.collect(Collectors.joining(",")))
45+
.map(status -> futurePersist(progress, status));
46+
}
47+
48+
private ClientFuture futurePersist(BestillingProgress progress, String status) {
49+
50+
return () -> {
51+
transactionHelperService.persister(progress, BestillingProgress::setSkattekortStatus, status);
52+
return progress;
53+
};
54+
}
55+
56+
@Override
57+
public void release(List<String> identer) {
58+
// Deletion is not yet supported
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package no.nav.dolly.bestilling.skattekort;
2+
3+
import no.nav.dolly.bestilling.skattekort.command.SkattekortPostCommand;
4+
import no.nav.dolly.config.Consumers;
5+
import no.nav.testnav.libs.dto.skattekortservice.v1.SkattekortRequestDTO;
6+
import no.nav.testnav.libs.securitycore.domain.ServerProperties;
7+
import no.nav.testnav.libs.standalone.servletsecurity.exchange.TokenExchange;
8+
import org.springframework.stereotype.Service;
9+
import org.springframework.web.reactive.function.client.WebClient;
10+
import reactor.core.publisher.Flux;
11+
12+
@Service
13+
public class SkattekortConsumer {
14+
15+
private final WebClient webClient;
16+
private final ServerProperties serverProperties;
17+
private final TokenExchange tokenExchange;
18+
19+
public SkattekortConsumer(WebClient.Builder webClientBuilder, Consumers consumers, TokenExchange tokenExchange) {
20+
21+
this.serverProperties = consumers.getTestnavSkattekortService();
22+
this.webClient = webClientBuilder
23+
.baseUrl(serverProperties.getUrl())
24+
.build();
25+
this.tokenExchange = tokenExchange;
26+
}
27+
28+
public Flux<String> sendSkattekort(SkattekortRequestDTO skattekortRequestDTO) {
29+
30+
return tokenExchange.exchange(serverProperties)
31+
.flatMapMany(token -> new SkattekortPostCommand(webClient, skattekortRequestDTO, token.getTokenValue()).call());
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package no.nav.dolly.bestilling.skattekort.command;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import no.nav.dolly.errorhandling.ErrorStatusDecoder;
5+
import no.nav.dolly.util.RequestHeaderUtil;
6+
import no.nav.testnav.libs.dto.skattekortservice.v1.IdentifikatorForEnhetEllerPerson;
7+
import no.nav.testnav.libs.dto.skattekortservice.v1.SkattekortRequestDTO;
8+
import no.nav.testnav.libs.reactivecore.utils.WebClientFilter;
9+
import no.nav.testnav.libs.securitycore.config.UserConstant;
10+
import org.springframework.web.reactive.function.client.WebClient;
11+
import org.springframework.web.reactive.function.client.WebClientResponseException;
12+
import reactor.core.publisher.Flux;
13+
import reactor.core.publisher.Mono;
14+
import reactor.util.retry.Retry;
15+
16+
import java.time.Duration;
17+
import java.util.concurrent.Callable;
18+
19+
import static no.nav.dolly.domain.CommonKeysAndUtils.HEADER_NAV_CALL_ID;
20+
import static no.nav.dolly.domain.CommonKeysAndUtils.HEADER_NAV_CONSUMER_ID;
21+
import static no.nav.dolly.util.TokenXUtil.getUserJwt;
22+
import static org.apache.commons.lang3.StringUtils.isNotBlank;
23+
import static org.springframework.http.HttpHeaders.ACCEPT;
24+
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
25+
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
26+
27+
@RequiredArgsConstructor
28+
public class SkattekortPostCommand implements Callable<Flux<String>> {
29+
30+
private static final String SKATTEKORT_URL = "/api/v1/skattekort";
31+
private static final String CONSUMER = "Dolly";
32+
33+
private final WebClient webClient;
34+
private final SkattekortRequestDTO request;
35+
private final String token;
36+
37+
@Override
38+
public Flux<String> call() {
39+
40+
return webClient.post().uri(uriBuilder -> uriBuilder
41+
.path(SKATTEKORT_URL)
42+
.build())
43+
.header(ACCEPT, APPLICATION_JSON_VALUE)
44+
.header(HEADER_NAV_CALL_ID, RequestHeaderUtil.getNavCallId())
45+
.header(HEADER_NAV_CONSUMER_ID, CONSUMER)
46+
.header(AUTHORIZATION, "Bearer " + token)
47+
.header(UserConstant.USER_HEADER_JWT, getUserJwt())
48+
.bodyValue(request)
49+
.retrieve()
50+
.bodyToFlux(String.class)
51+
.map(status -> getArbeidsgiverAndYear(request) + ErrorStatusDecoder.encodeStatus(status))
52+
.doOnError(WebClientFilter::logErrorMessage)
53+
.retryWhen(Retry.backoff(3, Duration.ofSeconds(5))
54+
.filter(WebClientFilter::is5xxException))
55+
.onErrorResume(throwable -> throwable instanceof WebClientResponseException.NotFound,
56+
throwable -> Mono.empty());
57+
}
58+
59+
private static String getArbeidsgiverAndYear(SkattekortRequestDTO skattekort) {
60+
61+
return skattekort.getArbeidsgiver().stream()
62+
.findFirst()
63+
.map(arbeidsgiver -> String.format("%s+%s:",
64+
getArbeidsgiver(arbeidsgiver.getArbeidsgiveridentifikator()),
65+
arbeidsgiver.getArbeidstaker().stream()
66+
.findFirst()
67+
.map(arbeidstaker -> arbeidstaker.getInntektsaar().toString())
68+
.orElse("inntektsår")))
69+
.orElse("organisasjonsnummer+inntektsår:");
70+
}
71+
72+
private static String getArbeidsgiver(IdentifikatorForEnhetEllerPerson identifikator) {
73+
74+
return isNotBlank(identifikator.getOrganisasjonsnummer()) ?
75+
identifikator.getOrganisasjonsnummer() :
76+
identifikator.getPersonidentifikator();
77+
}
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package no.nav.dolly.bestilling.skattekort.mapper;
2+
3+
import ma.glasnost.orika.MapperFactory;
4+
import no.nav.dolly.domain.resultset.skattekort.SkattekortRequestDTO;
5+
import no.nav.dolly.mapper.MappingStrategy;
6+
import org.springframework.stereotype.Component;
7+
8+
@Component
9+
public class SkattekortMappingStrategy implements MappingStrategy {
10+
11+
@Override
12+
public void register(MapperFactory factory) {
13+
factory.classMap(SkattekortRequestDTO.class, no.nav.testnav.libs.dto.skattekortservice.v1.SkattekortRequestDTO.class)
14+
.mapNulls(false)
15+
.field("arbeidsgiverSkatt", "arbeidsgiver")
16+
.register();
17+
}
18+
}

apps/dolly-backend/src/main/java/no/nav/dolly/config/Consumers.java

+1
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,5 @@ public class Consumers {
4444
private ServerProperties testnavSyntSykemeldingApi;
4545
private ServerProperties testnavTpsMessagingService;
4646
private ServerProperties testnavUdistubProxy;
47+
private ServerProperties testnavSkattekortService;
4748
}

apps/dolly-backend/src/main/java/no/nav/dolly/domain/jpa/BestillingProgress.java

+3
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ public class BestillingProgress implements Serializable {
118118
@Column(name = "ARBEIDSPLASSENCV_STATUS")
119119
private String arbeidsplassenCVStatus;
120120

121+
@Column(name = "SKATTEKORT_STATUS")
122+
private String skattekortStatus;
123+
121124
@Column(name = "master")
122125
@Enumerated(EnumType.STRING)
123126
private Master master;

apps/dolly-backend/src/main/java/no/nav/dolly/domain/resultset/BestilteKriterier.java

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import no.nav.dolly.domain.resultset.pensjon.PensjonData;
2222
import no.nav.dolly.domain.resultset.sigrunstub.RsLignetInntekt;
2323
import no.nav.dolly.domain.resultset.sigrunstub.RsPensjonsgivendeForFolketrygden;
24+
import no.nav.dolly.domain.resultset.skattekort.SkattekortRequestDTO;
2425
import no.nav.dolly.domain.resultset.skjerming.RsSkjerming;
2526
import no.nav.dolly.domain.resultset.sykemelding.RsSykemelding;
2627
import no.nav.dolly.domain.resultset.tpsmessagingservice.RsTpsMessaging;
@@ -59,4 +60,5 @@ public class BestilteKriterier {
5960
private RsSkjerming skjerming;
6061
private RsSykemelding sykemelding;
6162
private ArbeidsplassenCVDTO arbeidsplassenCV;
63+
private SkattekortRequestDTO skattekort;
6264
}

apps/dolly-backend/src/main/java/no/nav/dolly/domain/resultset/RsDollyBestilling.java

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import no.nav.dolly.domain.resultset.pensjon.PensjonData;
2323
import no.nav.dolly.domain.resultset.sigrunstub.RsLignetInntekt;
2424
import no.nav.dolly.domain.resultset.sigrunstub.RsPensjonsgivendeForFolketrygden;
25+
import no.nav.dolly.domain.resultset.skattekort.SkattekortRequestDTO;
2526
import no.nav.dolly.domain.resultset.skjerming.RsSkjerming;
2627
import no.nav.dolly.domain.resultset.sykemelding.RsSykemelding;
2728
import no.nav.dolly.domain.resultset.tpsmessagingservice.RsTpsMessaging;
@@ -75,6 +76,7 @@ public class RsDollyBestilling {
7576
private BankkontoData bankkonto;
7677
private RsSkjerming skjerming;
7778
private ArbeidsplassenCVDTO arbeidsplassenCV;
79+
private SkattekortRequestDTO skattekort;
7880

7981
public List<RsAareg> getAareg() {
8082
if (isNull(aareg)) {

apps/dolly-backend/src/main/java/no/nav/dolly/domain/resultset/SystemTyper.java

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public enum SystemTyper {
3737
SIGRUNSTUB("Skatteinntekt grunnlag (SIGRUN)"),
3838
SIGRUN_LIGNET("Lignet skatteinntekt (Sigrunstub)"),
3939
SIGRUN_PENSJONSGIVENDE("Pensjonsgivende inntekt (Sigrunstub)"),
40+
SKATTEKORT("Skattekort (SOKOS)"),
4041
SKJERMINGSREGISTER("Skjermingsregisteret"),
4142
SYKEMELDING("NAV sykemelding"),
4243
TPS_MESSAGING("Meldinger til TPS"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package no.nav.dolly.domain.resultset.skattekort;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
import no.nav.testnav.libs.dto.skattekortservice.v1.ArbeidsgiverSkatt;
8+
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
12+
import static java.util.Objects.isNull;
13+
14+
@Data
15+
@Builder
16+
@NoArgsConstructor
17+
@AllArgsConstructor
18+
public class SkattekortRequestDTO {
19+
20+
private List<ArbeidsgiverSkatt> arbeidsgiverSkatt;
21+
22+
public List<ArbeidsgiverSkatt> getArbeidsgiverSkatt() {
23+
24+
if (isNull(arbeidsgiverSkatt)) {
25+
26+
arbeidsgiverSkatt = new ArrayList<>();
27+
}
28+
return arbeidsgiverSkatt;
29+
}
30+
}

apps/dolly-backend/src/main/java/no/nav/dolly/elastic/ElasticBestilling.java

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import no.nav.dolly.domain.resultset.sykemelding.RsSykemelding;
2626
import no.nav.dolly.domain.resultset.udistub.model.RsUdiPerson;
2727
import no.nav.testnav.libs.data.arbeidsplassencv.v1.ArbeidsplassenCVDTO;
28+
import no.nav.testnav.libs.dto.skattekortservice.v1.SkattekortRequestDTO;
2829
import org.springframework.data.annotation.Id;
2930
import org.springframework.data.annotation.Transient;
3031
import org.springframework.data.domain.Persistable;
@@ -94,6 +95,8 @@ public boolean isNew() {
9495
@Field
9596
private ArbeidsplassenCVDTO arbeidsplassenCV;
9697
@Field
98+
private SkattekortRequestDTO skattekort;
99+
@Field
97100
private List<String> identer;
98101

99102
@Transient
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package no.nav.dolly.mapper;
2+
3+
import lombok.AccessLevel;
4+
import lombok.NoArgsConstructor;
5+
import no.nav.dolly.domain.jpa.BestillingProgress;
6+
import no.nav.dolly.domain.resultset.RsStatusRapport;
7+
8+
import java.util.Collection;
9+
import java.util.HashMap;
10+
import java.util.HashSet;
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.Set;
14+
15+
import static java.util.Collections.emptyList;
16+
import static no.nav.dolly.domain.resultset.SystemTyper.SKATTEKORT;
17+
import static no.nav.dolly.mapper.AbstractRsStatusMiljoeIdentForhold.decodeMsg;
18+
import static org.apache.commons.lang3.StringUtils.isNotBlank;
19+
20+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
21+
public final class BestillingSkattekortStatusMapper {
22+
23+
public static List<RsStatusRapport> buildSkattekortStatusMap(List<BestillingProgress> progressList) {
24+
25+
// status org+year ident
26+
Map<String, Map<String, Set<String>>> errorEnvIdents = new HashMap<>();
27+
28+
progressList.forEach(progress -> {
29+
if (isNotBlank(progress.getSkattekortStatus()) && isNotBlank(progress.getIdent())) {
30+
var entries = progress.getSkattekortStatus().split(",");
31+
for (var entry : entries) {
32+
var orgYear = entry.split(":")[0];
33+
var status = entry.split(":")[1];
34+
if (errorEnvIdents.containsKey(status)) {
35+
if (errorEnvIdents.get(status).containsKey(orgYear)) {
36+
errorEnvIdents.get(status).get(orgYear).add(progress.getIdent());
37+
} else {
38+
errorEnvIdents.get(status).put(orgYear, new HashSet<>(Set.of(progress.getIdent())));
39+
}
40+
} else {
41+
errorEnvIdents.put(status, new HashMap<>(Map.of(orgYear, new HashSet<>(Set.of(progress.getIdent())))));
42+
}
43+
}
44+
}
45+
});
46+
47+
if (errorEnvIdents.isEmpty()) {
48+
return emptyList();
49+
50+
} else {
51+
if (errorEnvIdents.entrySet().stream()
52+
.allMatch(entry -> entry.getKey().equals("Skattekort lagret"))) {
53+
54+
return errorEnvIdents.values().stream()
55+
.map(entry -> RsStatusRapport.builder()
56+
.id(SKATTEKORT)
57+
.navn(SKATTEKORT.getBeskrivelse())
58+
.statuser(List.of(RsStatusRapport.Status.builder()
59+
.melding("OK")
60+
.identer(entry.values().stream()
61+
.map(orgYear -> orgYear.stream().toList())
62+
.flatMap(Collection::stream)
63+
.distinct()
64+
.toList())
65+
.build()))
66+
.build())
67+
.toList();
68+
69+
} else {
70+
71+
return errorEnvIdents.entrySet().stream()
72+
.filter(entry -> !entry.getKey().equals("Skattekort lagret"))
73+
.map(entry -> RsStatusRapport.builder()
74+
.id(SKATTEKORT)
75+
.navn(SKATTEKORT.getBeskrivelse())
76+
.statuser(entry.getValue().entrySet().stream()
77+
.map(orgYear -> RsStatusRapport.Status.builder()
78+
.melding(formatMelding(orgYear.getKey(), entry.getKey()))
79+
.identer(orgYear.getValue().stream().toList())
80+
.build())
81+
.toList())
82+
.build())
83+
.toList();
84+
}
85+
}
86+
}
87+
88+
private static String formatMelding(String orgYear, String melding) {
89+
90+
return "FEIL: organisasjon:%s, inntektsår:%s, melding:%s".formatted(
91+
orgYear.split("\\+")[0],
92+
orgYear.split("\\+")[1],
93+
decodeMsg(melding));
94+
}
95+
}

0 commit comments

Comments
 (0)