combined memberNumber (partnerNumber+memberNumberSuffix)

This commit is contained in:
Michael Hoennig 2024-01-24 12:45:52 +01:00
parent bcf8ad2148
commit 51f406c84e
22 changed files with 482 additions and 238 deletions

View File

@ -80,3 +80,4 @@ alias fp='grep -r '@Accepts' src | sed -e 's/^.*@/@/g' | sort -u | wc -l'
alias gw-spotless='./gradlew spotlessApply -x pitest -x test -x :processResources' alias gw-spotless='./gradlew spotlessApply -x pitest -x test -x :processResources'
alias gw-test='. .aliases; ./gradlew test' alias gw-test='. .aliases; ./gradlew test'
alias gw-check='. .aliases; gw check -x pitest -x :dependencyCheckAnalyze'

View File

@ -11,7 +11,6 @@ import org.hibernate.annotations.GenericGenerator;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.UUID; import java.util.UUID;

View File

@ -26,12 +26,14 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@DisplayName("Debitor") @DisplayName("Debitor")
public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
public static final String DEBITOR_NUMBER_TAG = "D-";
// TODO: I would rather like to generate something matching this example: // TODO: I would rather like to generate something matching this example:
// debitor(1234500: Test AG, tes) // debitor(1234500: Test AG, tes)
// maybe remove withSepararator (always use ', ') and add withBusinessIdProp (with ': ' afterwards)? // maybe remove withSepararator (always use ', ') and add withBusinessIdProp (with ': ' afterwards)?
private static Stringify<HsOfficeDebitorEntity> stringify = private static Stringify<HsOfficeDebitorEntity> stringify =
stringify(HsOfficeDebitorEntity.class, "debitor") stringify(HsOfficeDebitorEntity.class, "debitor")
.withProp(HsOfficeDebitorEntity::getDebitorNumber) .withProp(e -> DEBITOR_NUMBER_TAG + e.getDebitorNumber())
.withProp(HsOfficeDebitorEntity::getPartner) .withProp(HsOfficeDebitorEntity::getPartner)
.withProp(HsOfficeDebitorEntity::getDefaultPrefix) .withProp(HsOfficeDebitorEntity::getDefaultPrefix)
.withSeparator(": ") .withSeparator(": ")
@ -75,16 +77,9 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
@Column(name = "defaultprefix", columnDefinition = "char(3) not null") @Column(name = "defaultprefix", columnDefinition = "char(3) not null")
private String defaultPrefix; private String defaultPrefix;
public String getDebitorNumberString() { private String getDebitorNumberString() {
// TODO: refactor if (partner == null || partner.getPartnerNumber() == null || debitorNumberSuffix == null ) {
if (partner.getPartnerNumber() == null ) { return null;
if (debitorNumberSuffix == null) {
return null;
}
return String.format("%02d", debitorNumberSuffix);
}
if (debitorNumberSuffix == null) {
return partner.getPartnerNumber() + "??";
} }
return partner.getPartnerNumber() + String.format("%02d", debitorNumberSuffix); return partner.getPartnerNumber() + String.format("%02d", debitorNumberSuffix);
} }
@ -100,6 +95,6 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
@Override @Override
public String toShortString() { public String toShortString() {
return getDebitorNumberString(); return DEBITOR_NUMBER_TAG + getDebitorNumberString();
} }
} }

View File

@ -44,7 +44,9 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
Integer memberNumber) { Integer memberNumber) {
context.define(currentUser, assumedRoles); context.define(currentUser, assumedRoles);
final var entities = membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid); final var entities = ( memberNumber != null)
? List.of(membershipRepo.findMembershipByMemberNumber(memberNumber))
: membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid);
final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class, final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class,
SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER); SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER);

View File

@ -34,7 +34,7 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
public static final String MEMBER_NUMBER_TAG = "M-"; public static final String MEMBER_NUMBER_TAG = "M-";
private static Stringify<HsOfficeMembershipEntity> stringify = stringify(HsOfficeMembershipEntity.class) private static Stringify<HsOfficeMembershipEntity> stringify = stringify(HsOfficeMembershipEntity.class)
.withProp(HsOfficeMembershipEntity::getMemberNumberString) .withProp(e -> MEMBER_NUMBER_TAG + e.getMemberNumber())
.withProp(e -> e.getPartner().toShortString()) .withProp(e -> e.getPartner().toShortString())
.withProp(e -> e.getMainDebitor().toShortString()) .withProp(e -> e.getMainDebitor().toShortString())
.withProp(e -> e.getValidity().asString()) .withProp(e -> e.getValidity().asString())
@ -91,25 +91,12 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
} }
return validity; return validity;
} }
public String getMemberNumberString() {
return MEMBER_NUMBER_TAG + getMemberNumber();
}
public Integer getMemberNumber() { public Integer getMemberNumber() {
// TODO: refactor if (partner == null || partner.getPartnerNumber() == null || memberNumberSuffix == null ) {
String combinedMemberNumber; return null;
if (partner.getPartnerNumber() == null ) {
if (memberNumberSuffix == null) {
combinedMemberNumber = null;
} else {combinedMemberNumber = MEMBER_NUMBER_TAG + memberNumberSuffix;}
} else if (memberNumberSuffix == null) {
combinedMemberNumber = partner.getPartnerNumber() + "??";
} else {
combinedMemberNumber = partner.getPartnerNumber() + memberNumberSuffix;
} }
return Optional.ofNullable(combinedMemberNumber).map(Integer::parseInt).orElse(null); return getPartner().getPartnerNumber() * 100 + Integer.parseInt(memberNumberSuffix, 10);
} }
@Override @Override
@ -119,7 +106,7 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
@Override @Override
public String toShortString() { public String toShortString() {
return "M-" + partner.getPartnerNumber() + String.valueOf(memberNumberSuffix); return "M-" + getMemberNumber();
} }
@PrePersist @PrePersist

View File

