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-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 java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.util.UUID;

View File

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

View File

@ -44,7 +44,9 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
Integer memberNumber) {
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,
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-";
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.getMainDebitor().toShortString())
.withProp(e -> e.getValidity().asString())
@ -91,25 +91,12 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
}
return validity;
}
public String getMemberNumberString() {
return MEMBER_NUMBER_TAG + getMemberNumber();
}
public Integer getMemberNumber() {
// TODO: refactor
String combinedMemberNumber;
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;
if (partner == null || partner.getPartnerNumber() == null || memberNumberSuffix == null ) {
return null;
}
return Optional.ofNullable(combinedMemberNumber).map(Integer::parseInt).orElse(null);
return getPartner().getPartnerNumber() * 100 + Integer.parseInt(memberNumberSuffix, 10);
}
@Override
@ -119,7 +106,7 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
@Override
public String toShortString() {
return "M-" + partner.getPartnerNumber() + String.valueOf(memberNumberSuffix);
return "M-" + getMemberNumber();
}
@PrePersist

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
get:
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:
- hs-office-memberships
operationId: listMemberships
@ -13,13 +14,13 @@ get:
schema:
type: string
format: uuid
description: UUID of the business partner.
- name: memberNumberSuffix
description: UUID of the business partner, exclusive to `memberNumber`.
- name: memberNumber
in: query
required: false
schema:
type: integer
description: Member number.
description: Member number, exclusive to `partnerUuid`.
responses:
"200":
description: OK

View File

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

View File

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

View File

