Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

191 lesen und schreiben der stimmzettelumschläge #663

Open
wants to merge 13 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs/src/services/ergebnismeldung-service/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ Welche Aktionen für die jeweiligen Dokumente bereits in einem Wahllokal vollzog

#### Erfassung der Stimmabgaben mit Wahlschein
Im Wählerverzeichnis wird bei einer Wahl durch den Schriftführer über den sogenannten "Stimmabgabevermerk" vermerkt,
wenn ein Wahlberechtigter mit Wahlschein seinen Stimmzettel in die Urne gelegt hat.
wenn ein Wahlberechtigter mit Wahlschein seinen Stimmzettel in die Urne im Wahllokal gelegt hat.
Über das Schreiben und Lesen der Wahlscheine kann die aktuelle Anzahl an Stimmabgabevermerken an WLS übermittelt
bzw. ausgelesen werden.

### Erfassung der Anzahl an Stimmzettelumschlaegen
Bei der Briefwahl zählt der Wahlvorstand die Stimmzettelumschlaege.
Über das Schreiben und Lesen der Stimmzettelumschlaege kann die aktuelle Anzahl eines Wahlbezirkes für eine Wahl an WLS
übermittelt bzw. ausgelesen werden.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
id: add authorities ergebnismeldung stimmzettelumschlaege
author: dragonfly28
realm: ${SSO_REALM}
changes:
- addRole:
name: Ergebnismeldung_BUSINESSACTION_GetStimmzettelumschlaege
clientRole: true
clientId: ${SSO_CLIENT_ID}
- assignRoleToGroup:
group: allErgebnismeldungAuthorities
role: Ergebnismeldung_BUSINESSACTION_GetStimmzettelumschlaege
clientId: ${SSO_CLIENT_ID}

- addRole:
name: Ergebnismeldung_BUSINESSACTION_PostStimmzettelumschlaege
clientRole: true
clientId: ${SSO_CLIENT_ID}
- assignRoleToGroup:
group: allErgebnismeldungAuthorities
role: Ergebnismeldung_BUSINESSACTION_PostStimmzettelumschlaege
clientId: ${SSO_CLIENT_ID}

- addRole:
name: Ergebnismeldung_READ_Stimmzettelumschlaege
clientRole: true
clientId: ${SSO_CLIENT_ID}
- assignRoleToGroup:
group: allErgebnismeldungAuthorities
role: Ergebnismeldung_READ_Stimmzettelumschlaege
clientId: ${SSO_CLIENT_ID}
- addRole:
name: Ergebnismeldung_WRITE_Stimmzettelumschlaege
clientRole: true
clientId: ${SSO_CLIENT_ID}
- assignRoleToGroup:
group: allErgebnismeldungAuthorities
role: Ergebnismeldung_WRITE_Stimmzettelumschlaege
clientId: ${SSO_CLIENT_ID}
- addRole:
name: Ergebnismeldung_DELETE_Stimmzettelumschlaege
clientRole: true
clientId: ${SSO_CLIENT_ID}
- assignRoleToGroup:
group: allErgebnismeldungAuthorities
role: Ergebnismeldung_DELETE_Stimmzettelumschlaege
clientId: ${SSO_CLIENT_ID}
dragonfly28 marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions stack/keycloak/migration/keycloak-changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ includes:
- path: add-authorities-ergebnismeldung-awerte.yml
- path: add-authorities-ergebnismeldung-status.yml
- path: add-authorities-ergebnismeldung-wahlscheine.yml
- path: add-authorities-ergebnismeldung-stimmzettelumschlaege.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege;