@ -28,13 +28,13 @@ public interface HsOfficeMembershipRepository extends Repository<HsOfficeMembers
AND (membership.memberNumberSuffix = :suffix) AND (membership.memberNumberSuffix = :suffix)
ORDER BY membership.memberNumberSuffix ORDER BY membership.memberNumberSuffix
""") """)
List<HsOfficeMembershipEntity> findMembershipsByPartnerNumberAndSuffix( HsOfficeMembershipEntity findMembershipByPartnerNumberAndSuffix(
@NotNull Integer partnerNumber, @NotNull Integer partnerNumber,
@NotNull String suffix); @NotNull String suffix);
default List<HsOfficeMembershipEntity> findMembershipsByMemberNumber(Integer memberNumber) { default HsOfficeMembershipEntity findMembershipByMemberNumber(Integer memberNumber) {
final var partnerNumber = memberNumber / 100; final var partnerNumber = memberNumber / 100;
final var suffix = memberNumber % 100; final var suffix = memberNumber % 100;
return findMembershipsByPartnerNumberAndSuffix(partnerNumber, String.format("%02d", suffix)); return findMembershipByPartnerNumberAndSuffix(partnerNumber, String.format("%02d", suffix));
} }
long count(); long count();

View File

@ -23,6 +23,7 @@ public interface HsOfficePartnerRepository extends Repository<HsOfficePartnerEnt
OR person.familyName like concat(cast(:name as text), '%') OR person.familyName like concat(cast(:name as text), '%')
""") """)
List<HsOfficePartnerEntity> findPartnerByOptionalNameLike(String name); List<HsOfficePartnerEntity> findPartnerByOptionalNameLike(String name);
HsOfficePartnerEntity findPartnerByPartnerNumber(Integer partnerNumber);
HsOfficePartnerEntity save(final HsOfficePartnerEntity entity); HsOfficePartnerEntity save(final HsOfficePartnerEntity entity);

View File

@ -23,8 +23,15 @@ components:
$ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner' $ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner'
mainDebitor: mainDebitor:
$ref: './hs-office-debitor-schemas.yaml#/components/schemas/HsOfficeDebitor' $ref: './hs-office-debitor-schemas.yaml#/components/schemas/HsOfficeDebitor'
memberNumberSuffix: memberNumber:
type: integer type: integer
minimum: 1000000
maximum: 9999999
memberNumberSuffix:
type: string
minLength: 2
maxLength: 2
pattern: '[0-9]+'
validFrom: validFrom:
type: string type: string
format: date format: date
@ -67,7 +74,10 @@ components:
format: uuid format: uuid
nullable: false nullable: false
memberNumberSuffix: memberNumberSuffix:
type: integer type: string
minLength: 2
maxLength: 2
pattern: '[0-9]+'
nullable: false nullable: false
validFrom: validFrom:
type: string type: string
@ -84,6 +94,7 @@ components:
type: boolean type: boolean
required: required:
- partnerUuid - partnerUuid
- memberNumberSuffix
- mainDebitorUuid - mainDebitorUuid
- validFrom - validFrom
- membershipFeeBillable - membershipFeeBillable

View File

@ -1,6 +1,7 @@
get: get:
summary: Returns a list of (optionally filtered) memberships. summary: Returns a list of (optionally filtered) memberships.
description: Returns the list of (optionally filtered) memberships which are visible to the current user or any of it's assumed roles. description: Returns the list of memberships which are visible to the current user or any of it's assumed roles.
The list can optionally be filtered by either the `partnerUuid` or the `memberNumber` - not both at the same time.
tags: tags:
- hs-office-memberships - hs-office-memberships
operationId: listMemberships operationId: listMemberships
@ -13,13 +14,13 @@ get:
schema: schema:
type: string type: string
format: uuid format: uuid
description: UUID of the business partner. description: UUID of the business partner, exclusive to `memberNumber`.
- name: memberNumberSuffix - name: memberNumber
in: query in: query
required: false required: false
schema: schema:
type: integer type: integer
description: Member number. description: Member number, exclusive to `partnerUuid`.
responses: responses:
"200": "200":
description: OK description: OK

View File

