From c98a5acb385a90568b38a3fd8de1306d7afc9bee Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 15 Nov 2024 11:54:18 +0100 Subject: [PATCH] OfficeScenarioTests CoopShares+Assets (#121) Co-authored-by: Michael Hoennig Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/121 Reviewed-by: Marc Sandlus --- .aliases | 3 + build.gradle | 7 +- doc/business-glossary-de.md | 110 +++++++++++++++++ .../{template.html => .template.html} | 0 doc/scenarios/README.txt | 1 + .../config/JsonObjectMapperConfiguration.java | 8 +- .../hsadminng/errors/CustomErrorResponse.java | 1 + ...OfficeCoopAssetsTransactionController.java | 12 +- .../HsOfficeCoopAssetsTransactionEntity.java | 16 +-- .../HsOfficeCoopAssetsTransactionType.java | 2 +- ...OfficeCoopSharesTransactionController.java | 12 +- .../HsOfficeCoopSharesTransactionEntity.java | 16 +-- .../HsOfficeCoopSharesTransactionType.java | 4 +- .../HsOfficeMembershipController.java | 4 +- .../HsOfficeMembershipRepository.java | 10 +- .../partner/HsOfficePartnerController.java | 1 + .../person/HsOfficePersonTypeConverter.java | 1 + .../hsadminng/system/SystemProcess.java | 2 +- .../hs-office-coopassets-schemas.yaml | 10 +- .../hs-office-coopassets-with-uuid.yaml | 2 +- .../hs-office/hs-office-coopassets.yaml | 4 +- .../hs-office-coopshares-schemas.yaml | 10 +- .../hs-office-coopshares-with-uuid.yaml | 2 +- .../hs-office/hs-office-coopshares.yaml | 4 +- .../503-relation/5030-hs-office-relation.sql | 3 + .../5110-hs-office-coopshares.sql | 8 +- .../5118-hs-office-coopshares-test-data.sql | 4 +- .../5120-hs-office-coopassets.sql | 16 +-- .../5128-hs-office-coopassets-test-data.sql | 4 +- .../hs/migration/BaseOfficeDataImport.java | 26 ++-- ...tsTransactionControllerAcceptanceTest.java | 36 +++--- ...opAssetsTransactionControllerRestTest.java | 2 +- ...ceCoopAssetsTransactionEntityUnitTest.java | 12 +- ...sTransactionRepositoryIntegrationTest.java | 24 ++-- ...esTransactionControllerAcceptanceTest.java | 44 +++---- ...opSharesTransactionControllerRestTest.java | 2 +- ...ceCoopSharesTransactionEntityUnitTest.java | 12 +- ...sTransactionRepositoryIntegrationTest.java | 20 ++-- .../HsOfficeMembershipControllerRestTest.java | 33 ++++- .../scenarios/HsOfficeScenarioTests.java | 113 +++++++++++++++--- .../hs/office/scenarios/PathAssertion.java | 32 ++++- .../hs/office/scenarios/ScenarioTest.java | 19 +-- .../hs/office/scenarios/TemplateResolver.java | 52 +++++--- .../scenarios/TemplateResolverUnitTest.java | 3 +- .../hs/office/scenarios/TestReport.java | 68 +++++++++-- .../hs/office/scenarios/UseCase.java | 74 ++++++++---- .../membership/CreateMembership.java | 14 ++- .../CreateCoopAssetsDepositTransaction.java | 12 ++ .../CreateCoopAssetsDisbursalTransaction.java | 17 +++ .../CreateCoopAssetsRevertTransaction.java | 27 +++++ .../CreateCoopAssetsTransaction.java | 53 ++++++++ ...eateCoopSharesCancellationTransaction.java | 17 +++ .../CreateCoopSharesRevertTransaction.java | 27 +++++ ...eateCoopSharesSubscriptionTransaction.java | 12 ++ .../CreateCoopSharesTransaction.java | 53 ++++++++ .../scenarios/partner/CreatePartner.java | 2 + 56 files changed, 836 insertions(+), 247 deletions(-) create mode 100644 doc/business-glossary-de.md rename doc/scenarios/{template.html => .template.html} (100%) create mode 100644 doc/scenarios/README.txt create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsDepositTransaction.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsDisbursalTransaction.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsRevertTransaction.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsTransaction.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesCancellationTransaction.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesRevertTransaction.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesSubscriptionTransaction.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesTransaction.java diff --git a/.aliases b/.aliases index b57cd717..8f8fafc2 100644 --- a/.aliases +++ b/.aliases @@ -95,3 +95,6 @@ if [ ! -f .environment ]; then cp .tc-environment .environment fi source .environment + +alias scenario-reports-upload='./gradlew scenarioTests convertMarkdownToHtml && ssh hsh03-hsngdev@h50.hostsharing.net "rm -f doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office/*.html" && scp build/doc/scenarios/*.html hsh03-hsngdev@h50.hostsharing.net:doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office' +alias scenario-reports-open='open https://hsngdev.hs-example.de/scenarios/office' diff --git a/build.gradle b/build.gradle index ed7a290d..04edf341 100644 --- a/build.gradle +++ b/build.gradle @@ -410,7 +410,7 @@ tasks.register('convertMarkdownToHtml') { group = 'Conversion' // Define the template file and input directory - def templateFile = file('doc/scenarios/template.html') + def templateFile = file('doc/scenarios/.template.html') // Task configuration and execution doFirst { @@ -425,13 +425,13 @@ tasks.register('convertMarkdownToHtml') { // Check if the template file exists if (!templateFile.exists()) { - throw new GradleException("Template file 'doc/scenarios/template.html' not found.") + throw new GradleException("Template file 'doc/scenarios/.template.html' not found.") } } doLast { // Gather all Markdown files in the current directory - fileTree(dir: '.', include: 'doc/scenarios/*.md').each { file -> + fileTree(dir: '.', include: 'build/doc/scenarios/*.md').each { file -> // Corrected way to create the output file path def outputFile = new File(file.parent, file.name.replaceAll(/\.md$/, '.html')) @@ -444,3 +444,4 @@ tasks.register('convertMarkdownToHtml') { } } } +convertMarkdownToHtml.dependsOn scenarioTests diff --git a/doc/business-glossary-de.md b/doc/business-glossary-de.md new file mode 100644 index 00000000..65d316a1 --- /dev/null +++ b/doc/business-glossary-de.md @@ -0,0 +1,110 @@ +### hsadminNg fachliches Glossar + + + +Dieses ist eine Sammlung von Fachbegriffen, die in diesem Projekt benutzt werden. +Ebenfalls aufgenommen sind technische Begriffe, die für Benutzer für das Verständnis der Schnittstellen nötig sind. + +Falls etwas fehlt, bitte Bescheid geben. + + +#### Partner + +In diesem System ist ein _Partner_ grundsätzlich jeglicher Geschäftspartner der _Hostsharing eG_. +Dies können grundsätzlich Kunden, siehe [Debitor](#Debitor), wie Lieferanten sein. +Derzeit sind aber nur Debitoren implementiert. + +Des Weiteren gibt es für jeden _Partner_ eine fünfstellige Partnernummer mit dem Prefix 'P-' (z.B. `P-123454`) +sowie Zusatzinformationen (z.B. Registergerichtnummer oder Geburtsdatum), die zur genauen Identifikation benötigt werden. + +Für einen _Partner_ kann es gleichzeitig mehrere [Debitoren](#Debitor) +und zeitlich nacheinander mehrere [Mitgliedschaften](#Mitgliedschaft) geben. + +Partner sind grundsätzlich als ist [Relation](#Relation) der Vertragsperson mit der Person _Hostsharing eG_ implementiert. + + +### Debitor + +Ein `Debitor` ist quasi ein Rechnungsempfänger für einen [Partner](#Partner). + +Für einen _Partner_ kann es gleichzeitig mehrere [Debitoren](#Debitor) geben, +z.B. für spezielle Projekte des Kunden oder verbundene Organisationen. + +Des Weiteren gibt es für jeden _Partner_ eine fünfstellige Partnernummer mit dem Prefix 'P-' (z.B. `P-123454`) +sowie Zusatzinformationen (z.B. Registergerichtsnummer oder Geburtsdatum), die zur genauen Identifikation benötigt werden. + +Debitoren sind grundsätzlich als ist [Relation](#Relation) der Vertragsperson mit der Person des Vertragspartners implementiert. + + +#### Relation + +Eine _Relation_ ist eine typisierte und mit Kontaktdaten versehene Beziehung einer (_Holder_)-Person zu einer _Anchor_-Person. + +Eine Relation ist eine Art Geschäftsrolle, wir haben hier aber keinen Begriff mit 'Rolle' verwendet, +weil 'Role' (engl.) zu leicht mit der [RBAC-Rolle](#RBAC-Role) verwechselt werden könnte. + +Die _Relation_ ist auch ein technisches Konzept und gehört nicht zur Domänensprache. +Dieses Konzept ist jedoch für das Verständnis der ([API](#API)) notwendig. + + +#### Ex-Partner + +Ex-Partner bilden [Personen](#Person) ab, die vormals [Partner](#Partner) waren. +Diese bleiben dadurch informationshalber im System verfügbar. + +Implementiert ist der _Ex-Partner_ als eine besondere Form der [Relation](#Relation) +der Person des Ex-Partner (_Holder_) zum neuen Partner (_Anchor_) dargestellt. +Dieses kann zu einer Kettenbildung führen. + + +#### Representative-Contact (ehemals _contractual_) + +Ein _Representative_ ist eine natürliche Person, die für eine nicht-natürliche Person vertretungsberechtigt ist. + +Implementiert ist der _Representative_ als eine besondere Form der [Relation](#Relation) +der Person des Repräsentanten (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt. + + +### VIP-Contact + +Ein _VIP-Contact_ ist eine natürliche Person, die für einen Geschäftspartner eine wichtige Funktion übernimmt, +nicht aber deren offizieller Repräsentant ist. + +Implementiert ist der _VIP-Contact_ als eine besondere Form der [Relation](#Relation) +der Person des VIP-Contact (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt. + + +### Operations-Contact + +Ein _Operations-_Contact_ ist_ eine natürliche Person, die für einen Geschäftspartner technischer Ansprechpartner ist + +Implementiert ist der _Operations-Contact_ als eine besondere Form der [Relation](#Relation) +der Person des _Operations-Contact_ (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt. + + +### Subscriber-Contact + +Ein _Subscriber-_Contact_ ist_ eine natürliche Person, die für einen Geschäftspartner eine bestimmte Mailingliste abonniert. + +Implementiert ist der _Subscriber-Contact_ als eine besondere Form der [Relation](#Relation) +der Person des _Subscriber-Contact_ (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt. +Zusätzlich wird diese Relation mit dem Kurznamen der abonnierten Mailingliste markiert. + + +#### Anchor / Relation-Anchor + +siehe [Relation](#Relation) + + +#### Holder / Relation-Holder + +siehe [Relation](#Relation) + + +#### API + +Und API (Application-Programming-Interface) verstehen wir eine über HTTPS angesprochene programmatisch bedienbare Schnittstell +zur Funktionalität des hsAdmin-NG-Systems. diff --git a/doc/scenarios/template.html b/doc/scenarios/.template.html similarity index 100% rename from doc/scenarios/template.html rename to doc/scenarios/.template.html diff --git a/doc/scenarios/README.txt b/doc/scenarios/README.txt new file mode 100644 index 00000000..bcba4110 --- /dev/null +++ b/doc/scenarios/README.txt @@ -0,0 +1 @@ +find the generated ScenarioReports in build/doc/scenarios diff --git a/src/main/java/net/hostsharing/hsadminng/config/JsonObjectMapperConfiguration.java b/src/main/java/net/hostsharing/hsadminng/config/JsonObjectMapperConfiguration.java index b7011f7f..d5ff80d9 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/JsonObjectMapperConfiguration.java +++ b/src/main/java/net/hostsharing/hsadminng/config/JsonObjectMapperConfiguration.java @@ -1,6 +1,7 @@ package net.hostsharing.hsadminng.config; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.openapitools.jackson.nullable.JsonNullableModule; @@ -9,15 +10,20 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; + @Configuration public class JsonObjectMapperConfiguration { @Bean @Primary public Jackson2ObjectMapperBuilder customObjectMapper() { + // HOWTO: add JSON converters and specify other JSON mapping configurations return new Jackson2ObjectMapperBuilder() .modules(new JsonNullableModule(), new JavaTimeModule()) - .featuresToEnable(JsonParser.Feature.ALLOW_COMMENTS, JsonParser.Feature.ALLOW_COMMENTS) + .featuresToEnable( + JsonParser.Feature.ALLOW_COMMENTS, + DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS + ) .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); } } diff --git a/src/main/java/net/hostsharing/hsadminng/errors/CustomErrorResponse.java b/src/main/java/net/hostsharing/hsadminng/errors/CustomErrorResponse.java index 3df51ebb..2455ee76 100644 --- a/src/main/java/net/hostsharing/hsadminng/errors/CustomErrorResponse.java +++ b/src/main/java/net/hostsharing/hsadminng/errors/CustomErrorResponse.java @@ -46,6 +46,7 @@ public class CustomErrorResponse { this.path = path; this.statusCode = status.value(); this.statusPhrase = status.getReasonPhrase(); + // HOWTO: debug serverside error response - set a breakpoint here this.message = message.startsWith("ERROR: [") ? message : "ERROR: [" + statusCode + "] " + message; } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java index b8da7580..06bec54e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java @@ -36,7 +36,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse @Override @Transactional(readOnly = true) - public ResponseEntity> listCoopAssets( + public ResponseEntity> getListOfCoopAssets( final String currentSubject, final String assumedRoles, final UUID membershipUuid, @@ -55,7 +55,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse @Override @Transactional - public ResponseEntity addCoopAssetsTransaction( + public ResponseEntity postNewCoopAssetTransaction( final String currentSubject, final String assumedRoles, final HsOfficeCoopAssetsTransactionInsertResource requestBody) { @@ -77,7 +77,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse @Override @Transactional(readOnly = true) - public ResponseEntity getCoopAssetTransactionByUuid( + public ResponseEntity getSingleCoopAssetTransactionByUuid( final String currentSubject, final String assumedRoles, final UUID assetTransactionUuid) { context.define(currentSubject, assumedRoles); @@ -128,9 +128,9 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse } final BiConsumer RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { - if ( resource.getReverseEntryUuid() != null ) { - entity.setAdjustedAssetTx(coopAssetsTransactionRepo.findByUuid(resource.getReverseEntryUuid()) - .orElseThrow(() -> new EntityNotFoundException("ERROR: [400] reverseEntityUuid %s not found".formatted(resource.getReverseEntryUuid())))); + if ( resource.getRevertedAssetTxUuid() != null ) { + entity.setRevertedAssetTx(coopAssetsTransactionRepo.findByUuid(resource.getRevertedAssetTxUuid()) + .orElseThrow(() -> new EntityNotFoundException("ERROR: [400] reverseEntityUuid %s not found".formatted(resource.getRevertedAssetTxUuid())))); } }; }; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java index 20ef39b4..18e12a11 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java @@ -50,8 +50,8 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE .withProp(HsOfficeCoopAssetsTransactionEntity::getAssetValue) .withProp(HsOfficeCoopAssetsTransactionEntity::getReference) .withProp(HsOfficeCoopAssetsTransactionEntity::getComment) - .withProp(at -> ofNullable(at.getAdjustedAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null)) - .withProp(at -> ofNullable(at.getAdjustmentAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null)) + .withProp(at -> ofNullable(at.getRevertedAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null)) + .withProp(at -> ofNullable(at.getReversalAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null)) .quotedValues(false); @Id @@ -77,7 +77,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE * The signed value which directly affects the booking balance. * *

This means, that a DEPOSIT is always positive, a DISBURSAL is always negative, - * but an ADJUSTMENT can bei either positive or negative. + * but an REVERSAL can bei either positive or negative. * See {@link HsOfficeCoopAssetsTransactionType} for

more information. */ @Column(name = "assetvalue") @@ -96,14 +96,14 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE private String comment; /** - * Optionally, the UUID of the corresponding transaction for an adjustment transaction. + * Optionally, the UUID of the corresponding transaction for an reversal transaction. */ @OneToOne - @JoinColumn(name = "adjustedassettxuuid") - private HsOfficeCoopAssetsTransactionEntity adjustedAssetTx; + @JoinColumn(name = "revertedassettxuuid") + private HsOfficeCoopAssetsTransactionEntity revertedAssetTx; - @OneToOne(mappedBy = "adjustedAssetTx") - private HsOfficeCoopAssetsTransactionEntity adjustmentAssetTx; + @OneToOne(mappedBy = "revertedAssetTx") + private HsOfficeCoopAssetsTransactionEntity reversalAssetTx; @Override public HsOfficeCoopAssetsTransactionEntity load() { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionType.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionType.java index d7397622..ee53bec4 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionType.java @@ -4,7 +4,7 @@ public enum HsOfficeCoopAssetsTransactionType { /** * correction of wrong bookings, value can be positive or negative */ - ADJUSTMENT, + REVERSAL, /** * payment received from member after signing shares, value >0 diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java index 6712fefd..ebd47800 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java @@ -38,7 +38,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar @Override @Transactional(readOnly = true) - public ResponseEntity> listCoopShares( + public ResponseEntity> getListOfCoopShares( final String currentSubject, final String assumedRoles, final UUID membershipUuid, @@ -57,7 +57,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar @Override @Transactional - public ResponseEntity addCoopSharesTransaction( + public ResponseEntity postNewCoopSharesTransaction( final String currentSubject, final String assumedRoles, final HsOfficeCoopSharesTransactionInsertResource requestBody) { @@ -80,7 +80,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar @Override @Transactional(readOnly = true) - public ResponseEntity getCoopShareTransactionByUuid( + public ResponseEntity getSingleCoopShareTransactionByUuid( final String currentSubject, final String assumedRoles, final UUID shareTransactionUuid) { context.define(currentSubject, assumedRoles); @@ -131,9 +131,9 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar } final BiConsumer RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { - if ( resource.getAdjustedShareTxUuid() != null ) { - entity.setAdjustedShareTx(coopSharesTransactionRepo.findByUuid(resource.getAdjustedShareTxUuid()) - .orElseThrow(() -> new EntityNotFoundException("ERROR: [400] adjustedShareTxUuid %s not found".formatted(resource.getAdjustedShareTxUuid())))); + if ( resource.getRevertedShareTxUuid() != null ) { + entity.setRevertedShareTx(coopSharesTransactionRepo.findByUuid(resource.getRevertedShareTxUuid()) + .orElseThrow(() -> new EntityNotFoundException("ERROR: [400] revertedShareTxUuid %s not found".formatted(resource.getRevertedShareTxUuid())))); } }; } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java index 8af8b624..54787ad7 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java @@ -48,8 +48,8 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseE .withProp(HsOfficeCoopSharesTransactionEntity::getShareCount) .withProp(HsOfficeCoopSharesTransactionEntity::getReference) .withProp(HsOfficeCoopSharesTransactionEntity::getComment) - .withProp(at -> ofNullable(at.getAdjustedShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null)) - .withProp(at -> ofNullable(at.getAdjustmentShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null)) + .withProp(at -> ofNullable(at.getRevertedShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null)) + .withProp(at -> ofNullable(at.getReversalShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null)) .quotedValues(false); @Id @@ -71,7 +71,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseE * The signed value which directly affects the booking balance. * *

This means, that a SUBSCRIPTION is always positive, a CANCELLATION is always negative, - * but an ADJUSTMENT can bei either positive or negative. + * but an REVERSAL can bei either positive or negative. * See {@link HsOfficeCoopSharesTransactionType} for

more information. */ @Column(name = "valuedate") @@ -93,14 +93,14 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseE private String comment; /** - * Optionally, the UUID of the corresponding transaction for an adjustment transaction. + * Optionally, the UUID of the corresponding transaction for a REVERSAL transaction. */ @OneToOne - @JoinColumn(name = "adjustedsharetxuuid") - private HsOfficeCoopSharesTransactionEntity adjustedShareTx; + @JoinColumn(name = "revertedsharetxuuid") + private HsOfficeCoopSharesTransactionEntity revertedShareTx; - @OneToOne(mappedBy = "adjustedShareTx") - private HsOfficeCoopSharesTransactionEntity adjustmentShareTx; + @OneToOne(mappedBy = "revertedShareTx") + private HsOfficeCoopSharesTransactionEntity reversalShareTx; @Override public HsOfficeCoopSharesTransactionEntity load() { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java index bcd46cb3..0a9b3176 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java @@ -2,9 +2,9 @@ package net.hostsharing.hsadminng.hs.office.coopshares; public enum HsOfficeCoopSharesTransactionType { /** - * correction of wrong bookings, with either positive or negative value + * reversal of wrong bookings, with either positive or negative value identical to reversed transaction */ - ADJUSTMENT, + REVERSAL, /** * shares signed, e.g. with the declaration of accession, value >0 diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java index 7a491961..62dcd1c2 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java @@ -16,6 +16,8 @@ import java.util.List; import java.util.UUID; import java.util.function.BiConsumer; +import static java.util.Optional.ofNullable; + @RestController public class HsOfficeMembershipController implements HsOfficeMembershipsApi { @@ -39,7 +41,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi { context.define(currentSubject, assumedRoles); final var entities = ( memberNumber != null) - ? List.of(membershipRepo.findMembershipByMemberNumber(memberNumber)) + ? ofNullable(membershipRepo.findMembershipByMemberNumber(memberNumber)).stream().toList() : membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid); final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class, diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java index c61a863e..d7f36d07 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java @@ -14,14 +14,16 @@ public interface HsOfficeMembershipRepository extends Repository findAll(); @Query(""" SELECT membership FROM HsOfficeMembershipEntity membership WHERE ( CAST(:partnerUuid as org.hibernate.type.UUIDCharType) IS NULL OR membership.partner.uuid = :partnerUuid ) ORDER BY membership.partner.partnerNumber, membership.memberNumberSuffix - """) + """) List findMembershipsByOptionalPartnerUuid(UUID partnerUuid); + @Query(""" SELECT membership FROM HsOfficeMembershipEntity membership WHERE (:partnerNumber = membership.partner.partnerNumber) @@ -31,10 +33,12 @@ public interface HsOfficeMembershipRepository extends Repository { diff --git a/src/main/java/net/hostsharing/hsadminng/system/SystemProcess.java b/src/main/java/net/hostsharing/hsadminng/system/SystemProcess.java index 8b302098..c53c814d 100644 --- a/src/main/java/net/hostsharing/hsadminng/system/SystemProcess.java +++ b/src/main/java/net/hostsharing/hsadminng/system/SystemProcess.java @@ -14,6 +14,7 @@ public class SystemProcess { @Getter private String stdOut; + @Getter private String stdErr; @@ -21,7 +22,6 @@ public class SystemProcess { this.processBuilder = new ProcessBuilder(command); } - public String getCommand() { return processBuilder.command().toString(); } diff --git a/src/main/resources/api-definition/hs-office/hs-office-coopassets-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-coopassets-schemas.yaml index 16f4806b..30725a82 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-coopassets-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-coopassets-schemas.yaml @@ -6,7 +6,7 @@ components: HsOfficeCoopAssetsTransactionType: type: string enum: - - ADJUSTMENT + - REVERSAL - DEPOSIT - DISBURSAL - TRANSFER @@ -32,15 +32,15 @@ components: type: string comment: type: string - adjustedAssetTx: + revertedAssetTx: $ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction' - adjustmentAssetTx: + reversalAssetTx: $ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction' HsOfficeReferencedCoopAssetsTransaction: description: Similar to `HsOfficeCoopAssetsTransaction` but without the self-referencing properties - (`adjustedAssetTx` and `adjustmentAssetTx`), to avoid recursive JSON. + (`revertedAssetTx` and `reversalAssetTx`), to avoid recursive JSON. type: object properties: uuid: @@ -80,7 +80,7 @@ components: maxLength: 48 comment: type: string - reverseEntry.uuid: + revertedAssetTx.uuid: type: string format: uuid required: diff --git a/src/main/resources/api-definition/hs-office/hs-office-coopassets-with-uuid.yaml b/src/main/resources/api-definition/hs-office/hs-office-coopassets-with-uuid.yaml index 51d51c2c..4361e54f 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-coopassets-with-uuid.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-coopassets-with-uuid.yaml @@ -2,7 +2,7 @@ get: tags: - hs-office-coopAssets description: 'Fetch a single asset transaction by its uuid, if visible for the current subject.' - operationId: getCoopAssetTransactionByUuid + operationId: getSingleCoopAssetTransactionByUuid parameters: - $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/assumedRoles' diff --git a/src/main/resources/api-definition/hs-office/hs-office-coopassets.yaml b/src/main/resources/api-definition/hs-office/hs-office-coopassets.yaml index bff3e1d5..55880595 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-coopassets.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-coopassets.yaml @@ -3,7 +3,7 @@ get: description: Returns the list of (optionally filtered) cooperative asset transactions which are visible to the current subject or any of it's assumed roles. tags: - hs-office-coopAssets - operationId: listCoopAssets + operationId: getListOfCoopAssets parameters: - $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/assumedRoles' @@ -46,7 +46,7 @@ post: summary: Adds a new cooperative asset transaction. tags: - hs-office-coopAssets - operationId: addCoopAssetsTransaction + operationId: postNewCoopAssetTransaction parameters: - $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/assumedRoles' diff --git a/src/main/resources/api-definition/hs-office/hs-office-coopshares-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-coopshares-schemas.yaml index 50ea65a5..8d190634 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-coopshares-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-coopshares-schemas.yaml @@ -6,7 +6,7 @@ components: HsOfficeCoopSharesTransactionType: type: string enum: - - ADJUSTMENT + - REVERSAL - SUBSCRIPTION - CANCELLATION @@ -27,15 +27,15 @@ components: type: string comment: type: string - adjustedShareTx: + revertedShareTx: $ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction' - adjustmentShareTx: + reversalShareTx: $ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction' HsOfficeReferencedCoopSharesTransaction: description: Similar to `HsOfficeCoopSharesTransaction` but without the self-referencing properties - (`adjustedShareTx` and `adjustmentShareTx`), to avoid recursive JSON. + (`revertedShareTx` and `reversalShareTx`), to avoid recursive JSON. type: object properties: uuid: @@ -73,7 +73,7 @@ components: maxLength: 48 comment: type: string - adjustedShareTx.uuid: + revertedShareTx.uuid: type: string format: uuid required: diff --git a/src/main/resources/api-definition/hs-office/hs-office-coopshares-with-uuid.yaml b/src/main/resources/api-definition/hs-office/hs-office-coopshares-with-uuid.yaml index a37dbf7e..b8d6177e 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-coopshares-with-uuid.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-coopshares-with-uuid.yaml @@ -2,7 +2,7 @@ get: tags: - hs-office-coopShares description: 'Fetch a single share transaction by its uuid, if visible for the current subject.' - operationId: getCoopShareTransactionByUuid + operationId: getSingleCoopShareTransactionByUuid parameters: - $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/assumedRoles' diff --git a/src/main/resources/api-definition/hs-office/hs-office-coopshares.yaml b/src/main/resources/api-definition/hs-office/hs-office-coopshares.yaml index 215df97e..69dad5ad 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-coopshares.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-coopshares.yaml @@ -3,7 +3,7 @@ get: description: Returns the list of (optionally filtered) cooperative share transactions which are visible to the current subject or any of it's assumed roles. tags: - hs-office-coopShares - operationId: listCoopShares + operationId: getListOfCoopShares parameters: - $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/assumedRoles' @@ -46,7 +46,7 @@ post: summary: Adds a new cooperative share transaction. tags: - hs-office-coopShares - operationId: addCoopSharesTransaction + operationId: postNewCoopSharesTransaction parameters: - $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/assumedRoles' diff --git a/src/main/resources/db/changelog/5-hs-office/503-relation/5030-hs-office-relation.sql b/src/main/resources/db/changelog/5-hs-office/503-relation/5030-hs-office-relation.sql index a1498fb3..1463612a 100644 --- a/src/main/resources/db/changelog/5-hs-office/503-relation/5030-hs-office-relation.sql +++ b/src/main/resources/db/changelog/5-hs-office/503-relation/5030-hs-office-relation.sql @@ -28,6 +28,9 @@ create table if not exists hs_office.relation ); --// +-- TODO.impl: unique constraint, to prevent using the same person multiple times as a partner, or better: +-- ( anchorUuid, holderUuid, type) + -- ============================================================================ --changeset michael.hoennig:hs-office-relation-MAIN-TABLE-JOURNAL endDelimiter:--// diff --git a/src/main/resources/db/changelog/5-hs-office/511-coopshares/5110-hs-office-coopshares.sql b/src/main/resources/db/changelog/5-hs-office/511-coopshares/5110-hs-office-coopshares.sql index 213dd1cb..2aa6fe72 100644 --- a/src/main/resources/db/changelog/5-hs-office/511-coopshares/5110-hs-office-coopshares.sql +++ b/src/main/resources/db/changelog/5-hs-office/511-coopshares/5110-hs-office-coopshares.sql @@ -4,7 +4,7 @@ --changeset michael.hoennig:hs-office-coopshares-MAIN-TABLE endDelimiter:--// -- ---------------------------------------------------------------------------- -CREATE TYPE hs_office.CoopSharesTransactionType AS ENUM ('ADJUSTMENT', 'SUBSCRIPTION', 'CANCELLATION'); +CREATE TYPE hs_office.CoopSharesTransactionType AS ENUM ('REVERSAL', 'SUBSCRIPTION', 'CANCELLATION'); CREATE CAST (character varying as hs_office.CoopSharesTransactionType) WITH INOUT AS IMPLICIT; @@ -17,7 +17,7 @@ create table if not exists hs_office.coopsharetx valueDate date not null, shareCount integer not null, reference varchar(48) not null, - adjustedShareTxUuid uuid unique REFERENCES hs_office.coopsharetx(uuid) DEFERRABLE INITIALLY DEFERRED, + revertedShareTxUuid uuid unique REFERENCES hs_office.coopsharetx(uuid) DEFERRABLE INITIALLY DEFERRED, comment varchar(512) ); --// @@ -28,8 +28,8 @@ create table if not exists hs_office.coopsharetx alter table hs_office.coopsharetx add constraint reverse_entry_missing - check ( transactionType = 'ADJUSTMENT' and adjustedShareTxUuid is not null - or transactionType <> 'ADJUSTMENT' and adjustedShareTxUuid is null); + check ( transactionType = 'REVERSAL' and revertedShareTxUuid is not null + or transactionType <> 'REVERSAL' and revertedShareTxUuid is null); --// -- ============================================================================ diff --git a/src/main/resources/db/changelog/5-hs-office/511-coopshares/5118-hs-office-coopshares-test-data.sql b/src/main/resources/db/changelog/5-hs-office/511-coopshares/5118-hs-office-coopshares-test-data.sql index 0f148a90..e7229595 100644 --- a/src/main/resources/db/changelog/5-hs-office/511-coopshares/5118-hs-office-coopshares-test-data.sql +++ b/src/main/resources/db/changelog/5-hs-office/511-coopshares/5118-hs-office-coopshares-test-data.sql @@ -27,12 +27,12 @@ begin raise notice 'creating test coopSharesTransaction: %', givenPartnerNumber::text || givenMemberNumberSuffix; subscriptionEntryUuid := uuid_generate_v4(); insert - into hs_office.coopsharetx(uuid, membershipuuid, transactiontype, valuedate, sharecount, reference, comment, adjustedShareTxUuid) + into hs_office.coopsharetx(uuid, membershipuuid, transactiontype, valuedate, sharecount, reference, comment, revertedShareTxUuid) values (uuid_generate_v4(), membership.uuid, 'SUBSCRIPTION', '2010-03-15', 4, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-1', 'initial subscription', null), (uuid_generate_v4(), membership.uuid, 'CANCELLATION', '2021-09-01', -2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-2', 'cancelling some', null), (subscriptionEntryUuid, membership.uuid, 'SUBSCRIPTION', '2022-10-20', 2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-3', 'some subscription', null), - (uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-21', -2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-4', 'some adjustment', subscriptionEntryUuid); + (uuid_generate_v4(), membership.uuid, 'REVERSAL', '2022-10-21', -2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-4', 'some reversal', subscriptionEntryUuid); end; $$; --// diff --git a/src/main/resources/db/changelog/5-hs-office/512-coopassets/5120-hs-office-coopassets.sql b/src/main/resources/db/changelog/5-hs-office/512-coopassets/5120-hs-office-coopassets.sql index 4e5568ad..8feac5da 100644 --- a/src/main/resources/db/changelog/5-hs-office/512-coopassets/5120-hs-office-coopassets.sql +++ b/src/main/resources/db/changelog/5-hs-office/512-coopassets/5120-hs-office-coopassets.sql @@ -4,7 +4,7 @@ --changeset michael.hoennig:hs-office-coopassets-MAIN-TABLE endDelimiter:--// -- ---------------------------------------------------------------------------- -CREATE TYPE hs_office.CoopAssetsTransactionType AS ENUM ('ADJUSTMENT', +CREATE TYPE hs_office.CoopAssetsTransactionType AS ENUM ('REVERSAL', 'DEPOSIT', 'DISBURSAL', 'TRANSFER', @@ -22,9 +22,9 @@ create table if not exists hs_office.coopassettx membershipUuid uuid not null references hs_office.membership(uuid), transactionType hs_office.CoopAssetsTransactionType not null, valueDate date not null, - assetValue money not null, + assetValue numeric(12,2) not null, -- https://wiki.postgresql.org/wiki/Don't_Do_This#Don.27t_use_money reference varchar(48) not null, - adjustedAssetTxUuid uuid unique REFERENCES hs_office.coopassettx(uuid) DEFERRABLE INITIALLY DEFERRED, + revertedAssetTxUuid uuid unique REFERENCES hs_office.coopassettx(uuid) DEFERRABLE INITIALLY DEFERRED, comment varchar(512) ); --// @@ -36,20 +36,20 @@ create table if not exists hs_office.coopassettx alter table hs_office.coopassettx add constraint reverse_entry_missing - check ( transactionType = 'ADJUSTMENT' and adjustedAssetTxUuid is not null - or transactionType <> 'ADJUSTMENT' and adjustedAssetTxUuid is null); + check ( transactionType = 'REVERSAL' and revertedAssetTxUuid is not null + or transactionType <> 'REVERSAL' and revertedAssetTxUuid is null); --// -- ============================================================================ --changeset michael.hoennig:hs-office-coopassets-ASSET-VALUE-CONSTRAINT endDelimiter:--// -- ---------------------------------------------------------------------------- -create or replace function hs_office.coopassetstx_check_positive_total(forMembershipUuid UUID, newAssetValue money) +create or replace function hs_office.coopassetstx_check_positive_total(forMembershipUuid UUID, newAssetValue numeric(12, 5)) returns boolean language plpgsql as $$ declare - currentAssetValue money; - totalAssetValue money; + currentAssetValue numeric(12,2); + totalAssetValue numeric(12,2); begin select sum(cat.assetValue) from hs_office.coopassettx cat diff --git a/src/main/resources/db/changelog/5-hs-office/512-coopassets/5128-hs-office-coopassets-test-data.sql b/src/main/resources/db/changelog/5-hs-office/512-coopassets/5128-hs-office-coopassets-test-data.sql index ed824b12..ac43a731 100644 --- a/src/main/resources/db/changelog/5-hs-office/512-coopassets/5128-hs-office-coopassets-test-data.sql +++ b/src/main/resources/db/changelog/5-hs-office/512-coopassets/5128-hs-office-coopassets-test-data.sql @@ -27,12 +27,12 @@ begin raise notice 'creating test coopAssetsTransaction: %', givenPartnerNumber || givenMemberNumberSuffix; lossEntryUuid := uuid_generate_v4(); insert - into hs_office.coopassettx(uuid, membershipuuid, transactiontype, valuedate, assetvalue, reference, comment, adjustedAssetTxUuid) + into hs_office.coopassettx(uuid, membershipuuid, transactiontype, valuedate, assetvalue, reference, comment, revertedAssetTxUuid) values (uuid_generate_v4(), membership.uuid, 'DEPOSIT', '2010-03-15', 320.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-1', 'initial deposit', null), (uuid_generate_v4(), membership.uuid, 'DISBURSAL', '2021-09-01', -128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-2', 'partial disbursal', null), (lossEntryUuid, membership.uuid, 'DEPOSIT', '2022-10-20', 128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-3', 'some loss', null), - (uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-21', -128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-3', 'some adjustment', lossEntryUuid); + (uuid_generate_v4(), membership.uuid, 'REVERSAL', '2022-10-21', -128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-3', 'some reversal', lossEntryUuid); end; $$; --// diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/BaseOfficeDataImport.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/BaseOfficeDataImport.java index 70f21e11..0bdf47da 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/BaseOfficeDataImport.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/BaseOfficeDataImport.java @@ -442,7 +442,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport { 34002=CoopAssetsTransaction(M-1002000: 2016-12-31, DISBURSAL, -100.00, 1002000, for cancellation D), 34003=CoopAssetsTransaction(M-1002000: 2016-12-31, LOSS, -20.00, 1002000, for cancellation D), 35001=CoopAssetsTransaction(M-1909000: 2024-01-15, DEPOSIT, 128.00, 1909000, for subscription E), - 35002=CoopAssetsTransaction(M-1909000: 2024-01-20, ADJUSTMENT, -128.00, 1909000, chargeback for subscription E, M-1909000:DEP:+128.00), + 35002=CoopAssetsTransaction(M-1909000: 2024-01-20, REVERSAL, -128.00, 1909000, chargeback for subscription E, M-1909000:DEP:+128.00), 358=CoopAssetsTransaction(M-1000300: 2000-12-06, DEPOSIT, 5120, 1000300, for subscription A), 442=CoopAssetsTransaction(M-1015200: 2003-07-07, DEPOSIT, 64, 1015200), 577=CoopAssetsTransaction(M-1000300: 2011-12-12, DEPOSIT, 1024, 1000300), @@ -795,23 +795,23 @@ public abstract class BaseOfficeDataImport extends CsvDataImport { ? HsOfficeCoopSharesTransactionType.SUBSCRIPTION : "UNSUBSCRIPTION".equals(rec.getString("action")) ? HsOfficeCoopSharesTransactionType.CANCELLATION - : HsOfficeCoopSharesTransactionType.ADJUSTMENT + : HsOfficeCoopSharesTransactionType.REVERSAL ) .shareCount(rec.getInteger("quantity")) .comment(rec.getString("comment")) .reference(member.getMemberNumber().toString()) .build(); - if (shareTransaction.getTransactionType() == HsOfficeCoopSharesTransactionType.ADJUSTMENT) { + if (shareTransaction.getTransactionType() == HsOfficeCoopSharesTransactionType.REVERSAL) { final var negativeValue = -shareTransaction.getShareCount(); - final var adjustedShareTx = coopShares.values().stream().filter(a -> - a.getTransactionType() != HsOfficeCoopSharesTransactionType.ADJUSTMENT && + final var revertedShareTx = coopShares.values().stream().filter(a -> + a.getTransactionType() != HsOfficeCoopSharesTransactionType.REVERSAL && a.getMembership() == shareTransaction.getMembership() && a.getShareCount() == negativeValue) .findAny() .orElseThrow(() -> new IllegalStateException( - "cannot determine share reverse entry for adjustment " + shareTransaction)); - shareTransaction.setAdjustedShareTx(adjustedShareTx); + "cannot determine share reverse entry for reversal " + shareTransaction)); + shareTransaction.setRevertedShareTx(revertedShareTx); } coopShares.put(rec.getInteger("member_share_id"), shareTransaction); }); @@ -837,7 +837,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport { final var assetTypeMapping = new HashMap() { { - put("ADJUSTMENT", HsOfficeCoopAssetsTransactionType.ADJUSTMENT); + put("ADJUSTMENT", HsOfficeCoopAssetsTransactionType.REVERSAL); put("HANDOVER", HsOfficeCoopAssetsTransactionType.TRANSFER); put("ADOPTION", HsOfficeCoopAssetsTransactionType.ADOPTION); put("LOSS", HsOfficeCoopAssetsTransactionType.LOSS); @@ -865,16 +865,16 @@ public abstract class BaseOfficeDataImport extends CsvDataImport { .reference(member.getMemberNumber().toString()) .build(); - if (assetTransaction.getTransactionType() == HsOfficeCoopAssetsTransactionType.ADJUSTMENT) { + if (assetTransaction.getTransactionType() == HsOfficeCoopAssetsTransactionType.REVERSAL) { final var negativeValue = assetTransaction.getAssetValue().negate(); - final var adjustedAssetTx = coopAssets.values().stream().filter(a -> - a.getTransactionType() != HsOfficeCoopAssetsTransactionType.ADJUSTMENT && + final var revertedAssetTx = coopAssets.values().stream().filter(a -> + a.getTransactionType() != HsOfficeCoopAssetsTransactionType.REVERSAL && a.getMembership() == assetTransaction.getMembership() && a.getAssetValue().equals(negativeValue)) .findAny() .orElseThrow(() -> new IllegalStateException( - "cannot determine asset reverse entry for adjustment " + assetTransaction)); - assetTransaction.setAdjustedAssetTx(adjustedAssetTx); + "cannot determine asset reverse entry for reversal " + assetTransaction)); + assetTransaction.setRevertedAssetTx(revertedAssetTx); } coopAssets.put(rec.getInteger("member_asset_id"), assetTransaction); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java index 6ac97292..d5785ffa 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java @@ -55,7 +55,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased EntityManager em; @Nested - class ListCoopAssetsTransactions { + class GetListOfCoopAssetsTransactions { @Test void globalAdmin_canViewAllCoopAssetsTransactions() { @@ -109,21 +109,21 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased "valueDate": "2022-10-20", "reference": "ref 1000202-3", "comment": "some loss", - "adjustmentAssetTx": { - "transactionType": "ADJUSTMENT", + "reversalAssetTx": { + "transactionType": "REVERSAL", "assetValue": -128.00, "valueDate": "2022-10-21", "reference": "ref 1000202-3", - "comment": "some adjustment" + "comment": "some reversal" } }, { - "transactionType": "ADJUSTMENT", + "transactionType": "REVERSAL", "assetValue": -128.00, "valueDate": "2022-10-21", "reference": "ref 1000202-3", - "comment": "some adjustment", - "adjustedAssetTx": { + "comment": "some reversal", + "revertedAssetTx": { "transactionType": "DEPOSIT", "assetValue": 128.00, "valueDate": "2022-10-20", @@ -166,10 +166,10 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased } @Nested - class AddCoopAssetsTransaction { + class PostNewCoopAssetTransaction { @Test - void globalAdmin_canAddCoopAssetsTransaction() { + void globalAdmin_canPostNewCoopAssetTransaction() { context.define("superuser-alex@hostsharing.net"); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); @@ -214,7 +214,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased } @Test - void globalAdmin_canAddCoopAssetsAdjustmentTransaction() { + void globalAdmin_canAddCoopAssetsReversalTransaction() { context.define("superuser-alex@hostsharing.net"); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); @@ -238,12 +238,12 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased .body(""" { "membership.uuid": "%s", - "transactionType": "ADJUSTMENT", + "transactionType": "REVERSAL", "assetValue": %s, "valueDate": "2022-10-30", - "reference": "test ref adjustment", - "comment": "some coop assets adjustment transaction", - "reverseEntry.uuid": "%s" + "reference": "test ref reversal", + "comment": "some coop assets reversal transaction", + "revertedAssetTx.uuid": "%s" } """.formatted( givenMembership.getUuid(), @@ -258,12 +258,12 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased .body("uuid", isUuidValid()) .body("", lenientlyEquals(""" { - "transactionType": "ADJUSTMENT", + "transactionType": "REVERSAL", "assetValue": -256.00, "valueDate": "2022-10-30", - "reference": "test ref adjustment", - "comment": "some coop assets adjustment transaction", - "adjustedAssetTx": { + "reference": "test ref reversal", + "comment": "some coop assets reversal transaction", + "revertedAssetTx": { "transactionType": "DEPOSIT", "assetValue": 256.00, "valueDate": "2022-10-20", diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java index b7f44b35..46779410 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java @@ -77,7 +77,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest { ASSETS_VALUE_MUST_NOT_BE_NULL( requestBody -> requestBody - .with("transactionType", "ADJUSTMENT") + .with("transactionType", "REVERSAL") .with("assetValue", 0.00), "[assetValue must not be 0 but is \"0.00\"]"), diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java index 09f704d6..96a0edfb 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java @@ -21,14 +21,14 @@ class HsOfficeCoopAssetsTransactionEntityUnitTest { .build(); - final HsOfficeCoopAssetsTransactionEntity givenCoopAssetAdjustmentTransaction = HsOfficeCoopAssetsTransactionEntity.builder() + final HsOfficeCoopAssetsTransactionEntity givenCoopAssetReversalTransaction = HsOfficeCoopAssetsTransactionEntity.builder() .membership(TEST_MEMBERSHIP) .reference("some-ref") .valueDate(LocalDate.parse("2020-01-15")) - .transactionType(HsOfficeCoopAssetsTransactionType.ADJUSTMENT) + .transactionType(HsOfficeCoopAssetsTransactionType.REVERSAL) .assetValue(new BigDecimal("-128.00")) .comment("some comment") - .adjustedAssetTx(givenCoopAssetTransaction) + .revertedAssetTx(givenCoopAssetTransaction) .build(); final HsOfficeCoopAssetsTransactionEntity givenEmptyCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder().build(); @@ -41,12 +41,12 @@ class HsOfficeCoopAssetsTransactionEntityUnitTest { } @Test - void toStringWithReverseEntryContainsReverseEntry() { - givenCoopAssetTransaction.setAdjustedAssetTx(givenCoopAssetAdjustmentTransaction); + void toStringWithRevertedAssetTxContainsRevertedAssetTx() { + givenCoopAssetTransaction.setRevertedAssetTx(givenCoopAssetReversalTransaction); final var result = givenCoopAssetTransaction.toString(); - assertThat(result).isEqualTo("CoopAssetsTransaction(M-1000101: 2020-01-01, DEPOSIT, 128.00, some-ref, some comment, M-1000101:ADJ:-128.00)"); + assertThat(result).isEqualTo("CoopAssetsTransaction(M-1000101: 2020-01-01, DEPOSIT, 128.00, some-ref, some comment, M-1000101:REV:-128.00)"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java index 8f650fa9..4c2f866d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java @@ -69,7 +69,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder() .membership(givenMembership) .transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT) - .assetValue(new BigDecimal("128.00")) + .assetValue(new BigDecimal("6400.00")) .valueDate(LocalDate.parse("2022-10-18")) .reference("temp ref A") .build(); @@ -98,7 +98,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder() .membership(givenMembership) .transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT) - .assetValue(new BigDecimal("128.00")) + .assetValue(new BigDecimal("6400.00")) .valueDate(LocalDate.parse("2022-10-18")) .reference("temp ref B") .build(); @@ -142,18 +142,18 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase result, "CoopAssetsTransaction(M-1000101: 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)", "CoopAssetsTransaction(M-1000101: 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)", - "CoopAssetsTransaction(M-1000101: 2022-10-20, DEPOSIT, 128.00, ref 1000101-3, some loss, M-1000101:ADJ:-128.00)", - "CoopAssetsTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -128.00, ref 1000101-3, some adjustment, M-1000101:DEP:+128.00)", + "CoopAssetsTransaction(M-1000101: 2022-10-20, DEPOSIT, 128.00, ref 1000101-3, some loss, M-1000101:REV:-128.00)", + "CoopAssetsTransaction(M-1000101: 2022-10-21, REVERSAL, -128.00, ref 1000101-3, some reversal, M-1000101:DEP:+128.00)", "CoopAssetsTransaction(M-1000202: 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)", "CoopAssetsTransaction(M-1000202: 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)", - "CoopAssetsTransaction(M-1000202: 2022-10-20, DEPOSIT, 128.00, ref 1000202-3, some loss, M-1000202:ADJ:-128.00)", - "CoopAssetsTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -128.00, ref 1000202-3, some adjustment, M-1000202:DEP:+128.00)", + "CoopAssetsTransaction(M-1000202: 2022-10-20, DEPOSIT, 128.00, ref 1000202-3, some loss, M-1000202:REV:-128.00)", + "CoopAssetsTransaction(M-1000202: 2022-10-21, REVERSAL, -128.00, ref 1000202-3, some reversal, M-1000202:DEP:+128.00)", "CoopAssetsTransaction(M-1000303: 2010-03-15, DEPOSIT, 320.00, ref 1000303-1, initial deposit)", "CoopAssetsTransaction(M-1000303: 2021-09-01, DISBURSAL, -128.00, ref 1000303-2, partial disbursal)", - "CoopAssetsTransaction(M-1000303: 2022-10-20, DEPOSIT, 128.00, ref 1000303-3, some loss, M-1000303:ADJ:-128.00)", - "CoopAssetsTransaction(M-1000303: 2022-10-21, ADJUSTMENT, -128.00, ref 1000303-3, some adjustment, M-1000303:DEP:+128.00)"); + "CoopAssetsTransaction(M-1000303: 2022-10-20, DEPOSIT, 128.00, ref 1000303-3, some loss, M-1000303:REV:-128.00)", + "CoopAssetsTransaction(M-1000303: 2022-10-21, REVERSAL, -128.00, ref 1000303-3, some reversal, M-1000303:DEP:+128.00)"); } @Test @@ -173,8 +173,8 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase result, "CoopAssetsTransaction(M-1000202: 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)", "CoopAssetsTransaction(M-1000202: 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)", - "CoopAssetsTransaction(M-1000202: 2022-10-20, DEPOSIT, 128.00, ref 1000202-3, some loss, M-1000202:ADJ:-128.00)", - "CoopAssetsTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -128.00, ref 1000202-3, some adjustment, M-1000202:DEP:+128.00)"); + "CoopAssetsTransaction(M-1000202: 2022-10-20, DEPOSIT, 128.00, ref 1000202-3, some loss, M-1000202:REV:-128.00)", + "CoopAssetsTransaction(M-1000202: 2022-10-21, REVERSAL, -128.00, ref 1000202-3, some reversal, M-1000202:DEP:+128.00)"); } @Test @@ -211,8 +211,8 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase result, "CoopAssetsTransaction(M-1000101: 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)", "CoopAssetsTransaction(M-1000101: 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)", - "CoopAssetsTransaction(M-1000101: 2022-10-20, DEPOSIT, 128.00, ref 1000101-3, some loss, M-1000101:ADJ:-128.00)", - "CoopAssetsTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -128.00, ref 1000101-3, some adjustment, M-1000101:DEP:+128.00)"); + "CoopAssetsTransaction(M-1000101: 2022-10-20, DEPOSIT, 128.00, ref 1000101-3, some loss, M-1000101:REV:-128.00)", + "CoopAssetsTransaction(M-1000101: 2022-10-21, REVERSAL, -128.00, ref 1000101-3, some reversal, M-1000101:DEP:+128.00)"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java index a1e8a675..c2e6a11f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java @@ -62,7 +62,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased } @Nested - class ListCoopSharesTransactions { + class getListOfCoopSharesTransactions { @Test void globalAdmin_canViewAllCoopSharesTransactions() { @@ -108,21 +108,21 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased "valueDate": "2022-10-20", "reference": "ref 1000202-3", "comment": "some subscription", - "adjustmentShareTx": { - "transactionType": "ADJUSTMENT", + "reversalShareTx": { + "transactionType": "REVERSAL", "shareCount": -2, "valueDate": "2022-10-21", "reference": "ref 1000202-4", - "comment": "some adjustment" + "comment": "some reversal" } }, { - "transactionType": "ADJUSTMENT", + "transactionType": "REVERSAL", "shareCount": -2, "valueDate": "2022-10-21", "reference": "ref 1000202-4", - "comment": "some adjustment", - "adjustedShareTx": { + "comment": "some reversal", + "revertedShareTx": { "transactionType": "SUBSCRIPTION", "shareCount": 2, "valueDate": "2022-10-20", @@ -191,7 +191,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased } @Test - void globalAdmin_canAddCoopSharesAdjustmentTransaction() { + void globalAdmin_canAddCoopSharesReversalTransaction() { context.define("superuser-alex@hostsharing.net"); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); @@ -213,16 +213,16 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased .header("current-subject", "superuser-alex@hostsharing.net") .contentType(ContentType.JSON) .body(""" - { - "membership.uuid": "%s", - "transactionType": "ADJUSTMENT", - "shareCount": %s, - "valueDate": "2022-10-30", - "reference": "test ref adjustment", - "comment": "some coop shares adjustment transaction", - "adjustedShareTx.uuid": "%s" - } - """.formatted( + { + "membership.uuid": "%s", + "transactionType": "REVERSAL", + "shareCount": %s, + "valueDate": "2022-10-30", + "reference": "test reversal ref", + "comment": "some coop shares reversal transaction", + "revertedShareTx.uuid": "%s" + } + """.formatted( givenMembership.getUuid(), -givenTransaction.getShareCount(), givenTransaction.getUuid())) @@ -235,12 +235,12 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased .body("uuid", isUuidValid()) .body("", lenientlyEquals(""" { - "transactionType": "ADJUSTMENT", + "transactionType": "REVERSAL", "shareCount": -13, "valueDate": "2022-10-30", - "reference": "test ref adjustment", - "comment": "some coop shares adjustment transaction", - "adjustedShareTx": { + "reference": "test reversal ref", + "comment": "some coop shares reversal transaction", + "revertedShareTx": { "transactionType": "SUBSCRIPTION", "shareCount": 13, "valueDate": "2022-10-20", diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerRestTest.java index 7024c668..821e8871 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerRestTest.java @@ -73,7 +73,7 @@ class HsOfficeCoopSharesTransactionControllerRestTest { SHARES_COUNT_MUST_NOT_BE_NULL( requestBody -> requestBody - .with("transactionType", "ADJUSTMENT") + .with("transactionType", "REVERSAL") .with("shareCount", 0), "[shareCount must not be 0 but is \"0\"]"), diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java index 6d15cb78..6394a8d7 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java @@ -20,14 +20,14 @@ class HsOfficeCoopSharesTransactionEntityUnitTest { .build(); - final HsOfficeCoopSharesTransactionEntity givenCoopShareAdjustmentTransaction = HsOfficeCoopSharesTransactionEntity.builder() + final HsOfficeCoopSharesTransactionEntity givenCoopShareReversalTransaction = HsOfficeCoopSharesTransactionEntity.builder() .membership(TEST_MEMBERSHIP) .reference("some-ref") .valueDate(LocalDate.parse("2020-01-15")) - .transactionType(HsOfficeCoopSharesTransactionType.ADJUSTMENT) + .transactionType(HsOfficeCoopSharesTransactionType.REVERSAL) .shareCount(-4) .comment("some comment") - .adjustedShareTx(givenCoopSharesTransaction) + .revertedShareTx(givenCoopSharesTransaction) .build(); final HsOfficeCoopSharesTransactionEntity givenEmptyCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder().build(); @@ -40,12 +40,12 @@ class HsOfficeCoopSharesTransactionEntityUnitTest { } @Test - void toStringWithReverseEntryContainsReverseEntry() { - givenCoopSharesTransaction.setAdjustedShareTx(givenCoopShareAdjustmentTransaction); + void toStringWithRevertedAssetTxContainsRevertedAssetTx() { + givenCoopSharesTransaction.setRevertedShareTx(givenCoopShareReversalTransaction); final var result = givenCoopSharesTransaction.toString(); - assertThat(result).isEqualTo("CoopShareTransaction(M-1000101: 2020-01-01, SUBSCRIPTION, 4, some-ref, some comment, M-1000101:ADJ:-4)"); + assertThat(result).isEqualTo("CoopShareTransaction(M-1000101: 2020-01-01, SUBSCRIPTION, 4, some-ref, some comment, M-1000101:REV:-4)"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java index ac74bee6..613ccc2b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java @@ -141,18 +141,18 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase result, "CoopShareTransaction(M-1000101: 2010-03-15, SUBSCRIPTION, 4, ref 1000101-1, initial subscription)", "CoopShareTransaction(M-1000101: 2021-09-01, CANCELLATION, -2, ref 1000101-2, cancelling some)", - "CoopShareTransaction(M-1000101: 2022-10-20, SUBSCRIPTION, 2, ref 1000101-3, some subscription, M-1000101:ADJ:-2)", - "CoopShareTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -2, ref 1000101-4, some adjustment, M-1000101:SUB:+2)", + "CoopShareTransaction(M-1000101: 2022-10-20, SUBSCRIPTION, 2, ref 1000101-3, some subscription, M-1000101:REV:-2)", + "CoopShareTransaction(M-1000101: 2022-10-21, REVERSAL, -2, ref 1000101-4, some reversal, M-1000101:SUB:+2)", "CoopShareTransaction(M-1000202: 2010-03-15, SUBSCRIPTION, 4, ref 1000202-1, initial subscription)", "CoopShareTransaction(M-1000202: 2021-09-01, CANCELLATION, -2, ref 1000202-2, cancelling some)", - "CoopShareTransaction(M-1000202: 2022-10-20, SUBSCRIPTION, 2, ref 1000202-3, some subscription, M-1000202:ADJ:-2)", - "CoopShareTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -2, ref 1000202-4, some adjustment, M-1000202:SUB:+2)", + "CoopShareTransaction(M-1000202: 2022-10-20, SUBSCRIPTION, 2, ref 1000202-3, some subscription, M-1000202:REV:-2)", + "CoopShareTransaction(M-1000202: 2022-10-21, REVERSAL, -2, ref 1000202-4, some reversal, M-1000202:SUB:+2)", "CoopShareTransaction(M-1000303: 2010-03-15, SUBSCRIPTION, 4, ref 1000303-1, initial subscription)", "CoopShareTransaction(M-1000303: 2021-09-01, CANCELLATION, -2, ref 1000303-2, cancelling some)", - "CoopShareTransaction(M-1000303: 2022-10-20, SUBSCRIPTION, 2, ref 1000303-3, some subscription, M-1000303:ADJ:-2)", - "CoopShareTransaction(M-1000303: 2022-10-21, ADJUSTMENT, -2, ref 1000303-4, some adjustment, M-1000303:SUB:+2)"); + "CoopShareTransaction(M-1000303: 2022-10-20, SUBSCRIPTION, 2, ref 1000303-3, some subscription, M-1000303:REV:-2)", + "CoopShareTransaction(M-1000303: 2022-10-21, REVERSAL, -2, ref 1000303-4, some reversal, M-1000303:SUB:+2)"); } @Test @@ -172,8 +172,8 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase result, "CoopShareTransaction(M-1000202: 2010-03-15, SUBSCRIPTION, 4, ref 1000202-1, initial subscription)", "CoopShareTransaction(M-1000202: 2021-09-01, CANCELLATION, -2, ref 1000202-2, cancelling some)", - "CoopShareTransaction(M-1000202: 2022-10-20, SUBSCRIPTION, 2, ref 1000202-3, some subscription, M-1000202:ADJ:-2)", - "CoopShareTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -2, ref 1000202-4, some adjustment, M-1000202:SUB:+2)"); + "CoopShareTransaction(M-1000202: 2022-10-20, SUBSCRIPTION, 2, ref 1000202-3, some subscription, M-1000202:REV:-2)", + "CoopShareTransaction(M-1000202: 2022-10-21, REVERSAL, -2, ref 1000202-4, some reversal, M-1000202:SUB:+2)"); } @Test @@ -210,8 +210,8 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase result, "CoopShareTransaction(M-1000101: 2010-03-15, SUBSCRIPTION, 4, ref 1000101-1, initial subscription)", "CoopShareTransaction(M-1000101: 2021-09-01, CANCELLATION, -2, ref 1000101-2, cancelling some)", - "CoopShareTransaction(M-1000101: 2022-10-20, SUBSCRIPTION, 2, ref 1000101-3, some subscription, M-1000101:ADJ:-2)", - "CoopShareTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -2, ref 1000101-4, some adjustment, M-1000101:SUB:+2)"); + "CoopShareTransaction(M-1000101: 2022-10-20, SUBSCRIPTION, 2, ref 1000101-3, some subscription, M-1000101:REV:-2)", + "CoopShareTransaction(M-1000101: 2022-10-21, REVERSAL, -2, ref 1000101-4, some reversal, M-1000101:SUB:+2)"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java index 455008a2..2306fcca 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java @@ -20,6 +20,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import java.util.UUID; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -44,8 +45,36 @@ public class HsOfficeMembershipControllerRestTest { @MockBean EntityManagerWrapper em; + @Nested + class GetMemberships { + + @Test + void findMembershipByNonExistingMemberNumberReturnsEmptyList() throws Exception { + + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/hs/office/memberships?memberNumber=12345") + .header("current-subject", "superuser-alex@hostsharing.net") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "partner.uuid": null, + "memberNumberSuffix": "01", + "validFrom": "2022-10-13", + "membershipFeeBillable": "true" + } + """) + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$", hasSize(0))); + } + } + @Nested class AddMembership { + @Test void respondBadRequest_ifPartnerUuidIsMissing() throws Exception { @@ -98,7 +127,9 @@ public class HsOfficeMembershipControllerRestTest { .andExpect(status().is4xxClientError()) .andExpect(jsonPath("statusCode", is(400))) .andExpect(jsonPath("statusPhrase", is("Bad Request"))) - .andExpect(jsonPath("message", is("ERROR: [400] Unable to find Partner by partner.uuid: " + givenPartnerUuid))); + .andExpect(jsonPath( + "message", + is("ERROR: [400] Unable to find Partner by partner.uuid: " + givenPartnerUuid))); } @ParameterizedTest diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java index b08e9f3c..13af695b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java @@ -13,6 +13,12 @@ import net.hostsharing.hsadminng.hs.office.scenarios.debitor.DontDeleteDefaultDe import net.hostsharing.hsadminng.hs.office.scenarios.debitor.InvalidateSepaMandateForDebitor; import net.hostsharing.hsadminng.hs.office.scenarios.membership.CancelMembership; import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateMembership; +import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsDepositTransaction; +import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsDisbursalTransaction; +import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsRevertTransaction; +import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares.CreateCoopSharesCancellationTransaction; +import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares.CreateCoopSharesRevertTransaction; +import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares.CreateCoopSharesSubscriptionTransaction; import net.hostsharing.hsadminng.hs.office.scenarios.partner.AddOperationsContactToPartner; import net.hostsharing.hsadminng.hs.office.scenarios.partner.CreatePartner; import net.hostsharing.hsadminng.hs.office.scenarios.debitor.DeleteDebitor; @@ -49,7 +55,7 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(1010) - @Produces(explicitly = "Partner: Test AG", implicitly = {"Person: Test AG", "Contact: Test AG - Hamburg"}) + @Produces(explicitly = "Partner: P-31010 - Test AG", implicitly = {"Person: Test AG", "Contact: Test AG - Hamburg"}) void shouldCreateLegalPersonAsPartner() { new CreatePartner(this) .given("partnerNumber", 31010) @@ -71,7 +77,7 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(1011) - @Produces(explicitly = "Partner: Michelle Matthieu", implicitly = {"Person: Michelle Matthieu", "Contact: Michelle Matthieu"}) + @Produces(explicitly = "Partner: P-31011 - Michelle Matthieu", implicitly = {"Person: Michelle Matthieu", "Contact: Michelle Matthieu"}) void shouldCreateNaturalPersonAsPartner() { new CreatePartner(this) .given("partnerNumber", 31011) @@ -148,7 +154,7 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(1100) - @Requires("Partner: Michelle Matthieu") + @Requires("Partner: P-31011 - Michelle Matthieu") void shouldAmendContactData() { new AmendContactData(this) .given("partnerName", "Matthieu") @@ -158,7 +164,7 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(1101) - @Requires("Partner: Michelle Matthieu") + @Requires("Partner: P-31011 - Michelle Matthieu") void shouldAddPhoneNumberToContactData() { new AddPhoneNumberToContactData(this) .given("partnerName", "Matthieu") @@ -169,7 +175,7 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(1102) - @Requires("Partner: Michelle Matthieu") + @Requires("Partner: P-31011 - Michelle Matthieu") void shouldRemovePhoneNumberFromContactData() { new RemovePhoneNumberFromContactData(this) .given("partnerName", "Matthieu") @@ -179,7 +185,7 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(1103) - @Requires("Partner: Test AG") + @Requires("Partner: P-31010 - Test AG") void shouldReplaceContactData() { new ReplaceContactData(this) .given("partnerName", "Test AG") @@ -201,7 +207,7 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(1201) - @Requires("Partner: Michelle Matthieu") + @Requires("Partner: P-31011 - Michelle Matthieu") void shouldUpdatePersonData() { new ShouldUpdatePersonData(this) .given("oldFamilyName", "Matthieu") @@ -211,7 +217,7 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(2010) - @Requires("Partner: Test AG") + @Requires("Partner: P-31010 - Test AG") @Produces("Debitor: Test AG - main debitor") void shouldCreateSelfDebitorForPartner() { new CreateSelfDebitorForPartner(this, "Debitor: Test AG - main debitor") @@ -261,18 +267,18 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(2020) - @Requires("Debitor: Test AG - main debitor") + @Requires("Debitor: D-3101000 - Test AG - main debitor") @Disabled("see TODO.spec in DontDeleteDefaultDebitor") void shouldNotDeleteDefaultDebitor() { new DontDeleteDefaultDebitor(this) - .given("partnerNumber", 31020) + .given("partnerNumber", 31010) .given("debitorSuffix", "00") .doRun(); } @Test @Order(3100) - @Requires("Debitor: Test AG - main debitor") + @Requires("Debitor: D-3101000 - Test AG - main debitor") @Produces("SEPA-Mandate: Test AG") void shouldCreateSepaMandateForDebitor() { new CreateSepaMandateForDebitor(this) @@ -313,12 +319,11 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(4000) - @Requires("Partner: Test AG") - @Produces("Membership: Test AG 00") + @Requires("Partner: P-31010 - Test AG") + @Produces("Membership: M-3101000 - Test AG") void shouldCreateMembershipForPartner() { new CreateMembership(this) .given("partnerName", "Test AG") - .given("memberNumberSuffix", "00") .given("validFrom", "2024-10-15") .given("newStatus", "ACTIVE") .given("membershipFeeBillable", "true") @@ -326,9 +331,87 @@ class HsOfficeScenarioTests extends ScenarioTest { .keep(); } + @Test + @Order(4201) + @Requires("Membership: M-3101000 - Test AG") + @Produces("Coop-Shares SUBSCRIPTION Transaction") + void shouldSubscribeCoopShares() { + new CreateCoopSharesSubscriptionTransaction(this) + .given("memberNumber", "3101000") + .given("reference", "sign 2024-01-15") + .given("shareCount", 100) + .given("comment", "Signing the Membership") + .given("transactionDate", "2024-01-15") + .doRun(); + } + + @Test + @Order(4202) + @Requires("Membership: M-3101000 - Test AG") + void shouldRevertCoopSharesSubscription() { + new CreateCoopSharesRevertTransaction(this) + .given("memberNumber", "3101000") + .given("comment", "reverting some incorrect transaction") + .given("dateOfIncorrectTransaction", "2024-02-15") + .doRun(); + } + + @Test + @Order(4202) + @Requires("Coop-Shares SUBSCRIPTION Transaction") + @Produces("Coop-Shares CANCELLATION Transaction") + void shouldCancelCoopSharesSubscription() { + new CreateCoopSharesCancellationTransaction(this) + .given("memberNumber", "3101000") + .given("reference", "cancel 2024-01-15") + .given("sharesToCancel", 8) + .given("comment", "Cancelling 8 Shares") + .given("transactionDate", "2024-02-15") + .doRun(); + } + + @Test + @Order(4301) + @Requires("Membership: M-3101000 - Test AG") + @Produces("Coop-Assets DEPOSIT Transaction") + void shouldSubscribeCoopAssets() { + new CreateCoopAssetsDepositTransaction(this) + .given("memberNumber", "3101000") + .given("reference", "sign 2024-01-15") + .given("assetValue", 100*64) + .given("comment", "disposal for initial shares") + .given("transactionDate", "2024-01-15") + .doRun(); + } + + @Test + @Order(4302) + @Requires("Membership: M-3101000 - Test AG") + void shouldRevertCoopAssetsSubscription() { + new CreateCoopAssetsRevertTransaction(this) + .given("memberNumber", "3101000") + .given("comment", "reverting some incorrect transaction") + .given("dateOfIncorrectTransaction", "2024-02-15") + .doRun(); + } + + @Test + @Order(4302) + @Requires("Coop-Assets DEPOSIT Transaction") + @Produces("Coop-Assets DISBURSAL Transaction") + void shouldDisburseCoopAssets() { + new CreateCoopAssetsDisbursalTransaction(this) + .given("memberNumber", "3101000") + .given("reference", "cancel 2024-01-15") + .given("valueToDisburse", 8*64) + .given("comment", "disbursal according to shares cancellation") + .given("transactionDate", "2024-02-15") + .doRun(); + } + @Test @Order(4900) - @Requires("Membership: Test AG 00") + @Requires("Membership: M-3101000 - Test AG") void shouldCancelMembershipOfPartner() { new CancelMembership(this) .given("memberNumber", "3101000") diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/PathAssertion.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/PathAssertion.java index ffd9df8e..6d3dc2a2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/PathAssertion.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/PathAssertion.java @@ -4,6 +4,9 @@ import net.hostsharing.hsadminng.hs.office.scenarios.UseCase.HttpResponse; import java.util.function.Consumer; +import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; +import static org.junit.jupiter.api.Assertions.fail; + public class PathAssertion { private final String path; @@ -14,10 +17,35 @@ public class PathAssertion { @SuppressWarnings({ "unchecked", "rawtypes" }) public Consumer contains(final String resolvableValue) { - return response -> response.path(path).contains(ScenarioTest.resolve(resolvableValue)); + return response -> { + try { + response.path(path).map(this::asString).contains(ScenarioTest.resolve(resolvableValue, DROP_COMMENTS)); + } catch (final AssertionError e) { + // without this, the error message is often lacking important context + fail(e.getMessage() + " in `path(\"" + path + "\").contains(\"" + resolvableValue + "\")`" ); + } + }; } public Consumer doesNotExist() { - return response -> response.path(path).isNull(); // here, null Optional means key not found in JSON + return response -> { + try { + response.path(path).isNull(); // here, null Optional means key not found in JSON + } catch (final AssertionError e) { + // without this, the error message is often lacking important context + fail(e.getMessage() + " in `path(\"" + path + "\").doesNotExist()`" ); + } + }; + } + + private String asString(final Object value) { + if (value instanceof Double doubleValue) { + if (doubleValue % 1 == 0) { + return String.valueOf(doubleValue.intValue()); // avoid trailing ".0" + } else { + return doubleValue.toString(); + } + } + return value.toString(); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java index 4600584a..530eba74 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.scenarios; import lombok.SneakyThrows; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository; +import net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver; import net.hostsharing.hsadminng.lambda.Reducer; import net.hostsharing.hsadminng.rbac.context.ContextBasedTest; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; @@ -26,6 +27,8 @@ import java.util.stream.Collectors; import static java.util.Arrays.asList; import static java.util.Optional.ofNullable; +import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; +import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; import static org.assertj.core.api.Assertions.assertThat; public abstract class ScenarioTest extends ContextBasedTest { @@ -38,11 +41,11 @@ public abstract class ScenarioTest extends ContextBasedTest { public String toString() { return ObjectUtils.toString(uuid); } + } - private final static Map> aliases = new HashMap<>(); - private final static Map properties = new HashMap<>(); + private final static Map properties = new HashMap<>(); public final TestReport testReport = new TestReport(aliases); @LocalServerPort @@ -139,9 +142,9 @@ public abstract class ScenarioTest extends ContextBasedTest { } static UUID uuid(final String nameWithPlaceholders) { - final var resoledName = resolve(nameWithPlaceholders); - final UUID alias = ofNullable(knowVariables().get(resoledName)).filter(v -> v instanceof UUID).map(UUID.class::cast).orElse(null); - assertThat(alias).as("alias '" + resoledName + "' not found in aliases nor in properties [" + + final var resolvedName = resolve(nameWithPlaceholders, DROP_COMMENTS); + final UUID alias = ofNullable(knowVariables().get(resolvedName)).filter(v -> v instanceof UUID).map(UUID.class::cast).orElse(null); + assertThat(alias).as("alias '" + resolvedName + "' not found in aliases nor in properties [" + knowVariables().keySet().stream().map(v -> "'" + v + "'").collect(Collectors.joining(", ")) + "]" ).isNotNull(); return alias; @@ -162,13 +165,13 @@ public abstract class ScenarioTest extends ContextBasedTest { return map; } - public static String resolve(final String text) { - final var resolved = new TemplateResolver(text, ScenarioTest.knowVariables()).resolve(); + public static String resolve(final String text, final Resolver resolver) { + final var resolved = new TemplateResolver(text, ScenarioTest.knowVariables()).resolve(resolver); return resolved; } public static Object resolveTyped(final String text) { - final var resolved = resolve(text); + final var resolved = resolve(text, DROP_COMMENTS); try { return UUID.fromString(resolved); } catch (final IllegalArgumentException e) { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java index aaa8855a..002d0512 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java @@ -10,29 +10,39 @@ import java.util.Objects; import java.util.regex.Pattern; import java.util.stream.Collectors; +import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; + public class TemplateResolver { - private final static Pattern pattern = Pattern.compile(",(\\s*})", Pattern.MULTILINE); - private static final String IF_NOT_FOUND_SYMBOL = "???"; + public enum Resolver { + DROP_COMMENTS, // deletes comments ('#{whatever}' -> '') + KEEP_COMMENTS // keep comments ('#{whatever}' -> 'whatever') + } enum PlaceholderPrefix { RAW('%') { @Override - String convert(final Object value) { + String convert(final Object value, final Resolver resolver) { return value != null ? value.toString() : ""; } }, JSON_QUOTED('$'){ @Override - String convert(final Object value) { + String convert(final Object value, final Resolver resolver) { return jsonQuoted(value); } }, URI_ENCODED('&'){ @Override - String convert(final Object value) { + String convert(final Object value, final Resolver resolver) { return value != null ? URLEncoder.encode(value.toString(), StandardCharsets.UTF_8) : ""; } + }, + COMMENT('#'){ + @Override + String convert(final Object value, final Resolver resolver) { + return resolver == DROP_COMMENTS ? "" : value.toString(); + } }; private final char prefixChar; @@ -42,19 +52,24 @@ public class TemplateResolver { } static boolean contains(final char givenChar) { - return Arrays.stream(values()).anyMatch(p -> p.prefixChar == givenChar); + return Arrays.stream(values()).anyMatch(p -> p.prefixChar == givenChar); } static PlaceholderPrefix ofPrefixChar(final char givenChar) { return Arrays.stream(values()).filter(p -> p.prefixChar == givenChar).findFirst().orElseThrow(); } - abstract String convert(final Object value); + abstract String convert(final Object value, final Resolver resolver); } + private static final Pattern COMMA_RIGHT_BEFORE_CLOSING_BRACE = Pattern.compile(",(\\s*})", Pattern.MULTILINE); + private static final String IF_NOT_FOUND_SYMBOL = "???"; + private final String template; private final Map properties; private final StringBuilder resolved = new StringBuilder(); + + private Resolver resolver; private int position = 0; public TemplateResolver(final String template, final Map properties) { @@ -62,7 +77,8 @@ public class TemplateResolver { this.properties = properties; } - String resolve() { + String resolve(final Resolver resolver) { + this.resolver = resolver; final var resolved = copy(); final var withoutDroppedLines = dropLinesWithNullProperties(resolved); final var result = removeDanglingCommas(withoutDroppedLines); @@ -70,7 +86,7 @@ public class TemplateResolver { } private static String removeDanglingCommas(final String withoutDroppedLines) { - return pattern.matcher(withoutDroppedLines).replaceAll("$1"); + return COMMA_RIGHT_BEFORE_CLOSING_BRACE.matcher(withoutDroppedLines).replaceAll("$1"); } private String dropLinesWithNullProperties(final String text) { @@ -119,10 +135,10 @@ public class TemplateResolver { placeholder.append(fetchChar()); } } - final var name = new TemplateResolver(placeholder.toString(), properties).resolve(); - final var value = propVal(name); + final var content = new TemplateResolver(placeholder.toString(), properties).resolve(resolver); + final var value = intro != '#' ? propVal(content) : content; resolved.append( - PlaceholderPrefix.ofPrefixChar(intro).convert(value) + PlaceholderPrefix.ofPrefixChar(intro).convert(value, resolver) ); skipChar('}'); } @@ -134,12 +150,12 @@ public class TemplateResolver { } else if (nameExpression.contains(IF_NOT_FOUND_SYMBOL)) { final var parts = StringUtils.split(nameExpression, IF_NOT_FOUND_SYMBOL); return Arrays.stream(parts).filter(Objects::nonNull).findFirst().orElseGet(() -> { - if ( parts[parts.length-1].isEmpty() ) { - // => whole expression ends with IF_NOT_FOUND_SYMBOL, thus last null element was optional - return null; - } - // => last alternative element in expression was null and not optional - throw new IllegalStateException("Missing required value in property-chain: " + nameExpression); + if ( parts[parts.length-1].isEmpty() ) { + // => whole expression ends with IF_NOT_FOUND_SYMBOL, thus last null element was optional + return null; + } + // => last alternative element in expression was null and not optional + throw new IllegalStateException("Missing required value in property-chain: " + nameExpression); }); } else { final var val = properties.get(nameExpression); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolverUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolverUnitTest.java index 2d63e204..435d44d3 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolverUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolverUnitTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import java.util.Map; +import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; import static org.assertj.core.api.Assertions.assertThat; class TemplateResolverUnitTest { @@ -42,7 +43,7 @@ class TemplateResolverUnitTest { Map.entry("simple placeholder", "einfach"), Map.entry("nested placeholder", "verschachtelt"), Map.entry("with-special-chars", "3&3 AG") - )).resolve(); + )).resolve(DROP_COMMENTS); assertThat(resolved).isEqualTo(""" with optional JSON quotes: diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TestReport.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TestReport.java index 8aba4319..ec6e0fd6 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TestReport.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TestReport.java @@ -1,6 +1,8 @@ package net.hostsharing.hsadminng.hs.office.scenarios; import lombok.SneakyThrows; +import net.hostsharing.hsadminng.system.SystemProcess; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.TestInfo; @@ -9,29 +11,41 @@ import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Method; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; public class TestReport { - private final Map aliases; - private final StringBuilder markdownLog = new StringBuilder(); // records everything for debugging purposes + public static final File BUILD_DOC_SCENARIOS = new File("build/doc/scenarios"); + private final static File markdownLogFile = new File(BUILD_DOC_SCENARIOS, ".last-debug-log.md"); + public static final SimpleDateFormat MM_DD_YYYY_HH_MM_SS = new SimpleDateFormat("MM-dd-yyyy hh:mm:ss"); - private PrintWriter markdownReport; + private final Map aliases; + private final PrintWriter markdownLog; // records everything for debugging purposes + private File markdownReportFile; + private PrintWriter markdownReport; // records only the use-case under test, without its pre-requisites private int silent; // do not print anything to test-report if >0 + static { + assertThat(BUILD_DOC_SCENARIOS.isDirectory() || BUILD_DOC_SCENARIOS.mkdirs()) + .as("mkdir " + BUILD_DOC_SCENARIOS).isTrue(); + } + + @SneakyThrows public TestReport(final Map aliases) { this.aliases = aliases; + this.markdownLog = new PrintWriter(new FileWriter(markdownLogFile)); } public void createTestLogMarkdownFile(final TestInfo testInfo) throws IOException { final var testMethodName = testInfo.getTestMethod().map(Method::getName).orElseThrow(); final var testMethodOrder = testInfo.getTestMethod().map(m -> m.getAnnotation(Order.class).value()).orElseThrow(); - assertThat(new File("doc/scenarios/").isDirectory() || new File("doc/scenarios/").mkdirs()).as("mkdir doc/scenarios/").isTrue(); - markdownReport = new PrintWriter(new FileWriter("doc/scenarios/" + testMethodOrder + "-" + testMethodName + ".md")); - print("## Scenario #" + testInfo.getTestMethod().map(TestReport::orderNumber).orElseThrow() + ": " + - testMethodName.replaceAll("([a-z])([A-Z]+)", "$1 $2")); + markdownReportFile = new File(BUILD_DOC_SCENARIOS, testMethodOrder + "-" + testMethodName + ".md"); + markdownReport = new PrintWriter(new FileWriter(markdownReportFile)); + print("## Scenario #" + determineScenarioTitle(testInfo)); } @SneakyThrows @@ -45,7 +59,7 @@ public class TestReport { } // but the debugLog should contain all output, even if silent - markdownLog.append(outputWithCommentsForUuids); + markdownLog.print(outputWithCommentsForUuids); } public void printLine(final String output) { @@ -56,10 +70,32 @@ public class TestReport { printLine("\n" +output + "\n"); } + void silent(final Runnable code) { + silent++; + code.run(); + silent--; + } + public void close() { if (markdownReport != null) { + printPara("---"); + printPara("generated on " + MM_DD_YYYY_HH_MM_SS.format(new Date()) + " for branch " + currentGitBranch()); markdownReport.close(); + System.out.println("SCENARIO REPORT: " + asClickableLink(markdownReportFile)); } + markdownLog.close(); + System.out.println("DEBUG LOG: " + asClickableLink(markdownLogFile)); + } + + private static @NotNull String determineScenarioTitle(final TestInfo testInfo) { + final var convertedTestMethodName = + testInfo.getTestMethod().map(TestReport::orderNumber).orElseThrow() + ": " + + testInfo.getTestMethod().map(Method::getName).map(t -> t.replaceAll("([a-z])([A-Z]+)", "$1 $2")).orElseThrow(); + return convertedTestMethodName.replaceAll(": should ", ": "); + } + + private String asClickableLink(final File file) { + return file.toURI().toString().replace("file:/", "file:///"); } private static Object orderNumber(final Method method) { @@ -83,10 +119,16 @@ public class TestReport { return result.toString(); } - void silent(final Runnable code) { - silent++; - code.run(); - silent--; + @SneakyThrows + private String currentGitBranch() { + try { + final var gitRevParse = new SystemProcess("git", "rev-parse", "--abbrev-ref", "HEAD"); + gitRevParse.execute(); + return gitRevParse.getStdOut().split("\\R", 2)[0]; + } catch (final IOException exc) { + // TODO.test: the git call does not work in Jenkins, we have to find out why + System.err.println(exc); + return "unknown"; + } } - } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java index f224fff2..23244d64 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.scenarios; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.PathNotFoundException; import io.restassured.http.ContentType; import lombok.Getter; import lombok.SneakyThrows; @@ -33,6 +34,8 @@ import java.util.function.Function; import java.util.function.Supplier; import static java.net.URLEncoder.encode; +import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; +import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.KEEP_COMMENTS; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.platform.commons.util.StringUtils.isBlank; @@ -50,6 +53,7 @@ public abstract class UseCase> { private final Map givenProperties = new LinkedHashMap<>(); private String nextTitle; // just temporary to override resultAlias for sub-use-cases + private String introduction; public UseCase(final ScenarioTest testSuite) { this(testSuite, getResultAliasFromProducesAnnotationInCallStack()); @@ -71,6 +75,9 @@ public abstract class UseCase> { } public final HttpResponse doRun() { + if (introduction != null) { + testReport.printPara(introduction); + } testReport.printPara("### Given Properties"); testReport.printLine(""" | name | value | @@ -81,7 +88,7 @@ public abstract class UseCase> { testReport.silent(() -> requirements.forEach((alias, factory) -> { if (!ScenarioTest.containsAlias(alias)) { - factory.apply(alias).run().keep(); + factory.apply(alias).run().keepAs(alias); } }) ); @@ -95,6 +102,11 @@ public abstract class UseCase> { protected void verify(final HttpResponse response) { } + public UseCase introduction(final String introduction) { + this.introduction = introduction; + return this; + } + public final UseCase given(final String propName, final Object propValue) { givenProperties.put(propName, propValue); ScenarioTest.putProperty(propName, propValue); @@ -106,11 +118,11 @@ public abstract class UseCase> { } public final void obtain( - final String alias, + final String title, final Supplier http, final Function extractor, final String... extraInfo) { - withTitle(ScenarioTest.resolve(alias), () -> { + withTitle(title, () -> { final var response = http.get().keep(extractor); Arrays.stream(extraInfo).forEach(testReport::printPara); return response; @@ -118,15 +130,15 @@ public abstract class UseCase> { } public final void obtain(final String alias, final Supplier http, final String... extraInfo) { - withTitle(ScenarioTest.resolve(alias), () -> { + withTitle(alias, () -> { final var response = http.get().keep(); Arrays.stream(extraInfo).forEach(testReport::printPara); return response; }); } - public HttpResponse withTitle(final String title, final Supplier code) { - this.nextTitle = title; + public HttpResponse withTitle(final String resolvableTitle, final Supplier code) { + this.nextTitle = resolvableTitle; final var response = code.get(); this.nextTitle = null; return response; @@ -134,7 +146,7 @@ public abstract class UseCase> { @SneakyThrows public final HttpResponse httpGet(final String uriPathWithPlaceholders) { - final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders); + final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders, DROP_COMMENTS); final var request = HttpRequest.newBuilder() .GET() .uri(new URI("http://localhost:" + testSuite.port + uriPath)) @@ -147,7 +159,7 @@ public abstract class UseCase> { @SneakyThrows public final HttpResponse httpPost(final String uriPathWithPlaceholders, final JsonTemplate bodyJsonTemplate) { - final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders); + final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders, DROP_COMMENTS); final var requestBody = bodyJsonTemplate.resolvePlaceholders(); final var request = HttpRequest.newBuilder() .POST(BodyPublishers.ofString(requestBody)) @@ -162,7 +174,7 @@ public abstract class UseCase> { @SneakyThrows public final HttpResponse httpPatch(final String uriPathWithPlaceholders, final JsonTemplate bodyJsonTemplate) { - final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders); + final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders, DROP_COMMENTS); final var requestBody = bodyJsonTemplate.resolvePlaceholders(); final var request = HttpRequest.newBuilder() .method(HttpMethod.PATCH.toString(), BodyPublishers.ofString(requestBody)) @@ -177,7 +189,7 @@ public abstract class UseCase> { @SneakyThrows public final HttpResponse httpDelete(final String uriPathWithPlaceholders) { - final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders); + final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders, DROP_COMMENTS); final var request = HttpRequest.newBuilder() .DELETE() .uri(new URI("http://localhost:" + testSuite.port + uriPath)) @@ -197,7 +209,7 @@ public abstract class UseCase> { final String title, final Supplier http, final Consumer... assertions) { - withTitle(ScenarioTest.resolve(title), () -> { + withTitle(title, () -> { final var response = http.get(); Arrays.stream(assertions).forEach(assertion -> assertion.accept(response)); return response; @@ -209,7 +221,7 @@ public abstract class UseCase> { } public String uriEncoded(final String text) { - return encode(ScenarioTest.resolve(text), StandardCharsets.UTF_8); + return encode(ScenarioTest.resolve(text, DROP_COMMENTS), StandardCharsets.UTF_8); } public static class JsonTemplate { @@ -221,7 +233,7 @@ public abstract class UseCase> { } String resolvePlaceholders() { - return ScenarioTest.resolve(template); + return ScenarioTest.resolve(template, DROP_COMMENTS); } } @@ -266,7 +278,7 @@ public abstract class UseCase> { } public HttpResponse keep(final Function extractor) { - final var alias = nextTitle != null ? nextTitle : resultAlias; + final var alias = nextTitle != null ? ScenarioTest.resolve(nextTitle, DROP_COMMENTS) : resultAlias; assertThat(alias).as("cannot keep result, no alias found").isNotNull(); final var value = extractor.apply(this); @@ -276,15 +288,20 @@ public abstract class UseCase> { return this; } - public HttpResponse keep() { - final var alias = nextTitle != null ? nextTitle : resultAlias; - assertThat(alias).as("cannot keep result, no alias found").isNotNull(); + public HttpResponse keepAs(final String alias) { ScenarioTest.putAlias( - alias, + nonNullAlias(alias), new ScenarioTest.Alias<>(UseCase.this.getClass(), locationUuid)); return this; } + public HttpResponse keep() { + final var alias = nextTitle != null ? ScenarioTest.resolve(nextTitle, DROP_COMMENTS) : resultAlias; + assertThat(alias).as("cannot keep result, no title or alias found for locationUuid: " + locationUuid).isNotNull(); + + return keepAs(alias); + } + @SneakyThrows public HttpResponse expectArrayElements(final int expectedElementCount) { final var rootNode = objectMapper.readTree(response.body()); @@ -298,20 +315,20 @@ public abstract class UseCase> { @SneakyThrows public String getFromBody(final String path) { - return JsonPath.parse(response.body()).read(ScenarioTest.resolve(path)); + return JsonPath.parse(response.body()).read(ScenarioTest.resolve(path, DROP_COMMENTS)); } @SneakyThrows - public Optional getFromBodyAsOptional(final String path) { + public Optional getFromBodyAsOptional(final String path) { try { - return Optional.ofNullable(JsonPath.parse(response.body()).read(ScenarioTest.resolve(path))); - } catch (final Exception e) { + return Optional.ofNullable(JsonPath.parse(response.body()).read(ScenarioTest.resolve(path, DROP_COMMENTS))); + } catch (final PathNotFoundException e) { return null; // means the property did not exist at all, not that it was there with value null } } @SneakyThrows - public OptionalAssert path(final String path) { + public OptionalAssert path(final String path) { return assertThat(getFromBodyAsOptional(path)); } @@ -320,9 +337,9 @@ public abstract class UseCase> { // the title if (nextTitle != null) { - testReport.printLine("\n### " + nextTitle + "\n"); + testReport.printLine("\n### " + ScenarioTest.resolve(nextTitle, KEEP_COMMENTS) + "\n"); } else if (resultAlias != null) { - testReport.printLine("\n### " + resultAlias + "\n"); + testReport.printLine("\n### Create " + resultAlias + "\n"); } else { fail("please wrap the http...-call in the UseCase using `withTitle(...)`"); } @@ -342,6 +359,13 @@ public abstract class UseCase> { testReport.printLine("```"); testReport.printLine(""); } + + private String nonNullAlias(final String alias) { + // This marker tag should not appear in the source-code, as here is nothing to fix. + // But if it appears in generated Markdown files, it should show up when that marker tag is searched. + final var onlyVisibleInGeneratedMarkdownNotInSource = new String(new char[]{'F', 'I', 'X', 'M', 'E'}); + return alias == null ? "unknown alias -- " + onlyVisibleInGeneratedMarkdownNotInSource : alias; + } } protected T self() { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/CreateMembership.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/CreateMembership.java index 379bba1e..890dfedf 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/CreateMembership.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/CreateMembership.java @@ -1,8 +1,8 @@ package net.hostsharing.hsadminng.hs.office.scenarios.membership; import io.restassured.http.ContentType; -import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; +import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; @@ -16,10 +16,18 @@ public class CreateMembership extends UseCase { @Override protected HttpResponse run() { + + obtain("Partner: %{partnerName}", () -> + httpGet("/api/hs/office/partners?name=&{partnerName}") + .expecting(OK).expecting(JSON), + response -> response.expectArrayElements(1).getFromBody("[0].uuid"), + "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." + ); + return httpPost("/api/hs/office/memberships", usingJsonBody(""" { - "partner.uuid": ${Partner: Test AG}, - "memberNumberSuffix": ${memberNumberSuffix}, + "partner.uuid": ${Partner: %{partnerName}}, + "memberNumberSuffix": ${%{memberNumberSuffix???}???00}, "status": "ACTIVE", "validFrom": ${validFrom}, "membershipFeeBillable": ${membershipFeeBillable} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsDepositTransaction.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsDepositTransaction.java new file mode 100644 index 00000000..af9b01a1 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsDepositTransaction.java @@ -0,0 +1,12 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets; + +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; + +public class CreateCoopAssetsDepositTransaction extends CreateCoopAssetsTransaction { + + public CreateCoopAssetsDepositTransaction(final ScenarioTest testSuite) { + super(testSuite); + + given("transactionType", "DEPOSIT"); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsDisbursalTransaction.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsDisbursalTransaction.java new file mode 100644 index 00000000..542f75d8 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsDisbursalTransaction.java @@ -0,0 +1,17 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets; + +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; + +public class CreateCoopAssetsDisbursalTransaction extends CreateCoopAssetsTransaction { + + public CreateCoopAssetsDisbursalTransaction(final ScenarioTest testSuite) { + super(testSuite); + } + + @Override + protected HttpResponse run() { + given("transactionType", "DISBURSAL"); + given("assetValue", "-%{valueToDisburse}"); + return super.run(); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsRevertTransaction.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsRevertTransaction.java new file mode 100644 index 00000000..0750847c --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsRevertTransaction.java @@ -0,0 +1,27 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets; + +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; + +public class CreateCoopAssetsRevertTransaction extends CreateCoopAssetsTransaction { + + public CreateCoopAssetsRevertTransaction(final ScenarioTest testSuite) { + super(testSuite); + + requires("CoopAssets-Transaction with incorrect assetValue", alias -> + new CreateCoopAssetsDepositTransaction(testSuite) + .given("memberNumber", "3101000") + .given("reference", "sign %{dateOfIncorrectTransaction}") // same as revertedAssetTx + .given("assetValue", 10) + .given("comment", "coop-assets deposit transaction with wrong asset value") + .given("transactionDate", "%{dateOfIncorrectTransaction}") + ); + } + + @Override + protected HttpResponse run() { + given("transactionType", "REVERSAL"); + given("assetValue", -100); + given("revertedAssetTx", uuid("CoopAssets-Transaction with incorrect assetValue")); + return super.run(); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsTransaction.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsTransaction.java new file mode 100644 index 00000000..56bc2a55 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsTransaction.java @@ -0,0 +1,53 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets; + +import io.restassured.http.ContentType; +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; +import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; +import org.springframework.http.HttpStatus; + +import static io.restassured.http.ContentType.JSON; +import static org.springframework.http.HttpStatus.OK; + +public abstract class CreateCoopAssetsTransaction extends UseCase { + + public CreateCoopAssetsTransaction(final ScenarioTest testSuite) { + super(testSuite); + } + + @Override + protected HttpResponse run() { + + obtain("#{Find }membershipUuid", () -> + httpGet("/api/hs/office/memberships?memberNumber=&{memberNumber}") + .expecting(OK).expecting(JSON).expectArrayElements(1), + response -> response.getFromBody("$[0].uuid") + ); + + return withTitle("Create the Coop-Assets-%{transactionType} Transaction", () -> + httpPost("/api/hs/office/coopassetstransactions", usingJsonBody(""" + { + "membership.uuid": ${membershipUuid}, + "transactionType": ${transactionType}, + "reference": ${reference}, + "assetValue": ${assetValue}, + "comment": ${comment}, + "valueDate": ${transactionDate}, + "revertedAssetTx.uuid": ${revertedAssetTx???} + } + """)) + .expecting(HttpStatus.CREATED).expecting(ContentType.JSON) + ); + } + + @Override + protected void verify(final HttpResponse response) { + verify("Verify Coop-Assets %{transactionType}-Transaction", + () -> httpGet("/api/hs/office/coopassetstransactions/" + response.getLocationUuid()) + .expecting(HttpStatus.OK).expecting(ContentType.JSON), + path("transactionType").contains("%{transactionType}"), + path("assetValue").contains("%{assetValue}"), + path("comment").contains("%{comment}"), + path("valueDate").contains("%{transactionDate}") + ); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesCancellationTransaction.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesCancellationTransaction.java new file mode 100644 index 00000000..16549b36 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesCancellationTransaction.java @@ -0,0 +1,17 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares; + +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; + +public class CreateCoopSharesCancellationTransaction extends CreateCoopSharesTransaction { + + public CreateCoopSharesCancellationTransaction(final ScenarioTest testSuite) { + super(testSuite); + } + + @Override + protected HttpResponse run() { + given("transactionType", "CANCELLATION"); + given("shareCount", "-%{sharesToCancel}"); + return super.run(); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesRevertTransaction.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesRevertTransaction.java new file mode 100644 index 00000000..03963834 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesRevertTransaction.java @@ -0,0 +1,27 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares; + +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; + +public class CreateCoopSharesRevertTransaction extends CreateCoopSharesTransaction { + + public CreateCoopSharesRevertTransaction(final ScenarioTest testSuite) { + super(testSuite); + + requires("CoopShares-Transaction with incorrect shareCount", alias -> + new CreateCoopSharesSubscriptionTransaction(testSuite) + .given("memberNumber", "3101000") + .given("reference", "sign %{dateOfIncorrectTransaction}") // same as revertedShareTx + .given("shareCount", 100) + .given("comment", "coop-shares subscription transaction with wrong share count") + .given("transactionDate", "%{dateOfIncorrectTransaction}") + ); + } + + @Override + protected HttpResponse run() { + given("transactionType", "REVERSAL"); + given("shareCount", -100); + given("revertedShareTx", uuid("CoopShares-Transaction with incorrect shareCount")); + return super.run(); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesSubscriptionTransaction.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesSubscriptionTransaction.java new file mode 100644 index 00000000..84db07dd --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesSubscriptionTransaction.java @@ -0,0 +1,12 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares; + +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; + +public class CreateCoopSharesSubscriptionTransaction extends CreateCoopSharesTransaction { + + public CreateCoopSharesSubscriptionTransaction(final ScenarioTest testSuite) { + super(testSuite); + + given("transactionType", "SUBSCRIPTION"); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesTransaction.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesTransaction.java new file mode 100644 index 00000000..cd8d9c14 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesTransaction.java @@ -0,0 +1,53 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares; + +import io.restassured.http.ContentType; +import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; +import org.springframework.http.HttpStatus; + +import static io.restassured.http.ContentType.JSON; +import static org.springframework.http.HttpStatus.OK; + +public abstract class CreateCoopSharesTransaction extends UseCase { + + public CreateCoopSharesTransaction(final ScenarioTest testSuite) { + super(testSuite); + } + + @Override + protected HttpResponse run() { + + obtain("#{Find }membershipUuid", () -> + httpGet("/api/hs/office/memberships?memberNumber=&{memberNumber}") + .expecting(OK).expecting(JSON).expectArrayElements(1), + response -> response.getFromBody("$[0].uuid") + ); + + return withTitle("Create the Coop-Shares-%{transactionType} Transaction", () -> + httpPost("/api/hs/office/coopsharestransactions", usingJsonBody(""" + { + "membership.uuid": ${membershipUuid}, + "transactionType": ${transactionType}, + "reference": ${reference}, + "shareCount": ${shareCount}, + "comment": ${comment}, + "valueDate": ${transactionDate}, + "revertedShareTx.uuid": ${revertedShareTx???} + } + """)) + .expecting(HttpStatus.CREATED).expecting(ContentType.JSON) + ); + } + + @Override + protected void verify(final HttpResponse response) { + verify("Verify Coop-Shares %{transactionType}-Transaction", + () -> httpGet("/api/hs/office/coopsharestransactions/" + response.getLocationUuid()) + .expecting(HttpStatus.OK).expecting(ContentType.JSON), + path("transactionType").contains("%{transactionType}"), + path("shareCount").contains("%{shareCount}"), + path("comment").contains("%{comment}"), + path("valueDate").contains("%{transactionDate}") + ); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java index a3aa727c..3896bbf6 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java @@ -16,6 +16,8 @@ public class CreatePartner extends UseCase { public CreatePartner(final ScenarioTest testSuite) { super(testSuite); + + introduction("A partner can be a client or a vendor, currently we only use them for clients."); } @Override