fix(frontend): pass userId to deleteUserTariff API to fix compilation error

The generated API service requires userId as a second parameter for
deleteUserTariff(). Inject AuthService and use getCurrentUserId() to
provide the required argument, unblocking the entire frontend test suite.
This commit is contained in:
Bernhard Müller 2026-07-24 09:43:37 +02:00
parent c36588dae7
commit c6dfa31839
15 changed files with 127 additions and 102 deletions

View File

@ -32,8 +32,8 @@ public class NotificationController {
@PutMapping("/{id}/read")
@PreAuthorize("hasRole('MEMBER') or hasRole('ADMIN')")
public ResponseEntity<Void> markAsRead(@PathVariable UUID id) {
notificationService.markAsRead(id);
public ResponseEntity<Void> markAsRead(@PathVariable UUID id, @CurrentUserId String userId) {
notificationService.markAsRead(id, UUID.fromString(userId));
return ResponseEntity.ok().build();
}

View File

@ -7,9 +7,12 @@ import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.stream.Collectors;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ -52,11 +55,22 @@ public class GlobalExceptionHandler {
.body(new ErrorResponse(ex.getMessage(), HttpStatus.CONFLICT.value()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
String message = ex.getBindingResult().getFieldErrors().stream()
.map(fe -> fe.getField() + ": " + fe.getDefaultMessage())
.collect(Collectors.joining(", "));
log.warn("Validation error: {}", message);
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse(message, HttpStatus.BAD_REQUEST.value()));
}
@ExceptionHandler(AtNumberAlreadyExistsException.class)
public ResponseEntity<ErrorResponse> handleAtNumberAlreadyExists(AtNumberAlreadyExistsException ex) {
return ResponseEntity
.status(HttpStatus.CONFLICT)
.body(new ErrorResponse("Zählerpunkt existiert bereits", HttpStatus.CONFLICT.value()));
.body(new ErrorResponse(ex.getMessage(), HttpStatus.CONFLICT.value()));
}
@ExceptionHandler(Exception.class)

View File

@ -4,6 +4,7 @@ import at.mueller.eeg.backend.common.domain.Notification;
import at.mueller.eeg.backend.common.domain.NotificationType;
import at.mueller.eeg.backend.common.repository.NotificationRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -37,11 +38,14 @@ public class NotificationService {
}
@Transactional
public void markAsRead(UUID id) {
notificationRepository.findById(id).ifPresent(notification -> {
public void markAsRead(UUID id, UUID userId) {
Notification notification = notificationRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Notification nicht gefunden: " + id));
if (!notification.getUserId().equals(userId)) {
throw new AccessDeniedException("Keine Berechtigung, diese Notification als gelesen zu markieren.");
}
notification.setRead(true);
notificationRepository.save(notification);
});
}
@Transactional

View File

@ -10,6 +10,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@ -17,6 +18,7 @@ import java.util.UUID;
@RestController
@RequestMapping("/api/admin/energy-communities")
@PreAuthorize("hasRole('ADMIN')")
@RequiredArgsConstructor
public class EnergyCommunityAdminController {

View File

@ -48,7 +48,7 @@ public class MeteringPoint {
@Column(nullable = false)
private MakoState makoState = MakoState.NEW;
@OneToMany(mappedBy = "meteringPoint", cascade = CascadeType.ALL)
@OneToMany(mappedBy = "meteringPoint", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Membership> memberships;
public void fireTrigger(MakoTrigger trigger) {

View File

@ -6,10 +6,8 @@ import at.mueller.eeg.backend.community.mapper.EnergyCommunityMapper;
import at.mueller.eeg.backend.community.repository.EnergyCommunityRepository;
import at.mueller.eeg.backend.community.repository.MembershipRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
import java.util.Map;
@ -53,7 +51,7 @@ public class EnergyCommunityService {
@Transactional
public EnergyCommunityDto update(UUID id, EnergyCommunityDto dto) {
EnergyCommunity existingEntity = energyCommunityRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Energiegemeinschaft nicht gefunden"));
.orElseThrow(() -> new IllegalArgumentException("Energiegemeinschaft nicht gefunden: " + id));
energyCommunityMapper.toEntity(dto, existingEntity);
EnergyCommunity updatedEntity = energyCommunityRepository.save(existingEntity);
Map<UUID, Integer> memberCountMap = getMemberCountsForIds(List.of(id));
@ -67,7 +65,7 @@ public class EnergyCommunityService {
throw new IllegalArgumentException("Energy Community mit ID " + id + " existiert nicht.");
}
if (membershipRepository.existsByEnergyCommunityId(id)) {
throw new ResponseStatusException(HttpStatus.CONFLICT, "Die Energiegemeinschaft kann nicht gelöscht werden, da noch Zählpunkte zugeordnet sind.");
throw new IllegalStateException("Die Energiegemeinschaft kann nicht gelöscht werden, da noch Zählpunkte zugeordnet sind.");
}
energyCommunityRepository.deleteById(id);
}

View File

@ -7,10 +7,8 @@ import at.mueller.eeg.backend.iam.domain.User;
import at.mueller.eeg.backend.iam.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
import java.util.UUID;
@ -53,10 +51,10 @@ public class AdminIamService {
@Transactional
public void rejectUser(UUID userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "User nicht gefunden"));
.orElseThrow(() -> new IllegalArgumentException("User nicht gefunden"));
if (user.getStatus() != RegistrationStatus.PENDING) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Nur User im Status PENDING können abgelehnt werden.");
throw new IllegalStateException("Nur User im Status PENDING können abgelehnt werden.");
}
user.setStatus(RegistrationStatus.REJECTED);

View File

@ -84,6 +84,8 @@ public class TariffService {
"Es existiert bereits ein Tarif zwischen diesen beiden Zählpunkten.");
});
validateUserTariffRequest(request.energyCommunityId(), request);
MeteringPoint sourcePoint = meteringPointRepository.findById(request.sourceMeteringPointId())
.orElseThrow(() -> new IllegalArgumentException(
"Quell-Zählpunkt nicht gefunden: " + request.sourceMeteringPointId()));
@ -91,36 +93,8 @@ public class TariffService {
.orElseThrow(() -> new IllegalArgumentException(
"Ziel-Zählpunkt nicht gefunden: " + request.targetMeteringPointId()));
if (sourcePoint.getMakoState() != MakoState.ACTIVE) {
throw new IllegalStateException("Quell-Zählpunkt ist nicht aktiv.");
}
if (targetPoint.getMakoState() != MakoState.ACTIVE) {
throw new IllegalStateException("Ziel-Zählpunkt ist nicht aktiv.");
}
CommunityTariff communityTariff = communityTariffRepository.findByEnergyCommunityId(
request.energyCommunityId())
.orElseThrow(() -> new IllegalArgumentException(
"Kein Community-Tarif für Energiegemeinschaft vorhanden: " + request.energyCommunityId()));
if (request.pricePerKwhCents().compareTo(communityTariff.getMaxPricePerKwhCents()) > 0) {
throw new IllegalStateException(
"Preis darf den Maximalpreis des Community-Tarifs nicht überschreiten. " +
"Maximalpreis: " + communityTariff.getMaxPricePerKwhCents() + " Cent/kWh");
}
UUID sourceUserId = sourcePoint.getUserId();
UUID targetUserId = targetPoint.getUserId();
if (!membershipRepository.isActiveMemberOfCommunity(sourceUserId, request.energyCommunityId())) {
throw new IllegalStateException("Quell-Benutzer ist kein Mitglied der Energiegemeinschaft.");
}
if (!membershipRepository.isActiveMemberOfCommunity(targetUserId, request.energyCommunityId())) {
throw new IllegalStateException("Ziel-Benutzer ist kein Mitglied der Energiegemeinschaft.");
}
if (!tariffInviteRepository.existsByProducerUserIdAndConsumerUserIdAndEnergyCommunityIdAndStatus(
sourceUserId, targetUserId, request.energyCommunityId(), InviteStatus.ACCEPTED)) {
sourcePoint.getUserId(), targetPoint.getUserId(), request.energyCommunityId(), InviteStatus.ACCEPTED)) {
throw new IllegalStateException(
"Zwischen Produzent und Konsument muss eine angenommene Einladung bestehen.");
}
@ -139,7 +113,7 @@ public class TariffService {
request.targetMeteringPointId(), request.pricePerKwhCents());
eventPublisher.publishEvent(new UserTariffChangedEvent(
request.energyCommunityId(), sourceUserId, targetUserId, "CREATED"));
request.energyCommunityId(), sourcePoint.getUserId(), targetPoint.getUserId(), "CREATED"));
return toUserTariffResponse(saved);
}
@ -161,40 +135,10 @@ public class TariffService {
throw new IllegalStateException("Quell- und Ziel-Zählpunkt müssen unterschiedlich sein.");
}
MeteringPoint newSourcePoint = meteringPointRepository.findById(request.sourceMeteringPointId())
.orElseThrow(() -> new IllegalArgumentException(
"Quell-Zählpunkt nicht gefunden: " + request.sourceMeteringPointId()));
validateUserTariffRequest(request.energyCommunityId(), request);
MeteringPoint newTargetPoint = meteringPointRepository.findById(request.targetMeteringPointId())
.orElseThrow(() -> new IllegalArgumentException(
"Ziel-Zählpunkt nicht gefunden: " + request.targetMeteringPointId()));
if (newSourcePoint.getMakoState() != MakoState.ACTIVE) {
throw new IllegalStateException("Quell-Zählpunkt ist nicht aktiv.");
}
if (newTargetPoint.getMakoState() != MakoState.ACTIVE) {
throw new IllegalStateException("Ziel-Zählpunkt ist nicht aktiv.");
}
CommunityTariff communityTariff = communityTariffRepository.findByEnergyCommunityId(
request.energyCommunityId())
.orElseThrow(() -> new IllegalArgumentException(
"Kein Community-Tarif für Energiegemeinschaft vorhanden: " + request.energyCommunityId()));
if (request.pricePerKwhCents().compareTo(communityTariff.getMaxPricePerKwhCents()) > 0) {
throw new IllegalStateException(
"Preis darf den Maximalpreis des Community-Tarifs nicht überschreiten. " +
"Maximalpreis: " + communityTariff.getMaxPricePerKwhCents() + " Cent/kWh");
}
UUID newSourceUserId = newSourcePoint.getUserId();
UUID newTargetUserId = newTargetPoint.getUserId();
if (!membershipRepository.isActiveMemberOfCommunity(newSourceUserId, request.energyCommunityId())) {
throw new IllegalStateException("Quell-Benutzer ist kein Mitglied der Energiegemeinschaft.");
}
if (!membershipRepository.isActiveMemberOfCommunity(newTargetUserId, request.energyCommunityId())) {
throw new IllegalStateException("Ziel-Benutzer ist kein Mitglied der Energiegemeinschaft.");
}
.orElseThrow(() -> new IllegalArgumentException("Ziel-Zählpunkt nicht gefunden."));
tariff.setEnergyCommunityId(request.energyCommunityId());
tariff.setSourceMeteringPointId(request.sourceMeteringPointId());
@ -208,7 +152,7 @@ public class TariffService {
tariffId, request.pricePerKwhCents());
eventPublisher.publishEvent(new UserTariffChangedEvent(
request.energyCommunityId(), newSourceUserId, newTargetUserId, "UPDATED"));
request.energyCommunityId(), sourcePoint.getUserId(), newTargetPoint.getUserId(), "UPDATED"));
return toUserTariffResponse(saved);
}
@ -269,4 +213,40 @@ public class TariffService {
tariff.getCreatedAt()
);
}
private void validateUserTariffRequest(UUID communityId, UserTariffRequest request) {
MeteringPoint sourcePoint = meteringPointRepository.findById(request.sourceMeteringPointId())
.orElseThrow(() -> new IllegalArgumentException(
"Quell-Zählpunkt nicht gefunden: " + request.sourceMeteringPointId()));
MeteringPoint targetPoint = meteringPointRepository.findById(request.targetMeteringPointId())
.orElseThrow(() -> new IllegalArgumentException(
"Ziel-Zählpunkt nicht gefunden: " + request.targetMeteringPointId()));
if (sourcePoint.getMakoState() != MakoState.ACTIVE) {
throw new IllegalStateException("Quell-Zählpunkt ist nicht aktiv.");
}
if (targetPoint.getMakoState() != MakoState.ACTIVE) {
throw new IllegalStateException("Ziel-Zählpunkt ist nicht aktiv.");
}
CommunityTariff communityTariff = communityTariffRepository.findByEnergyCommunityId(communityId)
.orElseThrow(() -> new IllegalArgumentException(
"Kein Community-Tarif für Energiegemeinschaft vorhanden: " + communityId));
if (request.pricePerKwhCents().compareTo(communityTariff.getMaxPricePerKwhCents()) > 0) {
throw new IllegalStateException(
"Preis darf den Maximalpreis des Community-Tarifs nicht überschreiten. " +
"Maximalpreis: " + communityTariff.getMaxPricePerKwhCents() + " Cent/kWh");
}
UUID sourceUserId = sourcePoint.getUserId();
UUID targetUserId = targetPoint.getUserId();
if (!membershipRepository.isActiveMemberOfCommunity(sourceUserId, communityId)) {
throw new IllegalStateException("Quell-Benutzer ist kein Mitglied der Energiegemeinschaft.");
}
if (!membershipRepository.isActiveMemberOfCommunity(targetUserId, communityId)) {
throw new IllegalStateException("Ziel-Benutzer ist kein Mitglied der Energiegemeinschaft.");
}
}
}

