diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java index b1dd3172..1e82b848 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java @@ -57,12 +57,15 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi { final String currentSubject, final String assumedRoles, final String name, + final UUID partnerUuid, final String partnerNumber) { context.define(currentSubject, assumedRoles); final var entities = partnerNumber != null - ? debitorRepo.findDebitorByPartnerNumber(cropTag("P-", partnerNumber)) - : debitorRepo.findDebitorByOptionalNameLike(name); + ? debitorRepo.findDebitorsByPartnerNumber(cropTag("P-", partnerNumber)) + : partnerUuid != null + ? debitorRepo.findDebitorsByPartnerUuid(partnerUuid) + : debitorRepo.findDebitorsByOptionalNameLike(name); final var resources = mapper.mapList(entities, HsOfficeDebitorResource.class, ENTITY_TO_RESOURCE_POSTMAPPER); return ResponseEntity.ok(resources); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java index a48bfa2b..fa02053b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java @@ -14,6 +14,9 @@ public interface HsOfficeDebitorRepository extends Repository findByUuid(UUID id); + @Timed("app.office.debitors.repo.findDebitorByPartnerUuid") + List findDebitorsByPartnerUuid(UUID partnerUuid); + @Query(""" SELECT debitor FROM HsOfficeDebitorEntity debitor JOIN HsOfficePartnerEntity partner @@ -32,7 +35,7 @@ public interface HsOfficeDebitorRepository extends Repository findDebitorByPartnerNumber(int partnerNumber) { + default List findDebitorsByPartnerNumber(int partnerNumber) { final var result = findDebitorByPartnerNumberAndOptionalDebitorNumberSuffix(partnerNumber, null); return result; } @@ -56,7 +59,7 @@ public interface HsOfficeDebitorRepository extends Repository findDebitorByOptionalNameLike(String name); + List findDebitorsByOptionalNameLike(String name); @Timed("app.office.debitors.repo.save") HsOfficeDebitorEntity save(final HsOfficeDebitorEntity entity); diff --git a/src/main/resources/api-definition/hs-office/hs-office-debitors-with-debitorNumber.yaml b/src/main/resources/api-definition/hs-office/hs-office-debitors-with-debitorNumber.yaml index 9e34b758..565f2523 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-debitors-with-debitorNumber.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-debitors-with-debitorNumber.yaml @@ -12,8 +12,8 @@ get: schema: type: number format: integer - minimum: 1000000 - maximum: 9999999 +# minimum: 1000000 +# maximum: 9999999 description: debitor-number of the debitor to fetch. responses: "200": diff --git a/src/main/resources/api-definition/hs-office/hs-office-debitors.yaml b/src/main/resources/api-definition/hs-office/hs-office-debitors.yaml index 74fab6dc..05533d03 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-debitors.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-debitors.yaml @@ -13,13 +13,20 @@ get: schema: type: string description: Prefix of name properties from person or contact to filter the results. + - name: partnerUuid + in: query + required: false + schema: + type: string + format: uuid + description: UUID of the business partner, exclusive to `memberNumber`. - name: partnerNumber in: query required: false schema: type: string - minLength: 9 - maxLength: 9 + minLength: 7 + maxLength: 7 pattern: 'P-[0-9]{5}' description: Partner number of the requested debitor. responses: diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemRepositoryIntegrationTest.java index 091c2c62..b6c8d757 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemRepositoryIntegrationTest.java @@ -120,7 +120,7 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup // given context("superuser-alex@hostsharing.net"); final var count = rbacBookingItemRepo.count(); - final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0); + final var givenDebitor = debitorRepo.findDebitorsByOptionalNameLike("First").get(0); final var givenProject = realProjectRepo.findAllByDebitorUuid(givenDebitor.getUuid()).get(0); // when @@ -151,7 +151,7 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup // when attempt(em, () -> { - final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0); + final var givenDebitor = debitorRepo.findDebitorsByOptionalNameLike("First").get(0); final var givenProject = realProjectRepo.findAllByDebitorUuid(givenDebitor.getUuid()).get(0); final var newBookingItem = HsBookingItemRbacEntity.builder() .project(givenProject) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java index 4fb18e61..a05687e4 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java @@ -31,7 +31,10 @@ import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType. import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid; import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.startsWith; @SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, @@ -74,6 +77,69 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu @PersistenceContext EntityManager em; + @Nested + class GetSingleDebitor { + + @Test + void globalAdmin_withoutAssumedRoles_canGetDebitorByDebitorUuid() { + + final var givenDebitor = jpaAttempt.transacted(() -> { + context("superuser-alex@hostsharing.net"); + return debitorRepo.findDebitorByDebitorNumber(1000212).orElseThrow(); + }).assertSuccessful().returnedValue(); + + RestAssured // @formatter:off + .given() + .header("current-subject", "superuser-alex@hostsharing.net") + .port(port) + .when() + .get("http://localhost/api/hs/office/debitors/" + givenDebitor.getUuid()) + .then().log().all().assertThat() + .statusCode(200) + .contentType("application/json") + .body("", lenientlyEquals(""" + { + "debitorNumber": "D-1000212", + "partner": { "partnerNumber": "P-10002" }, + "debitorRel": { + "contact": { "caption": "second contact" } + }, + "vatId": null, + "vatCountryCode": null, + "vatBusiness": true + } + """)); + // @formatter:on + } + + @Test + void globalAdmin_withoutAssumedRoles_canGetDebitorByDebitorNumber() { + + RestAssured // @formatter:off + .given() + .header("current-subject", "superuser-alex@hostsharing.net") + .port(port) + .when() + .get("http://localhost/api/hs/office/debitors/D-1000212") + .then().log().all().assertThat() + .statusCode(200) + .contentType("application/json") + .body("", lenientlyEquals(""" + { + "debitorNumber": "D-1000212", + "partner": { "partnerNumber": "P-10002" }, + "debitorRel": { + "contact": { "caption": "second contact" } + }, + "vatId": null, + "vatCountryCode": null, + "vatBusiness": true + } + """)); + // @formatter:on + } + } + @Nested class GetListOfDebitors { @@ -232,80 +298,27 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu // @formatter:on } - @Test - void globalAdmin_withoutAssumedRoles_canGetDebitorByDebitorNumber() { - - RestAssured // @formatter:off - .given() - .header("current-subject", "superuser-alex@hostsharing.net") - .port(port) - .when() - .get("http://localhost/api/hs/office/debitors/P-10002") - .then().log().all().assertThat() - .statusCode(200) - .contentType("application/json") - .body("", lenientlyEquals(""" - [ - { - "debitorNumber": "D-1000211", - "partner": { "partnerNumber": "P-10002" }, - "debitorRel": { - "contact": { "caption": "second contact" } - }, - "vatId": null, - "vatCountryCode": null, - "vatBusiness": true - }, - { - "debitorNumber": "D-1000212", - "partner": { "partnerNumber": "P-10002" }, - "debitorRel": { - "contact": { "caption": "second contact" } - }, - "vatId": null, - "vatCountryCode": null, - "vatBusiness": true - }, - { - "debitorNumber": "D-1000213", - "partner": { "partnerNumber": "P-10002" }, - "debitorRel": { - "contact": { "caption": "second contact" } - }, - "vatId": null, - "vatCountryCode": null, - "vatBusiness": true - } - ] - """)); - // @formatter:on - } - @Test void globalAdmin_withoutAssumedRoles_canFindDebitorsByPartnerNumber() { RestAssured // @formatter:off - .given() + .given() .header("current-subject", "superuser-alex@hostsharing.net") .port(port) - .when() - .get("http://localhost/api/hs/office/debitors?debitorNumber=D-1000212") - .then().log().all().assertThat() + .when() + .get("http://localhost/api/hs/office/debitors?partnerNumber=P-10002") + .then().log().all().assertThat() .statusCode(200) .contentType("application/json") .body("", lenientlyEquals(""" - [ - { - "debitorNumber": "D-1000212", - "partner": { "partnerNumber": "P-10002" }, - "debitorRel": { - "contact": { "caption": "second contact" } - }, - "vatId": null, - "vatCountryCode": null, - "vatBusiness": true - } - ] + [ + { + "debitorNumber": "D-1000212", + "partner": { + "partnerNumber": "P-10002" + } + } + ] """)); // @formatter:on } @@ -493,7 +506,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu @Test void globalAdmin_withoutAssumedRole_canGetArbitraryDebitor() { context.define("superuser-alex@hostsharing.net"); - final var givenDebitorUuid = debitorRepo.findDebitorByOptionalNameLike("First").get(0).getUuid(); + final var givenDebitorUuid = debitorRepo.findDebitorsByOptionalNameLike("First").get(0).getUuid(); RestAssured // @formatter:off .given() @@ -558,7 +571,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu @Test void normalUser_canNotGetUnrelatedDebitor() { context.define("superuser-alex@hostsharing.net"); - final var givenDebitorUuid = debitorRepo.findDebitorByOptionalNameLike("First").get(0).getUuid(); + final var givenDebitorUuid = debitorRepo.findDebitorsByOptionalNameLike("First").get(0).getUuid(); RestAssured // @formatter:off .given() @@ -573,7 +586,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu @Test void contactAdminUser_canGetRelatedDebitorExceptRefundBankAccount() { context.define("superuser-alex@hostsharing.net"); - final var givenDebitorUuid = debitorRepo.findDebitorByOptionalNameLike("first contact").get(0).getUuid(); + final var givenDebitorUuid = debitorRepo.findDebitorsByOptionalNameLike("first contact").get(0).getUuid(); RestAssured // @formatter:off .given() diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java index f821d3b6..0be9d9ca 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java @@ -234,7 +234,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean context("superuser-alex@hostsharing.net"); // when - final var result = debitorRepo.findDebitorByOptionalNameLike(null); + final var result = debitorRepo.findDebitorsByOptionalNameLike(null); // then allTheseDebitorsAreReturned( @@ -256,7 +256,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean context("superuser-alex@hostsharing.net", assumedRole); // when: - final var result = debitorRepo.findDebitorByOptionalNameLike(""); + final var result = debitorRepo.findDebitorsByOptionalNameLike(""); // then: exactlyTheseDebitorsAreReturned(result, @@ -270,7 +270,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean context("selfregistered-test-user@hostsharing.org"); // when: - final var result = debitorRepo.findDebitorByOptionalNameLike(null); + final var result = debitorRepo.findDebitorsByOptionalNameLike(null); // then: assertThat(result).isEmpty(); @@ -303,7 +303,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean context("superuser-alex@hostsharing.net"); // when - final var result = debitorRepo.findDebitorByPartnerNumber(10003); + final var result = debitorRepo.findDebitorsByPartnerNumber(10003); // then exactlyTheseDebitorsAreReturned(result, @@ -320,7 +320,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean context("superuser-alex@hostsharing.net"); // when - final var result = debitorRepo.findDebitorByOptionalNameLike("third contact"); + final var result = debitorRepo.findDebitorsByOptionalNameLike("third contact"); // then exactlyTheseDebitorsAreReturned(result, "debitor(D-1000313: rel(anchor='IF Third OHG', type='DEBITOR', holder='IF Third OHG'), thi)"); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java index b7198aaa..ff5c4e46 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java @@ -138,7 +138,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl void globalAdmin_canPostNewSepaMandate() { context.define("superuser-alex@hostsharing.net"); - final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("Third").get(0); + final var givenDebitor = debitorRepo.findDebitorsByOptionalNameLike("Third").get(0); final var givenBankAccount = bankAccountRepo.findByIbanOrderByIbanAsc("DE02200505501015871393").get(0); final var location = RestAssured // @formatter:off @@ -180,7 +180,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl void globalAdmin_canNotPostNewSepaMandateWhenDebitorUuidIsMissing() { context.define("superuser-alex@hostsharing.net"); - final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("Third").get(0); + final var givenDebitor = debitorRepo.findDebitorsByOptionalNameLike("Third").get(0); final var givenBankAccount = bankAccountRepo.findByIbanOrderByIbanAsc("DE02200505501015871393").get(0); final var location = RestAssured // @formatter:off @@ -205,7 +205,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl void globalAdmin_canNotPostNewSepaMandate_ifBankAccountDoesNotExist() { context.define("superuser-alex@hostsharing.net"); - final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("Third").get(0); + final var givenDebitor = debitorRepo.findDebitorsByOptionalNameLike("Third").get(0); final var givenBankAccountUuid = UUID.fromString("00000000-0000-0000-0000-000000000000"); final var location = RestAssured // @formatter:off diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java index 8046fc68..8e7512c3 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java @@ -66,7 +66,7 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC // given context("superuser-alex@hostsharing.net"); final var count = sepaMandateRepo.count(); - final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0); + final var givenDebitor = debitorRepo.findDebitorsByOptionalNameLike("First").get(0); final var givenBankAccount = bankAccountRepo.findByOptionalHolderLike("Paul Winkler").get(0); // when @@ -100,7 +100,7 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC // when attempt(em, () -> { - final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0); + final var givenDebitor = debitorRepo.findDebitorsByOptionalNameLike("First").get(0); final var givenBankAccount = bankAccountRepo.findByOptionalHolderLike("Paul Winkler").get(0); final var newSepaMandate = HsOfficeSepaMandateEntity.builder() .debitor(givenDebitor) @@ -397,7 +397,7 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC private HsOfficeSepaMandateEntity givenSomeTemporarySepaMandate(final String iban) { return jpaAttempt.transacted(() -> { context("superuser-alex@hostsharing.net"); - final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0); + final var givenDebitor = debitorRepo.findDebitorsByOptionalNameLike("First").get(0); final var givenBankAccount = bankAccountRepo.findByIbanOrderByIbanAsc(iban).get(0); final var newSepaMandate = HsOfficeSepaMandateEntity.builder() .debitor(givenDebitor)