@ -75,8 +75,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest {
void globalAdmin_canFindCoopAssetsTransactionsByMemberNumberSuffix() { void globalAdmin_canFindCoopAssetsTransactionsByMemberNumberSuffix() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202);
.get(0);
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
@ -118,8 +117,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest {
void globalAdmin_canFindCoopAssetsTransactionsByMemberNumberSuffixAndDateRange() { void globalAdmin_canFindCoopAssetsTransactionsByMemberNumberSuffixAndDateRange() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202);
.get(0);
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
@ -153,8 +151,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest {
void globalAdmin_canAddCoopAssetsTransaction() { void globalAdmin_canAddCoopAssetsTransaction() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
.get(0);
final var location = RestAssured // @formatter:off final var location = RestAssured // @formatter:off
.given() .given()
@ -199,8 +196,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest {
void globalAdmin_canNotCancelMoreAssetsThanCurrentlySubscribed() { void globalAdmin_canNotCancelMoreAssetsThanCurrentlySubscribed() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
.get(0);
final var location = RestAssured // @formatter:off final var location = RestAssured // @formatter:off
.given() .given()

View File

@ -62,8 +62,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var count = coopAssetsTransactionRepo.count(); final var count = coopAssetsTransactionRepo.count();
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
.get(0);
// when // when
final var result = attempt(em, () -> { final var result = attempt(em, () -> {
@ -96,7 +95,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
// when // when
attempt(em, () -> { attempt(em, () -> {
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101).get(0); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder() final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
.membership(givenMembership) .membership(givenMembership)
.transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT) .transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT)
@ -159,7 +158,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
public void globalAdmin_canViewCoopAssetsTransactions_filteredByMembershipUuid() { public void globalAdmin_canViewCoopAssetsTransactions_filteredByMembershipUuid() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(null).get(1000202); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202);
// when // when
final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange( final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange(
@ -179,8 +178,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
public void globalAdmin_canViewCoopAssetsTransactions_filteredByMembershipUuidAndValueDateRange() { public void globalAdmin_canViewCoopAssetsTransactions_filteredByMembershipUuidAndValueDateRange() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202);
.get(0);
// when // when
final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange( final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange(

View File

@ -75,7 +75,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest {
void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffix() { void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffix() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202).get(0); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202);
RestAssured // @formatter:off RestAssured // @formatter:off
.given().header("current-user", "superuser-alex@hostsharing.net").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions?membershipUuid=" + givenMembership.getUuid()).then().log().all().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals(""" .given().header("current-user", "superuser-alex@hostsharing.net").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions?membershipUuid=" + givenMembership.getUuid()).then().log().all().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals("""
@ -109,7 +109,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest {
void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffixAndDateRange() { void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffixAndDateRange() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202).get(0); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202);
RestAssured // @formatter:off RestAssured // @formatter:off
.given().header("current-user", "superuser-alex@hostsharing.net").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions?membershipUuid=" + givenMembership.getUuid() + "&fromValueDate=2020-01-01&toValueDate=2021-12-31").then().log().all().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals(""" .given().header("current-user", "superuser-alex@hostsharing.net").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions?membershipUuid=" + givenMembership.getUuid() + "&fromValueDate=2020-01-01&toValueDate=2021-12-31").then().log().all().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals("""
@ -134,7 +134,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest {
void globalAdmin_canAddCoopSharesTransaction() { void globalAdmin_canAddCoopSharesTransaction() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101).get(0); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
final var location = RestAssured // @formatter:off final var location = RestAssured // @formatter:off
.given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body(""" .given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body("""
@ -165,7 +165,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest {
void globalAdmin_canNotCancelMoreSharesThanCurrentlySubscribed() { void globalAdmin_canNotCancelMoreSharesThanCurrentlySubscribed() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101).get(0); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
final var location = RestAssured // @formatter:off final var location = RestAssured // @formatter:off
.given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body(""" .given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body("""

View File

@ -61,8 +61,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var count = coopSharesTransactionRepo.count(); final var count = coopSharesTransactionRepo.count();
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
.get(0);
// when // when
final var result = attempt(em, () -> { final var result = attempt(em, () -> {
@ -95,8 +94,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// when // when
attempt(em, () -> { attempt(em, () -> {
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
.get(0);
final var newCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder() final var newCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder()
.membership(givenMembership) .membership(givenMembership)
.transactionType(HsOfficeCoopSharesTransactionType.SUBSCRIPTION) .transactionType(HsOfficeCoopSharesTransactionType.SUBSCRIPTION)
@ -115,7 +113,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", "")) .map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted( .containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames, initialGrantNames,
"{ grant perm view on coopsharestransaction#temprefB to role membership#10001:....tenant by system and assume }", "{ grant perm view on coopsharestransaction#temprefB to role membership#1000101:....tenant by system and assume }",
null)); null));
} }
@ -142,25 +140,24 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// then // then
allTheseCoopSharesTransactionsAreReturned( allTheseCoopSharesTransactionsAreReturned(
result, result,
"CoopShareTransaction(10001, 2010-03-15, SUBSCRIPTION, 4, ref 10001-1, initial subscription)", "CoopShareTransaction(1000101, 2010-03-15, SUBSCRIPTION, 4, ref 1000101-1, initial subscription)",
"CoopShareTransaction(10001, 2021-09-01, CANCELLATION, -2, ref 10001-2, cancelling some)", "CoopShareTransaction(1000101, 2021-09-01, CANCELLATION, -2, ref 1000101-2, cancelling some)",
"CoopShareTransaction(10001, 2022-10-20, ADJUSTMENT, 2, ref 10001-3, some adjustment)", "CoopShareTransaction(1000101, 2022-10-20, ADJUSTMENT, 2, ref 1000101-3, some adjustment)",
"CoopShareTransaction(10002, 2010-03-15, SUBSCRIPTION, 4, ref 10002-1, initial subscription)", "CoopShareTransaction(1000202, 2010-03-15, SUBSCRIPTION, 4, ref 1000202-1, initial subscription)",
"CoopShareTransaction(10002, 2021-09-01, CANCELLATION, -2, ref 10002-2, cancelling some)", "CoopShareTransaction(1000202, 2021-09-01, CANCELLATION, -2, ref 1000202-2, cancelling some)",
"CoopShareTransaction(10002, 2022-10-20, ADJUSTMENT, 2, ref 10002-3, some adjustment)", "CoopShareTransaction(1000202, 2022-10-20, ADJUSTMENT, 2, ref 1000202-3, some adjustment)",
"CoopShareTransaction(10003, 2010-03-15, SUBSCRIPTION, 4, ref 10003-1, initial subscription)", "CoopShareTransaction(1000303, 2010-03-15, SUBSCRIPTION, 4, ref 1000303-1, initial subscription)",
"CoopShareTransaction(10003, 2021-09-01, CANCELLATION, -2, ref 10003-2, cancelling some)", "CoopShareTransaction(1000303, 2021-09-01, CANCELLATION, -2, ref 1000303-2, cancelling some)",
"CoopShareTransaction(10003, 2022-10-20, ADJUSTMENT, 2, ref 10003-3, some adjustment)"); "CoopShareTransaction(1000303, 2022-10-20, ADJUSTMENT, 2, ref 1000303-3, some adjustment)");
} }
@Test @Test
public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuid() { public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuid() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202);
.get(0);
// when // when
final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange( final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(
@ -171,17 +168,16 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// then // then
allTheseCoopSharesTransactionsAreReturned( allTheseCoopSharesTransactionsAreReturned(
result, result,
"CoopShareTransaction(10002, 2010-03-15, SUBSCRIPTION, 4, ref 10002-1, initial subscription)", "CoopShareTransaction(1000202, 2010-03-15, SUBSCRIPTION, 4, ref 1000202-1, initial subscription)",
"CoopShareTransaction(10002, 2021-09-01, CANCELLATION, -2, ref 10002-2, cancelling some)", "CoopShareTransaction(1000202, 2021-09-01, CANCELLATION, -2, ref 1000202-2, cancelling some)",
"CoopShareTransaction(10002, 2022-10-20, ADJUSTMENT, 2, ref 10002-3, some adjustment)"); "CoopShareTransaction(1000202, 2022-10-20, ADJUSTMENT, 2, ref 1000202-3, some adjustment)");
} }
@Test @Test
public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuidAndValueDateRange() { public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuidAndValueDateRange() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202);
.get(0);
// when // when
final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange( final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(
@ -192,7 +188,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// then // then
allTheseCoopSharesTransactionsAreReturned( allTheseCoopSharesTransactionsAreReturned(
result, result,
"CoopShareTransaction(10002, 2021-09-01, CANCELLATION, -2, ref 10002-2, cancelling some)"); "CoopShareTransaction(1000202, 2021-09-01, CANCELLATION, -2, ref 1000202-2, cancelling some)");
} }
@Test @Test
@ -209,9 +205,9 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// then: // then:
exactlyTheseCoopSharesTransactionsAreReturned( exactlyTheseCoopSharesTransactionsAreReturned(
result, result,
"CoopShareTransaction(10001, 2010-03-15, SUBSCRIPTION, 4, ref 10001-1, initial subscription)", "CoopShareTransaction(1000101, 2010-03-15, SUBSCRIPTION, 4, ref 1000101-1, initial subscription)",
"CoopShareTransaction(10001, 2021-09-01, CANCELLATION, -2, ref 10001-2, cancelling some)", "CoopShareTransaction(1000101, 2021-09-01, CANCELLATION, -2, ref 1000101-2, cancelling some)",
"CoopShareTransaction(10001, 2022-10-20, ADJUSTMENT, 2, ref 10001-3, some adjustment)"); "CoopShareTransaction(1000101, 2022-10-20, ADJUSTMENT, 2, ref 1000101-3, some adjustment)");
} }
} }

View File

@ -29,7 +29,7 @@ class HsOfficeDebitorEntityUnitTest {
final var result = given.toString(); final var result = given.toString();
assertThat(result).isEqualTo("debitor(1234567: LEGAL some trade name: som)"); assertThat(result).isEqualTo("debitor(D-1234567: LEGAL some trade name: som)");
} }
@Test @Test
@ -46,7 +46,7 @@ class HsOfficeDebitorEntityUnitTest {
final var result = given.toString(); final var result = given.toString();
assertThat(result).isEqualTo("debitor(1234567: <person=null>)"); assertThat(result).isEqualTo("debitor(D-1234567: <person=null>)");
} }
@Test @Test
@ -60,6 +60,60 @@ class HsOfficeDebitorEntityUnitTest {
final var result = given.toShortString(); final var result = given.toShortString();
assertThat(result).isEqualTo("1234567"); assertThat(result).isEqualTo("D-1234567");
}
@Test
void getDebitorNumberWithPartnerNumberAndDebitorNumberSuffix() {
final var given = HsOfficeDebitorEntity.builder()
.partner(HsOfficePartnerEntity.builder()
.partnerNumber(12345)
.build())
.debitorNumberSuffix((byte)67)
.build();
final var result = given.getDebitorNumber();
assertThat(result).isEqualTo(1234567);
}
@Test
void getDebitorNumberWithoutPartnerReturnsNull() {
final var given = HsOfficeDebitorEntity.builder()
.partner(null)
.debitorNumberSuffix((byte)67)
.build();
final var result = given.getDebitorNumber();
assertThat(result).isNull();
}
@Test
void getDebitorNumberWithoutPartnerNumberReturnsNull() {
final var given = HsOfficeDebitorEntity.builder()
.partner(HsOfficePartnerEntity.builder()
.partnerNumber(null)
.build())
.debitorNumberSuffix((byte)67)
.build();
final var result = given.getDebitorNumber();
assertThat(result).isNull();
}
@Test
void getDebitorNumberWithoutDebitorNumberSuffixReturnsNull() {
final var given = HsOfficeDebitorEntity.builder()
.partner(HsOfficePartnerEntity.builder()
.partnerNumber(12345)
.build())
.debitorNumberSuffix(null)
.build();
final var result = given.getDebitorNumber();
assertThat(result).isNull();
} }
} }

View File

@ -211,9 +211,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
// then // then
allTheseDebitorsAreReturned( allTheseDebitorsAreReturned(
result, result,
"debitor(1000111: LEGAL First GmbH: fir)", "debitor(D-1000111: LEGAL First GmbH: fir)",
"debitor(1000212: LEGAL Second e.K.: sec)", "debitor(D-1000212: LEGAL Second e.K.: sec)",
"debitor(1000313: SOLE_REPRESENTATION Third OHG: thi)"); "debitor(D-1000313: SOLE_REPRESENTATION Third OHG: thi)");
} }
@ParameterizedTest @ParameterizedTest
@ -231,8 +231,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
// then: // then:
exactlyTheseDebitorsAreReturned(result, exactlyTheseDebitorsAreReturned(result,
"debitor(1000111: LEGAL First GmbH: fir)", "debitor(D-1000111: LEGAL First GmbH: fir)",
"debitor(1000120: LEGAL First GmbH: fif)"); "debitor(D-1000120: LEGAL First GmbH: fif)");
} }
@Test @Test
@ -260,7 +260,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
final var result = debitorRepo.findDebitorByDebitorNumber(1000313); final var result = debitorRepo.findDebitorByDebitorNumber(1000313);
// then // then
exactlyTheseDebitorsAreReturned(result, "debitor(1000313: SOLE_REPRESENTATION Third OHG: thi)"); exactlyTheseDebitorsAreReturned(result, "debitor(D-1000313: SOLE_REPRESENTATION Third OHG: thi)");
} }
} }
@ -276,7 +276,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
final var result = debitorRepo.findDebitorByOptionalNameLike("third contact"); final var result = debitorRepo.findDebitorByOptionalNameLike("third contact");
// then // then
exactlyTheseDebitorsAreReturned(result, "debitor(1000313: SOLE_REPRESENTATION Third OHG: thi)"); exactlyTheseDebitorsAreReturned(result, "debitor(D-1000313: SOLE_REPRESENTATION Third OHG: thi)");
} }
} }

