Compare commits

..

No commits in common. "e82502848146685be372d8685b5a2c54a41fd5c3" and "6152c65282bcf23b398468123f83d641accca81e" have entirely different histories.

14 changed files with 72 additions and 300 deletions

View File

@ -51,7 +51,6 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacO
.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.getAdjustedAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
.withProp(at -> ofNullable(at.getAdjustmentAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
.quotedValues(false); .quotedValues(false);
@Id @Id
@ -102,11 +101,8 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacO
@JoinColumn(name = "adjustedassettxuuid") @JoinColumn(name = "adjustedassettxuuid")
private HsOfficeCoopAssetsTransactionEntity adjustedAssetTx; private HsOfficeCoopAssetsTransactionEntity adjustedAssetTx;
@OneToOne(mappedBy = "adjustedAssetTx")
private HsOfficeCoopAssetsTransactionEntity adjustmentAssetTx;
public String getTaggedMemberNumber() { public String getTaggedMemberNumber() {
return ofNullable(membership).map(HsOfficeMembershipEntity::toShortString).orElse("M-???????"); return ofNullable(membership).map(HsOfficeMembershipEntity::toShortString).orElse("M-?????");
} }
@Override @Override
@ -116,10 +112,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacO
@Override @Override
public String toShortString() { public String toShortString() {
return "%s:%.3s:%+1.2f".formatted( return "%s:%+1.2f".formatted(getTaggedMemberNumber(), Optional.ofNullable(assetValue).orElse(BigDecimal.ZERO));
getTaggedMemberNumber(),
transactionType,
ofNullable(assetValue).orElse(BigDecimal.ZERO));
} }
public static RbacView rbac() { public static RbacView rbac() {

View File

@ -1,10 +1,7 @@
package net.hostsharing.hsadminng.hs.office.coopshares; package net.hostsharing.hsadminng.hs.office.coopshares;
import jakarta.persistence.EntityNotFoundException;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopSharesApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopSharesApi;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionResource;
import net.hostsharing.hsadminng.mapper.Mapper; import net.hostsharing.hsadminng.mapper.Mapper;
@ -21,7 +18,6 @@ import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.function.BiConsumer;
import static java.lang.String.join; import static java.lang.String.join;
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionTypeResource.CANCELLATION; import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionTypeResource.CANCELLATION;
@ -68,7 +64,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
context.define(currentUser, assumedRoles); context.define(currentUser, assumedRoles);
validate(requestBody); validate(requestBody);
final var entityToSave = mapper.map(requestBody, HsOfficeCoopSharesTransactionEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); final var entityToSave = mapper.map(requestBody, HsOfficeCoopSharesTransactionEntity.class);
final var saved = coopSharesTransactionRepo.save(entityToSave); final var saved = coopSharesTransactionRepo.save(entityToSave);
@ -135,10 +131,4 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
} }
} }
final BiConsumer<HsOfficeCoopSharesTransactionInsertResource, HsOfficeCoopSharesTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
if ( resource.getAdjustedShareTxUuid() != null ) {
entity.setAdjustedShareTx(coopSharesTransactionRepo.findByUuid(resource.getAdjustedShareTxUuid())
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] adjustedShareTxUuid %s not found".formatted(resource.getAdjustedShareTxUuid()))));
}
};
} }

View File