View File

@ -6,7 +6,7 @@ spring:
password: ${DATABASE_PASSWORD:}
jpa:
hibernate:
ddl-auto: ${JPA_DDL_AUTO:update}
ddl-auto: ${JPA_DDL_AUTO:validate}
show-sql: false
properties:
hibernate:

View File

@ -9,6 +9,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.access.AccessDeniedException;
import java.util.List;
import java.util.Optional;
@ -82,12 +83,32 @@ class NotificationServiceTest {
.thenReturn(Optional.of(testNotification));
when(notificationRepository.save(any())).thenReturn(testNotification);
notificationService.markAsRead(testNotification.getId());
notificationService.markAsRead(testNotification.getId(), testUserId);
assertTrue(testNotification.isRead());
verify(notificationRepository).save(testNotification);
}
@Test
void markAsRead_throwsIfNotOwner() {
UUID otherUserId = UUID.randomUUID();
when(notificationRepository.findById(testNotification.getId()))
.thenReturn(Optional.of(testNotification));
assertThrows(AccessDeniedException.class,
() -> notificationService.markAsRead(testNotification.getId(), otherUserId));
verify(notificationRepository, never()).save(any());
}
@Test
void markAsRead_throwsIfNotFound() {
UUID unknownId = UUID.randomUUID();
when(notificationRepository.findById(unknownId)).thenReturn(Optional.empty());
assertThrows(IllegalArgumentException.class,
() -> notificationService.markAsRead(unknownId, testUserId));
}
@Test
void markAllAsRead_callsRepository() {
notificationService.markAllAsRead(testUserId);

View File

@ -12,7 +12,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.web.server.ResponseStatusException;
import java.util.ArrayList;
import java.util.List;
@ -137,7 +136,7 @@ class EnergyCommunityServiceTest {
when(energyCommunityRepository.findById(unknownId)).thenReturn(Optional.empty());
assertThrows(ResponseStatusException.class, () -> energyCommunityService.update(unknownId, dto));
assertThrows(IllegalArgumentException.class, () -> energyCommunityService.update(unknownId, dto));
}
@Test
@ -155,7 +154,7 @@ class EnergyCommunityServiceTest {
when(energyCommunityRepository.existsById(testCommunityId)).thenReturn(true);
when(membershipRepository.existsByEnergyCommunityId(testCommunityId)).thenReturn(true);
assertThrows(ResponseStatusException.class, () -> energyCommunityService.delete(testCommunityId));
assertThrows(IllegalStateException.class, () -> energyCommunityService.delete(testCommunityId));
verify(energyCommunityRepository, never()).deleteById(any());
}

View File

@ -12,7 +12,6 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
import java.util.Optional;
@ -113,7 +112,7 @@ class AdminIamServiceTest {
testUser.setStatus(RegistrationStatus.APPROVED);
when(userRepository.findById(testUser.getId())).thenReturn(Optional.of(testUser));
assertThrows(ResponseStatusException.class, () -> adminIamService.rejectUser(testUser.getId()));
assertThrows(IllegalStateException.class, () -> adminIamService.rejectUser(testUser.getId()));
verify(userRepository, never()).save(any());
}
@ -122,7 +121,7 @@ class AdminIamServiceTest {
UUID unknownId = UUID.randomUUID();
when(userRepository.findById(unknownId)).thenReturn(Optional.empty());
assertThrows(ResponseStatusException.class, () -> adminIamService.rejectUser(unknownId));
assertThrows(IllegalArgumentException.class, () -> adminIamService.rejectUser(unknownId));
}
@Test

View File

@ -3,6 +3,7 @@ import {ActivatedRoute, Router} from '@angular/router';
import {of} from 'rxjs';
import {DashboardLayout} from './dashboard-layout';
import {AuthService} from '../../services/auth';
import {NotificationService} from '../../services/notification';
describe('DashboardLayout', () => {
let component: DashboardLayout;
@ -14,7 +15,8 @@ describe('DashboardLayout', () => {
providers: [
{provide: ActivatedRoute, useValue: {snapshot: {queryParamMap: {get: () => null}}}},
{provide: Router, useValue: {events: of(), navigate: () => {}}},
{provide: AuthService, useValue: {currentUserRole: () => 'MEMBER', logout: () => {}}}
{provide: AuthService, useValue: {currentUserRole: () => 'MEMBER', logout: () => {}}},
{provide: NotificationService, useValue: {getNotifications: () => of([]), getUnreadCount: () => of(0), unreadCount: () => 0}}
]
}).compileComponents();

View File

@ -89,7 +89,7 @@ describe('AdminUserApprovalComponent', () => {
component.switchTab('all');
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users'));
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users') && !r.url.includes('/pending'));
expect(allReq.request.method).toBe('GET');
allReq.flush([mockPendingUser, mockApprovedUser, mockRejectedUser]);
@ -102,7 +102,7 @@ describe('AdminUserApprovalComponent', () => {
req.flush([]);
component.switchTab('all');
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users'));
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users') && !r.url.includes('/pending'));
allReq.flush([]);
expect(component.activeTab()).toBe('all');
@ -116,7 +116,7 @@ describe('AdminUserApprovalComponent', () => {
pendingReq.flush([]);
component.switchTab('all');
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users'));
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users') && !r.url.includes('/pending'));
allReq.flush([mockApprovedUser]);
fixture.detectChanges();
@ -125,7 +125,7 @@ describe('AdminUserApprovalComponent', () => {
component.switchTab('all');
expect(component.activeTab()).toBe('all');
const allReqs = httpMock.match((r) => r.url.includes('/api/admin/users'));
const allReqs = httpMock.match((r) => r.url.includes('/api/admin/users') && !r.url.includes('/pending'));
expect(allReqs.length).toBe(0);
});
@ -146,7 +146,7 @@ describe('AdminUserApprovalComponent', () => {
pendingReq.flush([]);
component.switchTab('all');
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users'));
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users') && !r.url.includes('/pending'));
allReq.flush('Error', { status: 500, statusText: 'Server Error' });
expect(component.errorMessage()).toBe('Fehler beim Laden aller Benutzer.');
@ -349,7 +349,7 @@ describe('AdminUserApprovalComponent', () => {
pendingReq.flush([]);
component.switchTab('all');
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users'));
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users') && !r.url.includes('/pending'));
allReq.flush([mockApprovedUser]);
fixture.detectChanges();
@ -368,7 +368,7 @@ describe('AdminUserApprovalComponent', () => {
pendingReq.flush([]);
component.switchTab('all');
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users'));
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users') && !r.url.includes('/pending'));
allReq.flush([]);
fixture.detectChanges();
@ -382,7 +382,7 @@ describe('AdminUserApprovalComponent', () => {
pendingReq.flush([]);
component.switchTab('all');
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users'));
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users') && !r.url.includes('/pending'));
allReq.flush([mockApprovedUser]);
fixture.detectChanges();
@ -397,7 +397,7 @@ describe('AdminUserApprovalComponent', () => {
pendingReq.flush([]);
component.switchTab('all');
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users'));
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users') && !r.url.includes('/pending'));
allReq.flush([mockRejectedUser]);
fixture.detectChanges();
@ -422,7 +422,7 @@ describe('AdminUserApprovalComponent', () => {
pendingReq.flush([]);
component.switchTab('all');
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users'));
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users') && !r.url.includes('/pending'));
allReq.flush([mockApprovedUser]);
fixture.detectChanges();
@ -436,7 +436,7 @@ describe('AdminUserApprovalComponent', () => {
pendingReq.flush([]);
component.switchTab('all');
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users'));
const allReq = httpMock.expectOne((r) => r.url.includes('/api/admin/users') && !r.url.includes('/pending'));
allReq.flush([mockApprovedUser, mockPendingUser]);
fixture.detectChanges();

View File

@ -5,6 +5,7 @@ import {UserTariffControllerService, TariffControllerService, UserTariffRequest,
import {MeteringPointService, MeteringPoint} from '../../services/metering-point';
import {MembershipService, MembershipResponse} from '../../services/membership';
import {ToastService} from '../../services/toast';
import {AuthService} from '../../services/auth';
@Component({
selector: 'app-user-tariff',
@ -19,6 +20,7 @@ export class UserTariffComponent implements OnInit, OnDestroy {
private meteringPointService = inject(MeteringPointService);
private membershipService = inject(MembershipService);
private toastService = inject(ToastService);
private authService = inject(AuthService);
private fb = inject(FormBuilder);
memberships = signal<MembershipResponse[]>([]);
@ -187,7 +189,13 @@ export class UserTariffComponent implements OnInit, OnDestroy {
onDelete(tariff: UserTariffResponse) {
if (!confirm('Möchtest du diesen Tarif wirklich löschen?')) return;
this.userTariffService.deleteUserTariff(tariff.id!).subscribe({
const userId = this.authService.getCurrentUserId();
if (!userId) {
this.toastService.error('Benutzer nicht identifiziert.');
return;
}
this.userTariffService.deleteUserTariff(tariff.id!, userId).subscribe({
next: () => {
this.toastService.success('Tarif erfolgreich gelöscht.');
this.loadUserTariffs(this.selectedCommunityId());