Michael Hoennig
2022-10-19 c2dd3d8de9e9a6973d46c1ba2d8296c7d0894d0c
add hs-office-coopshares entity+repository
5 files added
1 files modified
368 ■■■■■ changed files
src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java 73 ●●●●● patch | view | raw | blame | history
src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepository.java 28 ●●●●● patch | view | raw | blame | history
src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java 5 ●●●●● patch | view | raw | blame | history
src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java 2 ●●● patch | view | raw | blame | history
src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityTest.java 33 ●●●●● patch | view | raw | blame | history
src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java 227 ●●●●● patch | view | raw | blame | history
src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
New 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);
    }
}
src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepository.java
New 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();
}
src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java
New file
@@ -0,0 +1,5 @@
package net.hostsharing.hsadminng.hs.office.coopshares;
public enum HsOfficeCoopSharesTransactionType {
    ADJUSTMENT, SUBSCRIPTION, CANCELLATION;
}
src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java
@@ -91,7 +91,7 @@
    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")
src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityTest.java
New 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");
    }
}
src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
New 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);
    }
}