@ -6,7 +6,6 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
@ -42,14 +41,12 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacObject { public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacObject {
private static Stringify<HsOfficeCoopSharesTransactionEntity> stringify = stringify(HsOfficeCoopSharesTransactionEntity.class) private static Stringify<HsOfficeCoopSharesTransactionEntity> stringify = stringify(HsOfficeCoopSharesTransactionEntity.class)
.withIdProp(HsOfficeCoopSharesTransactionEntity::getMemberNumberTagged) .withProp(HsOfficeCoopSharesTransactionEntity::getMemberNumberTagged)
.withProp(HsOfficeCoopSharesTransactionEntity::getValueDate) .withProp(HsOfficeCoopSharesTransactionEntity::getValueDate)
.withProp(HsOfficeCoopSharesTransactionEntity::getTransactionType) .withProp(HsOfficeCoopSharesTransactionEntity::getTransactionType)
.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.getAdjustmentShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null))
.quotedValues(false); .quotedValues(false);
@Id @Id
@ -92,16 +89,6 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacO
@Column(name = "comment") @Column(name = "comment")
private String comment; private String comment;
/**
* Optionally, the UUID of the corresponding transaction for an adjustment transaction.
*/
@OneToOne
@JoinColumn(name = "adjustedsharetxuuid")
private HsOfficeCoopSharesTransactionEntity adjustedShareTx;
@OneToOne(mappedBy = "adjustedShareTx")
private HsOfficeCoopSharesTransactionEntity adjustmentShareTx;
@Override @Override
public String toString() { public String toString() {
return stringify.apply(this); return stringify.apply(this);
@ -113,7 +100,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacO
@Override @Override
public String toShortString() { public String toShortString() {
return "%s:%.3s:%+d".formatted(getMemberNumberTagged(), transactionType, shareCount); return "%s%+d".formatted(getMemberNumberTagged(), shareCount);
} }
public static RbacView rbac() { public static RbacView rbac() {

View File

@ -33,14 +33,12 @@ components:
comment: comment:
type: string type: string
adjustedAssetTx: adjustedAssetTx:
$ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction' $ref: '#/components/schemas/HsOfficeAdjustedCoopAssetsTransaction'
adjustmentAssetTx:
$ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction'
HsOfficeReferencedCoopAssetsTransaction: HsOfficeAdjustedCoopAssetsTransaction:
description: description:
Similar to `HsOfficeCoopAssetsTransaction` but without the self-referencing properties Similar to `HsOfficeCoopAssetsTransaction` but without the `reverseEntry`,
(`adjustedAssetTx` and `adjustmentAssetTx`), to avoid recursive JSON. otherwise the JSON would be recursive.
type: object type: object
properties: properties:
uuid: uuid:

View File

@ -12,31 +12,6 @@ components:
HsOfficeCoopSharesTransaction: HsOfficeCoopSharesTransaction:
type: object type: object
properties:
uuid:
type: string
format: uuid
transactionType:
$ref: '#/components/schemas/HsOfficeCoopSharesTransactionType'
shareCount:
type: integer
valueDate:
type: string
format: date
reference:
type: string
comment:
type: string
adjustedShareTx:
$ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction'
adjustmentShareTx:
$ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction'
HsOfficeReferencedCoopSharesTransaction:
description:
Similar to `HsOfficeCoopSharesTransaction` but without the self-referencing properties
(`adjustedShareTx` and `adjustmentShareTx`), to avoid recursive JSON.
type: object
properties: properties:
uuid: uuid:
type: string type: string
@ -73,9 +48,6 @@ components:
maxLength: 48 maxLength: 48
comment: comment:
type: string type: string
adjustedShareTxUuid:
type: string
format: uuid
required: required:
- membershipUuid - membershipUuid
- transactionType - transactionType

View File

@ -15,23 +15,12 @@ create table if not exists hs_office_coopsharestransaction
membershipUuid uuid not null references hs_office_membership(uuid), membershipUuid uuid not null references hs_office_membership(uuid),
transactionType HsOfficeCoopSharesTransactionType not null, transactionType HsOfficeCoopSharesTransactionType not null,
valueDate date not null, valueDate date not null,
shareCount integer not null, shareCount integer,
reference varchar(48) not null, reference varchar(48),
adjustedShareTxUuid uuid unique REFERENCES hs_office_coopsharestransaction(uuid) DEFERRABLE INITIALLY DEFERRED,
comment varchar(512) comment varchar(512)
); );
--// --//
-- ============================================================================
--changeset hs-office-coopshares-BUSINESS-RULES:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
alter table hs_office_coopsharestransaction
add constraint hs_office_coopsharestransaction_reverse_entry_missing
check ( transactionType = 'ADJUSTMENT' and adjustedShareTxUuid is not null
or transactionType <> 'ADJUSTMENT' and adjustedShareTxUuid is null);
--//
-- ============================================================================ -- ============================================================================
--changeset hs-office-coopshares-SHARE-COUNT-CONSTRAINT:1 endDelimiter:--// --changeset hs-office-coopshares-SHARE-COUNT-CONSTRAINT:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------

View File

@ -16,11 +16,9 @@ create or replace procedure createHsOfficeCoopSharesTransactionTestData(
declare declare
currentTask varchar; currentTask varchar;
membership hs_office_membership; membership hs_office_membership;
subscriptionEntryUuid uuid;
begin begin
currentTask = 'creating coopSharesTransaction test-data ' || givenPartnerNumber::text || givenMemberNumberSuffix; currentTask = 'creating coopSharesTransaction test-data ' || givenPartnerNumber::text || givenMemberNumberSuffix;
execute format('set local hsadminng.currentTask to %L', currentTask); execute format('set local hsadminng.currentTask to %L', currentTask);
SET CONSTRAINTS ALL DEFERRED;
call defineContext(currentTask); call defineContext(currentTask);
select m.uuid select m.uuid
@ -31,14 +29,12 @@ begin
into membership; into membership;
raise notice 'creating test coopSharesTransaction: %', givenPartnerNumber::text || givenMemberNumberSuffix; raise notice 'creating test coopSharesTransaction: %', givenPartnerNumber::text || givenMemberNumberSuffix;
subscriptionEntryUuid := uuid_generate_v4();
insert insert
into hs_office_coopsharestransaction(uuid, membershipuuid, transactiontype, valuedate, sharecount, reference, comment, adjustedShareTxUuid) into hs_office_coopsharestransaction(uuid, membershipuuid, transactiontype, valuedate, sharecount, reference, comment)
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'),
(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'),
(subscriptionEntryUuid, membership.uuid, 'SUBSCRIPTION', '2022-10-20', 2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-3', 'some subscription', null), (uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-20', 2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-3', 'some adjustment');
(uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-21', -2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-4', 'some adjustment', subscriptionEntryUuid);
end; $$; end; $$;
--// --//

View File

@ -108,28 +108,14 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
"assetValue": 128.00, "assetValue": 128.00,
"valueDate": "2022-10-20", "valueDate": "2022-10-20",
"reference": "ref 1000202-3", "reference": "ref 1000202-3",
"comment": "some loss", "comment": "some loss"
"adjustmentAssetTx": {
"transactionType": "ADJUSTMENT",
"assetValue": -128.00,
"valueDate": "2022-10-21",
"reference": "ref 1000202-3",
"comment": "some adjustment"
}
}, },
{ {
"transactionType": "ADJUSTMENT", "transactionType": "ADJUSTMENT",
"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 adjustment"
"adjustedAssetTx": {
"transactionType": "DEPOSIT",
"assetValue": 128.00,
"valueDate": "2022-10-20",
"reference": "ref 1000202-3",
"comment": "some loss"
}
} }
] ]
""")); // @formatter:on """)); // @formatter:on

