Merge branch 'master' into TP-20240927-importfixes
This commit is contained in:
commit
a4d665bf5b
3
.aliases
3
.aliases
@ -95,3 +95,6 @@ if [ ! -f .environment ]; then
|
|||||||
cp .tc-environment .environment
|
cp .tc-environment .environment
|
||||||
fi
|
fi
|
||||||
source .environment
|
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'
|
||||||
|
@ -410,7 +410,7 @@ tasks.register('convertMarkdownToHtml') {
|
|||||||
group = 'Conversion'
|
group = 'Conversion'
|
||||||
|
|
||||||
// Define the template file and input directory
|
// 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
|
// Task configuration and execution
|
||||||
doFirst {
|
doFirst {
|
||||||
@ -425,13 +425,13 @@ tasks.register('convertMarkdownToHtml') {
|
|||||||
|
|
||||||
// Check if the template file exists
|
// Check if the template file exists
|
||||||
if (!templateFile.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 {
|
doLast {
|
||||||
// Gather all Markdown files in the current directory
|
// 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
|
// Corrected way to create the output file path
|
||||||
def outputFile = new File(file.parent, file.name.replaceAll(/\.md$/, '.html'))
|
def outputFile = new File(file.parent, file.name.replaceAll(/\.md$/, '.html'))
|
||||||
|
|
||||||
@ -444,3 +444,4 @@ tasks.register('convertMarkdownToHtml') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
convertMarkdownToHtml.dependsOn scenarioTests
|
||||||
|
110
doc/business-glossary-de.md
Normal file
110
doc/business-glossary-de.md
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
### hsadminNg fachliches Glossar
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Currently, this business glossary is only available in German because in many cases,
|
||||||
|
the German terms are important for comprehensibility for those using this software.
|
||||||
|
-->
|
||||||
|
|
||||||
|
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.
|
1
doc/scenarios/README.txt
Normal file
1
doc/scenarios/README.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
find the generated ScenarioReports in build/doc/scenarios
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.config;
|
package net.hostsharing.hsadminng.config;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
import org.openapitools.jackson.nullable.JsonNullableModule;
|
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.context.annotation.Primary;
|
||||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class JsonObjectMapperConfiguration {
|
public class JsonObjectMapperConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Primary
|
@Primary
|
||||||
public Jackson2ObjectMapperBuilder customObjectMapper() {
|
public Jackson2ObjectMapperBuilder customObjectMapper() {
|
||||||
|
// HOWTO: add JSON converters and specify other JSON mapping configurations
|
||||||
return new Jackson2ObjectMapperBuilder()
|
return new Jackson2ObjectMapperBuilder()
|
||||||
.modules(new JsonNullableModule(), new JavaTimeModule())
|
.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);
|
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ public class CustomErrorResponse {
|
|||||||
this.path = path;
|
this.path = path;
|
||||||
this.statusCode = status.value();
|
this.statusCode = status.value();
|
||||||
this.statusPhrase = status.getReasonPhrase();
|
this.statusPhrase = status.getReasonPhrase();
|
||||||
|
// HOWTO: debug serverside error response - set a breakpoint here
|
||||||
this.message = message.startsWith("ERROR: [") ? message : "ERROR: [" + statusCode + "] " + message;
|
this.message = message.startsWith("ERROR: [") ? message : "ERROR: [" + statusCode + "] " + message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ResponseEntity<List<HsOfficeCoopAssetsTransactionResource>> listCoopAssets(
|
public ResponseEntity<List<HsOfficeCoopAssetsTransactionResource>> getListOfCoopAssets(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final UUID membershipUuid,
|
final UUID membershipUuid,
|
||||||
@ -55,7 +55,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> addCoopAssetsTransaction(
|
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> postNewCoopAssetTransaction(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final HsOfficeCoopAssetsTransactionInsertResource requestBody) {
|
final HsOfficeCoopAssetsTransactionInsertResource requestBody) {
|
||||||
@ -77,7 +77,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> getCoopAssetTransactionByUuid(
|
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> getSingleCoopAssetTransactionByUuid(
|
||||||
final String currentSubject, final String assumedRoles, final UUID assetTransactionUuid) {
|
final String currentSubject, final String assumedRoles, final UUID assetTransactionUuid) {
|
||||||
|
|
||||||
context.define(currentSubject, assumedRoles);
|
context.define(currentSubject, assumedRoles);
|
||||||
@ -128,9 +128,9 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
}
|
}
|
||||||
|
|
||||||
final BiConsumer<HsOfficeCoopAssetsTransactionInsertResource, HsOfficeCoopAssetsTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
final BiConsumer<HsOfficeCoopAssetsTransactionInsertResource, HsOfficeCoopAssetsTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||||
if ( resource.getReverseEntryUuid() != null ) {
|
if ( resource.getRevertedAssetTxUuid() != null ) {
|
||||||
entity.setAdjustedAssetTx(coopAssetsTransactionRepo.findByUuid(resource.getReverseEntryUuid())
|
entity.setRevertedAssetTx(coopAssetsTransactionRepo.findByUuid(resource.getRevertedAssetTxUuid())
|
||||||
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] reverseEntityUuid %s not found".formatted(resource.getReverseEntryUuid()))));
|
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] reverseEntityUuid %s not found".formatted(resource.getRevertedAssetTxUuid()))));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -50,8 +50,8 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
|
|||||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getAssetValue)
|
.withProp(HsOfficeCoopAssetsTransactionEntity::getAssetValue)
|
||||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getReference)
|
.withProp(HsOfficeCoopAssetsTransactionEntity::getReference)
|
||||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getComment)
|
.withProp(HsOfficeCoopAssetsTransactionEntity::getComment)
|
||||||
.withProp(at -> ofNullable(at.getAdjustedAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
|
.withProp(at -> ofNullable(at.getRevertedAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
|
||||||
.withProp(at -> ofNullable(at.getAdjustmentAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
|
.withProp(at -> ofNullable(at.getReversalAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
|
||||||
.quotedValues(false);
|
.quotedValues(false);
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@ -77,7 +77,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
|
|||||||
* The signed value which directly affects the booking balance.
|
* The signed value which directly affects the booking balance.
|
||||||
*
|
*
|
||||||
* <p>This means, that a DEPOSIT is always positive, a DISBURSAL is always negative,
|
* <p>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</p> more information.
|
* See {@link HsOfficeCoopAssetsTransactionType} for</p> more information.
|
||||||
*/
|
*/
|
||||||
@Column(name = "assetvalue")
|
@Column(name = "assetvalue")
|
||||||
@ -96,14 +96,14 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
|
|||||||
private String comment;
|
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
|
@OneToOne
|
||||||
@JoinColumn(name = "adjustedassettxuuid")
|
@JoinColumn(name = "revertedassettxuuid")
|
||||||
private HsOfficeCoopAssetsTransactionEntity adjustedAssetTx;
|
private HsOfficeCoopAssetsTransactionEntity revertedAssetTx;
|
||||||
|
|
||||||
@OneToOne(mappedBy = "adjustedAssetTx")
|
@OneToOne(mappedBy = "revertedAssetTx")
|
||||||
private HsOfficeCoopAssetsTransactionEntity adjustmentAssetTx;
|
private HsOfficeCoopAssetsTransactionEntity reversalAssetTx;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HsOfficeCoopAssetsTransactionEntity load() {
|
public HsOfficeCoopAssetsTransactionEntity load() {
|
||||||
|
@ -4,7 +4,7 @@ public enum HsOfficeCoopAssetsTransactionType {
|
|||||||
/**
|
/**
|
||||||
* correction of wrong bookings, value can be positive or negative
|
* correction of wrong bookings, value can be positive or negative
|
||||||
*/
|
*/
|
||||||
ADJUSTMENT,
|
REVERSAL,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* payment received from member after signing shares, value >0
|
* payment received from member after signing shares, value >0
|
||||||
|
@ -38,7 +38,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ResponseEntity<List<HsOfficeCoopSharesTransactionResource>> listCoopShares(
|
public ResponseEntity<List<HsOfficeCoopSharesTransactionResource>> getListOfCoopShares(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final UUID membershipUuid,
|
final UUID membershipUuid,
|
||||||
@ -57,7 +57,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public ResponseEntity<HsOfficeCoopSharesTransactionResource> addCoopSharesTransaction(
|
public ResponseEntity<HsOfficeCoopSharesTransactionResource> postNewCoopSharesTransaction(
|
||||||
final String currentSubject,
|
final String currentSubject,
|
||||||
final String assumedRoles,
|
final String assumedRoles,
|
||||||
final HsOfficeCoopSharesTransactionInsertResource requestBody) {
|
final HsOfficeCoopSharesTransactionInsertResource requestBody) {
|
||||||
@ -80,7 +80,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ResponseEntity<HsOfficeCoopSharesTransactionResource> getCoopShareTransactionByUuid(
|
public ResponseEntity<HsOfficeCoopSharesTransactionResource> getSingleCoopShareTransactionByUuid(
|
||||||
final String currentSubject, final String assumedRoles, final UUID shareTransactionUuid) {
|
final String currentSubject, final String assumedRoles, final UUID shareTransactionUuid) {
|
||||||
|
|
||||||
context.define(currentSubject, assumedRoles);
|
context.define(currentSubject, assumedRoles);
|
||||||
@ -131,9 +131,9 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
|||||||
}
|
}
|
||||||
|
|
||||||
final BiConsumer<HsOfficeCoopSharesTransactionInsertResource, HsOfficeCoopSharesTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
final BiConsumer<HsOfficeCoopSharesTransactionInsertResource, HsOfficeCoopSharesTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||||
if ( resource.getAdjustedShareTxUuid() != null ) {
|
if ( resource.getRevertedShareTxUuid() != null ) {
|
||||||
entity.setAdjustedShareTx(coopSharesTransactionRepo.findByUuid(resource.getAdjustedShareTxUuid())
|
entity.setRevertedShareTx(coopSharesTransactionRepo.findByUuid(resource.getRevertedShareTxUuid())
|
||||||
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] adjustedShareTxUuid %s not found".formatted(resource.getAdjustedShareTxUuid()))));
|
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] revertedShareTxUuid %s not found".formatted(resource.getRevertedShareTxUuid()))));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -48,8 +48,8 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseE
|
|||||||
.withProp(HsOfficeCoopSharesTransactionEntity::getShareCount)
|
.withProp(HsOfficeCoopSharesTransactionEntity::getShareCount)
|
||||||
.withProp(HsOfficeCoopSharesTransactionEntity::getReference)
|
.withProp(HsOfficeCoopSharesTransactionEntity::getReference)
|
||||||
.withProp(HsOfficeCoopSharesTransactionEntity::getComment)
|
.withProp(HsOfficeCoopSharesTransactionEntity::getComment)
|
||||||
.withProp(at -> ofNullable(at.getAdjustedShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null))
|
.withProp(at -> ofNullable(at.getRevertedShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null))
|
||||||
.withProp(at -> ofNullable(at.getAdjustmentShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null))
|
.withProp(at -> ofNullable(at.getReversalShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null))
|
||||||
.quotedValues(false);
|
.quotedValues(false);
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@ -71,7 +71,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseE
|
|||||||
* The signed value which directly affects the booking balance.
|
* The signed value which directly affects the booking balance.
|
||||||
*
|
*
|
||||||
* <p>This means, that a SUBSCRIPTION is always positive, a CANCELLATION is always negative,
|
* <p>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</p> more information.
|
* See {@link HsOfficeCoopSharesTransactionType} for</p> more information.
|
||||||
*/
|
*/
|
||||||
@Column(name = "valuedate")
|
@Column(name = "valuedate")
|
||||||
@ -93,14 +93,14 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseE
|
|||||||
private String comment;
|
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
|
@OneToOne
|
||||||
@JoinColumn(name = "adjustedsharetxuuid")
|
@JoinColumn(name = "revertedsharetxuuid")
|
||||||
private HsOfficeCoopSharesTransactionEntity adjustedShareTx;
|
private HsOfficeCoopSharesTransactionEntity revertedShareTx;
|
||||||
|
|
||||||
@OneToOne(mappedBy = "adjustedShareTx")
|
@OneToOne(mappedBy = "revertedShareTx")
|
||||||
private HsOfficeCoopSharesTransactionEntity adjustmentShareTx;
|
private HsOfficeCoopSharesTransactionEntity reversalShareTx;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HsOfficeCoopSharesTransactionEntity load() {
|
public HsOfficeCoopSharesTransactionEntity load() {
|
||||||
|
@ -2,9 +2,9 @@ package net.hostsharing.hsadminng.hs.office.coopshares;
|
|||||||
|
|
||||||
public enum HsOfficeCoopSharesTransactionType {
|
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
|
* shares signed, e.g. with the declaration of accession, value >0
|
||||||
|
@ -16,6 +16,8 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
import static java.util.Optional.ofNullable;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
|
||||||
public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||||
@ -39,7 +41,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
|||||||
context.define(currentSubject, assumedRoles);
|
context.define(currentSubject, assumedRoles);
|
||||||
|
|
||||||
final var entities = ( memberNumber != null)
|
final var entities = ( memberNumber != null)
|
||||||
? List.of(membershipRepo.findMembershipByMemberNumber(memberNumber))
|
? ofNullable(membershipRepo.findMembershipByMemberNumber(memberNumber)).stream().toList()
|
||||||
: membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid);
|
: membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid);
|
||||||
|
|
||||||
final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class,
|
final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class,
|
||||||
|
@ -14,14 +14,16 @@ public interface HsOfficeMembershipRepository extends Repository<HsOfficeMembers
|
|||||||
|
|
||||||
HsOfficeMembershipEntity save(final HsOfficeMembershipEntity entity);
|
HsOfficeMembershipEntity save(final HsOfficeMembershipEntity entity);
|
||||||
|
|
||||||
|
List<HsOfficeMembershipEntity> findAll();
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT membership FROM HsOfficeMembershipEntity membership
|
SELECT membership FROM HsOfficeMembershipEntity membership
|
||||||
WHERE ( CAST(:partnerUuid as org.hibernate.type.UUIDCharType) IS NULL
|
WHERE ( CAST(:partnerUuid as org.hibernate.type.UUIDCharType) IS NULL
|
||||||
OR membership.partner.uuid = :partnerUuid )
|
OR membership.partner.uuid = :partnerUuid )
|
||||||
ORDER BY membership.partner.partnerNumber, membership.memberNumberSuffix
|
ORDER BY membership.partner.partnerNumber, membership.memberNumberSuffix
|
||||||
""")
|
""")
|
||||||
List<HsOfficeMembershipEntity> findMembershipsByOptionalPartnerUuid(UUID partnerUuid);
|
List<HsOfficeMembershipEntity> findMembershipsByOptionalPartnerUuid(UUID partnerUuid);
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT membership FROM HsOfficeMembershipEntity membership
|
SELECT membership FROM HsOfficeMembershipEntity membership
|
||||||
WHERE (:partnerNumber = membership.partner.partnerNumber)
|
WHERE (:partnerNumber = membership.partner.partnerNumber)
|
||||||
@ -31,10 +33,12 @@ public interface HsOfficeMembershipRepository extends Repository<HsOfficeMembers
|
|||||||
HsOfficeMembershipEntity findMembershipByPartnerNumberAndSuffix(
|
HsOfficeMembershipEntity findMembershipByPartnerNumberAndSuffix(
|
||||||
@NotNull Integer partnerNumber,
|
@NotNull Integer partnerNumber,
|
||||||
@NotNull String suffix);
|
@NotNull String suffix);
|
||||||
|
|
||||||
default HsOfficeMembershipEntity findMembershipByMemberNumber(Integer memberNumber) {
|
default HsOfficeMembershipEntity findMembershipByMemberNumber(Integer memberNumber) {
|
||||||
final var partnerNumber = memberNumber / 100;
|
final var partnerNumber = memberNumber / 100;
|
||||||
final var suffix = memberNumber % 100;
|
final String suffix = String.format("%02d", memberNumber % 100);
|
||||||
return findMembershipByPartnerNumberAndSuffix(partnerNumber, String.format("%02d", suffix));
|
final var result = findMembershipByPartnerNumberAndSuffix(partnerNumber, suffix);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
long count();
|
long count();
|
||||||
|
@ -143,6 +143,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
|||||||
|
|
||||||
private void optionallyCreateExPartnerRelation(final HsOfficePartnerEntity saved, final HsOfficeRelationRealEntity previousPartnerRel) {
|
private void optionallyCreateExPartnerRelation(final HsOfficePartnerEntity saved, final HsOfficeRelationRealEntity previousPartnerRel) {
|
||||||
if (!saved.getPartnerRel().getUuid().equals(previousPartnerRel.getUuid())) {
|
if (!saved.getPartnerRel().getUuid().equals(previousPartnerRel.getUuid())) {
|
||||||
|
// TODO.impl: we also need to use the new partner-person as the anchor
|
||||||
relationRepo.save(previousPartnerRel.toBuilder().uuid(null).type(EX_PARTNER).build());
|
relationRepo.save(previousPartnerRel.toBuilder().uuid(null).type(EX_PARTNER).build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import jakarta.persistence.AttributeConverter;
|
|||||||
import jakarta.persistence.Converter;
|
import jakarta.persistence.Converter;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
// HOWTO: convert data types for exchange between PostgreSQL and Java/Hibernate/JPA-Entities
|
||||||
@Converter(autoApply = true)
|
@Converter(autoApply = true)
|
||||||
public class HsOfficePersonTypeConverter implements AttributeConverter<HsOfficePersonType, String> {
|
public class HsOfficePersonTypeConverter implements AttributeConverter<HsOfficePersonType, String> {
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ public class SystemProcess {
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private String stdOut;
|
private String stdOut;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private String stdErr;
|
private String stdErr;
|
||||||
|
|
||||||
@ -21,7 +22,6 @@ public class SystemProcess {
|
|||||||
this.processBuilder = new ProcessBuilder(command);
|
this.processBuilder = new ProcessBuilder(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getCommand() {
|
public String getCommand() {
|
||||||
return processBuilder.command().toString();
|
return processBuilder.command().toString();
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ components:
|
|||||||
HsOfficeCoopAssetsTransactionType:
|
HsOfficeCoopAssetsTransactionType:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
- ADJUSTMENT
|
- REVERSAL
|
||||||
- DEPOSIT
|
- DEPOSIT
|
||||||
- DISBURSAL
|
- DISBURSAL
|
||||||
- TRANSFER
|
- TRANSFER
|
||||||
@ -32,15 +32,15 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
comment:
|
comment:
|
||||||
type: string
|
type: string
|
||||||
adjustedAssetTx:
|
revertedAssetTx:
|
||||||
$ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction'
|
$ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction'
|
||||||
adjustmentAssetTx:
|
reversalAssetTx:
|
||||||
$ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction'
|
$ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction'
|
||||||
|
|
||||||
HsOfficeReferencedCoopAssetsTransaction:
|
HsOfficeReferencedCoopAssetsTransaction:
|
||||||
description:
|
description:
|
||||||
Similar to `HsOfficeCoopAssetsTransaction` but without the self-referencing properties
|
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
|
type: object
|
||||||
properties:
|
properties:
|
||||||
uuid:
|
uuid:
|
||||||
@ -80,7 +80,7 @@ components:
|
|||||||
maxLength: 48
|
maxLength: 48
|
||||||
comment:
|
comment:
|
||||||
type: string
|
type: string
|
||||||
reverseEntry.uuid:
|
revertedAssetTx.uuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
required:
|
required:
|
||||||
|
@ -2,7 +2,7 @@ get:
|
|||||||
tags:
|
tags:
|
||||||
- hs-office-coopAssets
|
- hs-office-coopAssets
|
||||||
description: 'Fetch a single asset transaction by its uuid, if visible for the current subject.'
|
description: 'Fetch a single asset transaction by its uuid, if visible for the current subject.'
|
||||||
operationId: getCoopAssetTransactionByUuid
|
operationId: getSingleCoopAssetTransactionByUuid
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
|
@ -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.
|
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:
|
tags:
|
||||||
- hs-office-coopAssets
|
- hs-office-coopAssets
|
||||||
operationId: listCoopAssets
|
operationId: getListOfCoopAssets
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
@ -46,7 +46,7 @@ post:
|
|||||||
summary: Adds a new cooperative asset transaction.
|
summary: Adds a new cooperative asset transaction.
|
||||||
tags:
|
tags:
|
||||||
- hs-office-coopAssets
|
- hs-office-coopAssets
|
||||||
operationId: addCoopAssetsTransaction
|
operationId: postNewCoopAssetTransaction
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
|
@ -6,7 +6,7 @@ components:
|
|||||||
HsOfficeCoopSharesTransactionType:
|
HsOfficeCoopSharesTransactionType:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
- ADJUSTMENT
|
- REVERSAL
|
||||||
- SUBSCRIPTION
|
- SUBSCRIPTION
|
||||||
- CANCELLATION
|
- CANCELLATION
|
||||||
|
|
||||||
@ -27,15 +27,15 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
comment:
|
comment:
|
||||||
type: string
|
type: string
|
||||||
adjustedShareTx:
|
revertedShareTx:
|
||||||
$ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction'
|
$ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction'
|
||||||
adjustmentShareTx:
|
reversalShareTx:
|
||||||
$ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction'
|
$ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction'
|
||||||
|
|
||||||
HsOfficeReferencedCoopSharesTransaction:
|
HsOfficeReferencedCoopSharesTransaction:
|
||||||
description:
|
description:
|
||||||
Similar to `HsOfficeCoopSharesTransaction` but without the self-referencing properties
|
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
|
type: object
|
||||||
properties:
|
properties:
|
||||||
uuid:
|
uuid:
|
||||||
@ -73,7 +73,7 @@ components:
|
|||||||
maxLength: 48
|
maxLength: 48
|
||||||
comment:
|
comment:
|
||||||
type: string
|
type: string
|
||||||
adjustedShareTx.uuid:
|
revertedShareTx.uuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
required:
|
required:
|
||||||
|
@ -2,7 +2,7 @@ get:
|
|||||||
tags:
|
tags:
|
||||||
- hs-office-coopShares
|
- hs-office-coopShares
|
||||||
description: 'Fetch a single share transaction by its uuid, if visible for the current subject.'
|
description: 'Fetch a single share transaction by its uuid, if visible for the current subject.'
|
||||||
operationId: getCoopShareTransactionByUuid
|
operationId: getSingleCoopShareTransactionByUuid
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
|
@ -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.
|
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:
|
tags:
|
||||||
- hs-office-coopShares
|
- hs-office-coopShares
|
||||||
operationId: listCoopShares
|
operationId: getListOfCoopShares
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
@ -46,7 +46,7 @@ post:
|
|||||||
summary: Adds a new cooperative share transaction.
|
summary: Adds a new cooperative share transaction.
|
||||||
tags:
|
tags:
|
||||||
- hs-office-coopShares
|
- hs-office-coopShares
|
||||||
operationId: addCoopSharesTransaction
|
operationId: postNewCoopSharesTransaction
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
|
@ -29,6 +29,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:--//
|
--changeset michael.hoennig:hs-office-relation-MAIN-TABLE-JOURNAL endDelimiter:--//
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
--changeset michael.hoennig:hs-office-coopshares-MAIN-TABLE endDelimiter:--//
|
--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;
|
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,
|
valueDate date not null,
|
||||||
shareCount integer not null,
|
shareCount integer not null,
|
||||||
reference varchar(48) 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)
|
comment varchar(512)
|
||||||
);
|
);
|
||||||
--//
|
--//
|
||||||
@ -28,8 +28,8 @@ create table if not exists hs_office.coopsharetx
|
|||||||
|
|
||||||
alter table hs_office.coopsharetx
|
alter table hs_office.coopsharetx
|
||||||
add constraint reverse_entry_missing
|
add constraint reverse_entry_missing
|
||||||
check ( transactionType = 'ADJUSTMENT' and adjustedShareTxUuid is not null
|
check ( transactionType = 'REVERSAL' and revertedShareTxUuid is not null
|
||||||
or transactionType <> 'ADJUSTMENT' and adjustedShareTxUuid is null);
|
or transactionType <> 'REVERSAL' and revertedShareTxUuid is null);
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
|
@ -27,12 +27,12 @@ begin
|
|||||||
raise notice 'creating test coopSharesTransaction: %', givenPartnerNumber::text || givenMemberNumberSuffix;
|
raise notice 'creating test coopSharesTransaction: %', givenPartnerNumber::text || givenMemberNumberSuffix;
|
||||||
subscriptionEntryUuid := uuid_generate_v4();
|
subscriptionEntryUuid := uuid_generate_v4();
|
||||||
insert
|
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
|
values
|
||||||
(uuid_generate_v4(), membership.uuid, 'SUBSCRIPTION', '2010-03-15', 4, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-1', 'initial subscription', null),
|
(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),
|
(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),
|
(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; $$;
|
end; $$;
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
--changeset michael.hoennig:hs-office-coopassets-MAIN-TABLE endDelimiter:--//
|
--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',
|
'DEPOSIT',
|
||||||
'DISBURSAL',
|
'DISBURSAL',
|
||||||
'TRANSFER',
|
'TRANSFER',
|
||||||
@ -22,9 +22,9 @@ create table if not exists hs_office.coopassettx
|
|||||||
membershipUuid uuid not null references hs_office.membership(uuid),
|
membershipUuid uuid not null references hs_office.membership(uuid),
|
||||||
transactionType hs_office.CoopAssetsTransactionType not null,
|
transactionType hs_office.CoopAssetsTransactionType not null,
|
||||||
valueDate date 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,
|
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)
|
comment varchar(512)
|
||||||
);
|
);
|
||||||
--//
|
--//
|
||||||
@ -36,20 +36,20 @@ create table if not exists hs_office.coopassettx
|
|||||||
|
|
||||||
alter table hs_office.coopassettx
|
alter table hs_office.coopassettx
|
||||||
add constraint reverse_entry_missing
|
add constraint reverse_entry_missing
|
||||||
check ( transactionType = 'ADJUSTMENT' and adjustedAssetTxUuid is not null
|
check ( transactionType = 'REVERSAL' and revertedAssetTxUuid is not null
|
||||||
or transactionType <> 'ADJUSTMENT' and adjustedAssetTxUuid is null);
|
or transactionType <> 'REVERSAL' and revertedAssetTxUuid is null);
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset michael.hoennig:hs-office-coopassets-ASSET-VALUE-CONSTRAINT endDelimiter:--//
|
--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
|
returns boolean
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
declare
|
declare
|
||||||
currentAssetValue money;
|
currentAssetValue numeric(12,2);
|
||||||
totalAssetValue money;
|
totalAssetValue numeric(12,2);
|
||||||
begin
|
begin
|
||||||
select sum(cat.assetValue)
|
select sum(cat.assetValue)
|
||||||
from hs_office.coopassettx cat
|
from hs_office.coopassettx cat
|
||||||
|
@ -27,12 +27,12 @@ begin
|
|||||||
raise notice 'creating test coopAssetsTransaction: %', givenPartnerNumber || givenMemberNumberSuffix;
|
raise notice 'creating test coopAssetsTransaction: %', givenPartnerNumber || givenMemberNumberSuffix;
|
||||||
lossEntryUuid := uuid_generate_v4();
|
lossEntryUuid := uuid_generate_v4();
|
||||||
insert
|
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
|
values
|
||||||
(uuid_generate_v4(), membership.uuid, 'DEPOSIT', '2010-03-15', 320.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-1', 'initial deposit', null),
|
(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),
|
(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),
|
(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; $$;
|
end; $$;
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
@ -445,7 +445,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
|||||||
34002=CoopAssetsTransaction(M-1002000: 2016-12-31, DISBURSAL, -100.00, 1002000, for cancellation D),
|
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),
|
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),
|
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),
|
358=CoopAssetsTransaction(M-1000300: 2000-12-06, DEPOSIT, 5120, 1000300, for subscription A),
|
||||||
442=CoopAssetsTransaction(M-1015200: 2003-07-07, DEPOSIT, 64, 1015200),
|
442=CoopAssetsTransaction(M-1015200: 2003-07-07, DEPOSIT, 64, 1015200),
|
||||||
577=CoopAssetsTransaction(M-1000300: 2011-12-12, DEPOSIT, 1024, 1000300),
|
577=CoopAssetsTransaction(M-1000300: 2011-12-12, DEPOSIT, 1024, 1000300),
|
||||||
@ -798,23 +798,23 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
|||||||
? HsOfficeCoopSharesTransactionType.SUBSCRIPTION
|
? HsOfficeCoopSharesTransactionType.SUBSCRIPTION
|
||||||
: "UNSUBSCRIPTION".equals(rec.getString("action"))
|
: "UNSUBSCRIPTION".equals(rec.getString("action"))
|
||||||
? HsOfficeCoopSharesTransactionType.CANCELLATION
|
? HsOfficeCoopSharesTransactionType.CANCELLATION
|
||||||
: HsOfficeCoopSharesTransactionType.ADJUSTMENT
|
: HsOfficeCoopSharesTransactionType.REVERSAL
|
||||||
)
|
)
|
||||||
.shareCount(rec.getInteger("quantity"))
|
.shareCount(rec.getInteger("quantity"))
|
||||||
.comment(rec.getString("comment"))
|
.comment(rec.getString("comment"))
|
||||||
.reference(member.getMemberNumber().toString())
|
.reference(member.getMemberNumber().toString())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
if (shareTransaction.getTransactionType() == HsOfficeCoopSharesTransactionType.ADJUSTMENT) {
|
if (shareTransaction.getTransactionType() == HsOfficeCoopSharesTransactionType.REVERSAL) {
|
||||||
final var negativeValue = -shareTransaction.getShareCount();
|
final var negativeValue = -shareTransaction.getShareCount();
|
||||||
final var adjustedShareTx = coopShares.values().stream().filter(a ->
|
final var revertedShareTx = coopShares.values().stream().filter(a ->
|
||||||
a.getTransactionType() != HsOfficeCoopSharesTransactionType.ADJUSTMENT &&
|
a.getTransactionType() != HsOfficeCoopSharesTransactionType.REVERSAL &&
|
||||||
a.getMembership() == shareTransaction.getMembership() &&
|
a.getMembership() == shareTransaction.getMembership() &&
|
||||||
a.getShareCount() == negativeValue)
|
a.getShareCount() == negativeValue)
|
||||||
.findAny()
|
.findAny()
|
||||||
.orElseThrow(() -> new IllegalStateException(
|
.orElseThrow(() -> new IllegalStateException(
|
||||||
"cannot determine share reverse entry for adjustment " + shareTransaction));
|
"cannot determine share reverse entry for reversal " + shareTransaction));
|
||||||
shareTransaction.setAdjustedShareTx(adjustedShareTx);
|
shareTransaction.setRevertedShareTx(revertedShareTx);
|
||||||
}
|
}
|
||||||
coopShares.put(rec.getInteger("member_share_id"), shareTransaction);
|
coopShares.put(rec.getInteger("member_share_id"), shareTransaction);
|
||||||
});
|
});
|
||||||
@ -840,7 +840,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
|||||||
final var assetTypeMapping = new HashMap<String, HsOfficeCoopAssetsTransactionType>() {
|
final var assetTypeMapping = new HashMap<String, HsOfficeCoopAssetsTransactionType>() {
|
||||||
|
|
||||||
{
|
{
|
||||||
put("ADJUSTMENT", HsOfficeCoopAssetsTransactionType.ADJUSTMENT);
|
put("ADJUSTMENT", HsOfficeCoopAssetsTransactionType.REVERSAL);
|
||||||
put("HANDOVER", HsOfficeCoopAssetsTransactionType.TRANSFER);
|
put("HANDOVER", HsOfficeCoopAssetsTransactionType.TRANSFER);
|
||||||
put("ADOPTION", HsOfficeCoopAssetsTransactionType.ADOPTION);
|
put("ADOPTION", HsOfficeCoopAssetsTransactionType.ADOPTION);
|
||||||
put("LOSS", HsOfficeCoopAssetsTransactionType.LOSS);
|
put("LOSS", HsOfficeCoopAssetsTransactionType.LOSS);
|
||||||
@ -868,16 +868,16 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
|||||||
.reference(member.getMemberNumber().toString())
|
.reference(member.getMemberNumber().toString())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
if (assetTransaction.getTransactionType() == HsOfficeCoopAssetsTransactionType.ADJUSTMENT) {
|
if (assetTransaction.getTransactionType() == HsOfficeCoopAssetsTransactionType.REVERSAL) {
|
||||||
final var negativeValue = assetTransaction.getAssetValue().negate();
|
final var negativeValue = assetTransaction.getAssetValue().negate();
|
||||||
final var adjustedAssetTx = coopAssets.values().stream().filter(a ->
|
final var revertedAssetTx = coopAssets.values().stream().filter(a ->
|
||||||
a.getTransactionType() != HsOfficeCoopAssetsTransactionType.ADJUSTMENT &&
|
a.getTransactionType() != HsOfficeCoopAssetsTransactionType.REVERSAL &&
|
||||||
a.getMembership() == assetTransaction.getMembership() &&
|
a.getMembership() == assetTransaction.getMembership() &&
|
||||||
a.getAssetValue().equals(negativeValue))
|
a.getAssetValue().equals(negativeValue))
|
||||||
.findAny()
|
.findAny()
|
||||||
.orElseThrow(() -> new IllegalStateException(
|
.orElseThrow(() -> new IllegalStateException(
|
||||||
"cannot determine asset reverse entry for adjustment " + assetTransaction));
|
"cannot determine asset reverse entry for reversal " + assetTransaction));
|
||||||
assetTransaction.setAdjustedAssetTx(adjustedAssetTx);
|
assetTransaction.setRevertedAssetTx(revertedAssetTx);
|
||||||
}
|
}
|
||||||
|
|
||||||
coopAssets.put(rec.getInteger("member_asset_id"), assetTransaction);
|
coopAssets.put(rec.getInteger("member_asset_id"), assetTransaction);
|
||||||
|
@ -55,7 +55,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
EntityManager em;
|
EntityManager em;
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class ListCoopAssetsTransactions {
|
class GetListOfCoopAssetsTransactions {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void globalAdmin_canViewAllCoopAssetsTransactions() {
|
void globalAdmin_canViewAllCoopAssetsTransactions() {
|
||||||
@ -109,21 +109,21 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
"valueDate": "2022-10-20",
|
"valueDate": "2022-10-20",
|
||||||
"reference": "ref 1000202-3",
|
"reference": "ref 1000202-3",
|
||||||
"comment": "some loss",
|
"comment": "some loss",
|
||||||
"adjustmentAssetTx": {
|
"reversalAssetTx": {
|
||||||
"transactionType": "ADJUSTMENT",
|
"transactionType": "REVERSAL",
|
||||||
"assetValue": -128.00,
|
"assetValue": -128.00,
|
||||||
"valueDate": "2022-10-21",
|
"valueDate": "2022-10-21",
|
||||||
"reference": "ref 1000202-3",
|
"reference": "ref 1000202-3",
|
||||||
"comment": "some adjustment"
|
"comment": "some reversal"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"transactionType": "ADJUSTMENT",
|
"transactionType": "REVERSAL",
|
||||||
"assetValue": -128.00,
|
"assetValue": -128.00,
|
||||||
"valueDate": "2022-10-21",
|
"valueDate": "2022-10-21",
|
||||||
"reference": "ref 1000202-3",
|
"reference": "ref 1000202-3",
|
||||||
"comment": "some adjustment",
|
"comment": "some reversal",
|
||||||
"adjustedAssetTx": {
|
"revertedAssetTx": {
|
||||||
"transactionType": "DEPOSIT",
|
"transactionType": "DEPOSIT",
|
||||||
"assetValue": 128.00,
|
"assetValue": 128.00,
|
||||||
"valueDate": "2022-10-20",
|
"valueDate": "2022-10-20",
|
||||||
@ -166,10 +166,10 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class AddCoopAssetsTransaction {
|
class PostNewCoopAssetTransaction {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void globalAdmin_canAddCoopAssetsTransaction() {
|
void globalAdmin_canPostNewCoopAssetTransaction() {
|
||||||
|
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
|
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
|
||||||
@ -214,7 +214,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void globalAdmin_canAddCoopAssetsAdjustmentTransaction() {
|
void globalAdmin_canAddCoopAssetsReversalTransaction() {
|
||||||
|
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
|
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
|
||||||
@ -238,12 +238,12 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
.body("""
|
.body("""
|
||||||
{
|
{
|
||||||
"membership.uuid": "%s",
|
"membership.uuid": "%s",
|
||||||
"transactionType": "ADJUSTMENT",
|
"transactionType": "REVERSAL",
|
||||||
"assetValue": %s,
|
"assetValue": %s,
|
||||||
"valueDate": "2022-10-30",
|
"valueDate": "2022-10-30",
|
||||||
"reference": "test ref adjustment",
|
"reference": "test ref reversal",
|
||||||
"comment": "some coop assets adjustment transaction",
|
"comment": "some coop assets reversal transaction",
|
||||||
"reverseEntry.uuid": "%s"
|
"revertedAssetTx.uuid": "%s"
|
||||||
}
|
}
|
||||||
""".formatted(
|
""".formatted(
|
||||||
givenMembership.getUuid(),
|
givenMembership.getUuid(),
|
||||||
@ -258,12 +258,12 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
.body("uuid", isUuidValid())
|
.body("uuid", isUuidValid())
|
||||||
.body("", lenientlyEquals("""
|
.body("", lenientlyEquals("""
|
||||||
{
|
{
|
||||||
"transactionType": "ADJUSTMENT",
|
"transactionType": "REVERSAL",
|
||||||
"assetValue": -256.00,
|
"assetValue": -256.00,
|
||||||
"valueDate": "2022-10-30",
|
"valueDate": "2022-10-30",
|
||||||
"reference": "test ref adjustment",
|
"reference": "test ref reversal",
|
||||||
"comment": "some coop assets adjustment transaction",
|
"comment": "some coop assets reversal transaction",
|
||||||
"adjustedAssetTx": {
|
"revertedAssetTx": {
|
||||||
"transactionType": "DEPOSIT",
|
"transactionType": "DEPOSIT",
|
||||||
"assetValue": 256.00,
|
"assetValue": 256.00,
|
||||||
"valueDate": "2022-10-20",
|
"valueDate": "2022-10-20",
|
||||||
|
@ -77,7 +77,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
|||||||
|
|
||||||
ASSETS_VALUE_MUST_NOT_BE_NULL(
|
ASSETS_VALUE_MUST_NOT_BE_NULL(
|
||||||
requestBody -> requestBody
|
requestBody -> requestBody
|
||||||
.with("transactionType", "ADJUSTMENT")
|
.with("transactionType", "REVERSAL")
|
||||||
.with("assetValue", 0.00),
|
.with("assetValue", 0.00),
|
||||||
"[assetValue must not be 0 but is \"0.00\"]"),
|
"[assetValue must not be 0 but is \"0.00\"]"),
|
||||||
|
|
||||||
|
@ -21,14 +21,14 @@ class HsOfficeCoopAssetsTransactionEntityUnitTest {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
final HsOfficeCoopAssetsTransactionEntity givenCoopAssetAdjustmentTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
|
final HsOfficeCoopAssetsTransactionEntity givenCoopAssetReversalTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||||
.membership(TEST_MEMBERSHIP)
|
.membership(TEST_MEMBERSHIP)
|
||||||
.reference("some-ref")
|
.reference("some-ref")
|
||||||
.valueDate(LocalDate.parse("2020-01-15"))
|
.valueDate(LocalDate.parse("2020-01-15"))
|
||||||
.transactionType(HsOfficeCoopAssetsTransactionType.ADJUSTMENT)
|
.transactionType(HsOfficeCoopAssetsTransactionType.REVERSAL)
|
||||||
.assetValue(new BigDecimal("-128.00"))
|
.assetValue(new BigDecimal("-128.00"))
|
||||||
.comment("some comment")
|
.comment("some comment")
|
||||||
.adjustedAssetTx(givenCoopAssetTransaction)
|
.revertedAssetTx(givenCoopAssetTransaction)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final HsOfficeCoopAssetsTransactionEntity givenEmptyCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder().build();
|
final HsOfficeCoopAssetsTransactionEntity givenEmptyCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder().build();
|
||||||
@ -41,12 +41,12 @@ class HsOfficeCoopAssetsTransactionEntityUnitTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void toStringWithReverseEntryContainsReverseEntry() {
|
void toStringWithRevertedAssetTxContainsRevertedAssetTx() {
|
||||||
givenCoopAssetTransaction.setAdjustedAssetTx(givenCoopAssetAdjustmentTransaction);
|
givenCoopAssetTransaction.setRevertedAssetTx(givenCoopAssetReversalTransaction);
|
||||||
|
|
||||||
final var result = givenCoopAssetTransaction.toString();
|
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
|
@Test
|
||||||
|
@ -69,7 +69,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
|||||||
final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
|
final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||||
.membership(givenMembership)
|
.membership(givenMembership)
|
||||||
.transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT)
|
.transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT)
|
||||||
.assetValue(new BigDecimal("128.00"))
|
.assetValue(new BigDecimal("6400.00"))
|
||||||
.valueDate(LocalDate.parse("2022-10-18"))
|
.valueDate(LocalDate.parse("2022-10-18"))
|
||||||
.reference("temp ref A")
|
.reference("temp ref A")
|
||||||
.build();
|
.build();
|
||||||
@ -98,7 +98,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
|||||||
final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
|
final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||||
.membership(givenMembership)
|
.membership(givenMembership)
|
||||||
.transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT)
|
.transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT)
|
||||||
.assetValue(new BigDecimal("128.00"))
|
.assetValue(new BigDecimal("6400.00"))
|
||||||
.valueDate(LocalDate.parse("2022-10-18"))
|
.valueDate(LocalDate.parse("2022-10-18"))
|
||||||
.reference("temp ref B")
|
.reference("temp ref B")
|
||||||
.build();
|
.build();
|
||||||
@ -142,18 +142,18 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
|||||||
result,
|
result,
|
||||||
"CoopAssetsTransaction(M-1000101: 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
|
"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: 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-20, DEPOSIT, 128.00, ref 1000101-3, some loss, M-1000101:REV:-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-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: 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: 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-20, DEPOSIT, 128.00, ref 1000202-3, some loss, M-1000202:REV:-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-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: 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: 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-20, DEPOSIT, 128.00, ref 1000303-3, some loss, M-1000303:REV:-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-21, REVERSAL, -128.00, ref 1000303-3, some reversal, M-1000303:DEP:+128.00)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -173,8 +173,8 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
|||||||
result,
|
result,
|
||||||
"CoopAssetsTransaction(M-1000202: 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)",
|
"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: 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-20, DEPOSIT, 128.00, ref 1000202-3, some loss, M-1000202:REV:-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-21, REVERSAL, -128.00, ref 1000202-3, some reversal, M-1000202:DEP:+128.00)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -211,8 +211,8 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
|||||||
result,
|
result,
|
||||||
"CoopAssetsTransaction(M-1000101: 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
|
"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: 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-20, DEPOSIT, 128.00, ref 1000101-3, some loss, M-1000101:REV:-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-21, REVERSAL, -128.00, ref 1000101-3, some reversal, M-1000101:DEP:+128.00)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class ListCoopSharesTransactions {
|
class getListOfCoopSharesTransactions {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void globalAdmin_canViewAllCoopSharesTransactions() {
|
void globalAdmin_canViewAllCoopSharesTransactions() {
|
||||||
@ -108,21 +108,21 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
"valueDate": "2022-10-20",
|
"valueDate": "2022-10-20",
|
||||||
"reference": "ref 1000202-3",
|
"reference": "ref 1000202-3",
|
||||||
"comment": "some subscription",
|
"comment": "some subscription",
|
||||||
"adjustmentShareTx": {
|
"reversalShareTx": {
|
||||||
"transactionType": "ADJUSTMENT",
|
"transactionType": "REVERSAL",
|
||||||
"shareCount": -2,
|
"shareCount": -2,
|
||||||
"valueDate": "2022-10-21",
|
"valueDate": "2022-10-21",
|
||||||
"reference": "ref 1000202-4",
|
"reference": "ref 1000202-4",
|
||||||
"comment": "some adjustment"
|
"comment": "some reversal"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"transactionType": "ADJUSTMENT",
|
"transactionType": "REVERSAL",
|
||||||
"shareCount": -2,
|
"shareCount": -2,
|
||||||
"valueDate": "2022-10-21",
|
"valueDate": "2022-10-21",
|
||||||
"reference": "ref 1000202-4",
|
"reference": "ref 1000202-4",
|
||||||
"comment": "some adjustment",
|
"comment": "some reversal",
|
||||||
"adjustedShareTx": {
|
"revertedShareTx": {
|
||||||
"transactionType": "SUBSCRIPTION",
|
"transactionType": "SUBSCRIPTION",
|
||||||
"shareCount": 2,
|
"shareCount": 2,
|
||||||
"valueDate": "2022-10-20",
|
"valueDate": "2022-10-20",
|
||||||
@ -191,7 +191,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void globalAdmin_canAddCoopSharesAdjustmentTransaction() {
|
void globalAdmin_canAddCoopSharesReversalTransaction() {
|
||||||
|
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
|
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
|
||||||
@ -213,16 +213,16 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
.header("current-subject", "superuser-alex@hostsharing.net")
|
.header("current-subject", "superuser-alex@hostsharing.net")
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.body("""
|
.body("""
|
||||||
{
|
{
|
||||||
"membership.uuid": "%s",
|
"membership.uuid": "%s",
|
||||||
"transactionType": "ADJUSTMENT",
|
"transactionType": "REVERSAL",
|
||||||
"shareCount": %s,
|
"shareCount": %s,
|
||||||
"valueDate": "2022-10-30",
|
"valueDate": "2022-10-30",
|
||||||
"reference": "test ref adjustment",
|
"reference": "test reversal ref",
|
||||||
"comment": "some coop shares adjustment transaction",
|
"comment": "some coop shares reversal transaction",
|
||||||
"adjustedShareTx.uuid": "%s"
|
"revertedShareTx.uuid": "%s"
|
||||||
}
|
}
|
||||||
""".formatted(
|
""".formatted(
|
||||||
givenMembership.getUuid(),
|
givenMembership.getUuid(),
|
||||||
-givenTransaction.getShareCount(),
|
-givenTransaction.getShareCount(),
|
||||||
givenTransaction.getUuid()))
|
givenTransaction.getUuid()))
|
||||||
@ -235,12 +235,12 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
|||||||
.body("uuid", isUuidValid())
|
.body("uuid", isUuidValid())
|
||||||
.body("", lenientlyEquals("""
|
.body("", lenientlyEquals("""
|
||||||
{
|
{
|
||||||
"transactionType": "ADJUSTMENT",
|
"transactionType": "REVERSAL",
|
||||||
"shareCount": -13,
|
"shareCount": -13,
|
||||||
"valueDate": "2022-10-30",
|
"valueDate": "2022-10-30",
|
||||||
"reference": "test ref adjustment",
|
"reference": "test reversal ref",
|
||||||
"comment": "some coop shares adjustment transaction",
|
"comment": "some coop shares reversal transaction",
|
||||||
"adjustedShareTx": {
|
"revertedShareTx": {
|
||||||
"transactionType": "SUBSCRIPTION",
|
"transactionType": "SUBSCRIPTION",
|
||||||
"shareCount": 13,
|
"shareCount": 13,
|
||||||
"valueDate": "2022-10-20",
|
"valueDate": "2022-10-20",
|
||||||
|
@ -73,7 +73,7 @@ class HsOfficeCoopSharesTransactionControllerRestTest {
|
|||||||
|
|
||||||
SHARES_COUNT_MUST_NOT_BE_NULL(
|
SHARES_COUNT_MUST_NOT_BE_NULL(
|
||||||
requestBody -> requestBody
|
requestBody -> requestBody
|
||||||
.with("transactionType", "ADJUSTMENT")
|
.with("transactionType", "REVERSAL")
|
||||||
.with("shareCount", 0),
|
.with("shareCount", 0),
|
||||||
"[shareCount must not be 0 but is \"0\"]"),
|
"[shareCount must not be 0 but is \"0\"]"),
|
||||||
|
|
||||||
|
@ -20,14 +20,14 @@ class HsOfficeCoopSharesTransactionEntityUnitTest {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
final HsOfficeCoopSharesTransactionEntity givenCoopShareAdjustmentTransaction = HsOfficeCoopSharesTransactionEntity.builder()
|
final HsOfficeCoopSharesTransactionEntity givenCoopShareReversalTransaction = HsOfficeCoopSharesTransactionEntity.builder()
|
||||||
.membership(TEST_MEMBERSHIP)
|
.membership(TEST_MEMBERSHIP)
|
||||||
.reference("some-ref")
|
.reference("some-ref")
|
||||||
.valueDate(LocalDate.parse("2020-01-15"))
|
.valueDate(LocalDate.parse("2020-01-15"))
|
||||||
.transactionType(HsOfficeCoopSharesTransactionType.ADJUSTMENT)
|
.transactionType(HsOfficeCoopSharesTransactionType.REVERSAL)
|
||||||
.shareCount(-4)
|
.shareCount(-4)
|
||||||
.comment("some comment")
|
.comment("some comment")
|
||||||
.adjustedShareTx(givenCoopSharesTransaction)
|
.revertedShareTx(givenCoopSharesTransaction)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final HsOfficeCoopSharesTransactionEntity givenEmptyCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder().build();
|
final HsOfficeCoopSharesTransactionEntity givenEmptyCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder().build();
|
||||||
@ -40,12 +40,12 @@ class HsOfficeCoopSharesTransactionEntityUnitTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void toStringWithReverseEntryContainsReverseEntry() {
|
void toStringWithRevertedAssetTxContainsRevertedAssetTx() {
|
||||||
givenCoopSharesTransaction.setAdjustedShareTx(givenCoopShareAdjustmentTransaction);
|
givenCoopSharesTransaction.setRevertedShareTx(givenCoopShareReversalTransaction);
|
||||||
|
|
||||||
final var result = givenCoopSharesTransaction.toString();
|
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
|
@Test
|
||||||
|
@ -141,18 +141,18 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
|
|||||||
result,
|
result,
|
||||||
"CoopShareTransaction(M-1000101: 2010-03-15, SUBSCRIPTION, 4, ref 1000101-1, initial subscription)",
|
"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: 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-20, SUBSCRIPTION, 2, ref 1000101-3, some subscription, M-1000101:REV:-2)",
|
||||||
"CoopShareTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -2, ref 1000101-4, some adjustment, M-1000101:SUB:+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: 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: 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-20, SUBSCRIPTION, 2, ref 1000202-3, some subscription, M-1000202:REV:-2)",
|
||||||
"CoopShareTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -2, ref 1000202-4, some adjustment, M-1000202:SUB:+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: 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: 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-20, SUBSCRIPTION, 2, ref 1000303-3, some subscription, M-1000303:REV:-2)",
|
||||||
"CoopShareTransaction(M-1000303: 2022-10-21, ADJUSTMENT, -2, ref 1000303-4, some adjustment, M-1000303:SUB:+2)");
|
"CoopShareTransaction(M-1000303: 2022-10-21, REVERSAL, -2, ref 1000303-4, some reversal, M-1000303:SUB:+2)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -172,8 +172,8 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
|
|||||||
result,
|
result,
|
||||||
"CoopShareTransaction(M-1000202: 2010-03-15, SUBSCRIPTION, 4, ref 1000202-1, initial subscription)",
|
"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: 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-20, SUBSCRIPTION, 2, ref 1000202-3, some subscription, M-1000202:REV:-2)",
|
||||||
"CoopShareTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -2, ref 1000202-4, some adjustment, M-1000202:SUB:+2)");
|
"CoopShareTransaction(M-1000202: 2022-10-21, REVERSAL, -2, ref 1000202-4, some reversal, M-1000202:SUB:+2)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -210,8 +210,8 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
|
|||||||
result,
|
result,
|
||||||
"CoopShareTransaction(M-1000101: 2010-03-15, SUBSCRIPTION, 4, ref 1000101-1, initial subscription)",
|
"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: 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-20, SUBSCRIPTION, 2, ref 1000101-3, some subscription, M-1000101:REV:-2)",
|
||||||
"CoopShareTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -2, ref 1000101-4, some adjustment, M-1000101:SUB:+2)");
|
"CoopShareTransaction(M-1000101: 2022-10-21, REVERSAL, -2, ref 1000101-4, some reversal, M-1000101:SUB:+2)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
@ -44,8 +45,36 @@ public class HsOfficeMembershipControllerRestTest {
|
|||||||
@MockBean
|
@MockBean
|
||||||
EntityManagerWrapper em;
|
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
|
@Nested
|
||||||
class AddMembership {
|
class AddMembership {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void respondBadRequest_ifPartnerUuidIsMissing() throws Exception {
|
void respondBadRequest_ifPartnerUuidIsMissing() throws Exception {
|
||||||
|
|
||||||
@ -98,7 +127,9 @@ public class HsOfficeMembershipControllerRestTest {
|
|||||||
.andExpect(status().is4xxClientError())
|
.andExpect(status().is4xxClientError())
|
||||||
.andExpect(jsonPath("statusCode", is(400)))
|
.andExpect(jsonPath("statusCode", is(400)))
|
||||||
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
|
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
|
||||||
.andExpect(jsonPath("message", is("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
|
@ParameterizedTest
|
||||||
|
@ -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.debitor.InvalidateSepaMandateForDebitor;
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CancelMembership;
|
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.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.AddOperationsContactToPartner;
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.partner.CreatePartner;
|
import net.hostsharing.hsadminng.hs.office.scenarios.partner.CreatePartner;
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.DeleteDebitor;
|
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.DeleteDebitor;
|
||||||
@ -49,7 +55,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1010)
|
@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() {
|
void shouldCreateLegalPersonAsPartner() {
|
||||||
new CreatePartner(this)
|
new CreatePartner(this)
|
||||||
.given("partnerNumber", 31010)
|
.given("partnerNumber", 31010)
|
||||||
@ -71,7 +77,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1011)
|
@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() {
|
void shouldCreateNaturalPersonAsPartner() {
|
||||||
new CreatePartner(this)
|
new CreatePartner(this)
|
||||||
.given("partnerNumber", 31011)
|
.given("partnerNumber", 31011)
|
||||||
@ -148,7 +154,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1100)
|
@Order(1100)
|
||||||
@Requires("Partner: Michelle Matthieu")
|
@Requires("Partner: P-31011 - Michelle Matthieu")
|
||||||
void shouldAmendContactData() {
|
void shouldAmendContactData() {
|
||||||
new AmendContactData(this)
|
new AmendContactData(this)
|
||||||
.given("partnerName", "Matthieu")
|
.given("partnerName", "Matthieu")
|
||||||
@ -158,7 +164,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1101)
|
@Order(1101)
|
||||||
@Requires("Partner: Michelle Matthieu")
|
@Requires("Partner: P-31011 - Michelle Matthieu")
|
||||||
void shouldAddPhoneNumberToContactData() {
|
void shouldAddPhoneNumberToContactData() {
|
||||||
new AddPhoneNumberToContactData(this)
|
new AddPhoneNumberToContactData(this)
|
||||||
.given("partnerName", "Matthieu")
|
.given("partnerName", "Matthieu")
|
||||||
@ -169,7 +175,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1102)
|
@Order(1102)
|
||||||
@Requires("Partner: Michelle Matthieu")
|
@Requires("Partner: P-31011 - Michelle Matthieu")
|
||||||
void shouldRemovePhoneNumberFromContactData() {
|
void shouldRemovePhoneNumberFromContactData() {
|
||||||
new RemovePhoneNumberFromContactData(this)
|
new RemovePhoneNumberFromContactData(this)
|
||||||
.given("partnerName", "Matthieu")
|
.given("partnerName", "Matthieu")
|
||||||
@ -179,7 +185,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1103)
|
@Order(1103)
|
||||||
@Requires("Partner: Test AG")
|
@Requires("Partner: P-31010 - Test AG")
|
||||||
void shouldReplaceContactData() {
|
void shouldReplaceContactData() {
|
||||||
new ReplaceContactData(this)
|
new ReplaceContactData(this)
|
||||||
.given("partnerName", "Test AG")
|
.given("partnerName", "Test AG")
|
||||||
@ -201,7 +207,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1201)
|
@Order(1201)
|
||||||
@Requires("Partner: Michelle Matthieu")
|
@Requires("Partner: P-31011 - Michelle Matthieu")
|
||||||
void shouldUpdatePersonData() {
|
void shouldUpdatePersonData() {
|
||||||
new ShouldUpdatePersonData(this)
|
new ShouldUpdatePersonData(this)
|
||||||
.given("oldFamilyName", "Matthieu")
|
.given("oldFamilyName", "Matthieu")
|
||||||
@ -211,7 +217,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(2010)
|
@Order(2010)
|
||||||
@Requires("Partner: Test AG")
|
@Requires("Partner: P-31010 - Test AG")
|
||||||
@Produces("Debitor: Test AG - main debitor")
|
@Produces("Debitor: Test AG - main debitor")
|
||||||
void shouldCreateSelfDebitorForPartner() {
|
void shouldCreateSelfDebitorForPartner() {
|
||||||
new CreateSelfDebitorForPartner(this, "Debitor: Test AG - main debitor")
|
new CreateSelfDebitorForPartner(this, "Debitor: Test AG - main debitor")
|
||||||
@ -261,18 +267,18 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(2020)
|
@Order(2020)
|
||||||
@Requires("Debitor: Test AG - main debitor")
|
@Requires("Debitor: D-3101000 - Test AG - main debitor")
|
||||||
@Disabled("see TODO.spec in DontDeleteDefaultDebitor")
|
@Disabled("see TODO.spec in DontDeleteDefaultDebitor")
|
||||||
void shouldNotDeleteDefaultDebitor() {
|
void shouldNotDeleteDefaultDebitor() {
|
||||||
new DontDeleteDefaultDebitor(this)
|
new DontDeleteDefaultDebitor(this)
|
||||||
.given("partnerNumber", 31020)
|
.given("partnerNumber", 31010)
|
||||||
.given("debitorSuffix", "00")
|
.given("debitorSuffix", "00")
|
||||||
.doRun();
|
.doRun();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(3100)
|
@Order(3100)
|
||||||
@Requires("Debitor: Test AG - main debitor")
|
@Requires("Debitor: D-3101000 - Test AG - main debitor")
|
||||||
@Produces("SEPA-Mandate: Test AG")
|
@Produces("SEPA-Mandate: Test AG")
|
||||||
void shouldCreateSepaMandateForDebitor() {
|
void shouldCreateSepaMandateForDebitor() {
|
||||||
new CreateSepaMandateForDebitor(this)
|
new CreateSepaMandateForDebitor(this)
|
||||||
@ -313,12 +319,11 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(4000)
|
@Order(4000)
|
||||||
@Requires("Partner: Test AG")
|
@Requires("Partner: P-31010 - Test AG")
|
||||||
@Produces("Membership: Test AG 00")
|
@Produces("Membership: M-3101000 - Test AG")
|
||||||
void shouldCreateMembershipForPartner() {
|
void shouldCreateMembershipForPartner() {
|
||||||
new CreateMembership(this)
|
new CreateMembership(this)
|
||||||
.given("partnerName", "Test AG")
|
.given("partnerName", "Test AG")
|
||||||
.given("memberNumberSuffix", "00")
|
|
||||||
.given("validFrom", "2024-10-15")
|
.given("validFrom", "2024-10-15")
|
||||||
.given("newStatus", "ACTIVE")
|
.given("newStatus", "ACTIVE")
|
||||||
.given("membershipFeeBillable", "true")
|
.given("membershipFeeBillable", "true")
|
||||||
@ -326,9 +331,87 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
.keep();
|
.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
|
@Test
|
||||||
@Order(4900)
|
@Order(4900)
|
||||||
@Requires("Membership: Test AG 00")
|
@Requires("Membership: M-3101000 - Test AG")
|
||||||
void shouldCancelMembershipOfPartner() {
|
void shouldCancelMembershipOfPartner() {
|
||||||
new CancelMembership(this)
|
new CancelMembership(this)
|
||||||
.given("memberNumber", "3101000")
|
.given("memberNumber", "3101000")
|
||||||
|
@ -4,6 +4,9 @@ import net.hostsharing.hsadminng.hs.office.scenarios.UseCase.HttpResponse;
|
|||||||
|
|
||||||
import java.util.function.Consumer;
|
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 {
|
public class PathAssertion {
|
||||||
|
|
||||||
private final String path;
|
private final String path;
|
||||||
@ -14,10 +17,35 @@ public class PathAssertion {
|
|||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
public Consumer<UseCase.HttpResponse> contains(final String resolvableValue) {
|
public Consumer<UseCase.HttpResponse> 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<HttpResponse> doesNotExist() {
|
public Consumer<HttpResponse> 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.scenarios;
|
|||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
|
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.lambda.Reducer;
|
||||||
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
|
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
|
||||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
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.Arrays.asList;
|
||||||
import static java.util.Optional.ofNullable;
|
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;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
public abstract class ScenarioTest extends ContextBasedTest {
|
public abstract class ScenarioTest extends ContextBasedTest {
|
||||||
@ -38,11 +41,11 @@ public abstract class ScenarioTest extends ContextBasedTest {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return ObjectUtils.toString(uuid);
|
return ObjectUtils.toString(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static Map<String, Alias<?>> aliases = new HashMap<>();
|
private final static Map<String, Alias<?>> aliases = new HashMap<>();
|
||||||
private final static Map<String, Object> properties = new HashMap<>();
|
|
||||||
|
|
||||||
|
private final static Map<String, Object> properties = new HashMap<>();
|
||||||
public final TestReport testReport = new TestReport(aliases);
|
public final TestReport testReport = new TestReport(aliases);
|
||||||
|
|
||||||
@LocalServerPort
|
@LocalServerPort
|
||||||
@ -139,9 +142,9 @@ public abstract class ScenarioTest extends ContextBasedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static UUID uuid(final String nameWithPlaceholders) {
|
static UUID uuid(final String nameWithPlaceholders) {
|
||||||
final var resoledName = resolve(nameWithPlaceholders);
|
final var resolvedName = resolve(nameWithPlaceholders, DROP_COMMENTS);
|
||||||
final UUID alias = ofNullable(knowVariables().get(resoledName)).filter(v -> v instanceof UUID).map(UUID.class::cast).orElse(null);
|
final UUID alias = ofNullable(knowVariables().get(resolvedName)).filter(v -> v instanceof UUID).map(UUID.class::cast).orElse(null);
|
||||||
assertThat(alias).as("alias '" + resoledName + "' not found in aliases nor in properties [" +
|
assertThat(alias).as("alias '" + resolvedName + "' not found in aliases nor in properties [" +
|
||||||
knowVariables().keySet().stream().map(v -> "'" + v + "'").collect(Collectors.joining(", ")) + "]"
|
knowVariables().keySet().stream().map(v -> "'" + v + "'").collect(Collectors.joining(", ")) + "]"
|
||||||
).isNotNull();
|
).isNotNull();
|
||||||
return alias;
|
return alias;
|
||||||
@ -162,13 +165,13 @@ public abstract class ScenarioTest extends ContextBasedTest {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String resolve(final String text) {
|
public static String resolve(final String text, final Resolver resolver) {
|
||||||
final var resolved = new TemplateResolver(text, ScenarioTest.knowVariables()).resolve();
|
final var resolved = new TemplateResolver(text, ScenarioTest.knowVariables()).resolve(resolver);
|
||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object resolveTyped(final String text) {
|
public static Object resolveTyped(final String text) {
|
||||||
final var resolved = resolve(text);
|
final var resolved = resolve(text, DROP_COMMENTS);
|
||||||
try {
|
try {
|
||||||
return UUID.fromString(resolved);
|
return UUID.fromString(resolved);
|
||||||
} catch (final IllegalArgumentException e) {
|
} catch (final IllegalArgumentException e) {
|
||||||
|
@ -10,29 +10,39 @@ import java.util.Objects;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||||
|
|
||||||
public class TemplateResolver {
|
public class TemplateResolver {
|
||||||
|
|
||||||
private final static Pattern pattern = Pattern.compile(",(\\s*})", Pattern.MULTILINE);
|
public enum Resolver {
|
||||||
private static final String IF_NOT_FOUND_SYMBOL = "???";
|
DROP_COMMENTS, // deletes comments ('#{whatever}' -> '')
|
||||||
|
KEEP_COMMENTS // keep comments ('#{whatever}' -> 'whatever')
|
||||||
|
}
|
||||||
|
|
||||||
enum PlaceholderPrefix {
|
enum PlaceholderPrefix {
|
||||||
RAW('%') {
|
RAW('%') {
|
||||||
@Override
|
@Override
|
||||||
String convert(final Object value) {
|
String convert(final Object value, final Resolver resolver) {
|
||||||
return value != null ? value.toString() : "";
|
return value != null ? value.toString() : "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
JSON_QUOTED('$'){
|
JSON_QUOTED('$'){
|
||||||
@Override
|
@Override
|
||||||
String convert(final Object value) {
|
String convert(final Object value, final Resolver resolver) {
|
||||||
return jsonQuoted(value);
|
return jsonQuoted(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
URI_ENCODED('&'){
|
URI_ENCODED('&'){
|
||||||
@Override
|
@Override
|
||||||
String convert(final Object value) {
|
String convert(final Object value, final Resolver resolver) {
|
||||||
return value != null ? URLEncoder.encode(value.toString(), StandardCharsets.UTF_8) : "";
|
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;
|
private final char prefixChar;
|
||||||
@ -42,19 +52,24 @@ public class TemplateResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static boolean contains(final char givenChar) {
|
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) {
|
static PlaceholderPrefix ofPrefixChar(final char givenChar) {
|
||||||
return Arrays.stream(values()).filter(p -> p.prefixChar == givenChar).findFirst().orElseThrow();
|
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 String template;
|
||||||
private final Map<String, Object> properties;
|
private final Map<String, Object> properties;
|
||||||
private final StringBuilder resolved = new StringBuilder();
|
private final StringBuilder resolved = new StringBuilder();
|
||||||
|
|
||||||
|
private Resolver resolver;
|
||||||
private int position = 0;
|
private int position = 0;
|
||||||
|
|
||||||
public TemplateResolver(final String template, final Map<String, Object> properties) {
|
public TemplateResolver(final String template, final Map<String, Object> properties) {
|
||||||
@ -62,7 +77,8 @@ public class TemplateResolver {
|
|||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
String resolve() {
|
String resolve(final Resolver resolver) {
|
||||||
|
this.resolver = resolver;
|
||||||
final var resolved = copy();
|
final var resolved = copy();
|
||||||
final var withoutDroppedLines = dropLinesWithNullProperties(resolved);
|
final var withoutDroppedLines = dropLinesWithNullProperties(resolved);
|
||||||
final var result = removeDanglingCommas(withoutDroppedLines);
|
final var result = removeDanglingCommas(withoutDroppedLines);
|
||||||
@ -70,7 +86,7 @@ public class TemplateResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String removeDanglingCommas(final String withoutDroppedLines) {
|
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) {
|
private String dropLinesWithNullProperties(final String text) {
|
||||||
@ -119,10 +135,10 @@ public class TemplateResolver {
|
|||||||
placeholder.append(fetchChar());
|
placeholder.append(fetchChar());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final var name = new TemplateResolver(placeholder.toString(), properties).resolve();
|
final var content = new TemplateResolver(placeholder.toString(), properties).resolve(resolver);
|
||||||
final var value = propVal(name);
|
final var value = intro != '#' ? propVal(content) : content;
|
||||||
resolved.append(
|
resolved.append(
|
||||||
PlaceholderPrefix.ofPrefixChar(intro).convert(value)
|
PlaceholderPrefix.ofPrefixChar(intro).convert(value, resolver)
|
||||||
);
|
);
|
||||||
skipChar('}');
|
skipChar('}');
|
||||||
}
|
}
|
||||||
@ -134,12 +150,12 @@ public class TemplateResolver {
|
|||||||
} else if (nameExpression.contains(IF_NOT_FOUND_SYMBOL)) {
|
} else if (nameExpression.contains(IF_NOT_FOUND_SYMBOL)) {
|
||||||
final var parts = StringUtils.split(nameExpression, IF_NOT_FOUND_SYMBOL);
|
final var parts = StringUtils.split(nameExpression, IF_NOT_FOUND_SYMBOL);
|
||||||
return Arrays.stream(parts).filter(Objects::nonNull).findFirst().orElseGet(() -> {
|
return Arrays.stream(parts).filter(Objects::nonNull).findFirst().orElseGet(() -> {
|
||||||
if ( parts[parts.length-1].isEmpty() ) {
|
if ( parts[parts.length-1].isEmpty() ) {
|
||||||
// => whole expression ends with IF_NOT_FOUND_SYMBOL, thus last null element was optional
|
// => whole expression ends with IF_NOT_FOUND_SYMBOL, thus last null element was optional
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// => last alternative element in expression was null and not optional
|
// => last alternative element in expression was null and not optional
|
||||||
throw new IllegalStateException("Missing required value in property-chain: " + nameExpression);
|
throw new IllegalStateException("Missing required value in property-chain: " + nameExpression);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
final var val = properties.get(nameExpression);
|
final var val = properties.get(nameExpression);
|
||||||
|
@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class TemplateResolverUnitTest {
|
class TemplateResolverUnitTest {
|
||||||
@ -42,7 +43,7 @@ class TemplateResolverUnitTest {
|
|||||||
Map.entry("simple placeholder", "einfach"),
|
Map.entry("simple placeholder", "einfach"),
|
||||||
Map.entry("nested placeholder", "verschachtelt"),
|
Map.entry("nested placeholder", "verschachtelt"),
|
||||||
Map.entry("with-special-chars", "3&3 AG")
|
Map.entry("with-special-chars", "3&3 AG")
|
||||||
)).resolve();
|
)).resolve(DROP_COMMENTS);
|
||||||
|
|
||||||
assertThat(resolved).isEqualTo("""
|
assertThat(resolved).isEqualTo("""
|
||||||
with optional JSON quotes:
|
with optional JSON quotes:
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.scenarios;
|
package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||||
|
|
||||||
import lombok.SneakyThrows;
|
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.Order;
|
||||||
import org.junit.jupiter.api.TestInfo;
|
import org.junit.jupiter.api.TestInfo;
|
||||||
|
|
||||||
@ -9,29 +11,41 @@ import java.io.FileWriter;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
public class TestReport {
|
public class TestReport {
|
||||||
|
|
||||||
private final Map<String, ?> aliases;
|
public static final File BUILD_DOC_SCENARIOS = new File("build/doc/scenarios");
|
||||||
private final StringBuilder markdownLog = new StringBuilder(); // records everything for debugging purposes
|
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<String, ?> 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
|
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<String, ?> aliases) {
|
public TestReport(final Map<String, ?> aliases) {
|
||||||
this.aliases = aliases;
|
this.aliases = aliases;
|
||||||
|
this.markdownLog = new PrintWriter(new FileWriter(markdownLogFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createTestLogMarkdownFile(final TestInfo testInfo) throws IOException {
|
public void createTestLogMarkdownFile(final TestInfo testInfo) throws IOException {
|
||||||
final var testMethodName = testInfo.getTestMethod().map(Method::getName).orElseThrow();
|
final var testMethodName = testInfo.getTestMethod().map(Method::getName).orElseThrow();
|
||||||
final var testMethodOrder = testInfo.getTestMethod().map(m -> m.getAnnotation(Order.class).value()).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();
|
markdownReportFile = new File(BUILD_DOC_SCENARIOS, testMethodOrder + "-" + testMethodName + ".md");
|
||||||
markdownReport = new PrintWriter(new FileWriter("doc/scenarios/" + testMethodOrder + "-" + testMethodName + ".md"));
|
markdownReport = new PrintWriter(new FileWriter(markdownReportFile));
|
||||||
print("## Scenario #" + testInfo.getTestMethod().map(TestReport::orderNumber).orElseThrow() + ": " +
|
print("## Scenario #" + determineScenarioTitle(testInfo));
|
||||||
testMethodName.replaceAll("([a-z])([A-Z]+)", "$1 $2"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@ -45,7 +59,7 @@ public class TestReport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// but the debugLog should contain all output, even if silent
|
// but the debugLog should contain all output, even if silent
|
||||||
markdownLog.append(outputWithCommentsForUuids);
|
markdownLog.print(outputWithCommentsForUuids);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void printLine(final String output) {
|
public void printLine(final String output) {
|
||||||
@ -56,10 +70,32 @@ public class TestReport {
|
|||||||
printLine("\n" +output + "\n");
|
printLine("\n" +output + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void silent(final Runnable code) {
|
||||||
|
silent++;
|
||||||
|
code.run();
|
||||||
|
silent--;
|
||||||
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
if (markdownReport != null) {
|
if (markdownReport != null) {
|
||||||
|
printPara("---");
|
||||||
|
printPara("generated on " + MM_DD_YYYY_HH_MM_SS.format(new Date()) + " for branch " + currentGitBranch());
|
||||||
markdownReport.close();
|
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) {
|
private static Object orderNumber(final Method method) {
|
||||||
@ -83,10 +119,16 @@ public class TestReport {
|
|||||||
return result.toString();
|
return result.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void silent(final Runnable code) {
|
@SneakyThrows
|
||||||
silent++;
|
private String currentGitBranch() {
|
||||||
code.run();
|
try {
|
||||||
silent--;
|
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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.scenarios;
|
|||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.jayway.jsonpath.JsonPath;
|
import com.jayway.jsonpath.JsonPath;
|
||||||
|
import com.jayway.jsonpath.PathNotFoundException;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
@ -33,6 +34,8 @@ import java.util.function.Function;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import static java.net.URLEncoder.encode;
|
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.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
import static org.junit.platform.commons.util.StringUtils.isBlank;
|
import static org.junit.platform.commons.util.StringUtils.isBlank;
|
||||||
@ -50,6 +53,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
private final Map<String, Object> givenProperties = new LinkedHashMap<>();
|
private final Map<String, Object> givenProperties = new LinkedHashMap<>();
|
||||||
|
|
||||||
private String nextTitle; // just temporary to override resultAlias for sub-use-cases
|
private String nextTitle; // just temporary to override resultAlias for sub-use-cases
|
||||||
|
private String introduction;
|
||||||
|
|
||||||
public UseCase(final ScenarioTest testSuite) {
|
public UseCase(final ScenarioTest testSuite) {
|
||||||
this(testSuite, getResultAliasFromProducesAnnotationInCallStack());
|
this(testSuite, getResultAliasFromProducesAnnotationInCallStack());
|
||||||
@ -71,6 +75,9 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final HttpResponse doRun() {
|
public final HttpResponse doRun() {
|
||||||
|
if (introduction != null) {
|
||||||
|
testReport.printPara(introduction);
|
||||||
|
}
|
||||||
testReport.printPara("### Given Properties");
|
testReport.printPara("### Given Properties");
|
||||||
testReport.printLine("""
|
testReport.printLine("""
|
||||||
| name | value |
|
| name | value |
|
||||||
@ -81,7 +88,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
testReport.silent(() ->
|
testReport.silent(() ->
|
||||||
requirements.forEach((alias, factory) -> {
|
requirements.forEach((alias, factory) -> {
|
||||||
if (!ScenarioTest.containsAlias(alias)) {
|
if (!ScenarioTest.containsAlias(alias)) {
|
||||||
factory.apply(alias).run().keep();
|
factory.apply(alias).run().keepAs(alias);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -95,6 +102,11 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
protected void verify(final HttpResponse response) {
|
protected void verify(final HttpResponse response) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UseCase<T> introduction(final String introduction) {
|
||||||
|
this.introduction = introduction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public final UseCase<T> given(final String propName, final Object propValue) {
|
public final UseCase<T> given(final String propName, final Object propValue) {
|
||||||
givenProperties.put(propName, propValue);
|
givenProperties.put(propName, propValue);
|
||||||
ScenarioTest.putProperty(propName, propValue);
|
ScenarioTest.putProperty(propName, propValue);
|
||||||
@ -106,11 +118,11 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final void obtain(
|
public final void obtain(
|
||||||
final String alias,
|
final String title,
|
||||||
final Supplier<HttpResponse> http,
|
final Supplier<HttpResponse> http,
|
||||||
final Function<HttpResponse, String> extractor,
|
final Function<HttpResponse, String> extractor,
|
||||||
final String... extraInfo) {
|
final String... extraInfo) {
|
||||||
withTitle(ScenarioTest.resolve(alias), () -> {
|
withTitle(title, () -> {
|
||||||
final var response = http.get().keep(extractor);
|
final var response = http.get().keep(extractor);
|
||||||
Arrays.stream(extraInfo).forEach(testReport::printPara);
|
Arrays.stream(extraInfo).forEach(testReport::printPara);
|
||||||
return response;
|
return response;
|
||||||
@ -118,15 +130,15 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final void obtain(final String alias, final Supplier<HttpResponse> http, final String... extraInfo) {
|
public final void obtain(final String alias, final Supplier<HttpResponse> http, final String... extraInfo) {
|
||||||
withTitle(ScenarioTest.resolve(alias), () -> {
|
withTitle(alias, () -> {
|
||||||
final var response = http.get().keep();
|
final var response = http.get().keep();
|
||||||
Arrays.stream(extraInfo).forEach(testReport::printPara);
|
Arrays.stream(extraInfo).forEach(testReport::printPara);
|
||||||
return response;
|
return response;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpResponse withTitle(final String title, final Supplier<HttpResponse> code) {
|
public HttpResponse withTitle(final String resolvableTitle, final Supplier<HttpResponse> code) {
|
||||||
this.nextTitle = title;
|
this.nextTitle = resolvableTitle;
|
||||||
final var response = code.get();
|
final var response = code.get();
|
||||||
this.nextTitle = null;
|
this.nextTitle = null;
|
||||||
return response;
|
return response;
|
||||||
@ -134,7 +146,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public final HttpResponse httpGet(final String uriPathWithPlaceholders) {
|
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()
|
final var request = HttpRequest.newBuilder()
|
||||||
.GET()
|
.GET()
|
||||||
.uri(new URI("http://localhost:" + testSuite.port + uriPath))
|
.uri(new URI("http://localhost:" + testSuite.port + uriPath))
|
||||||
@ -147,7 +159,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public final HttpResponse httpPost(final String uriPathWithPlaceholders, final JsonTemplate bodyJsonTemplate) {
|
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 requestBody = bodyJsonTemplate.resolvePlaceholders();
|
||||||
final var request = HttpRequest.newBuilder()
|
final var request = HttpRequest.newBuilder()
|
||||||
.POST(BodyPublishers.ofString(requestBody))
|
.POST(BodyPublishers.ofString(requestBody))
|
||||||
@ -162,7 +174,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public final HttpResponse httpPatch(final String uriPathWithPlaceholders, final JsonTemplate bodyJsonTemplate) {
|
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 requestBody = bodyJsonTemplate.resolvePlaceholders();
|
||||||
final var request = HttpRequest.newBuilder()
|
final var request = HttpRequest.newBuilder()
|
||||||
.method(HttpMethod.PATCH.toString(), BodyPublishers.ofString(requestBody))
|
.method(HttpMethod.PATCH.toString(), BodyPublishers.ofString(requestBody))
|
||||||
@ -177,7 +189,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public final HttpResponse httpDelete(final String uriPathWithPlaceholders) {
|
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()
|
final var request = HttpRequest.newBuilder()
|
||||||
.DELETE()
|
.DELETE()
|
||||||
.uri(new URI("http://localhost:" + testSuite.port + uriPath))
|
.uri(new URI("http://localhost:" + testSuite.port + uriPath))
|
||||||
@ -197,7 +209,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
final String title,
|
final String title,
|
||||||
final Supplier<UseCase.HttpResponse> http,
|
final Supplier<UseCase.HttpResponse> http,
|
||||||
final Consumer<UseCase.HttpResponse>... assertions) {
|
final Consumer<UseCase.HttpResponse>... assertions) {
|
||||||
withTitle(ScenarioTest.resolve(title), () -> {
|
withTitle(title, () -> {
|
||||||
final var response = http.get();
|
final var response = http.get();
|
||||||
Arrays.stream(assertions).forEach(assertion -> assertion.accept(response));
|
Arrays.stream(assertions).forEach(assertion -> assertion.accept(response));
|
||||||
return response;
|
return response;
|
||||||
@ -209,7 +221,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String uriEncoded(final String text) {
|
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 {
|
public static class JsonTemplate {
|
||||||
@ -221,7 +233,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String resolvePlaceholders() {
|
String resolvePlaceholders() {
|
||||||
return ScenarioTest.resolve(template);
|
return ScenarioTest.resolve(template, DROP_COMMENTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,7 +278,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public HttpResponse keep(final Function<HttpResponse, String> extractor) {
|
public HttpResponse keep(final Function<HttpResponse, String> 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();
|
assertThat(alias).as("cannot keep result, no alias found").isNotNull();
|
||||||
|
|
||||||
final var value = extractor.apply(this);
|
final var value = extractor.apply(this);
|
||||||
@ -276,15 +288,20 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpResponse keep() {
|
public HttpResponse keepAs(final String alias) {
|
||||||
final var alias = nextTitle != null ? nextTitle : resultAlias;
|
|
||||||
assertThat(alias).as("cannot keep result, no alias found").isNotNull();
|
|
||||||
ScenarioTest.putAlias(
|
ScenarioTest.putAlias(
|
||||||
alias,
|
nonNullAlias(alias),
|
||||||
new ScenarioTest.Alias<>(UseCase.this.getClass(), locationUuid));
|
new ScenarioTest.Alias<>(UseCase.this.getClass(), locationUuid));
|
||||||
return this;
|
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
|
@SneakyThrows
|
||||||
public HttpResponse expectArrayElements(final int expectedElementCount) {
|
public HttpResponse expectArrayElements(final int expectedElementCount) {
|
||||||
final var rootNode = objectMapper.readTree(response.body());
|
final var rootNode = objectMapper.readTree(response.body());
|
||||||
@ -298,20 +315,20 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public String getFromBody(final String path) {
|
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
|
@SneakyThrows
|
||||||
public Optional<String> getFromBodyAsOptional(final String path) {
|
public <T> Optional<T> getFromBodyAsOptional(final String path) {
|
||||||
try {
|
try {
|
||||||
return Optional.ofNullable(JsonPath.parse(response.body()).read(ScenarioTest.resolve(path)));
|
return Optional.ofNullable(JsonPath.parse(response.body()).read(ScenarioTest.resolve(path, DROP_COMMENTS)));
|
||||||
} catch (final Exception e) {
|
} catch (final PathNotFoundException e) {
|
||||||
return null; // means the property did not exist at all, not that it was there with value null
|
return null; // means the property did not exist at all, not that it was there with value null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public OptionalAssert<String> path(final String path) {
|
public <T> OptionalAssert<T> path(final String path) {
|
||||||
return assertThat(getFromBodyAsOptional(path));
|
return assertThat(getFromBodyAsOptional(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,9 +337,9 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
|
|
||||||
// the title
|
// the title
|
||||||
if (nextTitle != null) {
|
if (nextTitle != null) {
|
||||||
testReport.printLine("\n### " + nextTitle + "\n");
|
testReport.printLine("\n### " + ScenarioTest.resolve(nextTitle, KEEP_COMMENTS) + "\n");
|
||||||
} else if (resultAlias != null) {
|
} else if (resultAlias != null) {
|
||||||
testReport.printLine("\n### " + resultAlias + "\n");
|
testReport.printLine("\n### Create " + resultAlias + "\n");
|
||||||
} else {
|
} else {
|
||||||
fail("please wrap the http...-call in the UseCase using `withTitle(...)`");
|
fail("please wrap the http...-call in the UseCase using `withTitle(...)`");
|
||||||
}
|
}
|
||||||
@ -342,6 +359,13 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
testReport.printLine("```");
|
testReport.printLine("```");
|
||||||
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() {
|
protected T self() {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
|
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
|
||||||
|
|
||||||
import io.restassured.http.ContentType;
|
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.ScenarioTest;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
import static io.restassured.http.ContentType.JSON;
|
import static io.restassured.http.ContentType.JSON;
|
||||||
@ -16,10 +16,18 @@ public class CreateMembership extends UseCase<CreateMembership> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpResponse run() {
|
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("""
|
return httpPost("/api/hs/office/memberships", usingJsonBody("""
|
||||||
{
|
{
|
||||||
"partner.uuid": ${Partner: Test AG},
|
"partner.uuid": ${Partner: %{partnerName}},
|
||||||
"memberNumberSuffix": ${memberNumberSuffix},
|
"memberNumberSuffix": ${%{memberNumberSuffix???}???00},
|
||||||
"status": "ACTIVE",
|
"status": "ACTIVE",
|
||||||
"validFrom": ${validFrom},
|
"validFrom": ${validFrom},
|
||||||
"membershipFeeBillable": ${membershipFeeBillable}
|
"membershipFeeBillable": ${membershipFeeBillable}
|
||||||
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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<CreateCoopAssetsTransaction> {
|
||||||
|
|
||||||
|
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}")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
@ -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<CreateCoopSharesTransaction> {
|
||||||
|
|
||||||
|
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}")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,8 @@ public class CreatePartner extends UseCase<CreatePartner> {
|
|||||||
|
|
||||||
public CreatePartner(final ScenarioTest testSuite) {
|
public CreatePartner(final ScenarioTest testSuite) {
|
||||||
super(testSuite);
|
super(testSuite);
|
||||||
|
|
||||||
|
introduction("A partner can be a client or a vendor, currently we only use them for clients.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
Reference in New Issue
Block a user