View File

@ -61,7 +61,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
@PersistenceContext @PersistenceContext
EntityManager em; EntityManager em;
private static int tempMemberNumberSuffix = 10; private static int tempMemberNumberSuffix = 90; // TODO: check if we even need multiple distinct values
@Nested @Nested
@Accepts({ "Membership:F(Find)" }) @Accepts({ "Membership:F(Find)" })
@ -84,7 +84,8 @@ class HsOfficeMembershipControllerAcceptanceTest {
{ {
"partner": { "person": { "tradeName": "First GmbH" } }, "partner": { "person": { "tradeName": "First GmbH" } },
"mainDebitor": { "debitorNumber": 1000111 }, "mainDebitor": { "debitorNumber": 1000111 },
"memberNumberSuffix": 10001, "memberNumber": 1000101,
"memberNumberSuffix": "01",
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
"reasonForTermination": "NONE" "reasonForTermination": "NONE"
@ -92,7 +93,8 @@ class HsOfficeMembershipControllerAcceptanceTest {
{ {
"partner": { "person": { "tradeName": "Second e.K." } }, "partner": { "person": { "tradeName": "Second e.K." } },
"mainDebitor": { "debitorNumber": 1000212 }, "mainDebitor": { "debitorNumber": 1000212 },
"memberNumberSuffix": 10002, "memberNumber": 1000202,
"memberNumberSuffix": "02",
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
"reasonForTermination": "NONE" "reasonForTermination": "NONE"
@ -100,7 +102,8 @@ class HsOfficeMembershipControllerAcceptanceTest {
{ {
"partner": { "person": { "tradeName": "Third OHG" } }, "partner": { "person": { "tradeName": "Third OHG" } },
"mainDebitor": { "debitorNumber": 1000313 }, "mainDebitor": { "debitorNumber": 1000313 },
"memberNumberSuffix": 10003, "memberNumber": 1000303,
"memberNumberSuffix": "03",
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
"reasonForTermination": "NONE" "reasonForTermination": "NONE"
@ -109,6 +112,67 @@ class HsOfficeMembershipControllerAcceptanceTest {
""")); """));
// @formatter:on // @formatter:on
} }
@Test
void globalAdmin_canViewMembershipsByPartnerUuid() throws JSONException {
context.define("superuser-alex@hostsharing.net");
final var partner = partnerRepo.findPartnerByPartnerNumber(10001);
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.port(port)
.when()
.queryParam("partnerUuid", partner.getUuid() )
.get("http://localhost/api/hs/office/memberships")
.then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
[
{
"partner": { "person": { "tradeName": "First GmbH" } },
"mainDebitor": { "debitorNumber": 1000111 },
"memberNumber": 1000101,
"memberNumberSuffix": "01",
"validFrom": "2022-10-01",
"validTo": null,
"reasonForTermination": "NONE"
}
]
"""));
// @formatter:on
}
@Test
void globalAdmin_canViewMembershipsByMemberNumber() throws JSONException {
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.port(port)
.when()
.queryParam("memberNumber", 1000202 )
.get("http://localhost/api/hs/office/memberships")
.then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
[
{
"partner": { "person": { "tradeName": "Second e.K." } },
"mainDebitor": { "debitorNumber": 1000212 },
"memberNumber": 1000202,
"memberNumberSuffix": "02",
"validFrom": "2022-10-01",
"validTo": null,
"reasonForTermination": "NONE"
}
]
"""));
// @formatter:on
}
} }
@Nested @Nested
@ -121,6 +185,9 @@ class HsOfficeMembershipControllerAcceptanceTest {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Third").get(0); final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Third").get(0);
final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("Third").get(0); final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("Third").get(0);
final var givenMemberSuffixNumber = ++tempMemberNumberSuffix;
final var givenMemberSuffix = toPaddedSuffix(givenMemberSuffixNumber);
final var expectedMemberNumber = givenPartner.getPartnerNumber()*100+givenMemberSuffixNumber;
final var location = RestAssured // @formatter:off final var location = RestAssured // @formatter:off
.given() .given()
@ -130,11 +197,11 @@ class HsOfficeMembershipControllerAcceptanceTest {
{ {
"partnerUuid": "%s", "partnerUuid": "%s",
"mainDebitorUuid": "%s", "mainDebitorUuid": "%s",
"memberNumberSuffix": 20001, "memberNumberSuffix": "%s",
"validFrom": "2022-10-13", "validFrom": "2022-10-13",
"membershipFeeBillable": "true" "membershipFeeBillable": "true"
} }
""".formatted(givenPartner.getUuid(), givenDebitor.getUuid())) """.formatted(givenPartner.getUuid(), givenDebitor.getUuid(), givenMemberSuffix))
.port(port) .port(port)
.when() .when()
.post("http://localhost/api/hs/office/memberships") .post("http://localhost/api/hs/office/memberships")
@ -145,7 +212,8 @@ class HsOfficeMembershipControllerAcceptanceTest {
.body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumber())) .body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumber()))
.body("mainDebitor.debitorNumberSuffix", is((int) givenDebitor.getDebitorNumberSuffix())) .body("mainDebitor.debitorNumberSuffix", is((int) givenDebitor.getDebitorNumberSuffix()))
.body("partner.person.tradeName", is("Third OHG")) .body("partner.person.tradeName", is("Third OHG"))
.body("memberNumberSuffix", is(20001)) .body("memberNumber", is(expectedMemberNumber))
.body("memberNumberSuffix", is(givenMemberSuffix))
.body("validFrom", is("2022-10-13")) .body("validFrom", is("2022-10-13"))
.body("validTo", equalTo(null)) .body("validTo", equalTo(null))
.header("Location", startsWith("http://localhost")) .header("Location", startsWith("http://localhost"))
@ -166,9 +234,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
@Test @Test
void globalAdmin_canGetArbitraryMembership() { void globalAdmin_canGetArbitraryMembership() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenMembershipUuid = membershipRepo.findMembershipsByMemberNumber(1000101) final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000101).getUuid();
.get(0)
.getUuid();
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
@ -183,7 +249,8 @@ class HsOfficeMembershipControllerAcceptanceTest {
{ {
"partner": { "person": { "tradeName": "First GmbH" } }, "partner": { "person": { "tradeName": "First GmbH" } },
"mainDebitor": { "debitorNumber": 1000111 }, "mainDebitor": { "debitorNumber": 1000111 },
"memberNumberSuffix": 10001, "memberNumber": 1000101,
"memberNumberSuffix": "01",
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
"reasonForTermination": "NONE" "reasonForTermination": "NONE"
@ -195,9 +262,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
@Accepts({ "Membership:X(Access Control)" }) @Accepts({ "Membership:X(Access Control)" })
void normalUser_canNotGetUnrelatedMembership() { void normalUser_canNotGetUnrelatedMembership() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenMembershipUuid = membershipRepo.findMembershipsByMemberNumber(1000101) final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000101).getUuid();
.get(0)
.getUuid();
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
@ -213,9 +278,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
@Accepts({ "Membership:X(Access Control)" }) @Accepts({ "Membership:X(Access Control)" })
void debitorAgentUser_canGetRelatedMembership() { void debitorAgentUser_canGetRelatedMembership() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenMembershipUuid = membershipRepo.findMembershipsByMemberNumber(1000303) final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000303).getUuid();
.get(0)
.getUuid();
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
@ -234,7 +297,8 @@ class HsOfficeMembershipControllerAcceptanceTest {
"debitorNumber": 1000313, "debitorNumber": 1000313,
"billingContact": { "label": "third contact" } "billingContact": { "label": "third contact" }
}, },
"memberNumberSuffix": 10003, "memberNumber": 1000303,
"memberNumberSuffix": "03",
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
"reasonForTermination": "NONE" "reasonForTermination": "NONE"
@ -458,8 +522,10 @@ class HsOfficeMembershipControllerAcceptanceTest {
void cleanup() { void cleanup() {
jpaAttempt.transacted(() -> { jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net", null); context.define("superuser-alex@hostsharing.net", null);
final var query = em.createQuery("DELETE FROM HsOfficeMembershipEntity m WHERE m.memberNumberSuffix >= '20'"); final var query = em.createQuery("DELETE FROM HsOfficeMembershipEntity m WHERE m.memberNumberSuffix >= '90'");
query.executeUpdate(); if ( query.executeUpdate() > 0 ) {
}); query.toString();
}
}).assertSuccessful();
} }
} }

