Compare commits
2 Commits
07b5e4686b
...
9a9e3e2e73
Author | SHA1 | Date | |
---|---|---|---|
|
9a9e3e2e73 | ||
|
5f454a2ebe |
@ -0,0 +1,28 @@
|
||||
package net.hostsharing.hsadminng.config;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.Locale;
|
||||
|
||||
public class FormattedBigDecimalDeserializer extends JsonDeserializer<BigDecimal> {
|
||||
|
||||
@Override
|
||||
public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
final var numberStr = p.getText();
|
||||
try {
|
||||
final var format = NumberFormat.getInstance(Locale.GERMANY);
|
||||
if (format instanceof DecimalFormat) {
|
||||
((DecimalFormat) format).setParseBigDecimal(true);
|
||||
}
|
||||
return (BigDecimal) format.parse(numberStr);
|
||||
} catch (final ParseException e) {
|
||||
throw new IOException("Failed to parse BigDecimal from string: " + numberStr, e);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.config;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.openapitools.jackson.nullable.JsonNullableModule;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -9,15 +10,25 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Configuration
|
||||
public class JsonObjectMapperConfiguration {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public Jackson2ObjectMapperBuilder customObjectMapper() {
|
||||
// HOWTO: add JSON converters and specify other JSON mapping configurations
|
||||
return new Jackson2ObjectMapperBuilder()
|
||||
.modulesToInstall(formattedBigDecimalModule())
|
||||
.modules(new JsonNullableModule(), new JavaTimeModule())
|
||||
.featuresToEnable(JsonParser.Feature.ALLOW_COMMENTS, JsonParser.Feature.ALLOW_COMMENTS)
|
||||
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
}
|
||||
|
||||
private SimpleModule formattedBigDecimalModule() {
|
||||
final var module = new SimpleModule();
|
||||
module.addDeserializer(BigDecimal.class, new FormattedBigDecimalDeserializer());
|
||||
return module;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.Converter;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
// HOWTO: convert data types for exchange between PostgreSQL and Java/Hibernate/JPA-Entities
|
||||
@Converter(autoApply = true)
|
||||
public class HsOfficePersonTypeConverter implements AttributeConverter<HsOfficePersonType, String> {
|
||||
|
||||
|
@ -22,7 +22,7 @@ create table if not exists hs_office.coopassettx
|
||||
membershipUuid uuid not null references hs_office.membership(uuid),
|
||||
transactionType hs_office.CoopAssetsTransactionType not null,
|
||||
valueDate date not null,
|
||||
assetValue money not null,
|
||||
assetValue numeric(12,2) not null, -- TODO.impl: use money type, but we had problems with Hibernate conversion
|
||||
reference varchar(48) not null,
|
||||
revertedAssetTxUuid uuid unique REFERENCES hs_office.coopassettx(uuid) DEFERRABLE INITIALLY DEFERRED,
|
||||
comment varchar(512)
|
||||
@ -44,12 +44,12 @@ alter table hs_office.coopassettx
|
||||
--changeset michael.hoennig:hs-office-coopassets-ASSET-VALUE-CONSTRAINT endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
create or replace function hs_office.coopassetstx_check_positive_total(forMembershipUuid UUID, newAssetValue money)
|
||||
create or replace function hs_office.coopassetstx_check_positive_total(forMembershipUuid UUID, newAssetValue numeric(12, 5))
|
||||
returns boolean
|
||||
language plpgsql as $$
|
||||
declare
|
||||
currentAssetValue money;
|
||||
totalAssetValue money;
|
||||
currentAssetValue numeric(12,2);
|
||||
totalAssetValue numeric(12,2);
|
||||
begin
|
||||
select sum(cat.assetValue)
|
||||
from hs_office.coopassettx cat
|
||||
|
@ -69,7 +69,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
||||
final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.membership(givenMembership)
|
||||
.transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT)
|
||||
.assetValue(new BigDecimal("128.00"))
|
||||
.assetValue(new BigDecimal("6,400.00"))
|
||||
.valueDate(LocalDate.parse("2022-10-18"))
|
||||
.reference("temp ref A")
|
||||
.build();
|
||||
@ -98,7 +98,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
||||
final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.membership(givenMembership)
|
||||
.transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT)
|
||||
.assetValue(new BigDecimal("128.00"))
|
||||
.assetValue(new BigDecimal("6400.00"))
|
||||
.valueDate(LocalDate.parse("2022-10-18"))
|
||||
.reference("temp ref B")
|
||||
.build();
|
||||
|
@ -12,10 +12,13 @@ import net.hostsharing.hsadminng.hs.office.scenarios.debitor.FinallyDeleteSepaMa
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.DontDeleteDefaultDebitor;
|
||||
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.CreateCoopSharesCancellationTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateCoopSharesRevertTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateCoopSharesSubscriptionTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateMembership;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsDepositTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsDisbursalTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsRevertTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares.CreateCoopSharesCancellationTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares.CreateCoopSharesRevertTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares.CreateCoopSharesSubscriptionTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.partner.AddOperationsContactToPartner;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.partner.CreatePartner;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.DeleteDebitor;
|
||||
@ -348,7 +351,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
void shouldRevertCoopSharesSubscription() {
|
||||
new CreateCoopSharesRevertTransaction(this)
|
||||
.given("memberNumber", "3101000")
|
||||
.given("comment", "reverting some incorrect subscription")
|
||||
.given("comment", "reverting some incorrect transaction")
|
||||
.given("dateOfIncorrectTransaction", "2024-02-15")
|
||||
.doRun();
|
||||
}
|
||||
@ -367,6 +370,45 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
.doRun();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4301)
|
||||
@Requires("Membership: M-3101000 - Test AG")
|
||||
@Produces("Coop-Assets DEPOSIT Transaction")
|
||||
void shouldSubscribeCoopAssets() {
|
||||
new CreateCoopAssetsDepositTransaction(this)
|
||||
.given("memberNumber", "3101000")
|
||||
.given("reference", "sign 2024-01-15")
|
||||
.given("assetValue", 100*64)
|
||||
.given("comment", "disposal for initial shares")
|
||||
.given("transactionDate", "2024-01-15")
|
||||
.doRun();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4302)
|
||||
@Requires("Membership: M-3101000 - Test AG")
|
||||
void shouldRevertCoopAssetsSubscription() {
|
||||
new CreateCoopAssetsRevertTransaction(this)
|
||||
.given("memberNumber", "3101000")
|
||||
.given("comment", "reverting some incorrect transaction")
|
||||
.given("dateOfIncorrectTransaction", "2024-02-15")
|
||||
.doRun();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4302)
|
||||
@Requires("Coop-Assets DEPOSIT Transaction")
|
||||
@Produces("Coop-Assets DISBURSAL Transaction")
|
||||
void shouldDisburseCoopAssets() {
|
||||
new CreateCoopAssetsDisbursalTransaction(this)
|
||||
.given("memberNumber", "3101000")
|
||||
.given("reference", "cancel 2024-01-15")
|
||||
.given("valueToDisburse", 8*64)
|
||||
.given("comment", "disbursal according to shares cancellation")
|
||||
.given("transactionDate", "2024-02-15")
|
||||
.doRun();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4900)
|
||||
@Requires("Membership: M-3101000 - Test AG")
|
||||
|
@ -19,7 +19,7 @@ public class PathAssertion {
|
||||
public Consumer<UseCase.HttpResponse> contains(final String resolvableValue) {
|
||||
return response -> {
|
||||
try {
|
||||
response.path(path).map(Object::toString).contains(ScenarioTest.resolve(resolvableValue, DROP_COMMENTS));
|
||||
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 + "\")`" );
|
||||
@ -37,4 +37,15 @@ public class PathAssertion {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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}")
|
||||
);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
|
||||
@ -12,7 +12,7 @@ public class CreateCoopSharesRevertTransaction extends CreateCoopSharesTransacti
|
||||
.given("memberNumber", "3101000")
|
||||
.given("reference", "sign %{dateOfIncorrectTransaction}") // same as revertedShareTx
|
||||
.given("shareCount", 100)
|
||||
.given("comment", "reverting subscription transaction with wrong share count")
|
||||
.given("comment", "coop-shares subscription transaction with wrong share count")
|
||||
.given("transactionDate", "%{dateOfIncorrectTransaction}")
|
||||
);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
|
||||
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
@ -23,7 +23,7 @@ public abstract class CreateCoopSharesTransaction extends UseCase<CreateCoopShar
|
||||
response -> response.getFromBody("$[0].uuid")
|
||||
);
|
||||
|
||||
return withTitle("Create the CoopShares-%{transactionType} Transaction", () ->
|
||||
return withTitle("Create the Coop-Shares-%{transactionType} Transaction", () ->
|
||||
httpPost("/api/hs/office/coopsharestransactions", usingJsonBody("""
|
||||
{
|
||||
"membership.uuid": ${membershipUuid},
|
Loading…
Reference in New Issue
Block a user