coop-assets-transaction-reverse-entry (#37)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #37
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
Michael Hoennig 2024-04-10 12:44:56 +02:00
parent 48f4cf8ed6
commit f5de2a8850
12 changed files with 253 additions and 55 deletions

View File

@ -2,8 +2,7 @@ package net.hostsharing.hsadminng.hs.office.coopassets;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopAssetsApi;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.*;
import net.hostsharing.hsadminng.mapper.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
@ -13,11 +12,13 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import jakarta.persistence.EntityNotFoundException;
import jakarta.validation.ValidationException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.BiConsumer;
import static java.lang.String.join;
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.*;
@ -63,8 +64,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
context.define(currentUser, assumedRoles);
validate(requestBody);
final var entityToSave = mapper.map(requestBody, HsOfficeCoopAssetsTransactionEntity.class);
final var entityToSave = mapper.map(requestBody, HsOfficeCoopAssetsTransactionEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
final var saved = coopAssetsTransactionRepo.save(entityToSave);
final var uri =
@ -131,4 +131,10 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
}
}
}
final BiConsumer<HsOfficeCoopAssetsTransactionInsertResource, HsOfficeCoopAssetsTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
if ( resource.getReverseEntryUuid() != null ) {
entity.setAdjustedAssetTx(coopAssetsTransactionRepo.findByUuid(resource.getReverseEntryUuid())
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] reverseEntityUuid %s not found".formatted(resource.getReverseEntryUuid()))));
}
};
};

View File