import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Stimmzettelumschlaege {

@EmbeddedId
private BezirkUndWahlID bezirkUndWahlID;

private LocalDateTime urneneroeffnungsUhrzeit;

@NotNull
private long anzahlWaehler;

private Long anzahlWaehler2;
dragonfly28 marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege;

import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID;
import java.util.Optional;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.repository.CrudRepository;
import org.springframework.security.access.prepost.PreAuthorize;

@PreAuthorize("hasAuthority('Ergebnismeldung_READ_Stimmzettelumschlaege')")
public interface StimmzettelumschlaegeRepository extends CrudRepository<Stimmzettelumschlaege, BezirkUndWahlID> {

String CACHE = "STIMMZETTELUMSCHLAEGE_CACHE";

@Override
Iterable<Stimmzettelumschlaege> findAll();

@Override
@Cacheable(value = CACHE, key = "#p0")
Optional<Stimmzettelumschlaege> findById(BezirkUndWahlID bezirkUndWahlID);

@Override
@CachePut(value = CACHE, key = "#p0.bezirkUndWahlID")
@PreAuthorize("hasAuthority('Ergebnismeldung_WRITE_Stimmzettelumschlaege')")
<S extends Stimmzettelumschlaege> S save(S stimmzettelumschlaege);

@Override
@CacheEvict(value = CACHE, key = "#p0")
@PreAuthorize("hasAuthority('Ergebnismeldung_DELETE_Stimmzettelumschlaege')")
void deleteById(BezirkUndWahlID bezirkUndWahlID);

@Override
@CacheEvict(value = CACHE, key = "#p0.bezirkUndWahlID")
@PreAuthorize("hasAuthority('Ergebnismeldung_DELETE_Stimmzettelumschlaege')")
void delete(Stimmzettelumschlaege entity);

@Override
@CacheEvict(value = CACHE, allEntries = true)
@PreAuthorize("hasAuthority('Ergebnismeldung_DELETE_Stimmzettelumschlaege')")
void deleteAll(Iterable<? extends Stimmzettelumschlaege> entities);

@Override
@CacheEvict(value = CACHE, allEntries = true)
@PreAuthorize("hasAuthority('Ergebnismeldung_DELETE_Stimmzettelumschlaege')")
void deleteAll();
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ public class ExceptionConstants {

public static ExceptionDataWrapper GETAWERTE_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("600",
"getAWerte: Parameter unvollstaendig");

public static ExceptionDataWrapper GETAWERTE_UNSAVEABLE = new ExceptionDataWrapper("601",
"getAWerte: Die AWerte vom Client konnten nicht gespeichert werden.");

public static final ExceptionDataWrapper GET_STATUS_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("606", "getStatus: Parameter unvollstaendig");
public static final ExceptionDataWrapper POST_STATUS_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("607", "postStatus: Parameter unvollstaendig");
public static final ExceptionDataWrapper STATUS_UNSAVEABLE = new ExceptionDataWrapper("622", "postStatus: Der Status konnte nicht gespeichert werden.");
public static final ExceptionDataWrapper GET_STATUS_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("606",
"getStatus: Parameter unvollstaendig");
public static final ExceptionDataWrapper POST_STATUS_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("607",
"postStatus: Parameter unvollstaendig");
public static final ExceptionDataWrapper STATUS_UNSAVEABLE = new ExceptionDataWrapper("622",
"postStatus: Der Status konnte nicht gespeichert werden.");
dragonfly28 marked this conversation as resolved.
Show resolved Hide resolved

public static final ExceptionDataWrapper GET_WAHLSCHEINE_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("612",
"getWahlscheine: Parameter unvollstaendig");
Expand All @@ -24,6 +26,13 @@ public class ExceptionConstants {
public static final ExceptionDataWrapper WAHLSCHEINE_UNSAVEABLE = new ExceptionDataWrapper("618",
"postWahlscheine: Die Wahlscheine konnten nicht gespeichert werden.");

public static final ExceptionDataWrapper GET_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("610",
"getStimmzettelumschlaege: Parameter unvollstaendig");
public static final ExceptionDataWrapper POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("611",
"postStimmzettelumschlaege: Parameter unvollstaendig");
public static final ExceptionDataWrapper STIMMZETTELUMSCHLAEGE_UNSAVEABLE = new ExceptionDataWrapper("619",
"postStimmzettelumschlaege: Die Stimmzettelumschlaege konnten nicht gespeichert werden.");

public static final ExceptionDataWrapper KOMMUNIKATIONSFEHLER_MIT_MONITORING = new ExceptionDataWrapper("100",
"Bei der Kommunikation mit dem MonitoringService kam es zu einem Fehler.");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.rest.stimmzettelumschlaege;

import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.rest.AbstractController;
import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege.StimmzettelumschlaegeService;
import de.muenchen.oss.wahllokalsystem.wls.common.exception.rest.model.WlsExceptionDTO;
import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/businessActions/stimmzettelumschlaege")
@RequiredArgsConstructor
public class StimmzettelumschlaegeController extends AbstractController {

private final StimmzettelumschlaegeService stimmzettelumschlaegeService;
private final StimmzettelumschlaegeDTOMapper stimmzettelumschlaegeDTOMapper;

@Operation(description = "Lesen der Anzahl an Stimmzettelumschlaegen eines Wahlbezirkes für eine Wahl")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200", description = "Es existiert eine Anzahl an Stimmzettelumschlaegen",
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = StimmzettelumschlaegeDTO.class)) }
),
@ApiResponse(
responseCode = "204", description = "Es existiert keine Anzahl an Stimmzettelumschlaegen zu den entsprechenden Kriterien",
content = { @Content() }
),
@ApiResponse(
responseCode = "400", description = "Validierung der Anfrage war nicht erfolgreich",
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = WlsExceptionDTO.class)) }
),
@ApiResponse(
responseCode = "500", description = "Probleme bei der Verarbeitung der Anfrage",
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = WlsExceptionDTO.class)) }
)
}
)
@GetMapping("{wahlID}/{wahlbezirkID}")
public ResponseEntity<StimmzettelumschlaegeDTO> getStimmzettelumschlaege(@PathVariable("wahlID") final String wahlID,
@PathVariable("wahlbezirkID") final String wahlbezirkID) {
val stimmzettelumschlaege = stimmzettelumschlaegeService.getStimmzettelumschlaege(new BezirkUndWahlID(wahlID, wahlbezirkID));
return okWithBodyOrNoContent(stimmzettelumschlaege.map(stimmzettelumschlaegeDTOMapper::toDTO));
}

