add hs-office-coopshares entity+repository

This commit is contained in:
Michael Hoennig 2022-10-19 07:55:51 +02:00
parent 7376301ed4
commit c2dd3d8de9
6 changed files with 367 additions and 1 deletions

View File

@ -0,0 +1,73 @@
package net.hostsharing.hsadminng.hs.office.coopshares;
import com.vladmihalcea.hibernate.type.basic.PostgreSQLEnumType;
import lombok.*;
import net.hostsharing.hsadminng.Stringify;
import net.hostsharing.hsadminng.Stringifyable;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import javax.persistence.*;
import java.time.LocalDate;
import java.util.UUID;
import static net.hostsharing.hsadminng.Stringify.stringify;
@Entity
@Table(name = "hs_office_coopsharestransaction_rv")
@TypeDef(
name = "pgsql_enum",
typeClass = PostgreSQLEnumType.class
)
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("CoopShareTransaction")
public class HsOfficeCoopSharesTransactionEntity implements Stringifyable {
private static Stringify<HsOfficeCoopSharesTransactionEntity> stringify = stringify(HsOfficeCoopSharesTransactionEntity.class)
.withProp(e -> e.getMembership().getMemberNumber())
.withProp(HsOfficeCoopSharesTransactionEntity::getValueDate)
.withProp(HsOfficeCoopSharesTransactionEntity::getTransactionType)
.withProp(HsOfficeCoopSharesTransactionEntity::getShareCount)
.withProp(HsOfficeCoopSharesTransactionEntity::getReference)
.withSeparator(", ")
.quotedValues(false);
private @Id UUID uuid;
@ManyToOne
@JoinColumn(name = "membershipuuid")
private HsOfficeMembershipEntity membership;
@Column(name = "transactiontype")
@Enumerated(EnumType.STRING)
@Type( type = "pgsql_enum" )
private HsOfficeCoopSharesTransactionType transactionType;
@Column(name = "valuedate")
private LocalDate valueDate;
@Column(name = "sharecount")
private int shareCount;
@Column(name = "reference")
private String reference;
@Column(name = "comment")
private String comment;
@Override
public String toString() {
return stringify.apply(this);
}
@Override
public String toShortString() {
return "%s%+d".formatted(membership.getMemberNumber(), shareCount);
}
}

View File

@ -0,0 +1,28 @@
package net.hostsharing.hsadminng.hs.office.coopshares;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface HsOfficeCoopSharesTransactionRepository extends Repository<HsOfficeCoopSharesTransactionEntity, UUID> {
Optional<HsOfficeCoopSharesTransactionEntity> findByUuid(UUID id);
@Query("""
SELECT st FROM HsOfficeCoopSharesTransactionEntity st
WHERE (:memberNumber IS NULL OR st.membership.memberNumber = :memberNumber)
AND (:fromValueDate IS NULL OR (st.valueDate >= :fromValueDate))
AND (:toValueDate IS NULL OR (st.valueDate <= :toValueDate))
ORDER BY st.membership.memberNumber, st.valueDate
""")
List<HsOfficeCoopSharesTransactionEntity> findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(
Integer memberNumber, LocalDate fromValueDate, LocalDate toValueDate);
HsOfficeCoopSharesTransactionEntity save(final HsOfficeCoopSharesTransactionEntity entity);
long count();
}

View File

@ -0,0 +1,5 @@
package net.hostsharing.hsadminng.hs.office.coopshares;
public enum HsOfficeCoopSharesTransactionType {
ADJUSTMENT, SUBSCRIPTION, CANCELLATION;
}

View File

@ -91,7 +91,7 @@ public class ArchitectureTest {
public static final ArchRule HsOfficeMembershipPackageRule = classes()
.that().resideInAPackage("..hs.office.membership..")
.should().onlyBeAccessed().byClassesThat()
.resideInAnyPackage("..hs.office.membership..");
.resideInAnyPackage("..hs.office.membership..", "..hs.office.coopshares..");
@ArchTest
@SuppressWarnings("unused")

View File

@ -0,0 +1,33 @@
package net.hostsharing.hsadminng.hs.office.coopshares;
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
import static net.hostsharing.hsadminng.hs.office.membership.TestHsMembership.testMembership;
import static org.assertj.core.api.Assertions.assertThat;
class HsOfficeCoopSharesTransactionEntityTest {
final HsOfficeCoopSharesTransactionEntity givenSepaMandate = HsOfficeCoopSharesTransactionEntity.builder()
.membership(testMembership)
.reference("some-ref")
.valueDate(LocalDate.parse("2020-01-01"))
.transactionType(HsOfficeCoopSharesTransactionType.SUBSCRIPTION)
.shareCount(4)
.build();
@Test
void toStringContainsAlmostAllPropertiesAccount() {
final var result = givenSepaMandate.toString();
assertThat(result).isEqualTo("CoopShareTransaction(300001, 2020-01-01, SUBSCRIPTION, 4, some-ref)");
}
@Test
void toShortStringContainsOnlyMemberNumberAndSharesCountOnly() {
final var result = givenSepaMandate.toShortString();
assertThat(result).isEqualTo("300001+4");
}
}

View File

@ -0,0 +1,227 @@
package net.hostsharing.hsadminng.hs.office.coopshares;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.context.ContextBasedTest;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipRepository;
import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository;
import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository;
import net.hostsharing.test.Array;
import net.hostsharing.test.JpaAttempt;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.annotation.DirtiesContext;
import javax.persistence.EntityManager;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.grantDisplaysOf;
import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.roleNamesOf;
import static net.hostsharing.test.JpaAttempt.attempt;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
@ComponentScan(basePackageClasses = { HsOfficeCoopSharesTransactionRepository.class, Context.class, JpaAttempt.class })
@DirtiesContext
class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBasedTest {
@Autowired
HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo;
@Autowired
HsOfficeMembershipRepository membershipRepo;
@Autowired
RawRbacRoleRepository rawRoleRepo;
@Autowired
RawRbacGrantRepository rawGrantRepo;
@Autowired
EntityManager em;
@Autowired
JpaAttempt jpaAttempt;
@MockBean
HttpServletRequest request;
@Nested
class CreateCoopSharesTransaction {
@Test
public void globalAdmin_canCreateNewCoopShareTransaction() {
// given
context("superuser-alex@hostsharing.net");
final var count = coopSharesTransactionRepo.count();
final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10001)
.get(0);
// when
final var result = attempt(em, () -> {
final var newCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder()
.uuid(UUID.randomUUID())
.membership(givenMembership)
.transactionType(HsOfficeCoopSharesTransactionType.SUBSCRIPTION)
.shareCount(4)
.valueDate(LocalDate.parse("2022-10-18"))
.reference("temp ref A")
.build();
return coopSharesTransactionRepo.save(newCoopSharesTransaction);
});
// then
result.assertSuccessful();
assertThat(result.returnedValue()).isNotNull().extracting(HsOfficeCoopSharesTransactionEntity::getUuid).isNotNull();
assertThatCoopSharesTransactionIsPersisted(result.returnedValue());
assertThat(coopSharesTransactionRepo.count()).isEqualTo(count + 1);
}
@Test
public void createsAndGrantsRoles() {
// given
context("superuser-alex@hostsharing.net");
final var initialRoleNames = roleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll()).stream()
.map(s -> s.replace("FirstGmbH-firstcontact", "..."))
.map(s -> s.replace("hs_office_", ""))
.toList();
// when
attempt(em, () -> {
final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(
null,
10001).get(0);
final var newCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder()
.uuid(UUID.randomUUID())
.membership(givenMembership)
.transactionType(HsOfficeCoopSharesTransactionType.SUBSCRIPTION)
.shareCount(4)
.valueDate(LocalDate.parse("2022-10-18"))
.reference("temp ref B")
.build();
return coopSharesTransactionRepo.save(newCoopSharesTransaction);
});
// then
final var all = rawRoleRepo.findAll();
assertThat(roleNamesOf(all)).containsExactlyInAnyOrder(Array.from(initialRoleNames)); // no new roles created
assertThat(grantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("FirstGmbH-firstcontact", "..."))
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
"{ grant perm view on coopsharestransaction#temprefB to role membership#10001....tenant by system and assume }",
null));
}
private void assertThatCoopSharesTransactionIsPersisted(final HsOfficeCoopSharesTransactionEntity saved) {
final var found = coopSharesTransactionRepo.findByUuid(saved.getUuid());
assertThat(found).isNotEmpty().get().usingRecursiveComparison().isEqualTo(saved);
}
}
@Nested
class FindAllCoopSharesTransactions {
@Test
public void globalAdmin_withoutAssumedRole_canViewAllCoopSharesTransactions() {
// given
context("superuser-alex@hostsharing.net");
// when
final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(
null,
null,
null);
// then
allTheseCoopSharesTransactionsAreReturned(
result,
"CoopShareTransaction(10001, 2010-03-15, SUBSCRIPTION, 2, ref 10001-1)",
"CoopShareTransaction(10001, 2021-09-01, SUBSCRIPTION, 24, ref 10001-2)",
"CoopShareTransaction(10001, 2022-10-20, CANCELLATION, 12, ref 10001-3)",
"CoopShareTransaction(10002, 2010-03-15, SUBSCRIPTION, 2, ref 10002-1)",
"CoopShareTransaction(10002, 2021-09-01, SUBSCRIPTION, 24, ref 10002-2)",
"CoopShareTransaction(10002, 2022-10-20, CANCELLATION, 12, ref 10002-3)",
"CoopShareTransaction(10003, 2010-03-15, SUBSCRIPTION, 2, ref 10003-1)",
"CoopShareTransaction(10003, 2021-09-01, SUBSCRIPTION, 24, ref 10003-2)",
"CoopShareTransaction(10003, 2022-10-20, CANCELLATION, 12, ref 10003-3)");
}
@Test
public void normalUser_canViewOnlyRelatedCoopSharesTransactions() {
// given:
context("superuser-alex@hostsharing.net", "hs_office_partner#FirstGmbH-firstcontact.admin");
// "hs_office_person#FirstGmbH.admin",
// when:
final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(
null,
null,
null);
// then:
exactlyTheseCoopSharesTransactionsAreReturned(
result,
"CoopShareTransaction(10001, 2010-03-15, SUBSCRIPTION, 2, ref 10001-1)",
"CoopShareTransaction(10001, 2021-09-01, SUBSCRIPTION, 24, ref 10001-2)",
"CoopShareTransaction(10001, 2022-10-20, CANCELLATION, 12, ref 10001-3)");
}
}
@Test
public void auditJournalLogIsAvailable() {
// given
final var query = em.createNativeQuery("""
select c.currenttask, j.targettable, j.targetop
from tx_journal j
join tx_context c on j.contextId = c.contextId
where targettable = 'hs_office_coopsharestransaction';
""");
// when
@SuppressWarnings("unchecked") final List<Object[]> customerLogEntries = query.getResultList();
// then
assertThat(customerLogEntries).map(Arrays::toString).contains(
"[creating coopSharesTransaction test-data 10001, hs_office_coopsharestransaction, INSERT]",
"[creating coopSharesTransaction test-data 10002, hs_office_coopsharestransaction, INSERT]");
}
@BeforeEach
@AfterEach
void cleanup() {
jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", null);
em.createQuery("DELETE FROM HsOfficeCoopSharesTransactionEntity WHERE reference like 'temp ref%'");
});
}
void exactlyTheseCoopSharesTransactionsAreReturned(
final List<HsOfficeCoopSharesTransactionEntity> actualResult,
final String... coopSharesTransactionNames) {
assertThat(actualResult)
.extracting(coopSharesTransactionEntity -> coopSharesTransactionEntity.toString())
.containsExactlyInAnyOrder(coopSharesTransactionNames);
}
void allTheseCoopSharesTransactionsAreReturned(
final List<HsOfficeCoopSharesTransactionEntity> actualResult,
final String... coopSharesTransactionNames) {
assertThat(actualResult)
.extracting(coopSharesTransactionEntity -> coopSharesTransactionEntity.toString())
.contains(coopSharesTransactionNames);
}
}