@ -50,6 +50,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacO
.withProp(HsOfficeCoopAssetsTransactionEntity::getAssetValue)
.withProp(HsOfficeCoopAssetsTransactionEntity::getReference)
.withProp(HsOfficeCoopAssetsTransactionEntity::getComment)
.withProp(at -> ofNullable(at.getAdjustedAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
.quotedValues(false);
@Id
@ -93,6 +94,12 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacO
@Column(name = "comment")
private String comment;
/**
* Optionally, the UUID of the corresponding transaction for an adjustment transaction.
*/
@OneToOne
@JoinColumn(name = "adjustedassettxuuid")
private HsOfficeCoopAssetsTransactionEntity adjustedAssetTx;
public String getTaggedMemberNumber() {
return ofNullable(membership).map(HsOfficeMembershipEntity::toShortString).orElse("M-?????");

View File

@ -19,7 +19,13 @@ import org.hibernate.annotations.JoinFormula;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import jakarta.persistence.*;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Version;
import jakarta.validation.constraints.Pattern;
import java.io.IOException;

View File

@ -32,6 +32,30 @@ components:
type: string
comment:
type: string
adjustedAssetTx:
$ref: '#/components/schemas/HsOfficeAdjustedCoopAssetsTransaction'
HsOfficeAdjustedCoopAssetsTransaction:
description:
Similar to `HsOfficeCoopAssetsTransaction` but without the `reverseEntry`,
otherwise the JSON would be recursive.
type: object
properties:
uuid:
type: string
format: uuid
transactionType:
$ref: '#/components/schemas/HsOfficeCoopAssetsTransactionType'
assetValue:
type: number
format: currency
valueDate:
type: string
format: date
reference:
type: string
comment:
type: string
HsOfficeCoopAssetsTransactionInsert:
type: object
@ -54,6 +78,9 @@ components:
maxLength: 48
comment:
type: string
reverseEntryUuid:
type: string
format: uuid
required:
- membershipUuid
- transactionType

View File

@ -17,17 +17,29 @@ CREATE CAST (character varying as HsOfficeCoopAssetsTransactionType) WITH INOUT
create table if not exists hs_office_coopassetstransaction
(
uuid uuid unique references RbacObject (uuid) initially deferred,
version int not null default 0,
membershipUuid uuid not null references hs_office_membership(uuid),
transactionType HsOfficeCoopAssetsTransactionType not null,
valueDate date not null,
assetValue money,
reference varchar(48),
comment varchar(512)
uuid uuid unique references RbacObject (uuid) initially deferred,
version int not null default 0,
membershipUuid uuid not null references hs_office_membership(uuid),
transactionType HsOfficeCoopAssetsTransactionType not null,
valueDate date not null,
assetValue money not null,
reference varchar(48) not null,
adjustedAssetTxUuid uuid unique REFERENCES hs_office_coopassetstransaction(uuid) DEFERRABLE INITIALLY DEFERRED,
comment varchar(512)
);
--//
-- ============================================================================
--changeset hs-office-coopassets-BUSINESS-RULES:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
alter table hs_office_coopassetstransaction
add constraint hs_office_coopassetstransaction_reverse_entry_missing
check ( transactionType = 'ADJUSTMENT' and adjustedAssetTxUuid is not null
or transactionType <> 'ADJUSTMENT' and adjustedAssetTxUuid is null);
--//
-- ============================================================================
--changeset hs-office-coopassets-ASSET-VALUE-CONSTRAINT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
@ -40,9 +52,9 @@ declare
totalAssetValue money;
begin
select sum(cat.assetValue)
from hs_office_coopassetstransaction cat
where cat.membershipUuid = forMembershipUuid
into currentAssetValue;
from hs_office_coopassetstransaction cat
where cat.membershipUuid = forMembershipUuid
into currentAssetValue;
totalAssetValue := currentAssetValue + newAssetValue;
if totalAssetValue::numeric < 0 then
raise exception '[400] coop assets transaction would result in a negative balance of assets';
@ -53,9 +65,9 @@ end; $$;
alter table hs_office_coopassetstransaction
add constraint hs_office_coopassets_positive
check ( checkAssetsByMembershipUuid(membershipUuid, assetValue) );
--//
-- ============================================================================
--changeset hs-office-coopassets-MAIN-TABLE-JOURNAL:1 endDelimiter:--//
-- ----------------------------------------------------------------------------

View File

@ -14,11 +14,13 @@ create or replace procedure createHsOfficeCoopAssetsTransactionTestData(
)
language plpgsql as $$
declare
currentTask varchar;
membership hs_office_membership;
currentTask varchar;
membership hs_office_membership;
lossEntryUuid uuid;
begin
currentTask = 'creating coopAssetsTransaction test-data ' || givenPartnerNumber || givenMemberNumberSuffix;
execute format('set local hsadminng.currentTask to %L', currentTask);
SET CONSTRAINTS ALL DEFERRED;
call defineContext(currentTask);
select m.uuid
@ -29,12 +31,14 @@ begin
into membership;
raise notice 'creating test coopAssetsTransaction: %', givenPartnerNumber || givenMemberNumberSuffix;
lossEntryUuid := uuid_generate_v4();
insert
into hs_office_coopassetstransaction(uuid, membershipuuid, transactiontype, valuedate, assetvalue, reference, comment)
into hs_office_coopassetstransaction(uuid, membershipuuid, transactiontype, valuedate, assetvalue, reference, comment, adjustedAssetTxUuid)
values
(uuid_generate_v4(), membership.uuid, 'DEPOSIT', '2010-03-15', 320.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-1', 'initial deposit'),
(uuid_generate_v4(), membership.uuid, 'DISBURSAL', '2021-09-01', -128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-2', 'partial disbursal'),
(uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-20', 128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-3', 'some adjustment');
(uuid_generate_v4(), membership.uuid, 'DEPOSIT', '2010-03-15', 320.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-1', 'initial deposit', null),
(uuid_generate_v4(), membership.uuid, 'DISBURSAL', '2021-09-01', -128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-2', 'partial disbursal', null),
(lossEntryUuid, membership.uuid, 'DEPOSIT', '2022-10-20', 128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-3', 'some loss', null),
(uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-21', -128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-3', 'some adjustment', lossEntryUuid);
end; $$;
--//

View File

@ -19,9 +19,11 @@ import org.springframework.transaction.annotation.Transactional;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.DEPOSIT;
import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid;
import static net.hostsharing.test.JsonMatcher.lenientlyEquals;
import static org.assertj.core.api.Assertions.assertThat;
@ -69,7 +71,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
.then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", hasSize(9)); // @formatter:on
.body("", hasSize(12)); // @formatter:on
}
@Test
@ -104,10 +106,17 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
"comment": "partial disbursal"
},
{
"transactionType": "ADJUSTMENT",
"transactionType": "DEPOSIT",
"assetValue": 128.00,
"valueDate": "2022-10-20",
"reference": "ref 1000202-3",
"comment": "some loss"
},
{
"transactionType": "ADJUSTMENT",
"assetValue": -128.00,
"valueDate": "2022-10-21",
"reference": "ref 1000202-3",
"comment": "some adjustment"
}
]
@ -188,9 +197,77 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
.extract().header("Location"); // @formatter:on
// finally, the new coopAssetsTransaction can be accessed under the generated UUID
final var newUserUuid = UUID.fromString(
final var newAssetTxUuid = UUID.fromString(
location.substring(location.lastIndexOf('/') + 1));
assertThat(newUserUuid).isNotNull();
assertThat(newAssetTxUuid).isNotNull();
}
@Test
void globalAdmin_canAddCoopAssetsAdjustmentTransaction() {
context.define("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
final var givenTransaction = jpaAttempt.transacted(() -> {
// TODO.impl: introduce something like transactedAsSuperuser / transactedAs("...", ...)
context.define("superuser-alex@hostsharing.net");
return coopAssetsTransactionRepo.save(HsOfficeCoopAssetsTransactionEntity.builder()
.transactionType(DEPOSIT)
.valueDate(LocalDate.of(2022, 10, 20))
.membership(givenMembership)
.assetValue(new BigDecimal("256.00"))
.reference("test ref")
.build());
}).assertSuccessful().assertNotNull().returnedValue();
toCleanup(HsOfficeCoopAssetsTransactionRawEntity.class, givenTransaction.getUuid());
final var location = RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body("""
{
"membershipUuid": "%s",
"transactionType": "ADJUSTMENT",
"assetValue": %s,
"valueDate": "2022-10-30",
"reference": "test ref adjustment",
"comment": "some coop assets adjustment transaction",
"reverseEntryUuid": "%s"
}
""".formatted(
givenMembership.getUuid(),
givenTransaction.getAssetValue().negate().toString(),
givenTransaction.getUuid()))
.port(port)
.when()
.post("http://localhost/api/hs/office/coopassetstransactions")
.then().log().all().assertThat()
.statusCode(201)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
.body("", lenientlyEquals("""
{
"transactionType": "ADJUSTMENT",
"assetValue": -256.00,
"valueDate": "2022-10-30",
"reference": "test ref adjustment",
"comment": "some coop assets adjustment transaction",
"adjustedAssetTx": {
"transactionType": "DEPOSIT",
"assetValue": 256.00,
"valueDate": "2022-10-20",
"reference": "test ref"
}
}
""".formatted(givenTransaction.getUuid())))
.header("Location", startsWith("http://localhost"))
.extract().header("Location"); // @formatter:on
// finally, the new coopAssetsTransaction can be accessed under the generated UUID
final var newAssetTxUuid = UUID.fromString(
location.substring(location.lastIndexOf('/') + 1));
assertThat(newAssetTxUuid).isNotNull();
toCleanup(HsOfficeCoopAssetsTransactionRawEntity.class, newAssetTxUuid);
}
@Test
@ -199,7 +276,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
context.define("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
final var location = RestAssured // @formatter:off
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)

View File

@ -16,14 +16,36 @@ class HsOfficeCoopAssetsTransactionEntityUnitTest {
.valueDate(LocalDate.parse("2020-01-01"))
.transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT)
.assetValue(new BigDecimal("128.00"))
.comment("some comment")
.build();
final HsOfficeCoopAssetsTransactionEntity givenCoopAssetAdjustmentTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
.membership(TEST_MEMBERSHIP)
.reference("some-ref")
.valueDate(LocalDate.parse("2020-01-15"))
.transactionType(HsOfficeCoopAssetsTransactionType.ADJUSTMENT)
.assetValue(new BigDecimal("-128.00"))
.comment("some comment")
.adjustedAssetTx(givenCoopAssetTransaction)
.build();
final HsOfficeCoopAssetsTransactionEntity givenEmptyCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder().build();
@Test
void toStringContainsAlmostAllPropertiesAccount() {
void toStringContainsAllNonNullProperties() {
final var result = givenCoopAssetTransaction.toString();
assertThat(result).isEqualTo("CoopAssetsTransaction(M-1000101: 2020-01-01, DEPOSIT, 128.00, some-ref)");
assertThat(result).isEqualTo("CoopAssetsTransaction(M-1000101: 2020-01-01, DEPOSIT, 128.00, some-ref, some comment)");
}
@Test
void toStringWithReverseEntryContainsReverseEntry() {
givenCoopAssetTransaction.setAdjustedAssetTx(givenCoopAssetAdjustmentTransaction);
final var result = givenCoopAssetTransaction.toString();
assertThat(result).isEqualTo("CoopAssetsTransaction(M-1000101: 2020-01-01, DEPOSIT, 128.00, some-ref, some comment, M-1000101:-128.00)");
}
@Test

View File

@ -0,0 +1,18 @@
package net.hostsharing.hsadminng.hs.office.coopassets;
import lombok.NoArgsConstructor;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.UUID;
@Entity
@Table(name = "hs_office_coopassetstransaction")
@NoArgsConstructor
public class HsOfficeCoopAssetsTransactionRawEntity {
@Id
private UUID uuid;
}

View File

@ -127,7 +127,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
class FindAllCoopAssetsTransactions {
@Test
public void globalAdmin_anViewAllCoopAssetsTransactions() {
public void globalAdmin_canViewAllCoopAssetsTransactions() {
// given
context("superuser-alex@hostsharing.net");
@ -138,19 +138,22 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
null);
// then
allTheseCoopAssetsTransactionsAreReturned(
exactlyTheseCoopAssetsTransactionsAreReturned(
result,
"CoopAssetsTransaction(M-1000101: 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
"CoopAssetsTransaction(M-1000101: 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)",
"CoopAssetsTransaction(M-1000101: 2022-10-20, ADJUSTMENT, 128.00, ref 1000101-3, some adjustment)",
"CoopAssetsTransaction(M-1000101: 2022-10-20, DEPOSIT, 128.00, ref 1000101-3, some loss)",
"CoopAssetsTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -128.00, ref 1000101-3, some adjustment, M-1000101:+128.00)",
"CoopAssetsTransaction(M-1000202: 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)",
"CoopAssetsTransaction(M-1000202: 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)",
"CoopAssetsTransaction(M-1000202: 2022-10-20, ADJUSTMENT, 128.00, ref 1000202-3, some adjustment)",
"CoopAssetsTransaction(M-1000202: 2022-10-20, DEPOSIT, 128.00, ref 1000202-3, some loss)",
"CoopAssetsTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -128.00, ref 1000202-3, some adjustment, M-1000202:+128.00)",
"CoopAssetsTransaction(M-1000303: 2010-03-15, DEPOSIT, 320.00, ref 1000303-1, initial deposit)",
"CoopAssetsTransaction(M-1000303: 2021-09-01, DISBURSAL, -128.00, ref 1000303-2, partial disbursal)",
"CoopAssetsTransaction(M-1000303: 2022-10-20, ADJUSTMENT, 128.00, ref 1000303-3, some adjustment)");
"CoopAssetsTransaction(M-1000303: 2022-10-20, DEPOSIT, 128.00, ref 1000303-3, some loss)",
"CoopAssetsTransaction(M-1000303: 2022-10-21, ADJUSTMENT, -128.00, ref 1000303-3, some adjustment, M-1000303:+128.00)");
}
@Test
@ -166,11 +169,12 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
null);
// then
allTheseCoopAssetsTransactionsAreReturned(
exactlyTheseCoopAssetsTransactionsAreReturned(
result,
"CoopAssetsTransaction(M-1000202: 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)",
"CoopAssetsTransaction(M-1000202: 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)",
"CoopAssetsTransaction(M-1000202: 2022-10-20, ADJUSTMENT, 128.00, ref 1000202-3, some adjustment)");
"CoopAssetsTransaction(M-1000202: 2022-10-20, DEPOSIT, 128.00, ref 1000202-3, some loss)",
"CoopAssetsTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -128.00, ref 1000202-3, some adjustment, M-1000202:+128.00)");
}
@Test
@ -207,7 +211,8 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
result,
"CoopAssetsTransaction(M-1000101: 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
"CoopAssetsTransaction(M-1000101: 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)",
"CoopAssetsTransaction(M-1000101: 2022-10-20, ADJUSTMENT, 128.00, ref 1000101-3, some adjustment)");
"CoopAssetsTransaction(M-1000101: 2022-10-20, DEPOSIT, 128.00, ref 1000101-3, some loss)",
"CoopAssetsTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -128.00, ref 1000101-3, some adjustment, M-1000101:+128.00)");
}
}

View File

@ -387,16 +387,16 @@ public class ImportOfficeData extends ContextBasedTest {
assertThat(toFormattedString(coopAssets)).isEqualToIgnoringWhitespace("""
{
30000=CoopAssetsTransaction(M-1001700: 2000-12-06, DEPOSIT, 1280.00, for subscription A),
31000=CoopAssetsTransaction(M-1002000: 2000-12-06, DEPOSIT, 128.00, for subscription B),
32000=CoopAssetsTransaction(M-1001700: 2005-01-10, DEPOSIT, 2560.00, for subscription C),
33001=CoopAssetsTransaction(M-1001700: 2005-01-10, TRANSFER, -512.00, for transfer to 10),
33002=CoopAssetsTransaction(M-1002000: 2005-01-10, ADOPTION, 512.00, for transfer from 7),
34001=CoopAssetsTransaction(M-1002000: 2016-12-31, CLEARING, -8.00, for cancellation D),
34002=CoopAssetsTransaction(M-1002000: 2016-12-31, DISBURSAL, -100.00, for cancellation D),
34003=CoopAssetsTransaction(M-1002000: 2016-12-31, LOSS, -20.00, for cancellation D),
35001=CoopAssetsTransaction(M-1909000: 2024-01-15, DEPOSIT, 128.00, for subscription E),
35002=CoopAssetsTransaction(M-1909000: 2024-01-20, ADJUSTMENT, -128.00, chargeback for subscription E)
30000=CoopAssetsTransaction(M-1001700: 2000-12-06, DEPOSIT, 1280.00, legacy data import, for subscription A),
31000=CoopAssetsTransaction(M-1002000: 2000-12-06, DEPOSIT, 128.00, legacy data import, for subscription B),
32000=CoopAssetsTransaction(M-1001700: 2005-01-10, DEPOSIT, 2560.00, legacy data import, for subscription C),
33001=CoopAssetsTransaction(M-1001700: 2005-01-10, TRANSFER, -512.00, legacy data import, for transfer to 10),
33002=CoopAssetsTransaction(M-1002000: 2005-01-10, ADOPTION, 512.00, legacy data import, for transfer from 7),
34001=CoopAssetsTransaction(M-1002000: 2016-12-31, CLEARING, -8.00, legacy data import, for cancellation D),
34002=CoopAssetsTransaction(M-1002000: 2016-12-31, DISBURSAL, -100.00, legacy data import, for cancellation D),
34003=CoopAssetsTransaction(M-1002000: 2016-12-31, LOSS, -20.00, legacy data import, for cancellation D),
35001=CoopAssetsTransaction(M-1909000: 2024-01-15, DEPOSIT, 128.00, legacy data import, for subscription E),
35002=CoopAssetsTransaction(M-1909000: 2024-01-20, ADJUSTMENT, -128.00, legacy data import, chargeback for subscription E, M-1909000:+128.00)
}
""");
}
@ -849,8 +849,20 @@ public class ImportOfficeData extends ContextBasedTest {
.transactionType(assetTypeMapping.get(rec.getString("action")))
.assetValue(rec.getBigDecimal("amount"))
.comment(rec.getString("comment"))
.reference("legacy data import") // TODO.spec: or use value from comment column?
.build();
if (assetTransaction.getTransactionType() == HsOfficeCoopAssetsTransactionType.ADJUSTMENT) {
final var negativeValue = assetTransaction.getAssetValue().negate();
final var adjustedAssetTx = coopAssets.values().stream().filter(a ->
a.getTransactionType() != HsOfficeCoopAssetsTransactionType.ADJUSTMENT &&
a.getMembership() == assetTransaction.getMembership() &&
a.getAssetValue().equals(negativeValue))
.findAny()
.orElseThrow(() -> new IllegalStateException("cannot determine asset reverse entry for adjustment " + assetTransaction));
assetTransaction.setAdjustedAssetTx(adjustedAssetTx);
}
coopAssets.put(rec.getInteger("member_asset_id"), assetTransaction);
});
}