View File

@ -6,7 +6,10 @@ import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.mapper.Mapper; import net.hostsharing.hsadminng.mapper.Mapper;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.mockito.Mock; import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
@ -22,6 +25,7 @@ import jakarta.persistence.SynchronizationType;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -59,119 +63,166 @@ public class HsOfficeMembershipControllerRestTest {
when(emf.createEntityManager(any(SynchronizationType.class), any(Map.class))).thenReturn(em); when(emf.createEntityManager(any(SynchronizationType.class), any(Map.class))).thenReturn(em);
} }
@Test @Nested
void respondBadRequest_ifPartnerUuidIsMissing() throws Exception { class AddMembership {
@Test
void respondBadRequest_ifPartnerUuidIsMissing() throws Exception {
// when // when
mockMvc.perform(MockMvcRequestBuilders mockMvc.perform(MockMvcRequestBuilders
.post("/api/hs/office/memberships") .post("/api/hs/office/memberships")
.header("current-user", "superuser-alex@hostsharing.net") .header("current-user", "superuser-alex@hostsharing.net")
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.content(""" .content("""
{ {
"partnerUuid": null, "partnerUuid": null,
"mainDebitorUuid": "%s", "mainDebitorUuid": "%s",
"memberNumberSuffix": 20001, "memberNumberSuffix": "01",
"validFrom": "2022-10-13", "validFrom": "2022-10-13",
"membershipFeeBillable": "true" "membershipFeeBillable": "true"
} }
""".formatted(UUID.randomUUID())) """.formatted(UUID.randomUUID()))
.accept(MediaType.APPLICATION_JSON)) .accept(MediaType.APPLICATION_JSON))
// then // then
.andExpect(status().is4xxClientError()) .andExpect(status().is4xxClientError())
.andExpect(jsonPath("statusCode", is(400))) .andExpect(jsonPath("statusCode", is(400)))
.andExpect(jsonPath("statusPhrase", is("Bad Request"))) .andExpect(jsonPath("statusPhrase", is("Bad Request")))
.andExpect(jsonPath("message", is("[partnerUuid must not be null but is \"null\"]"))); .andExpect(jsonPath("message", is("[partnerUuid must not be null but is \"null\"]")));
}
@Test
void respondBadRequest_ifDebitorUuidIsMissing() throws Exception {
// when
mockMvc.perform(MockMvcRequestBuilders
.post("/api/hs/office/memberships")
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"partnerUuid": "%s",
"mainDebitorUuid": null,
"memberNumberSuffix": "01",
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
""".formatted(UUID.randomUUID()))
.accept(MediaType.APPLICATION_JSON))
// then
.andExpect(status().is4xxClientError())
.andExpect(jsonPath("statusCode", is(400)))
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
.andExpect(jsonPath("message", is("[mainDebitorUuid must not be null but is \"null\"]")));
}
@Test
void respondBadRequest_ifAnyGivenPartnerUuidCannotBeFound() throws Exception {
// given
final var givenPartnerUuid = UUID.randomUUID();
final var givenMainDebitorUuid = UUID.randomUUID();
when(em.find(HsOfficePartnerEntity.class, givenPartnerUuid)).thenReturn(null);
when(em.find(HsOfficeDebitorEntity.class, givenMainDebitorUuid)).thenReturn(mock(HsOfficeDebitorEntity.class));
// when
mockMvc.perform(MockMvcRequestBuilders
.post("/api/hs/office/memberships")
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"partnerUuid": "%s",
"mainDebitorUuid": "%s",
"memberNumberSuffix": "01",
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
""".formatted(givenPartnerUuid, givenMainDebitorUuid))
.accept(MediaType.APPLICATION_JSON))
// then
.andExpect(status().is4xxClientError())
.andExpect(jsonPath("statusCode", is(400)))
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
.andExpect(jsonPath("message", is("Unable to find Partner with uuid " + givenPartnerUuid)));
}
@Test
void respondBadRequest_ifAnyGivenDebitorUuidCannotBeFound() throws Exception {
// given
final var givenPartnerUuid = UUID.randomUUID();
final var givenMainDebitorUuid = UUID.randomUUID();
when(em.find(HsOfficePartnerEntity.class, givenPartnerUuid)).thenReturn(mock(HsOfficePartnerEntity.class));
when(em.find(HsOfficeDebitorEntity.class, givenMainDebitorUuid)).thenReturn(null);
// when
mockMvc.perform(MockMvcRequestBuilders
.post("/api/hs/office/memberships")
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"partnerUuid": "%s",
"mainDebitorUuid": "%s",
"memberNumberSuffix": "01",
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
""".formatted(givenPartnerUuid, givenMainDebitorUuid))
.accept(MediaType.APPLICATION_JSON))
// then
.andExpect(status().is4xxClientError())
.andExpect(jsonPath("statusCode", is(400)))
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
.andExpect(jsonPath("message", is("Unable to find Debitor with uuid " + givenMainDebitorUuid)));
}
@ParameterizedTest
@EnumSource(InvalidMemberSuffixVariants.class)
void respondBadRequest_ifMemberNumberSuffixIsInvalid(final InvalidMemberSuffixVariants testCase) throws Exception {
// when
mockMvc.perform(MockMvcRequestBuilders
.post("/api/hs/office/memberships")
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"partnerUuid": "%s",
"mainDebitorUuid": "%s",
%s
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
""".formatted(UUID.randomUUID(), UUID.randomUUID(), testCase.memberNumberSuffixEntry))
.accept(MediaType.APPLICATION_JSON))
// then
.andExpect(status().is4xxClientError())
.andExpect(jsonPath("statusCode", is(400)))
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
.andExpect(jsonPath("message", containsString(testCase.expectedErrorMessage)));
}
public enum InvalidMemberSuffixVariants {
MISSING("", "[memberNumberSuffix must not be null but is \"null\"]"),
TOO_SMALL("\"memberNumberSuffix\": \"9\",", "memberNumberSuffix size must be between 2 and 2 but is \"9\""),
TOO_LARGE("\"memberNumberSuffix\": \"100\",", "memberNumberSuffix size must be between 2 and 2 but is \"100\""),
NOT_NUMERIC("\"memberNumberSuffix\": \"AA\",", "memberNumberSuffix must match \"[0-9]+\" but is \"AA\""),
EMPTY("\"memberNumberSuffix\": \"\",", "memberNumberSuffix size must be between 2 and 2 but is \"\"");
private final String memberNumberSuffixEntry;
private final String expectedErrorMessage;
InvalidMemberSuffixVariants(final String memberNumberSuffixEntry, final String expectedErrorMessage) {
this.memberNumberSuffixEntry = memberNumberSuffixEntry;
this.expectedErrorMessage = expectedErrorMessage;
}
}
} }
@Test
void respondBadRequest_ifDebitorUuidIsMissing() throws Exception {
// when
mockMvc.perform(MockMvcRequestBuilders
.post("/api/hs/office/memberships")
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"partnerUuid": "%s",
"mainDebitorUuid": null,
"memberNumberSuffix": 20001,
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
""".formatted(UUID.randomUUID()))
.accept(MediaType.APPLICATION_JSON))
// then
.andExpect(status().is4xxClientError())
.andExpect(jsonPath("statusCode", is(400)))
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
.andExpect(jsonPath("message", is("[mainDebitorUuid must not be null but is \"null\"]")));
}
@Test
void respondBadRequest_ifAnyGivenPartnerUuidCannotBeFound() throws Exception {
// given
final var givenPartnerUuid = UUID.randomUUID();
final var givenMainDebitorUuid = UUID.randomUUID();
when(em.find(HsOfficePartnerEntity.class, givenPartnerUuid)).thenReturn(null);
when(em.find(HsOfficeDebitorEntity.class, givenMainDebitorUuid)).thenReturn(mock(HsOfficeDebitorEntity.class));
// when
mockMvc.perform(MockMvcRequestBuilders
.post("/api/hs/office/memberships")
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"partnerUuid": "%s",
"mainDebitorUuid": "%s",
"memberNumberSuffix": 20001,
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
""".formatted(givenPartnerUuid, givenMainDebitorUuid))
.accept(MediaType.APPLICATION_JSON))
// then
.andExpect(status().is4xxClientError())
.andExpect(jsonPath("statusCode", is(400)))
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
.andExpect(jsonPath("message", is("Unable to find Partner with uuid " + givenPartnerUuid)));
}
@Test
void respondBadRequest_ifAnyGivenDebitorUuidCannotBeFound() throws Exception {
// given
final var givenPartnerUuid = UUID.randomUUID();
final var givenMainDebitorUuid = UUID.randomUUID();
when(em.find(HsOfficePartnerEntity.class, givenPartnerUuid)).thenReturn(mock(HsOfficePartnerEntity.class));
when(em.find(HsOfficeDebitorEntity.class, givenMainDebitorUuid)).thenReturn(null);
// when
mockMvc.perform(MockMvcRequestBuilders
.post("/api/hs/office/memberships")
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"partnerUuid": "%s",
"mainDebitorUuid": "%s",
"memberNumberSuffix": 20001,
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
""".formatted(givenPartnerUuid, givenMainDebitorUuid))
.accept(MediaType.APPLICATION_JSON))
// then
.andExpect(status().is4xxClientError())
.andExpect(jsonPath("statusCode", is(400)))
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
.andExpect(jsonPath("message", is("Unable to find Debitor with uuid " + givenMainDebitorUuid)));
}
} }

View File

@ -1,6 +1,7 @@
package net.hostsharing.hsadminng.hs.office.membership; package net.hostsharing.hsadminng.hs.office.membership;
import com.vladmihalcea.hibernate.type.range.Range; import com.vladmihalcea.hibernate.type.range.Range;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import jakarta.persistence.PrePersist; import jakarta.persistence.PrePersist;
@ -26,17 +27,49 @@ class HsOfficeMembershipEntityUnitTest {
@Test @Test
void toStringContainsAllProps() { void toStringContainsAllProps() {
final var result = givenMembership.toString(); final var result = givenMembership.toString();
assertThat(result).isEqualTo("Membership(M-1000101, LEGAL Test Ltd., D-1000100, [2020-01-01,))");
assertThat(result).isEqualTo("Membership(M-1000101, LEGAL Test Ltd., 1000100, [2020-01-01,))");
} }
@Test @Test
void toShortStringContainsMemberNumberSuffixOnly() { void toShortStringContainsMemberNumberSuffixOnly() {
final var result = givenMembership.toShortString(); final var result = givenMembership.toShortString();
assertThat(result).isEqualTo("M-1000101"); assertThat(result).isEqualTo("M-1000101");
} }
@Test
void getMemberNumberWithPartnerAndSuffix() {
final var result = givenMembership.getMemberNumber();
assertThat(result).isEqualTo(1000101);
}
@Test
void getMemberNumberWithPartnerButWithoutSuffix() {
givenMembership.setMemberNumberSuffix(null);
final var result = givenMembership.getMemberNumber();
assertThat(result).isEqualTo(null);
}
@Test
void getMemberNumberWithoutPartnerButWithSuffix() {
givenMembership.setPartner(null);
final var result = givenMembership.getMemberNumber();
assertThat(result).isEqualTo(null);
}
@Test
void getMemberNumberWithoutPartnerNumberButWithSuffix() {
givenMembership.setPartner(HsOfficePartnerEntity.builder().build());
final var result = givenMembership.getMemberNumber();
assertThat(result).isEqualTo(null);
}
@Test
void getValidtyIfNull() {
givenMembership.setValidity(null);
final var result = givenMembership.getValidity();
assertThat(result).isEqualTo(Range.infinite(LocalDate.class));
}
@Test @Test
void initializesReasonForTerminationInPrePersistIfNull() throws Exception { void initializesReasonForTerminationInPrePersistIfNull() throws Exception {
final var givenUninitializedMembership = new HsOfficeMembershipEntity(); final var givenUninitializedMembership = new HsOfficeMembershipEntity();

View File

@ -184,9 +184,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
// then // then
exactlyTheseMembershipsAreReturned( exactlyTheseMembershipsAreReturned(
result, result,
"Membership(M-1000101, LEGAL First GmbH, 1000111, [2022-10-01,), NONE)", "Membership(M-1000101, LEGAL First GmbH, D-1000111, [2022-10-01,), NONE)",
"Membership(M-1000202, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)", "Membership(M-1000202, LEGAL Second e.K., D-1000212, [2022-10-01,), NONE)",
"Membership(M-1000303, SOLE_REPRESENTATION Third OHG, 1000313, [2022-10-01,), NONE)"); "Membership(M-1000303, SOLE_REPRESENTATION Third OHG, D-1000313, [2022-10-01,), NONE)");
} }
@Test @Test
@ -200,7 +200,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
// then // then
exactlyTheseMembershipsAreReturned(result, exactlyTheseMembershipsAreReturned(result,
"Membership(M-1000101, LEGAL First GmbH, 1000111, [2022-10-01,), NONE)"); "Membership(M-1000101, LEGAL First GmbH, D-1000111, [2022-10-01,), NONE)");
} }
@Test @Test
@ -209,11 +209,13 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
// when // when
final var result = membershipRepo.findMembershipsByMemberNumber(1000202); final var result = membershipRepo.findMembershipByMemberNumber(1000202);
// then // then
exactlyTheseMembershipsAreReturned(result, assertThat(result)
"Membership(M-1000202, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)"); .isNotNull()
.extracting(Object::toString)
.isEqualTo("Membership(M-1000202, LEGAL Second e.K., D-1000212, [2022-10-01,), NONE)");
} }
} }

View File

@ -211,6 +211,25 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
} }
} }
@Nested
class FindByPartnerNumber {
@Test
public void globalAdmin_withoutAssumedRole_canViewAllPartners() {
// given
context("superuser-alex@hostsharing.net");
// when
final var result = partnerRepo.findPartnerByPartnerNumber(10001);
// then
assertThat(result)
.isNotNull()
.extracting(Object::toString)
.isEqualTo("partner(LEGAL First GmbH: first contact)");
}
}
@Nested @Nested
class UpdatePartner { class UpdatePartner {

View File

@ -0,0 +1,32 @@
package net.hostsharing.hsadminng.hs.office.relationship;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
class HsOfficeRelationshipEntityUnitTest {
private HsOfficePersonEntity anchor = HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.LEGAL)
.tradeName("some trade name")
.build();
private HsOfficePersonEntity holder = HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.NATURAL)
.familyName("Meier")
.givenName("Mellie")
.build();
@Test
void toShortString() {
final var given = HsOfficeRelationshipEntity.builder()
.relType(HsOfficeRelationshipType.REPRESENTATIVE)
.relAnchor(anchor)
.relHolder(holder)
.build();
assertThat(given.toShortString()).isEqualTo("rel(relAnchor='LEGAL some trade name', relType='REPRESENTATIVE', relHolder='NATURAL Meier, Mellie')");
}
}

View File

@ -376,7 +376,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get() assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get()
.matches(mandate -> { .matches(mandate -> {
assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: LEGAL First GmbH: fir)"); assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(D-1000111: LEGAL First GmbH: fir)");
assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH"); assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH");
assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z - patched"); assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z - patched");
assertThat(mandate.getValidFrom()).isEqualTo("2020-06-05"); assertThat(mandate.getValidFrom()).isEqualTo("2020-06-05");
@ -417,7 +417,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
// finally, the sepaMandate is actually updated // finally, the sepaMandate is actually updated
assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get() assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get()
.matches(mandate -> { .matches(mandate -> {
assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: LEGAL First GmbH: fir)"); assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(D-1000111: LEGAL First GmbH: fir)");
assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH"); assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH");
assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z"); assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z");
assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2023-01-01)"); assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2023-01-01)");