@Operation(description = "Setzen der Anzahl an Stimmzettelumschlaegen eines Wahlbezirkes für eine Wahl")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200", description = "Anzahl an Stimmzettelumschlaegen erfolgreich gespeichert"
),
@ApiResponse(
responseCode = "400", description = "Validierung der Anfrage war nicht erfolgreich",
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = WlsExceptionDTO.class)) }
),
@ApiResponse(
responseCode = "500", description = "Probleme bei der Verarbeitung der Anfrage",
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = WlsExceptionDTO.class)) }
)
}
)
@PostMapping("{wahlID}/{wahlbezirkID}")
public void postStimmzettelumschlaege(@PathVariable("wahlID") final String wahlID, @PathVariable("wahlbezirkID") final String wahlbezirkID,
@RequestBody final StimmzettelumschlaegeDTO stimmzettelumschlaegeDTO) {
stimmzettelumschlaegeService.setStimmzettelumschlaege(new BezirkUndWahlID(wahlID, wahlbezirkID),
stimmzettelumschlaegeDTOMapper.toModel(stimmzettelumschlaegeDTO));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.rest.stimmzettelumschlaege;

import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;

public record StimmzettelumschlaegeDTO(@NotNull BezirkUndWahlID bezirkUndWahlID,
LocalDateTime urneneroeffnungsUhrzeit,
@NotNull long anzahlWaehler,
Long anzahlWaehler2) {
dragonfly28 marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.rest.stimmzettelumschlaege;

import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege.StimmzettelumschlaegeModel;
import org.mapstruct.Mapper;

@Mapper
public interface StimmzettelumschlaegeDTOMapper {

StimmzettelumschlaegeDTO toDTO(StimmzettelumschlaegeModel stimmzettelumschlaegeModel);

StimmzettelumschlaegeModel toModel(StimmzettelumschlaegeDTO stimmzettelumschlaegeDTO);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege;

import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;

public record StimmzettelumschlaegeModel(@NotNull BezirkUndWahlID bezirkUndWahlID,
LocalDateTime urneneroeffnungsUhrzeit,
@NotNull long anzahlWaehler,
long anzahlWaehler2) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege;

import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.Stimmzettelumschlaege;
import org.mapstruct.Mapper;

@Mapper
public interface StimmzettelumschlaegeModelMapper {
dragonfly28 marked this conversation as resolved.
Show resolved Hide resolved

StimmzettelumschlaegeModel toModel(Stimmzettelumschlaege entity);

Stimmzettelumschlaege toEntity(StimmzettelumschlaegeModel model);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege;

import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.StimmzettelumschlaegeRepository;
import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.exception.ExceptionConstants;
import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionFactory;
import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.parameters.P;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
@Slf4j
public class StimmzettelumschlaegeService {

private final StimmzettelumschlaegeRepository stimmzettelumschlaegeRepository;
private final StimmzettelumschlaegeModelMapper stimmzettelumschlaegeModelMapper;
private final StimmzettelumschlaegeValidator stimmzettelumschlaegeValidator;
private final ExceptionFactory exceptionFactory;

@PreAuthorize("hasAuthority('Ergebnismeldung_BUSINESSACTION_GetStimmzettelumschlaege')")
public Optional<StimmzettelumschlaegeModel> getStimmzettelumschlaege(final BezirkUndWahlID id) {
log.info("#getStimmzettelumschlaege");

stimmzettelumschlaegeValidator.validBezirkUndWahlIdOrThrow(id,
exceptionFactory.createFachlicheWlsException(ExceptionConstants.GET_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG));

val stimmzettelumschlaegeFromRepo = stimmzettelumschlaegeRepository.findById(id);
return stimmzettelumschlaegeFromRepo.map(stimmzettelumschlaegeModelMapper::toModel);
}

@PreAuthorize(
"hasAuthority('Ergebnismeldung_BUSINESSACTION_PostStimmzettelumschlaege')"
+ "and @bezirkIdPermisionEvaluator.tokenUserBezirkIdMatches(#param?.getWahlbezirkID(), authentication)"
)
public void setStimmzettelumschlaege(@P("param") final BezirkUndWahlID id, final StimmzettelumschlaegeModel stimmzettelumschlaege) {
log.info("#postStimmzettelumschlaege");

stimmzettelumschlaegeValidator.validBezirkUndWahlIdOrThrow(id,
exceptionFactory.createFachlicheWlsException(ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG));
stimmzettelumschlaegeValidator.validStimmzettelumschlaegeOrThrow(stimmzettelumschlaege);

try {
stimmzettelumschlaegeRepository.save(stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaege));
} catch (Exception e) {
log.error("#postStimmzettelumschlaege unsaveable:", e);
throw exceptionFactory.createTechnischeWlsException(ExceptionConstants.STIMMZETTELUMSCHLAEGE_UNSAVEABLE);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege;

import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.exception.ExceptionConstants;
import de.muenchen.oss.wahllokalsystem.wls.common.exception.FachlicheWlsException;
import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionFactory;
import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class StimmzettelumschlaegeValidator {

private final ExceptionFactory exceptionFactory;

public void validBezirkUndWahlIdOrThrow(final BezirkUndWahlID bezirkUndWahlId, final FachlicheWlsException exceptionOnInvalid)
throws FachlicheWlsException {
if (bezirkUndWahlId == null || StringUtils.isBlank(bezirkUndWahlId.getWahlID()) || StringUtils.isBlank(bezirkUndWahlId.getWahlbezirkID())) {
throw exceptionOnInvalid;
}
}

public void validStimmzettelumschlaegeOrThrow(final StimmzettelumschlaegeModel stimmzettelumschlaegeModel) {
dragonfly28 marked this conversation as resolved.
Show resolved Hide resolved
if (stimmzettelumschlaegeModel == null) {
throw exceptionFactory.createFachlicheWlsException(ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG);
}
}
dragonfly28 marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading