implement coop-asset-TRANSFER-transaction reversal (#125)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/125 Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
+100
-43
@@ -27,11 +27,13 @@ import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.REVERSAL;
|
||||
import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.TRANSFER;
|
||||
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.CLEARING;
|
||||
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.DEPOSIT;
|
||||
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.DISBURSAL;
|
||||
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.LOSS;
|
||||
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.TRANSFER;
|
||||
import static net.hostsharing.hsadminng.lambda.WithNonNull.withNonNull;
|
||||
|
||||
@RestController
|
||||
public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAssetsApi {
|
||||
@@ -66,7 +68,10 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
fromValueDate,
|
||||
toValueDate);
|
||||
|
||||
final var resources = mapper.mapList(entities, HsOfficeCoopAssetsTransactionResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
final var resources = mapper.mapList(
|
||||
entities,
|
||||
HsOfficeCoopAssetsTransactionResource.class,
|
||||
ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
return ResponseEntity.ok(resources);
|
||||
}
|
||||
|
||||
@@ -106,7 +111,11 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
if (result.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
return ResponseEntity.ok(mapper.map(result.get(), HsOfficeCoopAssetsTransactionResource.class));
|
||||
final var resource = mapper.map(
|
||||
result.get(),
|
||||
HsOfficeCoopAssetsTransactionResource.class,
|
||||
ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
return ResponseEntity.ok(resource);
|
||||
|
||||
}
|
||||
|
||||
@@ -131,7 +140,8 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
private static void validateCreditTransaction(
|
||||
final HsOfficeCoopAssetsTransactionInsertResource requestBody,
|
||||
final ArrayList<String> violations) {
|
||||
if (List.of(DISBURSAL, TRANSFER, CLEARING, LOSS).contains(requestBody.getTransactionType())
|
||||
if (List.of(DISBURSAL, HsOfficeCoopAssetsTransactionTypeResource.TRANSFER, CLEARING, LOSS)
|
||||
.contains(requestBody.getTransactionType())
|
||||
&& requestBody.getAssetValue().signum() > 0) {
|
||||
violations.add("for %s, assetValue must be negative but is \"%.2f\"".formatted(
|
||||
requestBody.getTransactionType(), requestBody.getAssetValue()));
|
||||
@@ -147,57 +157,108 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
}
|
||||
}
|
||||
|
||||
// TODO.refa: this logic needs to get extracted to a service
|
||||
final BiConsumer<HsOfficeCoopAssetsTransactionEntity, HsOfficeCoopAssetsTransactionResource> ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
|
||||
resource.setMembershipUuid(entity.getMembership().getUuid());
|
||||
resource.setMembershipMemberNumber(entity.getMembership().getTaggedMemberNumber());
|
||||
|
||||
if (entity.getReversalAssetTx() != null) {
|
||||
resource.getReversalAssetTx().setRevertedAssetTxUuid(entity.getUuid());
|
||||
resource.getReversalAssetTx().setMembershipUuid(entity.getMembership().getUuid());
|
||||
resource.getReversalAssetTx().setMembershipMemberNumber(entity.getTaggedMemberNumber());
|
||||
}
|
||||
withNonNull(
|
||||
resource.getReversalAssetTx(), reversalAssetTxResource -> {
|
||||
reversalAssetTxResource.setMembershipUuid(entity.getMembership().getUuid());
|
||||
reversalAssetTxResource.setMembershipMemberNumber(entity.getTaggedMemberNumber());
|
||||
reversalAssetTxResource.setRevertedAssetTxUuid(entity.getUuid());
|
||||
withNonNull(
|
||||
entity.getAdoptionAssetTx(), adoptionAssetTx ->
|
||||
reversalAssetTxResource.setAdoptionAssetTxUuid(adoptionAssetTx.getUuid()));
|
||||
withNonNull(
|
||||
entity.getTransferAssetTx(), transferAssetTxResource ->
|
||||
reversalAssetTxResource.setTransferAssetTxUuid(transferAssetTxResource.getUuid()));
|
||||
});
|
||||
|
||||
if (entity.getRevertedAssetTx() != null) {
|
||||
resource.getRevertedAssetTx().setReversalAssetTxUuid(entity.getUuid());
|
||||
resource.getRevertedAssetTx().setMembershipUuid(entity.getMembership().getUuid());
|
||||
resource.getRevertedAssetTx().setMembershipMemberNumber(entity.getTaggedMemberNumber());
|
||||
}
|
||||
withNonNull(
|
||||
resource.getRevertedAssetTx(), revertAssetTxResource -> {
|
||||
revertAssetTxResource.setMembershipUuid(entity.getMembership().getUuid());
|
||||
revertAssetTxResource.setMembershipMemberNumber(entity.getTaggedMemberNumber());
|
||||
revertAssetTxResource.setReversalAssetTxUuid(entity.getUuid());
|
||||
withNonNull(
|
||||
entity.getRevertedAssetTx().getAdoptionAssetTx(), adoptionAssetTx ->
|
||||
revertAssetTxResource.setAdoptionAssetTxUuid(adoptionAssetTx.getUuid()));
|
||||
withNonNull(
|
||||
entity.getRevertedAssetTx().getTransferAssetTx(), transferAssetTxResource ->
|
||||
revertAssetTxResource.setTransferAssetTxUuid(transferAssetTxResource.getUuid()));
|
||||
});
|
||||
|
||||
if (entity.getAdoptionAssetTx() != null) {
|
||||
resource.getAdoptionAssetTx().setTransferAssetTxUuid(entity.getUuid());
|
||||
resource.getAdoptionAssetTx().setMembershipUuid(entity.getAdoptionAssetTx().getMembership().getUuid());
|
||||
resource.getAdoptionAssetTx().setMembershipMemberNumber(entity.getAdoptionAssetTx().getTaggedMemberNumber());
|
||||
}
|
||||
withNonNull(
|
||||
resource.getAdoptionAssetTx(), adoptionAssetTxResource -> {
|
||||
adoptionAssetTxResource.setMembershipUuid(entity.getAdoptionAssetTx().getMembership().getUuid());
|
||||
adoptionAssetTxResource.setMembershipMemberNumber(entity.getAdoptionAssetTx().getTaggedMemberNumber());
|
||||
adoptionAssetTxResource.setTransferAssetTxUuid(entity.getUuid());
|
||||
withNonNull(
|
||||
entity.getAdoptionAssetTx().getReversalAssetTx(), reversalAssetTx ->
|
||||
adoptionAssetTxResource.setReversalAssetTxUuid(reversalAssetTx.getUuid()));
|
||||
});
|
||||
|
||||
if (entity.getTransferAssetTx() != null) {
|
||||
resource.getTransferAssetTx().setAdoptionAssetTxUuid(entity.getUuid());
|
||||
resource.getTransferAssetTx().setMembershipUuid(entity.getTransferAssetTx().getMembership().getUuid());
|
||||
resource.getTransferAssetTx().setMembershipMemberNumber(entity.getTransferAssetTx().getTaggedMemberNumber());
|
||||
}
|
||||
withNonNull(
|
||||
resource.getTransferAssetTx(), transferAssetTxResource -> {
|
||||
resource.getTransferAssetTx().setMembershipUuid(entity.getTransferAssetTx().getMembership().getUuid());
|
||||
resource.getTransferAssetTx()
|
||||
.setMembershipMemberNumber(entity.getTransferAssetTx().getTaggedMemberNumber());
|
||||
resource.getTransferAssetTx().setAdoptionAssetTxUuid(entity.getUuid());
|
||||
withNonNull(
|
||||
entity.getTransferAssetTx().getReversalAssetTx(), reversalAssetTx ->
|
||||
transferAssetTxResource.setReversalAssetTxUuid(reversalAssetTx.getUuid()));
|
||||
});
|
||||
};
|
||||
|
||||
// TODO.refa: this logic needs to get extracted to a service
|
||||
final BiConsumer<HsOfficeCoopAssetsTransactionInsertResource, HsOfficeCoopAssetsTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||
|
||||
if (resource.getMembershipUuid() != null) {
|
||||
final HsOfficeMembershipEntity membership = ofNullable(emw.find(HsOfficeMembershipEntity.class, resource.getMembershipUuid()))
|
||||
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] membership.uuid %s not found".formatted(
|
||||
final HsOfficeMembershipEntity membership = ofNullable(emw.find(
|
||||
HsOfficeMembershipEntity.class,
|
||||
resource.getMembershipUuid()))
|
||||
.orElseThrow(() -> new EntityNotFoundException("membership.uuid %s not found".formatted(
|
||||
resource.getMembershipUuid())));
|
||||
entity.setMembership(membership);
|
||||
}
|
||||
if (resource.getRevertedAssetTxUuid() != null) {
|
||||
|
||||
if (entity.getTransactionType() == REVERSAL) {
|
||||
if (resource.getRevertedAssetTxUuid() == null) {
|
||||
throw new ValidationException("REVERSAL asset transaction requires revertedAssetTx.uuid");
|
||||
}
|
||||
final var revertedAssetTx = coopAssetsTransactionRepo.findByUuid(resource.getRevertedAssetTxUuid())
|
||||
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] revertedEntityUuid %s not found".formatted(
|
||||
.orElseThrow(() -> new EntityNotFoundException("revertedAssetTx.uuid %s not found".formatted(
|
||||
resource.getRevertedAssetTxUuid())));
|
||||
revertedAssetTx.setReversalAssetTx(entity);
|
||||
entity.setRevertedAssetTx(revertedAssetTx);
|
||||
if (resource.getAssetValue().negate().compareTo(revertedAssetTx.getAssetValue()) != 0) {
|
||||
throw new ValidationException("given assetValue=" + resource.getAssetValue() +
|
||||
" but must be negative value from reverted asset tx: " + revertedAssetTx.getAssetValue());
|
||||
}
|
||||
|
||||
if (revertedAssetTx.getTransactionType() == TRANSFER) {
|
||||
final var adoptionAssetTx = revertedAssetTx.getAdoptionAssetTx();
|
||||
final var adoptionReversalAssetTx = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.transactionType(REVERSAL)
|
||||
.membership(adoptionAssetTx.getMembership())
|
||||
.revertedAssetTx(adoptionAssetTx)
|
||||
.assetValue(adoptionAssetTx.getAssetValue().negate())
|
||||
.comment(resource.getComment())
|
||||
.reference(resource.getReference())
|
||||
.valueDate(resource.getValueDate())
|
||||
.build();
|
||||
adoptionAssetTx.setReversalAssetTx(adoptionReversalAssetTx);
|
||||
adoptionReversalAssetTx.setRevertedAssetTx(adoptionAssetTx);
|
||||
}
|
||||
}
|
||||
|
||||
final var adoptingMembership = determineAdoptingMembership(resource);
|
||||
if (adoptingMembership != null) {
|
||||
final var adoptingAssetTx = coopAssetsTransactionRepo.save(createAdoptingAssetTx(entity, adoptingMembership));
|
||||
if (resource.getTransactionType() == HsOfficeCoopAssetsTransactionTypeResource.TRANSFER) {
|
||||
final var adoptingMembership = determineAdoptingMembership(resource);
|
||||
if ( entity.getMembership() == adoptingMembership) {
|
||||
throw new ValidationException("transferring and adopting membership must be different, but both are " +
|
||||
adoptingMembership.getTaggedMemberNumber());
|
||||
}
|
||||
final var adoptingAssetTx = createAdoptingAssetTx(entity, adoptingMembership);
|
||||
entity.setAdoptionAssetTx(adoptingAssetTx);
|
||||
}
|
||||
};
|
||||
@@ -206,11 +267,11 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
final var adoptingMembershipUuid = resource.getAdoptingMembershipUuid();
|
||||
final var adoptingMembershipMemberNumber = resource.getAdoptingMembershipMemberNumber();
|
||||
if (adoptingMembershipUuid != null && adoptingMembershipMemberNumber != null) {
|
||||
throw new IllegalArgumentException(
|
||||
// @formatter:off
|
||||
resource.getTransactionType() == TRANSFER
|
||||
? "[400] either adoptingMembership.uuid or adoptingMembership.memberNumber can be given, not both"
|
||||
: "[400] adoptingMembership.uuid and adoptingMembership.memberNumber must not be given for transactionType="
|
||||
throw new ValidationException(
|
||||
// @formatter:off
|
||||
resource.getTransactionType() == HsOfficeCoopAssetsTransactionTypeResource.TRANSFER
|
||||
? "either adoptingMembership.uuid or adoptingMembership.memberNumber can be given, not both"
|
||||
: "adoptingMembership.uuid and adoptingMembership.memberNumber must not be given for transactionType="
|
||||
+ resource.getTransactionType());
|
||||
// @formatter:on
|
||||
}
|
||||
@@ -232,13 +293,9 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
+ "' not found or not accessible");
|
||||
}
|
||||
|
||||
if (resource.getTransactionType() == TRANSFER) {
|
||||
throw new ValidationException(
|
||||
"either adoptingMembership.uuid or adoptingMembership.memberNumber must be given for transactionType="
|
||||
+ TRANSFER);
|
||||
}
|
||||
|
||||
return null;
|
||||
throw new ValidationException(
|
||||
"either adoptingMembership.uuid or adoptingMembership.memberNumber must be given for transactionType="
|
||||
+ HsOfficeCoopAssetsTransactionTypeResource.TRANSFER);
|
||||
}
|
||||
|
||||
private HsOfficeCoopAssetsTransactionEntity createAdoptingAssetTx(
|
||||
|
||||
+4
-4
@@ -98,21 +98,21 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
|
||||
private String comment;
|
||||
|
||||
// Optionally, the UUID of the corresponding transaction for a reversal transaction.
|
||||
@OneToOne(cascade = CascadeType.PERSIST) // TODO.impl: can probably be removed after office data migration
|
||||
@OneToOne
|
||||
@JoinColumn(name = "revertedassettxuuid")
|
||||
private HsOfficeCoopAssetsTransactionEntity revertedAssetTx;
|
||||
|
||||
// and the other way around
|
||||
@OneToOne(mappedBy = "revertedAssetTx")
|
||||
@OneToOne(mappedBy = "revertedAssetTx", cascade = CascadeType.PERSIST)
|
||||
private HsOfficeCoopAssetsTransactionEntity reversalAssetTx;
|
||||
|
||||
// Optionally, the UUID of the corresponding transaction for a transfer transaction.
|
||||
@OneToOne(cascade = CascadeType.PERSIST) // TODO.impl: can probably be removed after office data migration
|
||||
@OneToOne(cascade = CascadeType.PERSIST)
|
||||
@JoinColumn(name = "assetadoptiontxuuid")
|
||||
private HsOfficeCoopAssetsTransactionEntity adoptionAssetTx;
|
||||
|
||||
// and the other way around
|
||||
@OneToOne(mappedBy = "adoptionAssetTx")
|
||||
@OneToOne(mappedBy = "adoptionAssetTx", cascade = CascadeType.PERSIST)
|
||||
private HsOfficeCoopAssetsTransactionEntity transferAssetTx;
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.hostsharing.hsadminng.lambda;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class WithNonNull {
|
||||
public static <T> void withNonNull(final T target, final Consumer<T> code) {
|
||||
if (target != null ) {
|
||||
code.accept(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user