@ -75,7 +75,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest {
void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffix() {
context.define("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202).get(0);
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202);
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("""
@ -109,7 +109,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest {
void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffixAndDateRange() {
context.define("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202).get(0);
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202);
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("""
@ -134,7 +134,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest {
void globalAdmin_canAddCoopSharesTransaction() {
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
.given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body("""
@ -165,7 +165,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest {
void globalAdmin_canNotCancelMoreSharesThanCurrentlySubscribed() {
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
.given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body("""

View File

@ -61,8 +61,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// given
context("superuser-alex@hostsharing.net");
final var count = coopSharesTransactionRepo.count();
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101)
.get(0);
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
// when
final var result = attempt(em, () -> {
@ -95,8 +94,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// when
attempt(em, () -> {
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101)
.get(0);
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
final var newCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder()
.membership(givenMembership)
.transactionType(HsOfficeCoopSharesTransactionType.SUBSCRIPTION)
@ -115,7 +113,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
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));
}
@ -142,25 +140,24 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// then
allTheseCoopSharesTransactionsAreReturned(
result,
"CoopShareTransaction(10001, 2010-03-15, SUBSCRIPTION, 4, ref 10001-1, initial subscription)",
"CoopShareTransaction(10001, 2021-09-01, CANCELLATION, -2, ref 10001-2, cancelling some)",
"CoopShareTransaction(10001, 2022-10-20, ADJUSTMENT, 2, ref 10001-3, some adjustment)",
"CoopShareTransaction(1000101, 2010-03-15, SUBSCRIPTION, 4, ref 1000101-1, initial subscription)",
"CoopShareTransaction(1000101, 2021-09-01, CANCELLATION, -2, ref 1000101-2, cancelling some)",
"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(10002, 2021-09-01, CANCELLATION, -2, ref 10002-2, cancelling some)",
"CoopShareTransaction(10002, 2022-10-20, ADJUSTMENT, 2, ref 10002-3, some adjustment)",
"CoopShareTransaction(1000202, 2010-03-15, SUBSCRIPTION, 4, ref 1000202-1, initial subscription)",
"CoopShareTransaction(1000202, 2021-09-01, CANCELLATION, -2, ref 1000202-2, cancelling some)",
"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(10003, 2021-09-01, CANCELLATION, -2, ref 10003-2, cancelling some)",
"CoopShareTransaction(10003, 2022-10-20, ADJUSTMENT, 2, ref 10003-3, some adjustment)");
"CoopShareTransaction(1000303, 2010-03-15, SUBSCRIPTION, 4, ref 1000303-1, initial subscription)",
"CoopShareTransaction(1000303, 2021-09-01, CANCELLATION, -2, ref 1000303-2, cancelling some)",
"CoopShareTransaction(1000303, 2022-10-20, ADJUSTMENT, 2, ref 1000303-3, some adjustment)");
}
@Test
public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuid() {
// given
context("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202)
.get(0);
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202);
// when
final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(
@ -171,17 +168,16 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// then
allTheseCoopSharesTransactionsAreReturned(
result,
"CoopShareTransaction(10002, 2010-03-15, SUBSCRIPTION, 4, ref 10002-1, initial subscription)",
"CoopShareTransaction(10002, 2021-09-01, CANCELLATION, -2, ref 10002-2, cancelling some)",
"CoopShareTransaction(10002, 2022-10-20, ADJUSTMENT, 2, ref 10002-3, some adjustment)");
"CoopShareTransaction(1000202, 2010-03-15, SUBSCRIPTION, 4, ref 1000202-1, initial subscription)",
"CoopShareTransaction(1000202, 2021-09-01, CANCELLATION, -2, ref 1000202-2, cancelling some)",
"CoopShareTransaction(1000202, 2022-10-20, ADJUSTMENT, 2, ref 1000202-3, some adjustment)");
}
@Test
public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuidAndValueDateRange() {
// given
context("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202)
.get(0);
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202);
// when
final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(
@ -192,7 +188,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// then
allTheseCoopSharesTransactionsAreReturned(
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
@ -209,9 +205,9 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// then:
exactlyTheseCoopSharesTransactionsAreReturned(
result,
"CoopShareTransaction(10001, 2010-03-15, SUBSCRIPTION, 4, ref 10001-1, initial subscription)",
"CoopShareTransaction(10001, 2021-09-01, CANCELLATION, -2, ref 10001-2, cancelling some)",
"CoopShareTransaction(10001, 2022-10-20, ADJUSTMENT, 2, ref 10001-3, some adjustment)");
"CoopShareTransaction(1000101, 2010-03-15, SUBSCRIPTION, 4, ref 1000101-1, initial subscription)",
"CoopShareTransaction(1000101, 2021-09-01, CANCELLATION, -2, ref 1000101-2, cancelling some)",
"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();
assertThat(result).isEqualTo("debitor(1234567: LEGAL some trade name: som)");
assertThat(result).isEqualTo("debitor(D-1234567: LEGAL some trade name: som)");
}
@Test
@ -46,7 +46,7 @@ class HsOfficeDebitorEntityUnitTest {
final var result = given.toString();
assertThat(result).isEqualTo("debitor(1234567: <person=null>)");
assertThat(result).isEqualTo("debitor(D-1234567: <person=null>)");
}
@Test
@ -60,6 +60,60 @@ class HsOfficeDebitorEntityUnitTest {
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
allTheseDebitorsAreReturned(
result,
"debitor(1000111: LEGAL First GmbH: fir)",
"debitor(1000212: LEGAL Second e.K.: sec)",
"debitor(1000313: SOLE_REPRESENTATION Third OHG: thi)");
"debitor(D-1000111: LEGAL First GmbH: fir)",
"debitor(D-1000212: LEGAL Second e.K.: sec)",
"debitor(D-1000313: SOLE_REPRESENTATION Third OHG: thi)");
}
@ParameterizedTest
@ -231,8 +231,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
// then:
exactlyTheseDebitorsAreReturned(result,
"debitor(1000111: LEGAL First GmbH: fir)",
"debitor(1000120: LEGAL First GmbH: fif)");
"debitor(D-1000111: LEGAL First GmbH: fir)",
"debitor(D-1000120: LEGAL First GmbH: fif)");
}
@Test
@ -260,7 +260,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
final var result = debitorRepo.findDebitorByDebitorNumber(1000313);
// 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");
// 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
EntityManager em;
private static int tempMemberNumberSuffix = 10;
private static int tempMemberNumberSuffix = 90; // TODO: check if we even need multiple distinct values
@Nested
@Accepts({ "Membership:F(Find)" })
@ -84,7 +84,8 @@ class HsOfficeMembershipControllerAcceptanceTest {
{
"partner": { "person": { "tradeName": "First GmbH" } },
"mainDebitor": { "debitorNumber": 1000111 },
"memberNumberSuffix": 10001,
"memberNumber": 1000101,
"memberNumberSuffix": "01",
"validFrom": "2022-10-01",
"validTo": null,
"reasonForTermination": "NONE"
@ -92,7 +93,8 @@ class HsOfficeMembershipControllerAcceptanceTest {
{
"partner": { "person": { "tradeName": "Second e.K." } },
"mainDebitor": { "debitorNumber": 1000212 },
"memberNumberSuffix": 10002,
"memberNumber": 1000202,
"memberNumberSuffix": "02",
"validFrom": "2022-10-01",
"validTo": null,
"reasonForTermination": "NONE"
@ -100,7 +102,69 @@ class HsOfficeMembershipControllerAcceptanceTest {
{
"partner": { "person": { "tradeName": "Third OHG" } },
"mainDebitor": { "debitorNumber": 1000313 },
"memberNumberSuffix": 10003,
"memberNumber": 1000303,
"memberNumberSuffix": "03",
"validFrom": "2022-10-01",
"validTo": null,
"reasonForTermination": "NONE"
}
]
"""));
// @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"
@ -121,6 +185,9 @@ class HsOfficeMembershipControllerAcceptanceTest {
context.define("superuser-alex@hostsharing.net");
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("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
.given()
@ -130,11 +197,11 @@ class HsOfficeMembershipControllerAcceptanceTest {
{
"partnerUuid": "%s",
"mainDebitorUuid": "%s",
"memberNumberSuffix": 20001,
"memberNumberSuffix": "%s",
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
""".formatted(givenPartner.getUuid(), givenDebitor.getUuid()))
""".formatted(givenPartner.getUuid(), givenDebitor.getUuid(), givenMemberSuffix))
.port(port)
.when()
.post("http://localhost/api/hs/office/memberships")
@ -145,7 +212,8 @@ class HsOfficeMembershipControllerAcceptanceTest {
.body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumber()))
.body("mainDebitor.debitorNumberSuffix", is((int) givenDebitor.getDebitorNumberSuffix()))
.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("validTo", equalTo(null))
.header("Location", startsWith("http://localhost"))
@ -166,9 +234,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
@Test
void globalAdmin_canGetArbitraryMembership() {
context.define("superuser-alex@hostsharing.net");
final var givenMembershipUuid = membershipRepo.findMembershipsByMemberNumber(1000101)
.get(0)
.getUuid();
final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000101).getUuid();
RestAssured // @formatter:off
.given()
@ -183,7 +249,8 @@ class HsOfficeMembershipControllerAcceptanceTest {
{
"partner": { "person": { "tradeName": "First GmbH" } },
"mainDebitor": { "debitorNumber": 1000111 },
"memberNumberSuffix": 10001,
"memberNumber": 1000101,
"memberNumberSuffix": "01",
"validFrom": "2022-10-01",
"validTo": null,
"reasonForTermination": "NONE"
@ -195,9 +262,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
@Accepts({ "Membership:X(Access Control)" })
void normalUser_canNotGetUnrelatedMembership() {
context.define("superuser-alex@hostsharing.net");
final var givenMembershipUuid = membershipRepo.findMembershipsByMemberNumber(1000101)
.get(0)
.getUuid();
final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000101).getUuid();
RestAssured // @formatter:off
.given()
@ -213,9 +278,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
@Accepts({ "Membership:X(Access Control)" })
void debitorAgentUser_canGetRelatedMembership() {
context.define("superuser-alex@hostsharing.net");
final var givenMembershipUuid = membershipRepo.findMembershipsByMemberNumber(1000303)
.get(0)
.getUuid();
final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000303).getUuid();
RestAssured // @formatter:off
.given()
@ -234,7 +297,8 @@ class HsOfficeMembershipControllerAcceptanceTest {
"debitorNumber": 1000313,
"billingContact": { "label": "third contact" }
},
"memberNumberSuffix": 10003,
"memberNumber": 1000303,
"memberNumberSuffix": "03",
"validFrom": "2022-10-01",
"validTo": null,
"reasonForTermination": "NONE"
@ -458,8 +522,10 @@ class HsOfficeMembershipControllerAcceptanceTest {
void cleanup() {
jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net", null);
final var query = em.createQuery("DELETE FROM HsOfficeMembershipEntity m WHERE m.memberNumberSuffix >= '20'");
query.executeUpdate();
});
final var query = em.createQuery("DELETE FROM HsOfficeMembershipEntity m WHERE m.memberNumberSuffix >= '90'");
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.mapper.Mapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
@ -22,6 +25,7 @@ import jakarta.persistence.SynchronizationType;
import java.util.Map;
import java.util.UUID;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
@ -59,6 +63,8 @@ public class HsOfficeMembershipControllerRestTest {
when(emf.createEntityManager(any(SynchronizationType.class), any(Map.class))).thenReturn(em);
}
@Nested
class AddMembership {
@Test
void respondBadRequest_ifPartnerUuidIsMissing() throws Exception {
@ -71,7 +77,7 @@ public class HsOfficeMembershipControllerRestTest {
{
"partnerUuid": null,
"mainDebitorUuid": "%s",
"memberNumberSuffix": 20001,
"memberNumberSuffix": "01",
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
@ -97,7 +103,7 @@ public class HsOfficeMembershipControllerRestTest {
{
"partnerUuid": "%s",
"mainDebitorUuid": null,
"memberNumberSuffix": 20001,
"memberNumberSuffix": "01",
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
@ -129,7 +135,7 @@ public class HsOfficeMembershipControllerRestTest {
{
"partnerUuid": "%s",
"mainDebitorUuid": "%s",
"memberNumberSuffix": 20001,
"memberNumberSuffix": "01",
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
@ -161,7 +167,7 @@ public class HsOfficeMembershipControllerRestTest {
{
"partnerUuid": "%s",
"mainDebitorUuid": "%s",
"memberNumberSuffix": 20001,
"memberNumberSuffix": "01",
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
@ -174,4 +180,49 @@ public class HsOfficeMembershipControllerRestTest {
.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;
}
}
}
}

View File

@ -1,6 +1,7 @@
package net.hostsharing.hsadminng.hs.office.membership;
import com.vladmihalcea.hibernate.type.range.Range;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import org.junit.jupiter.api.Test;
import jakarta.persistence.PrePersist;
@ -26,17 +27,49 @@ class HsOfficeMembershipEntityUnitTest {
@Test
void toStringContainsAllProps() {
final var result = givenMembership.toString();
assertThat(result).isEqualTo("Membership(M-1000101, LEGAL Test Ltd., 1000100, [2020-01-01,))");
assertThat(result).isEqualTo("Membership(M-1000101, LEGAL Test Ltd., D-1000100, [2020-01-01,))");
}
@Test
void toShortStringContainsMemberNumberSuffixOnly() {
final var result = givenMembership.toShortString();
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
void initializesReasonForTerminationInPrePersistIfNull() throws Exception {
final var givenUninitializedMembership = new HsOfficeMembershipEntity();

View File

@ -184,9 +184,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
// then
exactlyTheseMembershipsAreReturned(
result,
"Membership(M-1000101, LEGAL First GmbH, 1000111, [2022-10-01,), NONE)",
"Membership(M-1000202, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)",
"Membership(M-1000303, SOLE_REPRESENTATION Third OHG, 1000313, [2022-10-01,), NONE)");
"Membership(M-1000101, LEGAL First GmbH, D-1000111, [2022-10-01,), NONE)",
"Membership(M-1000202, LEGAL Second e.K., D-1000212, [2022-10-01,), NONE)",
"Membership(M-1000303, SOLE_REPRESENTATION Third OHG, D-1000313, [2022-10-01,), NONE)");
}
@Test
@ -200,7 +200,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
// then
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
@ -209,11 +209,13 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
context("superuser-alex@hostsharing.net");
// when
final var result = membershipRepo.findMembershipsByMemberNumber(1000202);
final var result = membershipRepo.findMembershipByMemberNumber(1000202);
// then
exactlyTheseMembershipsAreReturned(result,
"Membership(M-1000202, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)");
assertThat(result)
.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
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");
assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get()
.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.getReference()).isEqualTo("temp ref CAT Z - patched");
assertThat(mandate.getValidFrom()).isEqualTo("2020-06-05");
@ -417,7 +417,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
// finally, the sepaMandate is actually updated
assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get()
.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.getReference()).isEqualTo("temp ref CAT Z");
assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2023-01-01)");