View File

@ -45,27 +45,27 @@ class HsOfficeCoopAssetsTransactionEntityUnitTest {
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:-128.00)");
} }
@Test @Test
void toShortStringContainsOnlyMemberNumberSuffixAndSharesCountOnly() { void toShortStringContainsOnlyMemberNumberSuffixAndSharesCountOnly() {
final var result = givenCoopAssetTransaction.toShortString(); final var result = givenCoopAssetTransaction.toShortString();
assertThat(result).isEqualTo("M-1000101:DEP:+128.00"); assertThat(result).isEqualTo("M-1000101:+128.00");
} }
@Test @Test
void toStringWithEmptyTransactionDoesNotThrowException() { void toStringWithEmptyTransactionDoesNotThrowException() {
final var result = givenEmptyCoopAssetsTransaction.toString(); final var result = givenEmptyCoopAssetsTransaction.toString();
assertThat(result).isEqualTo("CoopAssetsTransaction(M-???????: )"); assertThat(result).isEqualTo("CoopAssetsTransaction(M-?????: )");
} }
@Test @Test
void toShortStringEmptyTransactionDoesNotThrowException() { void toShortStringEmptyTransactionDoesNotThrowException() {
final var result = givenEmptyCoopAssetsTransaction.toShortString(); final var result = givenEmptyCoopAssetsTransaction.toShortString();
assertThat(result).isEqualTo("M-???????:nul:+0.00"); assertThat(result).isEqualTo("M-?????:+0.00");
} }
} }

View File

@ -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)",
"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, 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: 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)",
"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, 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: 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)",
"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, ADJUSTMENT, -128.00, ref 1000303-3, some adjustment, M-1000303:+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)",
"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, ADJUSTMENT, -128.00, ref 1000202-3, some adjustment, M-1000202:+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)",
"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, ADJUSTMENT, -128.00, ref 1000101-3, some adjustment, M-1000101:+128.00)");
} }
} }

View File