View File

@ -64,8 +64,10 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
return merged;
}
public UUID toCleanup(final Class<? extends RbacObject> entityClass, final UUID uuidToCleanup) {
out.println("toCleanup(" + entityClass.getSimpleName() + ", " + uuidToCleanup);
// TODO.test: back to `Class<? extends RbacObject> entityClass` but delete on raw table
// remove HsOfficeCoopAssetsTransactionRawEntity, which is not needed anymore after this change
public UUID toCleanup(final Class entityClass, final UUID uuidToCleanup) {
out.println("toCleanup(" + entityClass.getSimpleName() + ", " + uuidToCleanup + ")");
entitiesToCleanup.put(uuidToCleanup, entityClass);
return uuidToCleanup;
}
@ -120,7 +122,7 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
}
if (initialRbacObjects != null){
assertNoNewRbackObjectsRolesAndGrantsLeaked();
assertNoNewRbacObjectsRolesAndGrantsLeaked();
}
initialTestDataValidated = false;
@ -170,7 +172,7 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
out.println(ContextBasedTestWithCleanup.class.getSimpleName() + ".cleanupAndCheckCleanup");
cleanupTemporaryTestData();
deleteLeakedRbacObjects();
long rbacObjectCount = assertNoNewRbackObjectsRolesAndGrantsLeaked();
long rbacObjectCount = assertNoNewRbacObjectsRolesAndGrantsLeaked();
out.println("TOTAL OBJECT COUNT (after): " + rbacObjectCount);
}
@ -180,7 +182,7 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
final var caughtException = jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net", null);
em.remove(em.getReference(entityClass, uuid));
out.println("DELETING temporary " + entityClass.getSimpleName() + "#" + uuid + " successful");
out.println("DELETING temporary " + entityClass.getSimpleName() + "#" + uuid + " generated");
}).caughtException();
if (caughtException != null) {
out.println("DELETING temporary " + entityClass.getSimpleName() + "#" + uuid + " failed: " + caughtException);
@ -188,7 +190,7 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
});
}
private long assertNoNewRbackObjectsRolesAndGrantsLeaked() {
private long assertNoNewRbacObjectsRolesAndGrantsLeaked() {
return jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net");
assertEqual(initialRbacObjects, allRbacObjects());