introduce-booking-module #41
@ -26,7 +26,7 @@ import java.util.UUID;
|
|||||||
import java.util.function.BinaryOperator;
|
import java.util.function.BinaryOperator;
|
||||||
|
|
||||||
import static java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
import static java.util.stream.Collectors.toMap;
|
import static java.util.stream.Collectors.joining;
|
||||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveFromPostgresDateRange;
|
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveFromPostgresDateRange;
|
||||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
|
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
|
||||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange;
|
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange;
|
||||||
@ -51,9 +51,18 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
|
|||||||
.withProp(e -> e.getDebitor().toShortString())
|
.withProp(e -> e.getDebitor().toShortString())
|
||||||
.withProp(e -> e.getValidity().asString())
|
.withProp(e -> e.getValidity().asString())
|
||||||
.withProp(HsBookingItemEntity::getCaption)
|
.withProp(HsBookingItemEntity::getCaption)
|
||||||
.withProp(HsBookingItemEntity::getResources)
|
.withProp(HsBookingItemEntity::getResourcesAsString)
|
||||||
.quotedValues(false);
|
.quotedValues(false);
|
||||||
|
|
||||||
|
private String getResourcesAsString() {
|
||||||
|
return "{ " +
|
||||||
|
(
|
||||||
|
resources.keySet().stream().sorted()
|
||||||
|
.map(k -> k + ": " + resources.get(k)))
|
||||||
|
.collect(joining(", ")
|
||||||
|
) + " }";
|
||||||
|
}
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
@ -78,11 +87,6 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
|
|||||||
@Column(columnDefinition = "resources")
|
@Column(columnDefinition = "resources")
|
||||||
private Map<String, Object> resources = new TreeMap<>();
|
private Map<String, Object> resources = new TreeMap<>();
|
||||||
|
|
||||||
public Map<String, Object> getResources() {
|
|
||||||
return resources.entrySet().stream()
|
|
||||||
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue, HsBookingItemEntity::thereIsOnlyOneValuePerKey, TreeMap::new));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValidFrom(final LocalDate validFrom) {
|
public void setValidFrom(final LocalDate validFrom) {
|
||||||
setValidity(toPostgresDateRange(validFrom, getValidTo()));
|
setValidity(toPostgresDateRange(validFrom, getValidTo()));
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,11 @@ import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.ArbitraryBook
|
|||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemPatchResource;
|
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemPatchResource;
|
||||||
import net.hostsharing.hsadminng.mapper.EntityPatcher;
|
import net.hostsharing.hsadminng.mapper.EntityPatcher;
|
||||||
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
||||||
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static java.util.Arrays.stream;
|
import static java.util.Arrays.stream;
|
||||||
|
|
||||||
@ -38,16 +39,24 @@ public class HsBookingItemEntityPatcher implements EntityPatcher<HsBookingItemPa
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Map<String, Object> objectToMap(final Object obj) {
|
static Map<String, Object> objectToMap(final Object obj) {
|
||||||
final var map = stream(obj.getClass().getDeclaredFields())
|
return stream(obj.getClass().getDeclaredFields())
|
||||||
.map(field -> {
|
.map(field -> {
|
||||||
try {
|
try {
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
return Map.entry(field.getName(), field.get(obj));
|
return new ImmutablePair<>(field.getName(), field.get(obj));
|
||||||
} catch (final IllegalAccessException exc) {
|
} catch (final IllegalAccessException exc) {
|
||||||
throw new RuntimeException(exc);
|
throw new RuntimeException(exc);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
.reduce(
|
||||||
return map;
|
new HashMap<>(),
|
||||||
|
(map, pair) -> {
|
||||||
|
map.put(pair.getKey(), pair.getValue());
|
||||||
|
return map;
|
||||||
|
},
|
||||||
|
(map1, map2) -> {
|
||||||
|
map1.putAll(map2);
|
||||||
|
return map1;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ import jakarta.persistence.*;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
|
@ -2,9 +2,7 @@ package net.hostsharing.hsadminng.hs.office.coopshares;
|
|||||||
|
|
||||||
import jakarta.persistence.EntityNotFoundException;
|
import jakarta.persistence.EntityNotFoundException;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopSharesApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopSharesApi;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionInsertResource;
|
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionInsertResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionInsertResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionResource;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.Mapper;
|
||||||
|
@ -6,7 +6,6 @@ import lombok.Getter;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||||
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
|
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
||||||
|
@ -148,6 +148,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var givenBookingItemUuid = bookingItemRepo.findAll().stream()
|
final var givenBookingItemUuid = bookingItemRepo.findAll().stream()
|
||||||
.filter(bi -> bi.getDebitor().getDebitorNumber() == 1000111)
|
.filter(bi -> bi.getDebitor().getDebitorNumber() == 1000111)
|
||||||
|
.filter(item -> item.getCaption().equals("some CloudServer"))
|
||||||
.findAny().orElseThrow().getUuid();
|
.findAny().orElseThrow().getUuid();
|
||||||
|
|
||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
@ -192,8 +193,8 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var givenBookingItemUuid = bookingItemRepo.findAll().stream()
|
final var givenBookingItemUuid = bookingItemRepo.findAll().stream()
|
||||||
.filter(bi -> bi.getDebitor().getDebitorNumber() == 1000313)
|
.filter(bi -> bi.getDebitor().getDebitorNumber() == 1000313)
|
||||||
|
.filter(item -> item.getCaption().equals("some CloudServer"))
|
||||||
.findAny().orElseThrow().getUuid();
|
.findAny().orElseThrow().getUuid();
|
||||||
generateRbacDiagramForObjectPermission(givenBookingItemUuid, "SELECT", "booking-item-of-debitor-1000313");
|
|
||||||
|
|
||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
|
@ -35,12 +35,17 @@ class HsBookingItemEntityPatcherUnitTest extends PatchUnitTestBase<
|
|||||||
private static final LocalDate PATCHED_VALID_FROM = LocalDate.parse("2022-10-30");
|
private static final LocalDate PATCHED_VALID_FROM = LocalDate.parse("2022-10-30");
|
||||||
private static final LocalDate PATCHED_VALID_TO = LocalDate.parse("2022-12-31");
|
private static final LocalDate PATCHED_VALID_TO = LocalDate.parse("2022-12-31");
|
||||||
|
|
||||||
private static final ArbitraryBookingResourcesJsonResource PATCHED_RESOURCES = new ArbitraryBookingResourcesJsonResource() {
|
private static final ArbitraryBookingResourcesJsonResource INITIAL_RESOURCES = new ArbitraryBookingResourcesJsonResource() {
|
||||||
int cpus = 2;
|
Integer cpus = 1;
|
||||||
int sddStorage = 256;
|
Integer hddStorage = 1024;
|
||||||
int hddStorage = 2048;
|
|
||||||
};
|
};
|
||||||
private static final String PATCHED_CAPTION = "caption-patched";
|
private static final ArbitraryBookingResourcesJsonResource PATCHED_RESOURCES = new ArbitraryBookingResourcesJsonResource() {
|
||||||
|
Integer cpus = 2;
|
||||||
|
Integer sddStorage = 256;
|
||||||
|
Integer hddStorage = null;
|
||||||
|
};
|
||||||
|
private static final String INITIAL_CAPTION = "initial caption";
|
||||||
|
private static final String PATCHED_CAPTION = "patched caption";
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
@ -58,8 +63,8 @@ class HsBookingItemEntityPatcherUnitTest extends PatchUnitTestBase<
|
|||||||
final var entity = new HsBookingItemEntity();
|
final var entity = new HsBookingItemEntity();
|
||||||
entity.setUuid(INITIAL_BOOKING_ITEM_UUID);
|
entity.setUuid(INITIAL_BOOKING_ITEM_UUID);
|
||||||
entity.setDebitor(TEST_DEBITOR);
|
entity.setDebitor(TEST_DEBITOR);
|
||||||
entity.setResources(objectToMap(PATCHED_RESOURCES));
|
entity.setResources(objectToMap(INITIAL_RESOURCES));
|
||||||
entity.setCaption(PATCHED_CAPTION);
|
entity.setCaption(INITIAL_CAPTION);
|
||||||
entity.setValidity(Range.closedInfinite(GIVEN_VALID_FROM));
|
entity.setValidity(Range.closedInfinite(GIVEN_VALID_FROM));
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
@ -82,13 +87,13 @@ class HsBookingItemEntityPatcherUnitTest extends PatchUnitTestBase<
|
|||||||
HsBookingItemPatchResource::setCaption,
|
HsBookingItemPatchResource::setCaption,
|
||||||
PATCHED_CAPTION,
|
PATCHED_CAPTION,
|
||||||
HsBookingItemEntity::setCaption),
|
HsBookingItemEntity::setCaption),
|
||||||
// FIXME
|
new SimpleProperty<>(
|
||||||
// new JsonNullableProperty<>(
|
"resources",
|
||||||
// "resources",
|
HsBookingItemPatchResource::setResources,
|
||||||
// HsBookingItemPatchResource::setResources,
|
PATCHED_RESOURCES,
|
||||||
// PATCHED_RESOURCES,
|
HsBookingItemEntity::setResources,
|
||||||
// HsBookingItemEntity::setResources,
|
objectToMap(PATCHED_RESOURCES))
|
||||||
// objectToMap(PATCHED_RESOURCES)),
|
.notNullable(),
|
||||||
new JsonNullableProperty<>(
|
new JsonNullableProperty<>(
|
||||||
"validfrom",
|
"validfrom",
|
||||||
HsBookingItemPatchResource::setValidFrom,
|
HsBookingItemPatchResource::setValidFrom,
|
||||||
|
@ -28,7 +28,7 @@ class HsBookingItemEntityUnitTest {
|
|||||||
void toStringContainsAllPropertiesAndResourcesSortedByKey() {
|
void toStringContainsAllPropertiesAndResourcesSortedByKey() {
|
||||||
final var result = givenBookingItem.toString();
|
final var result = givenBookingItem.toString();
|
||||||
|
|
||||||
assertThat(result).isEqualTo("HsBookingItemEntity(D-1000100, [2020-01-01,2031-01-01), some caption, {CPUs=2, HDD-storage=2048, SSD-storage=512})");
|
assertThat(result).isEqualTo("HsBookingItemEntity(D-1000100, [2020-01-01,2031-01-01), some caption, { CPUs: 2, HDD-storage: 2048, SSD-storage: 512 })");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -109,16 +109,16 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
|||||||
final var all = rawRoleRepo.findAll();
|
final var all = rawRoleRepo.findAll();
|
||||||
assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from(
|
assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from(
|
||||||
initialRoleNames,
|
initialRoleNames,
|
||||||
"hs_booking_item#somenewbookingitem:ADMIN",
|
"hs_booking_item#D-1000111:some new booking item:ADMIN",
|
||||||
"hs_booking_item#somenewbookingitem:OWNER",
|
"hs_booking_item#D-1000111:some new booking item:OWNER",
|
||||||
"hs_booking_item#somenewbookingitem:TENANT"));
|
"hs_booking_item#D-1000111:some new booking item:TENANT"));
|
||||||
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
|
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
|
||||||
.map(s -> s.replace("hs_office_", ""))
|
.map(s -> s.replace("hs_office_", ""))
|
||||||
.containsExactlyInAnyOrder(fromFormatted(
|
.containsExactlyInAnyOrder(fromFormatted(
|
||||||
initialGrantNames,
|
initialGrantNames,
|
||||||
|
|
||||||
// insert+delete
|
// insert+delete
|
||||||
"{ grant perm:hs_booking_item#somenewbookingitem:DELETE to role:global#global:ADMIN by system and assume }",
|
"{ grant perm:hs_booking_item#D-1000111:some new booking item:DELETE to role:global#global:ADMIN by system and assume }",
|
||||||
|
|
||||||
// owner
|
// owner
|
||||||
|
|
||||||
@ -126,12 +126,12 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
|||||||
|
|
||||||
// tenant
|
// tenant
|
||||||
|
|
||||||
"{ grant perm:hs_booking_item#somenewbookingitem:SELECT to role:hs_booking_item#somenewbookingitem:TENANT by system and assume }",
|
"{ grant perm:hs_booking_item#D-1000111:some new booking item:SELECT to role:hs_booking_item#D-1000111:some new booking item:TENANT by system and assume }",
|
||||||
"{ grant perm:hs_booking_item#somenewbookingitem:UPDATE to role:hs_booking_item#somenewbookingitem:OWNER by system and assume }",
|
"{ grant perm:hs_booking_item#D-1000111:some new booking item:UPDATE to role:hs_booking_item#D-1000111:some new booking item:OWNER by system and assume }",
|
||||||
"{ grant role:hs_booking_item#somenewbookingitem:ADMIN to role:hs_booking_item#somenewbookingitem:OWNER by system and assume }",
|
"{ grant role:hs_booking_item#D-1000111:some new booking item:ADMIN to role:hs_booking_item#D-1000111:some new booking item:OWNER by system and assume }",
|
||||||
"{ grant role:hs_booking_item#somenewbookingitem:OWNER to role:relation#FirstGmbH-with-DEBITOR-FirstGmbH:AGENT by system and assume }",
|
"{ grant role:hs_booking_item#D-1000111:some new booking item:OWNER to role:relation#FirstGmbH-with-DEBITOR-FirstGmbH:AGENT by system and assume }",
|
||||||
"{ grant role:hs_booking_item#somenewbookingitem:TENANT to role:hs_booking_item#somenewbookingitem:ADMIN by system and assume }",
|
"{ grant role:hs_booking_item#D-1000111:some new booking item:TENANT to role:hs_booking_item#D-1000111:some new booking item:ADMIN by system and assume }",
|
||||||
"{ grant role:relation#FirstGmbH-with-DEBITOR-FirstGmbH:TENANT to role:hs_booking_item#somenewbookingitem:TENANT by system and assume }",
|
"{ grant role:relation#FirstGmbH-with-DEBITOR-FirstGmbH:TENANT to role:hs_booking_item#D-1000111:some new booking item:TENANT by system and assume }",
|
||||||
null));
|
null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +148,8 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
|||||||
public void globalAdmin_withoutAssumedRole_canViewAllBookingItemsOfArbitraryDebitor() {
|
public void globalAdmin_withoutAssumedRole_canViewAllBookingItemsOfArbitraryDebitor() {
|
||||||
// given
|
// given
|
||||||
context("superuser-alex@hostsharing.net");
|
context("superuser-alex@hostsharing.net");
|
||||||
final var debitorUuid = debitorRepo.findDebitorByDebitorNumber(1000212).stream().findAny().orElseThrow().getUuid();
|
final var debitorUuid = debitorRepo.findDebitorByDebitorNumber(1000212).stream()
|
||||||
|
.findAny().orElseThrow().getUuid();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final var result = bookingItemRepo.findAllByDebitorUuid(debitorUuid);
|
final var result = bookingItemRepo.findAllByDebitorUuid(debitorUuid);
|
||||||
@ -156,9 +157,9 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
|||||||
// then
|
// then
|
||||||
allTheseBookingItemsAreReturned(
|
allTheseBookingItemsAreReturned(
|
||||||
result,
|
result,
|
||||||
"HsBookingItemEntity(D-1000212, [2023-01-15,2024-04-15), some CloudServer, {CPUs=2, HDD-storage=1024})",
|
"HsBookingItemEntity(D-1000212, [2022-10-01,), some ManagedServer, { CPUs: 2, SDD-storage: 512 })",
|
||||||
"HsBookingItemEntity(D-1000212, [2022-10-01,), some ManagedServer, {CPUs=2, SDD-storage=512})",
|
"HsBookingItemEntity(D-1000212, [2023-01-15,2024-04-15), some CloudServer, { CPUs: 2, HDD-storage: 1024 })",
|
||||||
"HsBookingItemEntity(D-1000212, [2024-04-01,), some Whatever, {CPUs=1, HDD-storage=2048, SDD-storage=512})");
|
"HsBookingItemEntity(D-1000212, [2024-04-01,), some Whatever, { CPUs: 1, HDD-storage: 2048, SDD-storage: 512 })");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -173,9 +174,9 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
|||||||
// then:
|
// then:
|
||||||
exactlyTheseBookingItemsAreReturned(
|
exactlyTheseBookingItemsAreReturned(
|
||||||
result,
|
result,
|
||||||
"HsBookingItemEntity(D-1000111, [2023-01-15,2024-04-15), some CloudServer, {CPUs=2, HDD-storage=1024})",
|
"HsBookingItemEntity(D-1000111, [2022-10-01,), some ManagedServer, { CPUs: 2, SDD-storage: 512 })",
|
||||||
"HsBookingItemEntity(D-1000111, [2022-10-01,), some ManagedServer, {CPUs=2, SDD-storage=512})",
|
"HsBookingItemEntity(D-1000111, [2023-01-15,2024-04-15), some CloudServer, { CPUs: 2, HDD-storage: 1024 })",
|
||||||
"HsBookingItemEntity(D-1000111, [2024-04-01,), some Whatever, {CPUs=1, HDD-storage=2048, SDD-storage=512})");
|
"HsBookingItemEntity(D-1000111, [2024-04-01,), some Whatever, { CPUs: 1, HDD-storage: 2048, SDD-storage: 512 })");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.coopshares;
|
package net.hostsharing.hsadminng.hs.office.coopshares;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.office.membership.TestHsMembership.TEST_MEMBERSHIP;
|
import static net.hostsharing.hsadminng.hs.office.membership.TestHsMembership.TEST_MEMBERSHIP;
|
||||||
|
Loading…
Reference in New Issue
Block a user