@ -18,7 +18,6 @@ import org.springframework.transaction.annotation.Transactional;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.UUID; import java.util.UUID;
@ -68,15 +67,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
void globalAdmin_canViewAllCoopSharesTransactions() { void globalAdmin_canViewAllCoopSharesTransactions() {
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given().header("current-user", "superuser-alex@hostsharing.net").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions").then().log().all().assertThat().statusCode(200).contentType("application/json").body("", hasSize(9)); // @formatter:on
.header("current-user", "superuser-alex@hostsharing.net")
.port(port)
.when()
.get("http://localhost/api/hs/office/coopsharestransactions")
.then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", hasSize(12)); // @formatter:on
} }
@Test @Test
@ -103,33 +94,12 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
"comment": "cancelling some" "comment": "cancelling some"
}, },
{ {
"transactionType": "SUBSCRIPTION", "transactionType": "ADJUSTMENT",
"shareCount": 2, "shareCount": 2,
"valueDate": "2022-10-20", "valueDate": "2022-10-20",
"reference": "ref 1000202-3", "reference": "ref 1000202-3",
"comment": "some subscription",
"adjustmentShareTx": {
"transactionType": "ADJUSTMENT",
"shareCount": -2,
"valueDate": "2022-10-21",
"reference": "ref 1000202-4",
"comment": "some adjustment" "comment": "some adjustment"
} }
},
{
"transactionType": "ADJUSTMENT",
"shareCount": -2,
"valueDate": "2022-10-21",
"reference": "ref 1000202-4",
"comment": "some adjustment",
"adjustedShareTx": {
"transactionType": "SUBSCRIPTION",
"shareCount": 2,
"valueDate": "2022-10-20",
"reference": "ref 1000202-3",
"comment": "some subscription"
}
}
] ]
""")); // @formatter:on """)); // @formatter:on
} }
@ -186,76 +156,8 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
""")).header("Location", startsWith("http://localhost")).extract().header("Location"); // @formatter:on """)).header("Location", startsWith("http://localhost")).extract().header("Location"); // @formatter:on
// finally, the new coopSharesTransaction can be accessed under the generated UUID // finally, the new coopSharesTransaction can be accessed under the generated UUID
final var newShareTxUuid = UUID.fromString(location.substring(location.lastIndexOf('/') + 1)); final var newUserUuid = UUID.fromString(location.substring(location.lastIndexOf('/') + 1));
assertThat(newShareTxUuid).isNotNull(); assertThat(newUserUuid).isNotNull();
}
@Test
void globalAdmin_canAddCoopSharesAdjustmentTransaction() {
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 coopSharesTransactionRepo.save(HsOfficeCoopSharesTransactionEntity.builder()
.transactionType(HsOfficeCoopSharesTransactionType.SUBSCRIPTION)
.valueDate(LocalDate.of(2022, 10, 20))
.membership(givenMembership)
.shareCount(13)
.reference("test ref")
.build());
}).assertSuccessful().assertNotNull().returnedValue();
toCleanup(HsOfficeCoopSharesTransactionEntity.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",
"shareCount": %s,
"valueDate": "2022-10-30",
"reference": "test ref adjustment",
"comment": "some coop shares adjustment transaction",
"adjustedShareTxUuid": "%s"
}
""".formatted(
givenMembership.getUuid(),
-givenTransaction.getShareCount(),
givenTransaction.getUuid()))
.port(port)
.when()
.post("http://localhost/api/hs/office/coopsharestransactions")
.then().log().all().assertThat()
.statusCode(201)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
.body("", lenientlyEquals("""
{
"transactionType": "ADJUSTMENT",
"shareCount": -13,
"valueDate": "2022-10-30",
"reference": "test ref adjustment",
"comment": "some coop shares adjustment transaction",
"adjustedShareTx": {
"transactionType": "SUBSCRIPTION",
"shareCount": 13,
"valueDate": "2022-10-20",
"reference": "test ref"
}
}
"""))
.header("Location", startsWith("http://localhost"))
.extract().header("Location"); // @formatter:on
// finally, the new coopAssetsTransaction can be accessed under the generated UUID
final var newShareTxUuid = UUID.fromString(
location.substring(location.lastIndexOf('/') + 1));
assertThat(newShareTxUuid).isNotNull();
toCleanup(HsOfficeCoopSharesTransactionEntity.class, newShareTxUuid);
} }
@Test @Test
@ -264,7 +166,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
RestAssured // @formatter:off final var location = RestAssured // @formatter:off
.given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body(""" .given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body("""
{ {
"membershipUuid": "%s", "membershipUuid": "%s",

View File

@ -1,10 +1,7 @@
package net.hostsharing.hsadminng.hs.office.coopshares; package net.hostsharing.hsadminng.hs.office.coopshares;
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity;
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import static net.hostsharing.hsadminng.hs.office.membership.TestHsMembership.TEST_MEMBERSHIP; import static net.hostsharing.hsadminng.hs.office.membership.TestHsMembership.TEST_MEMBERSHIP;
@ -18,56 +15,34 @@ class HsOfficeCoopSharesTransactionEntityUnitTest {
.valueDate(LocalDate.parse("2020-01-01")) .valueDate(LocalDate.parse("2020-01-01"))
.transactionType(HsOfficeCoopSharesTransactionType.SUBSCRIPTION) .transactionType(HsOfficeCoopSharesTransactionType.SUBSCRIPTION)
.shareCount(4) .shareCount(4)
.comment("some comment")
.build(); .build();
final HsOfficeCoopSharesTransactionEntity givenCoopShareAdjustmentTransaction = HsOfficeCoopSharesTransactionEntity.builder()
.membership(TEST_MEMBERSHIP)
.reference("some-ref")
.valueDate(LocalDate.parse("2020-01-15"))
.transactionType(HsOfficeCoopSharesTransactionType.ADJUSTMENT)
.shareCount(-4)
.comment("some comment")
.adjustedShareTx(givenCoopSharesTransaction)
.build();
final HsOfficeCoopSharesTransactionEntity givenEmptyCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder().build(); final HsOfficeCoopSharesTransactionEntity givenEmptyCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder().build();
@Test @Test
void toStringContainsAllNonNullProperties() { void toStringContainsAlmostAllPropertiesAccount() {
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)"); assertThat(result).isEqualTo("CoopShareTransaction(M-1000101, 2020-01-01, SUBSCRIPTION, 4, some-ref)");
} }
@Test @Test
void toStringWithReverseEntryContainsReverseEntry() { void toShortStringContainsOnlyMemberNumberAndShareCountOnly() {
givenCoopSharesTransaction.setAdjustedShareTx(givenCoopShareAdjustmentTransaction);
final var result = givenCoopSharesTransaction.toString();
assertThat(result).isEqualTo("CoopShareTransaction(M-1000101: 2020-01-01, SUBSCRIPTION, 4, some-ref, some comment, M-1000101:ADJ:-4)");
}
@Test
void toShortStringContainsOnlyAbbreviatedString() {
final var result = givenCoopSharesTransaction.toShortString(); final var result = givenCoopSharesTransaction.toShortString();
assertThat(result).isEqualTo("M-1000101:SUB:+4"); assertThat(result).isEqualTo("M-1000101+4");
} }
@Test @Test
void toStringEmptyTransactionDoesNotThrowException() { void toStringEmptyTransactionDoesNotThrowException() {
final var result = givenEmptyCoopSharesTransaction.toString(); final var result = givenEmptyCoopSharesTransaction.toString();
assertThat(result).isEqualTo("CoopShareTransaction(null: 0)"); assertThat(result).isEqualTo("CoopShareTransaction(0)");
} }
@Test @Test
void toShortStringEmptyTransactionDoesNotThrowException() { void toShortStringEmptyTransactionDoesNotThrowException() {
final var result = givenEmptyCoopSharesTransaction.toShortString(); final var result = givenEmptyCoopSharesTransaction.toShortString();
assertThat(result).isEqualTo("null:nul:+0"); assertThat(result).isEqualTo("null+0");
} }
} }

View File

@ -126,7 +126,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
class FindAllCoopSharesTransactions { class FindAllCoopSharesTransactions {
@Test @Test
public void globalAdmin_canViewAllCoopSharesTransactions() { public void globalAdmin_anViewAllCoopSharesTransactions() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
@ -137,22 +137,19 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
null); null);
// then // then
exactlyTheseCoopSharesTransactionsAreReturned( allTheseCoopSharesTransactionsAreReturned(
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, ADJUSTMENT, 2, ref 1000101-3, some adjustment)",
"CoopShareTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -2, ref 1000101-4, some adjustment, 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, ADJUSTMENT, 2, ref 1000202-3, some adjustment)",
"CoopShareTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -2, ref 1000202-4, some adjustment, 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, ADJUSTMENT, 2, ref 1000303-3, some adjustment)");
"CoopShareTransaction(M-1000303: 2022-10-21, ADJUSTMENT, -2, ref 1000303-4, some adjustment, M-1000303:SUB:+2)");
} }
@Test @Test
@ -168,12 +165,11 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
null); null);
// then // then
exactlyTheseCoopSharesTransactionsAreReturned( allTheseCoopSharesTransactionsAreReturned(
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, ADJUSTMENT, 2, ref 1000202-3, some adjustment)");
"CoopShareTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -2, ref 1000202-4, some adjustment, M-1000202:SUB:+2)");
} }
@Test @Test
@ -191,7 +187,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// then // then
allTheseCoopSharesTransactionsAreReturned( allTheseCoopSharesTransactionsAreReturned(
result, result,
"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)");
} }
@Test @Test
@ -208,10 +204,9 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
// then: // then:
exactlyTheseCoopSharesTransactionsAreReturned( exactlyTheseCoopSharesTransactionsAreReturned(
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, ADJUSTMENT, 2, ref 1000101-3, some adjustment)");
"CoopShareTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -2, ref 1000101-4, some adjustment, M-1000101:SUB:+2)");
} }
} }

View File

@ -360,10 +360,10 @@ public class ImportOfficeData extends ContextBasedTest {
assertThat(toFormattedString(coopShares)).isEqualToIgnoringWhitespace(""" assertThat(toFormattedString(coopShares)).isEqualToIgnoringWhitespace("""
{ {
33443=CoopShareTransaction(M-1001700: 2000-12-06, SUBSCRIPTION, 20, legacy data import, initial share subscription), 33443=CoopShareTransaction(M-1001700, 2000-12-06, SUBSCRIPTION, 20, initial share subscription),
33451=CoopShareTransaction(M-1002000: 2000-12-06, SUBSCRIPTION, 2, legacy data import, initial share subscription), 33451=CoopShareTransaction(M-1002000, 2000-12-06, SUBSCRIPTION, 2, initial share subscription),
33701=CoopShareTransaction(M-1001700: 2005-01-10, SUBSCRIPTION, 40, legacy data import, increase), 33701=CoopShareTransaction(M-1001700, 2005-01-10, SUBSCRIPTION, 40, increase),
33810=CoopShareTransaction(M-1002000: 2016-12-31, CANCELLATION, 22, legacy data import, membership ended) 33810=CoopShareTransaction(M-1002000, 2016-12-31, CANCELLATION, 22, membership ended)
} }
"""); """);
} }
@ -396,7 +396,7 @@ public class ImportOfficeData extends ContextBasedTest {
34002=CoopAssetsTransaction(M-1002000: 2016-12-31, DISBURSAL, -100.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), 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), 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:DEP:+128.00) 35002=CoopAssetsTransaction(M-1909000: 2024-01-20, ADJUSTMENT, -128.00, legacy data import, chargeback for subscription E, M-1909000:+128.00)
} }
"""); """);
} }
@ -801,19 +801,8 @@ public class ImportOfficeData extends ContextBasedTest {
) )
.shareCount(rec.getInteger("quantity")) .shareCount(rec.getInteger("quantity"))
.comment( rec.getString("comment")) .comment( rec.getString("comment"))
.reference("legacy data import") // TODO.spec: or use value from comment column?
.build(); .build();
if (shareTransaction.getTransactionType() == HsOfficeCoopSharesTransactionType.ADJUSTMENT) {
final var negativeValue = -shareTransaction.getShareCount();
final var adjustedShareTx = coopShares.values().stream().filter(a ->
a.getTransactionType() != HsOfficeCoopSharesTransactionType.ADJUSTMENT &&
a.getMembership() == shareTransaction.getMembership() &&
a.getShareCount() == negativeValue)
.findAny()
.orElseThrow(() -> new IllegalStateException("cannot determine share reverse entry for adjustment " + shareTransaction));
shareTransaction.setAdjustedShareTx(adjustedShareTx);
}
coopShares.put(rec.getInteger("member_share_id"), shareTransaction); coopShares.put(rec.getInteger("member_share_id"), shareTransaction);
}); });
} }